-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathpipes.go
101 lines (86 loc) · 2.85 KB
/
pipes.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
package webpipes
import "encoding/base64"
import "fmt"
import "net/http"
import "log"
import "strings"
import "time"
// Require simple authentication in order to proceed, otherwise respond with
// a challenge/denial and send the connection down the 'bypass' channel.
func SimpleAuth(users map[string]string, realm string, bypass chan<- *Conn) Pipe {
return func(conn *Conn, req *http.Request) bool {
// Check for the 'Authorization' header and attempt authentication
var authenticated bool = false
authData := req.Header.Get("Authorization")
if len(authData) > 0 {
fields := strings.Fields(authData)
if len(fields) == 2 && fields[0] == "Basic" {
b64data := fields[1]
// TODO: Find a better way to decode this without len limit
data := make([]byte, 128, 128)
n, err := base64.StdEncoding.Decode(data, []byte(b64data))
// If decoding was successful
if err == nil && n > 0 {
data := string(data[0:n])
subStrings := strings.SplitN(data, ":", 2)
if len(subStrings) == 2 {
username, password := subStrings[0], subStrings[1]
pass, ok := users[username]
if ok && pass == password {
authenticated = true
}
}
}
}
}
if !authenticated {
// Send an authentication challenge (401)
// TODO: Handle realm escaping here, so it's a valid header
hdr := fmt.Sprintf("Basic realm=\"%s\"", realm)
conn.SetHeader("WWW_Authenticate", hdr)
// Pass the connection over the bypass channel and tell the component
// server to drop the connection, as we've already forwarded it on.
conn.HTTPStatusResponse(http.StatusUnauthorized)
bypass <- conn
return false
}
// The user is authenticated, so proceed
return true
}
}
// Write an CLF formatted access log to 'logger'
func AccessLog(logger *log.Logger) Pipe {
return func(conn *Conn, req *http.Request) bool {
var remoteHost = req.RemoteAddr // FIXME
var ident string = "-"
var authuser string = "-"
var now time.Time = time.Now().UTC()
var timestamp string = now.Format("[07/Jan/2006:15:04:05 -0700]")
var request string = fmt.Sprintf("%s %s %s", req.Method, req.URL, req.Proto)
var status int = conn.status
var size int64 = conn.written
var referer string = "-"
var userAgent string = "-"
if len(req.Referer()) > 0 {
referer = req.Referer()
}
if len(req.UserAgent()) > 0 {
userAgent = req.UserAgent()
}
// Spawn a new goroutine to perform the actual print to the logfile
// instead of making the pipeline wait.
go func() {
logger.Printf("%s %s %s %s \"%s\" %d %d \"%s\" \"%s\"\n",
remoteHost, ident, authuser, timestamp, request, status, size,
referer, userAgent)
}()
return true
}
}
// Logs a message to stderr for each connection.
func DebugPipe(str string, args ...interface{}) Pipe {
return func(conn *Conn, req *http.Request) bool {
log.Printf(str, args...)
return true
}
}