caddy/middleware/websockets/websocket.go

90 lines
2.4 KiB
Go
Raw Normal View History

2015-03-04 00:49:45 +08:00
package websockets
import (
2015-03-04 08:36:18 +08:00
"net"
"net/http"
2015-03-04 00:49:45 +08:00
"os/exec"
2015-03-04 08:36:18 +08:00
"strings"
2015-03-04 00:49:45 +08:00
"golang.org/x/net/websocket"
)
2015-03-04 08:36:18 +08:00
// WebSocket represents a web socket server instance. A WebSocket
2015-03-20 13:52:56 +08:00
// is instantiated for each new websocket request/connection.
2015-03-04 00:49:45 +08:00
type WebSocket struct {
2015-03-04 08:36:18 +08:00
WSConfig
*http.Request
2015-03-04 00:49:45 +08:00
}
// 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...)
2015-03-20 13:52:56 +08:00
2015-03-04 00:49:45 +08:00
cmd.Stdin = conn
cmd.Stdout = conn
2015-03-20 13:52:56 +08:00
cmd.Stderr = conn // TODO: Make this configurable from the Caddyfile
2015-03-04 00:49:45 +08:00
2015-03-20 13:52:56 +08:00
metavars, err := ws.buildEnv(cmd.Path)
2015-03-04 08:36:18 +08:00
if err != nil {
2015-03-20 13:52:56 +08:00
panic(err) // TODO
2015-03-04 08:36:18 +08:00
}
2015-03-04 00:49:45 +08:00
2015-03-20 13:52:56 +08:00
cmd.Env = metavars
2015-03-04 08:36:18 +08:00
err = cmd.Run()
2015-03-04 00:49:45 +08:00
if err != nil {
panic(err)
}
}
2015-03-04 08:36:18 +08:00
2015-03-20 13:52:56 +08:00
// buildEnv creates the meta-variables for the child process according
2015-03-04 08:36:18 +08:00
// to the CGI 1.1 specification: http://tools.ietf.org/html/rfc3875#section-4.1
2015-03-20 13:52:56 +08:00
// 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) {
2015-03-04 08:36:18 +08:00
remoteHost, remotePort, err := net.SplitHostPort(ws.RemoteAddr)
if err != nil {
2015-03-20 13:52:56 +08:00
return
2015-03-04 08:36:18 +08:00
}
2015-03-20 13:52:56 +08:00
2015-03-04 08:36:18 +08:00
serverHost, serverPort, err := net.SplitHostPort(ws.Host)
if err != nil {
2015-03-20 13:52:56 +08:00
return
2015-03-04 08:36:18 +08:00
}
2015-03-20 13:52:56 +08:00
metavars = []string{
2015-03-04 08:36:18 +08:00
`AUTH_TYPE=`, // Not used
`CONTENT_LENGTH=`, // Not used
`CONTENT_TYPE=`, // Not used
`GATEWAY_INTERFACE=` + GatewayInterface,
2015-03-04 08:36:18 +08:00
`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,
2015-03-20 13:52:56 +08:00
`SCRIPT_NAME=` + cmdPath, // path of the program being executed
2015-03-04 08:36:18 +08:00
`SERVER_NAME=` + serverHost,
`SERVER_PORT=` + serverPort,
`SERVER_PROTOCOL=` + ws.Proto,
`SERVER_SOFTWARE=` + ServerSoftware,
2015-03-04 08:36:18 +08:00
}
// 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)
2015-03-20 13:52:56 +08:00
metavars = append(metavars, "HTTP_"+header+"="+value)
2015-03-04 08:36:18 +08:00
}
2015-03-20 13:52:56 +08:00
return
2015-03-04 08:36:18 +08:00
}