mirror of
https://github.com/caddyserver/caddy.git
synced 2025-01-23 00:56:45 +01:00
Filled out more web socket stuff
This commit is contained in:
parent
411f3256cc
commit
37f0a37ed2
2 changed files with 94 additions and 21 deletions
|
@ -1,16 +1,19 @@
|
||||||
package websockets
|
package websockets
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"golang.org/x/net/websocket"
|
"golang.org/x/net/websocket"
|
||||||
)
|
)
|
||||||
|
|
||||||
// WebSocket represents a web socket server configuration.
|
// WebSocket represents a web socket server instance. A WebSocket
|
||||||
|
// struct is instantiated for each new websocket request.
|
||||||
type WebSocket struct {
|
type WebSocket struct {
|
||||||
Path string
|
WSConfig
|
||||||
Command string
|
*http.Request
|
||||||
Arguments []string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle handles a WebSocket connection. It launches the
|
// Handle handles a WebSocket connection. It launches the
|
||||||
|
@ -21,12 +24,67 @@ func (ws WebSocket) Handle(conn *websocket.Conn) {
|
||||||
cmd.Stdin = conn
|
cmd.Stdin = conn
|
||||||
cmd.Stdout = conn
|
cmd.Stdout = conn
|
||||||
|
|
||||||
// TODO: Set environment variables according to CGI 1.1
|
err := ws.buildEnv(cmd)
|
||||||
// cf. http://tools.ietf.org/html/rfc3875#section-4.1.4
|
if err != nil {
|
||||||
cmd.Env = append(cmd.Env, `GATEWAY_INTERFACE="caddy-CGI/1.1"`)
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
err := cmd.Run()
|
err = cmd.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// buildEnv sets the meta-variables for the child process according
|
||||||
|
// to the CGI 1.1 specification: http://tools.ietf.org/html/rfc3875#section-4.1
|
||||||
|
func (ws WebSocket) buildEnv(cmd *exec.Cmd) error {
|
||||||
|
remoteHost, remotePort, err := net.SplitHostPort(ws.RemoteAddr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
serverHost, serverPort, err := net.SplitHostPort(ws.Host)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.Env = []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=`, // TODO - absolute path to 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)
|
||||||
|
cmd.Env = append(cmd.Env, "HTTP_"+header+"="+value)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
// See CGI spec, 4.1.4
|
||||||
|
gatewayInterface = "caddy-CGI/1.1"
|
||||||
|
|
||||||
|
// See CGI spec, 4.1.17
|
||||||
|
serverSoftware = "caddy/0.1.0"
|
||||||
|
)
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
package websockets
|
package websockets
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
"errors"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/flynn/go-shlex"
|
"github.com/flynn/go-shlex"
|
||||||
|
@ -12,16 +12,31 @@ import (
|
||||||
"golang.org/x/net/websocket"
|
"golang.org/x/net/websocket"
|
||||||
)
|
)
|
||||||
|
|
||||||
// WebSockets is a type which holds configuration
|
type (
|
||||||
// for the websocket middleware collectively.
|
// WebSockets is a type that holds configuration for the
|
||||||
type WebSockets struct {
|
// websocket middleware generally, like a list of all the
|
||||||
Sockets []WebSocket
|
// websocket endpoints.
|
||||||
}
|
WebSockets struct {
|
||||||
|
Sockets []WSConfig
|
||||||
|
}
|
||||||
|
|
||||||
// ServeHTTP more or less converts the HTTP request to a WebSocket connection.
|
// WSConfig holds the configuration for a single websocket
|
||||||
|
// endpoint which may serve zero or more websocket connections.
|
||||||
|
WSConfig struct {
|
||||||
|
Path string
|
||||||
|
Command string
|
||||||
|
Arguments []string
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// ServeHTTP converts the HTTP request to a WebSocket connection and serves it up.
|
||||||
func (ws WebSockets) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
func (ws WebSockets) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
for _, socket := range ws.Sockets {
|
for _, sockconfig := range ws.Sockets {
|
||||||
if middleware.Path(r.URL.Path).Matches(socket.Path) {
|
if middleware.Path(r.URL.Path).Matches(sockconfig.Path) {
|
||||||
|
socket := WebSocket{
|
||||||
|
WSConfig: sockconfig,
|
||||||
|
Request: r,
|
||||||
|
}
|
||||||
websocket.Handler(socket.Handle).ServeHTTP(w, r)
|
websocket.Handler(socket.Handle).ServeHTTP(w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -30,7 +45,7 @@ func (ws WebSockets) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
// New constructs and configures a new websockets middleware instance.
|
// New constructs and configures a new websockets middleware instance.
|
||||||
func New(c middleware.Controller) (middleware.Middleware, error) {
|
func New(c middleware.Controller) (middleware.Middleware, error) {
|
||||||
var websocks []WebSocket
|
var websocks []WSConfig
|
||||||
|
|
||||||
var path string
|
var path string
|
||||||
var command string
|
var command string
|
||||||
|
@ -62,9 +77,9 @@ func New(c middleware.Controller) (middleware.Middleware, error) {
|
||||||
|
|
||||||
parts, err := shlex.Split(command)
|
parts, err := shlex.Split(command)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("Error parsing command for websocket use: " + err.Error())
|
return nil, errors.New("Error parsing command for websocket use: " + err.Error())
|
||||||
} else if len(parts) == 0 {
|
} else if len(parts) == 0 {
|
||||||
log.Fatal("No command found for use by websocket.")
|
return nil, errors.New("No command found for use by websocket")
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd = parts[0]
|
cmd = parts[0]
|
||||||
|
@ -72,7 +87,7 @@ func New(c middleware.Controller) (middleware.Middleware, error) {
|
||||||
args = parts[1:]
|
args = parts[1:]
|
||||||
}
|
}
|
||||||
|
|
||||||
websocks = append(websocks, WebSocket{
|
websocks = append(websocks, WSConfig{
|
||||||
Path: path,
|
Path: path,
|
||||||
Command: cmd,
|
Command: cmd,
|
||||||
Arguments: args,
|
Arguments: args,
|
||||||
|
|
Loading…
Reference in a new issue