1// Copyright 2011 The Go Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style 3// license that can be found in the LICENSE file. 4 5package fcgi 6 7// This file implements FastCGI from the perspective of a child process. 8 9import ( 10 "context" 11 "errors" 12 "fmt" 13 "io" 14 "io/ioutil" 15 "net" 16 "net/http" 17 "net/http/cgi" 18 "os" 19 "strings" 20 "sync" 21 "time" 22) 23 24// request holds the state for an in-progress request. As soon as it's complete, 25// it's converted to an http.Request. 26type request struct { 27 pw *io.PipeWriter 28 reqId uint16 29 params map[string]string 30 buf [1024]byte 31 rawParams []byte 32 keepConn bool 33} 34 35// envVarsContextKey uniquely identifies a mapping of CGI 36// environment variables to their values in a request context 37type envVarsContextKey struct{} 38 39func newRequest(reqId uint16, flags uint8) *request { 40 r := &request{ 41 reqId: reqId, 42 params: map[string]string{}, 43 keepConn: flags&flagKeepConn != 0, 44 } 45 r.rawParams = r.buf[:0] 46 return r 47} 48 49// parseParams reads an encoded []byte into Params. 50func (r *request) parseParams() { 51 text := r.rawParams 52 r.rawParams = nil 53 for len(text) > 0 { 54 keyLen, n := readSize(text) 55 if n == 0 { 56 return 57 } 58 text = text[n:] 59 valLen, n := readSize(text) 60 if n == 0 { 61 return 62 } 63 text = text[n:] 64 if int(keyLen)+int(valLen) > len(text) { 65 return 66 } 67 key := readString(text, keyLen) 68 text = text[keyLen:] 69 val := readString(text, valLen) 70 text = text[valLen:] 71 r.params[key] = val 72 } 73} 74 75// response implements http.ResponseWriter. 76type response struct { 77 req *request 78 header http.Header 79 w *bufWriter 80 wroteHeader bool 81} 82 83func newResponse(c *child, req *request) *response { 84 return &response{ 85 req: req, 86 header: http.Header{}, 87 w: newWriter(c.conn, typeStdout, req.reqId), 88 } 89} 90 91func (r *response) Header() http.Header { 92 return r.header 93} 94 95func (r *response) Write(data []byte) (int, error) { 96 if !r.wroteHeader { 97 r.WriteHeader(http.StatusOK) 98 } 99 return r.w.Write(data) 100} 101 102func (r *response) WriteHeader(code int) { 103 if r.wroteHeader { 104 return 105 } 106 r.wroteHeader = true 107 if code == http.StatusNotModified { 108 // Must not have body. 109 r.header.Del("Content-Type") 110 r.header.Del("Content-Length") 111 r.header.Del("Transfer-Encoding") 112 } else if r.header.Get("Content-Type") == "" { 113 r.header.Set("Content-Type", "text/html; charset=utf-8") 114 } 115 116 if r.header.Get("Date") == "" { 117 r.header.Set("Date", time.Now().UTC().Format(http.TimeFormat)) 118 } 119 120 fmt.Fprintf(r.w, "Status: %d %s\r\n", code, http.StatusText(code)) 121 r.header.Write(r.w) 122 r.w.WriteString("\r\n") 123} 124 125func (r *response) Flush() { 126 if !r.wroteHeader { 127 r.WriteHeader(http.StatusOK) 128 } 129 r.w.Flush() 130} 131 132func (r *response) Close() error { 133 r.Flush() 134 return r.w.Close() 135} 136 137type child struct { 138 conn *conn 139 handler http.Handler 140 141 mu sync.Mutex // protects requests: 142 requests map[uint16]*request // keyed by request ID 143} 144 145func newChild(rwc io.ReadWriteCloser, handler http.Handler) *child { 146 return &child{ 147 conn: newConn(rwc), 148 handler: handler, 149 requests: make(map[uint16]*request), 150 } 151} 152 153func (c *child) serve() { 154 defer c.conn.Close() 155 defer c.cleanUp() 156 var rec record 157 for { 158 if err := rec.read(c.conn.rwc); err != nil { 159 return 160 } 161 if err := c.handleRecord(&rec); err != nil { 162 return 163 } 164 } 165} 166 167var errCloseConn = errors.New("fcgi: connection should be closed") 168 169var emptyBody = ioutil.NopCloser(strings.NewReader("")) 170 171// ErrRequestAborted is returned by Read when a handler attempts to read the 172// body of a request that has been aborted by the web server. 173var ErrRequestAborted = errors.New("fcgi: request aborted by web server") 174 175// ErrConnClosed is returned by Read when a handler attempts to read the body of 176// a request after the connection to the web server has been closed. 177var ErrConnClosed = errors.New("fcgi: connection to web server closed") 178 179func (c *child) handleRecord(rec *record) error { 180 c.mu.Lock() 181 req, ok := c.requests[rec.h.Id] 182 c.mu.Unlock() 183 if !ok && rec.h.Type != typeBeginRequest && rec.h.Type != typeGetValues { 184 // The spec says to ignore unknown request IDs. 185 return nil 186 } 187 188 switch rec.h.Type { 189 case typeBeginRequest: 190 if req != nil { 191 // The server is trying to begin a request with the same ID 192 // as an in-progress request. This is an error. 193 return errors.New("fcgi: received ID that is already in-flight") 194 } 195 196 var br beginRequest 197 if err := br.read(rec.content()); err != nil { 198 return err 199 } 200 if br.role != roleResponder { 201 c.conn.writeEndRequest(rec.h.Id, 0, statusUnknownRole) 202 return nil 203 } 204 req = newRequest(rec.h.Id, br.flags) 205 c.mu.Lock() 206 c.requests[rec.h.Id] = req 207 c.mu.Unlock() 208 return nil 209 case typeParams: 210 // NOTE(eds): Technically a key-value pair can straddle the boundary 211 // between two packets. We buffer until we've received all parameters. 212 if len(rec.content()) > 0 { 213 req.rawParams = append(req.rawParams, rec.content()...) 214 return nil 215 } 216 req.parseParams() 217 return nil 218 case typeStdin: 219 content := rec.content() 220 if req.pw == nil { 221 var body io.ReadCloser 222 if len(content) > 0 { 223 // body could be an io.LimitReader, but it shouldn't matter 224 // as long as both sides are behaving. 225 body, req.pw = io.Pipe() 226 } else { 227 body = emptyBody 228 } 229 go c.serveRequest(req, body) 230 } 231 if len(content) > 0 { 232 // TODO(eds): This blocks until the handler reads from the pipe. 233 // If the handler takes a long time, it might be a problem. 234 req.pw.Write(content) 235 } else if req.pw != nil { 236 req.pw.Close() 237 } 238 return nil 239 case typeGetValues: 240 values := map[string]string{"FCGI_MPXS_CONNS": "1"} 241 c.conn.writePairs(typeGetValuesResult, 0, values) 242 return nil 243 case typeData: 244 // If the filter role is implemented, read the data stream here. 245 return nil 246 case typeAbortRequest: 247 c.mu.Lock() 248 delete(c.requests, rec.h.Id) 249 c.mu.Unlock() 250 c.conn.writeEndRequest(rec.h.Id, 0, statusRequestComplete) 251 if req.pw != nil { 252 req.pw.CloseWithError(ErrRequestAborted) 253 } 254 if !req.keepConn { 255 // connection will close upon return 256 return errCloseConn 257 } 258 return nil 259 default: 260 b := make([]byte, 8) 261 b[0] = byte(rec.h.Type) 262 c.conn.writeRecord(typeUnknownType, 0, b) 263 return nil 264 } 265} 266 267// filterOutUsedEnvVars returns a new map of env vars without the 268// variables in the given envVars map that are read for creating each http.Request 269func filterOutUsedEnvVars(envVars map[string]string) map[string]string { 270 withoutUsedEnvVars := make(map[string]string) 271 for k, v := range envVars { 272 if addFastCGIEnvToContext(k) { 273 withoutUsedEnvVars[k] = v 274 } 275 } 276 return withoutUsedEnvVars 277} 278 279func (c *child) serveRequest(req *request, body io.ReadCloser) { 280 r := newResponse(c, req) 281 httpReq, err := cgi.RequestFromMap(req.params) 282 if err != nil { 283 // there was an error reading the request 284 r.WriteHeader(http.StatusInternalServerError) 285 c.conn.writeRecord(typeStderr, req.reqId, []byte(err.Error())) 286 } else { 287 httpReq.Body = body 288 withoutUsedEnvVars := filterOutUsedEnvVars(req.params) 289 envVarCtx := context.WithValue(httpReq.Context(), envVarsContextKey{}, withoutUsedEnvVars) 290 httpReq = httpReq.WithContext(envVarCtx) 291 c.handler.ServeHTTP(r, httpReq) 292 } 293 r.Close() 294 c.mu.Lock() 295 delete(c.requests, req.reqId) 296 c.mu.Unlock() 297 c.conn.writeEndRequest(req.reqId, 0, statusRequestComplete) 298 299 // Consume the entire body, so the host isn't still writing to 300 // us when we close the socket below in the !keepConn case, 301 // otherwise we'd send a RST. (golang.org/issue/4183) 302 // TODO(bradfitz): also bound this copy in time. Or send 303 // some sort of abort request to the host, so the host 304 // can properly cut off the client sending all the data. 305 // For now just bound it a little and 306 io.CopyN(ioutil.Discard, body, 100<<20) 307 body.Close() 308 309 if !req.keepConn { 310 c.conn.Close() 311 } 312} 313 314func (c *child) cleanUp() { 315 c.mu.Lock() 316 defer c.mu.Unlock() 317 for _, req := range c.requests { 318 if req.pw != nil { 319 // race with call to Close in c.serveRequest doesn't matter because 320 // Pipe(Reader|Writer).Close are idempotent 321 req.pw.CloseWithError(ErrConnClosed) 322 } 323 } 324} 325 326// Serve accepts incoming FastCGI connections on the listener l, creating a new 327// goroutine for each. The goroutine reads requests and then calls handler 328// to reply to them. 329// If l is nil, Serve accepts connections from os.Stdin. 330// If handler is nil, http.DefaultServeMux is used. 331func Serve(l net.Listener, handler http.Handler) error { 332 if l == nil { 333 var err error 334 l, err = net.FileListener(os.Stdin) 335 if err != nil { 336 return err 337 } 338 defer l.Close() 339 } 340 if handler == nil { 341 handler = http.DefaultServeMux 342 } 343 for { 344 rw, err := l.Accept() 345 if err != nil { 346 return err 347 } 348 c := newChild(rw, handler) 349 go c.serve() 350 } 351} 352 353// ProcessEnv returns FastCGI environment variables associated with the request r 354// for which no effort was made to be included in the request itself - the data 355// is hidden in the request's context. As an example, if REMOTE_USER is set for a 356// request, it will not be found anywhere in r, but it will be included in 357// ProcessEnv's response (via r's context). 358func ProcessEnv(r *http.Request) map[string]string { 359 env, _ := r.Context().Value(envVarsContextKey{}).(map[string]string) 360 return env 361} 362 363// addFastCGIEnvToContext reports whether to include the FastCGI environment variable s 364// in the http.Request.Context, accessible via ProcessEnv. 365func addFastCGIEnvToContext(s string) bool { 366 // Exclude things supported by net/http natively: 367 switch s { 368 case "CONTENT_LENGTH", "CONTENT_TYPE", "HTTPS", 369 "PATH_INFO", "QUERY_STRING", "REMOTE_ADDR", 370 "REMOTE_HOST", "REMOTE_PORT", "REQUEST_METHOD", 371 "REQUEST_URI", "SCRIPT_NAME", "SERVER_PROTOCOL": 372 return false 373 } 374 if strings.HasPrefix(s, "HTTP_") { 375 return false 376 } 377 // Explicitly include FastCGI-specific things. 378 // This list is redundant with the default "return true" below. 379 // Consider this documentation of the sorts of things we expect 380 // to maybe see. 381 switch s { 382 case "REMOTE_USER": 383 return true 384 } 385 // Unknown, so include it to be safe. 386 return true 387} 388