package websockets import ( "net" "net/http" "os/exec" "strings" "golang.org/x/net/websocket" ) // WebSocket represents a web socket server instance. A WebSocket // is instantiated for each new websocket request/connection. type WebSocket struct { WSConfig *http.Request } // Handle handles a WebSocket connection. It launches the // specified command and streams input and output through // the command's stdin and stdout. func (ws WebSocket) Handle(conn *websocket.Conn) { cmd := exec.Command(ws.Command, ws.Arguments...) cmd.Stdin = conn cmd.Stdout = conn cmd.Stderr = conn // TODO: Make this configurable from the Caddyfile metavars, err := ws.buildEnv(cmd.Path) if err != nil { panic(err) // TODO } cmd.Env = metavars err = cmd.Run() if err != nil { panic(err) } } // buildEnv creates the meta-variables for the child process according // to the CGI 1.1 specification: http://tools.ietf.org/html/rfc3875#section-4.1 // cmdPath should be the path of the command being run. // The returned string slice can be set to the command's Env property. func (ws WebSocket) buildEnv(cmdPath string) (metavars []string, err error) { remoteHost, remotePort, err := net.SplitHostPort(ws.RemoteAddr) if err != nil { return } serverHost, serverPort, err := net.SplitHostPort(ws.Host) if err != nil { return } metavars = []string{ `AUTH_TYPE=`, // Not used `CONTENT_LENGTH=`, // Not used `CONTENT_TYPE=`, // Not used `GATEWAY_INTERFACE=` + GatewayInterface, `PATH_INFO=`, // TODO `PATH_TRANSLATED=`, // TODO `QUERY_STRING=` + ws.URL.RawQuery, `REMOTE_ADDR=` + remoteHost, `REMOTE_HOST=` + remoteHost, // TODO (Host lookups are slow; make this configurable) `REMOTE_IDENT=`, // Not used `REMOTE_PORT=` + remotePort, `REMOTE_USER=`, // Not used, `REQUEST_METHOD=` + ws.Method, `REQUEST_URI=` + ws.RequestURI, `SCRIPT_NAME=` + cmdPath, // path of the program being executed `SERVER_NAME=` + serverHost, `SERVER_PORT=` + serverPort, `SERVER_PROTOCOL=` + ws.Proto, `SERVER_SOFTWARE=` + ServerSoftware, } // Add each HTTP header to the environment as well for header, values := range ws.Header { value := strings.Join(values, ", ") header = strings.ToUpper(header) header = strings.Replace(header, "-", "_", -1) value = strings.Replace(value, "\n", " ", -1) metavars = append(metavars, "HTTP_"+header+"="+value) } return }