11package proxy
22
33import (
4+ "encoding/base64"
45 "io"
56 "net"
67 "net/http"
8+ "strings"
79 "time"
810
911 "github.com/golang/glog"
@@ -16,11 +18,34 @@ func NewProxyHandler(timeoutSeconds int) *ProxyHandler {
1618}
1719
1820type ProxyHandler struct {
19- Timeout time.Duration
21+ Timeout time.Duration
22+ Username * string
23+ Password * string
24+ LogAuth bool
25+ LogHeaders bool
2026}
2127
2228func (p * ProxyHandler ) ServeHTTP (w http.ResponseWriter , r * http.Request ) {
2329 glog .V (1 ).Infof ("Serving '%s' request from '%s' to '%s'\n " , r .Method , r .RemoteAddr , r .Host )
30+ if p .LogHeaders {
31+ for name , values := range r .Header {
32+ for i , value := range values {
33+ glog .V (1 ).Infof ("'%s': [%d] %s" , name , i , value )
34+ }
35+ }
36+ }
37+ if p .Username != nil && p .Password != nil {
38+ username , password , ok := proxyBasicAuth (r )
39+ if ! ok || username != * p .Username || password != * p .Password {
40+ if p .LogAuth {
41+ glog .Errorf ("Unauthorized, username: %s, password: %s\n " , username , password )
42+ } else {
43+ glog .Errorln ("Unauthorized" )
44+ }
45+ http .Error (w , "Unauthorized" , http .StatusUnauthorized )
46+ return
47+ }
48+ }
2449 if r .Method == http .MethodConnect {
2550 handleTunneling (w , r , p .Timeout )
2651 } else {
@@ -77,3 +102,53 @@ func copyHeader(dst, src http.Header) {
77102 }
78103 }
79104}
105+
106+ func proxyBasicAuth (r * http.Request ) (username , password string , ok bool ) {
107+ auth := r .Header .Get ("Proxy-Authorization" )
108+ if auth == "" {
109+ return
110+ }
111+ return parseBasicAuth (auth )
112+ }
113+
114+ // parseBasicAuth parses an HTTP Basic Authentication string.
115+ // "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==" returns ("Aladdin", "open sesame", true).
116+ func parseBasicAuth (auth string ) (username , password string , ok bool ) {
117+ const prefix = "Basic "
118+ // Case insensitive prefix match. See Issue 22736.
119+ if len (auth ) < len (prefix ) || ! equalFold (auth [:len (prefix )], prefix ) {
120+ return
121+ }
122+ c , err := base64 .StdEncoding .DecodeString (auth [len (prefix ):])
123+ if err != nil {
124+ return
125+ }
126+ cs := string (c )
127+ s := strings .IndexByte (cs , ':' )
128+ if s < 0 {
129+ return
130+ }
131+ return cs [:s ], cs [s + 1 :], true
132+ }
133+
134+ // EqualFold is strings.EqualFold, ASCII only. It reports whether s and t
135+ // are equal, ASCII-case-insensitively.
136+ func equalFold (s , t string ) bool {
137+ if len (s ) != len (t ) {
138+ return false
139+ }
140+ for i := 0 ; i < len (s ); i ++ {
141+ if lower (s [i ]) != lower (t [i ]) {
142+ return false
143+ }
144+ }
145+ return true
146+ }
147+
148+ // lower returns the ASCII lowercase version of b.
149+ func lower (b byte ) byte {
150+ if 'A' <= b && b <= 'Z' {
151+ return b + ('a' - 'A' )
152+ }
153+ return b
154+ }
0 commit comments