mirror of
				https://github.com/caddyserver/caddy.git
				synced 2025-10-31 10:37:24 -04:00 
			
		
		
		
	
		
			
				
	
	
		
			90 lines
		
	
	
		
			2.4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			90 lines
		
	
	
		
			2.4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| 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
 | |
| }
 |