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