Skip to content

Commit

Permalink
Merge between maxforks and passenv feature implementations
Browse files Browse the repository at this point in the history
  • Loading branch information
Alex Sergeyev committed Mar 14, 2014
2 parents 4dde622 + 1ad2d00 commit 8b4acba
Show file tree
Hide file tree
Showing 6 changed files with 67 additions and 16 deletions.
37 changes: 34 additions & 3 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import (
"fmt"
"os"
"path/filepath"
"runtime"
"strings"
"time"

"github.com/joewalnes/websocketd/libwebsocketd"
Expand All @@ -35,6 +37,18 @@ func (al *AddrList) Set(value string) error {
return nil
}

// Borrowed from net/http/cgi
var defaultPassEnv = map[string]string{
"darwin": "DYLD_LIBRARY_PATH",
"freebsd": "LD_LIBRARY_PATH",
"hpux": "LD_LIBRARY_PATH,SHLIB_PATH",
"irix": "LD_LIBRARY_PATH,LD_LIBRARYN32_PATH,LD_LIBRARY64_PATH",
"linux": "LD_LIBRARY_PATH",
"openbsd": "LD_LIBRARY_PATH",
"solaris": "LD_LIBRARY_PATH,LD_LIBRARY_PATH_32,LD_LIBRARY_PATH_64",
"windows": "SystemRoot,COMSPEC,PATHEXT,WINDIR",
}

func parseCommandLine() Config {
var mainConfig Config
var config libwebsocketd.Config
Expand All @@ -50,6 +64,9 @@ func parseCommandLine() Config {
versionFlag := flag.Bool("version", false, "Print version and exit")
licenseFlag := flag.Bool("license", false, "Print license and exit")
logLevelFlag := flag.String("loglevel", "access", "Log level, one of: debug, trace, access, info, error, fatal")
sslFlag := flag.Bool("ssl", false, "Use TLS on listening socket (see also --sslcert and --sslkey)")
sslCert := flag.String("sslcert", "", "Should point to certificate PEM file when --ssl is used")
sslKey := flag.String("sslkey", "", "Should point to certificate private key file when --ssl is used")
maxForksFlag := flag.Int("maxforks", 0, "Max forks, zero means unlimited")

// lib config options
Expand All @@ -59,9 +76,7 @@ func parseCommandLine() Config {
staticDirFlag := flag.String("staticdir", "", "Serve static content from this directory over HTTP")
cgiDirFlag := flag.String("cgidir", "", "Serve CGI scripts from this directory over HTTP")
devConsoleFlag := flag.Bool("devconsole", false, "Enable development console (cannot be used in conjunction with --staticdir)")
sslFlag := flag.Bool("ssl", false, "Use TLS on listening socket (see also --sslcert and --sslkey)")
sslCert := flag.String("sslcert", "", "Should point to certificate PEM file when --ssl is used")
sslKey := flag.String("sslkey", "", "Should point to certificate private key file when --ssl is used")
passEnvFlag := flag.String("passenv", defaultPassEnv[runtime.GOOS], "List of envvars to pass to subprocesses (others will be cleaned out)")

flag.Parse()

Expand Down Expand Up @@ -150,6 +165,22 @@ func parseCommandLine() Config {
mainConfig.CertFile = *sslCert
mainConfig.KeyFile = *sslKey

// Building config.ParentEnv to avoid calling Environ all the time in the scripts
// (caller is responsible for wiping environment if desired)
config.ParentEnv = make([]string, 0)
newlineCleaner := strings.NewReplacer("\n", " ", "\r", " ")
for _, key := range strings.Split(*passEnvFlag, ",") {
if key != "HTTPS" {
if v := os.Getenv(key); v != "" {
// inevitably adding flavor of libwebsocketd appendEnv func.
// it's slightly nicer than in net/http/cgi implementation
if clean := strings.TrimSpace(newlineCleaner.Replace(v)); clean != "" {
config.ParentEnv = append(config.ParentEnv, fmt.Sprintf("%s=%s", key, clean))
}
}
}
}

args := flag.Args()
if len(args) < 1 && config.ScriptDir == "" && config.StaticDir == "" && config.CgiDir == "" {
fmt.Fprintf(os.Stderr, "Please specify COMMAND or provide --dir, --staticdir or --cgidir argument.\n")
Expand Down
2 changes: 2 additions & 0 deletions help.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ Options:
--sslcert=FILE All three options must be used together or all of
--sslkey=FILE them should be omitted.
--passenv VAR[,VAR...] Lists environment variables allowed to be passed
to executed scripts.
--basepath=PATH Base path in URLs to serve from.
Default: / (root of domain)
Expand Down
1 change: 1 addition & 0 deletions libwebsocketd/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,5 @@ type Config struct {
DevConsole bool // Enable dev console. This disables StaticDir and CgiDir.
ServerSoftware string // Value to pass to SERVER_SOFTWARE environment variable (e.g. websocketd/1.2.3).
Env []string // Additional environment variables to pass to process ("key=value").
ParentEnv []string // Variables kept from os.Environ() before sanitizing it for subprocess.
}
25 changes: 16 additions & 9 deletions libwebsocketd/env.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import (
"fmt"
"net"
"net/http"
"os"
"strconv"
"strings"
"time"
Expand Down Expand Up @@ -78,9 +77,14 @@ func createEnv(req *http.Request, config *Config, urlInfo *URLInfo, id string, l
standardEnvCount += 1
}

parentEnv := os.Environ()
env := make([]string, 0, len(headers)+standardEnvCount+len(parentEnv)+len(config.Env))
for _, v := range parentEnv {
parentLen := len(config.ParentEnv)
env := make([]string, 0, len(headers)+standardEnvCount+parentLen+len(config.Env))

// This variable could be rewritten from outside
env = appendEnv(env, "SERVER_SOFTWARE", config.ServerSoftware)

parentStarts := len(env)
for _, v := range config.ParentEnv {
env = append(env, v)
}

Expand All @@ -93,7 +97,6 @@ func createEnv(req *http.Request, config *Config, urlInfo *URLInfo, id string, l
env = appendEnv(env, "SERVER_NAME", serverName)
env = appendEnv(env, "SERVER_PORT", serverPort)
env = appendEnv(env, "SERVER_PROTOCOL", req.Proto)
env = appendEnv(env, "SERVER_SOFTWARE", config.ServerSoftware)
env = appendEnv(env, "GATEWAY_INTERFACE", gatewayInterface)
env = appendEnv(env, "REQUEST_METHOD", req.Method)
env = appendEnv(env, "SCRIPT_NAME", urlInfo.ScriptPath)
Expand Down Expand Up @@ -130,20 +133,24 @@ func createEnv(req *http.Request, config *Config, urlInfo *URLInfo, id string, l
}

if log.MinLevel == LogDebug {
for k, v := range env {
log.Debug("env", "Std. variable: %s %v", k, v)
for i, v := range env {
if i >= parentStarts && i < parentLen+parentStarts {
log.Debug("env", "Parent envvar: %v", v)
} else {
log.Debug("env", "Std. variable: %v", v)
}
}
}

for k, hdrs := range headers {
header := fmt.Sprintf("HTTP_%s", headerDashToUnderscore.Replace(k))
env = appendEnv(env, header, hdrs...)
log.Debug("env", "Header variable %s=%v", header, hdrs)
log.Debug("env", "Header variable %s", env[len(env)-1])
}

for _, v := range config.Env {
env = append(env, v)
log.Debug("env", "External variable: %s %v", env, v)
log.Debug("env", "External variable: %s", v)
}

return env, nil
Expand Down
17 changes: 13 additions & 4 deletions libwebsocketd/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,11 @@ func (h *HttpWsMuxHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {

hdrs := req.Header

schema := "http"
wsschema, httpschema := "ws", "http"
if h.Config.Ssl {
schema = "https"
wsschema, httpschema = "wss", "https"
}
log.Associate("url", fmt.Sprintf("%s://%s%s", schema, req.Host, req.URL.RequestURI()))
log.Associate("url", fmt.Sprintf("%s://%s%s", httpschema, req.Host, req.URL.RequestURI()))

_, remoteHost, _, err := remoteDetails(req, h.Config)
if err != nil {
Expand Down Expand Up @@ -90,7 +90,7 @@ func (h *HttpWsMuxHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
if h.Config.DevConsole {
content := ConsoleContent
content = strings.Replace(content, "{{license}}", License, -1)
content = strings.Replace(content, "{{addr}}", fmt.Sprintf("%s://%s", schema, req.Host), -1)
content = strings.Replace(content, "{{addr}}", fmt.Sprintf("%s://%s%s", wsschema, req.Host, req.RequestURI), -1)
http.ServeContent(w, req, ".html", h.Config.StartupTime, strings.NewReader(content))
return
}
Expand All @@ -99,9 +99,18 @@ func (h *HttpWsMuxHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
if h.Config.CgiDir != "" {
filePath := path.Join(h.Config.CgiDir, fmt.Sprintf(".%s", filepath.FromSlash(req.URL.Path)))
if fi, err := os.Stat(filePath); err == nil && !fi.IsDir() {

log.Associate("cgiscript", filePath)
if h.noteForkCreated() == nil {
defer h.noteForkCompled()

// Make variables to supplement cgi... Environ it uses will show empty list.
envlen := len(h.Config.ParentEnv)
cgienv := make([]string, envlen+1)
if envlen > 0 {
copy(cgienv, h.Config.ParentEnv)
}
cgienv[envlen] = "SERVER_SOFTWARE=" + h.Config.ServerSoftware
cgiHandler := &cgi.Handler{
Path: filePath,
Env: []string{
Expand Down
1 change: 1 addition & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ func main() {
}
}

os.Clearenv() // it's ok to wipe it clean, we already read env variables from passenv into config
http.Handle(config.BasePath, libwebsocketd.NewHandler(config.Config, log, config.MaxForks))

if config.UsingScriptDir {
Expand Down

0 comments on commit 8b4acba

Please sign in to comment.