-
Notifications
You must be signed in to change notification settings - Fork 1k
/
http.go
132 lines (111 loc) · 3.51 KB
/
http.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
// Copyright 2013 Joe Walnes and the websocketd team.
// All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package libwebsocketd
import (
"fmt"
"net/http"
"strconv"
"strings"
"code.google.com/p/go.net/websocket"
)
type HttpWsMuxHandler struct {
Config *Config
Log *LogScope
}
// Main HTTP handler. Muxes between WebSocket handler, DevConsole or 404.
func (h HttpWsMuxHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
hdrs := req.Header
if strings.ToLower(hdrs.Get("Upgrade")) == "websocket" && strings.ToLower(hdrs.Get("Connection")) == "upgrade" {
// WebSocket
if hdrs.Get("Origin") == "null" {
// Fix up mismatch between how Chrome reports Origin
// when using file:// url (using the string "null"), and
// how the WebSocket library expects to see it.
hdrs.Set("Origin", "file:")
}
wsHandler := websocket.Handler(func(ws *websocket.Conn) {
acceptWebSocket(ws, h.Config, h.Log)
})
wsHandler.ServeHTTP(w, req)
} else if h.Config.DevConsole {
// Dev console (if enabled)
content := strings.Replace(ConsoleContent, "{{license}}", License, -1)
http.ServeContent(w, req, ".html", h.Config.StartupTime, strings.NewReader(content))
} else {
// 404
http.NotFound(w, req)
}
}
func acceptWebSocket(ws *websocket.Conn, config *Config, log *LogScope) {
defer ws.Close()
req := ws.Request()
id := generateId()
_, remoteHost, _, err := remoteDetails(ws, config)
if err != nil {
log.Error("session", "Could not understand remote address '%s': %s", req.RemoteAddr, err)
return
}
log = log.NewLevel(log.LogFunc)
log.Associate("id", id)
log.Associate("url", fmt.Sprintf("http://%s%s", req.RemoteAddr, req.URL.RequestURI()))
log.Associate("origin", req.Header.Get("Origin"))
log.Associate("remote", remoteHost)
log.Access("session", "CONNECT")
defer log.Access("session", "DISCONNECT")
urlInfo, err := parsePath(ws.Request().URL.Path, config)
if err != nil {
log.Access("session", "NOT FOUND: %s", err)
return
}
log.Debug("session", "URLInfo: %s", urlInfo)
env, err := createEnv(ws, config, urlInfo, id)
if err != nil {
log.Error("process", "Could not create ENV: %s", err)
return
}
commandName := config.CommandName
if config.UsingScriptDir {
commandName = urlInfo.FilePath
}
log.Associate("command", commandName)
launched, err := launchCmd(commandName, config.CommandArgs, env)
if err != nil {
log.Error("process", "Could not launch process %s %s (%s)", commandName, strings.Join(config.CommandArgs, " "), err)
return
}
log.Associate("pid", strconv.Itoa(launched.cmd.Process.Pid))
process := NewProcessEndpoint(launched, log)
wsEndpoint := NewWebSocketEndpoint(ws, log)
defer process.Terminate()
go process.ReadOutput(launched.stdout, config)
go wsEndpoint.ReadOutput(config)
go process.pipeStdErr(config)
pipeEndpoints(process, wsEndpoint, log)
}
func pipeEndpoints(process Endpoint, wsEndpoint *WebSocketEndpoint, log *LogScope) {
for {
select {
case msgFromProcess, ok := <-process.Output():
if ok {
log.Trace("send<-", "%s", msgFromProcess)
if !wsEndpoint.Send(msgFromProcess) {
return
}
} else {
// TODO: Log exit code. Mechanism differs on different platforms.
log.Trace("process", "Process terminated")
return
}
case msgFromSocket, ok := <-wsEndpoint.Output():
if ok {
log.Trace("recv->", "%s", msgFromSocket)
process.Send(msgFromSocket)
} else {
log.Trace("websocket", "WebSocket connection closed")
return
}
}
}
}