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 5 package fcgi 6 7 // This file implements FastCGI from the perspective of a child process. 8 9 import ( 10 "errors" 11 "fmt" 12 "io" 13 "io/ioutil" 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. 25 type 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 func newRequest(reqId uint16, flags uint8) *request { 35 r := &request{ 36 reqId: reqId, 37 params: map[string]string{}, 38 keepConn: flags&flagKeepConn != 0, 39 } 40 r.rawParams = r.buf[:0] 41 return r 42 } 43 44 // parseParams reads an encoded []byte into Params. 45 func (r *request) parseParams() { 46 text := r.rawParams 47 r.rawParams = nil 48 for len(text) > 0 { 49 keyLen, n := readSize(text) 50 if n == 0 { 51 return 52 } 53 text = text[n:] 54 valLen, n := readSize(text) 55 if n == 0 { 56 return 57 } 58 text = text[n:] 59 key := readString(text, keyLen) 60 text = text[keyLen:] 61 val := readString(text, valLen) 62 text = text[valLen:] 63 r.params[key] = val 64 } 65 } 66 67 // response implements http.ResponseWriter. 68 type response struct { 69 req *request 70 header http.Header 71 w *bufWriter 72 wroteHeader bool 73 } 74 75 func newResponse(c *child, req *request) *response { 76 return &response{ 77 req: req, 78 header: http.Header{}, 79 w: newWriter(c.conn, typeStdout, req.reqId), 80 } 81 } 82 83 func (r *response) Header() http.Header { 84 return r.header 85 } 86 87 func (r *response) Write(data []byte) (int, error) { 88 if !r.wroteHeader { 89 r.WriteHeader(http.StatusOK) 90 } 91 return r.w.Write(data) 92 } 93 94 func (r *response) WriteHeader(code int) { 95 if r.wroteHeader { 96 return 97 } 98 r.wroteHeader = true 99 if code == http.StatusNotModified { 100 // Must not have body. 101 r.header.Del("Content-Type") 102 r.header.Del("Content-Length") 103 r.header.Del("Transfer-Encoding") 104 } else if r.header.Get("Content-Type") == "" { 105 r.header.Set("Content-Type", "text/html; charset=utf-8") 106 } 107 108 if r.header.Get("Date") == "" { 109 r.header.Set("Date", time.Now().UTC().Format(http.TimeFormat)) 110 } 111 112 fmt.Fprintf(r.w, "Status: %d %s\r\n", code, http.StatusText(code)) 113 r.header.Write(r.w) 114 r.w.WriteString("\r\n") 115 } 116 117 func (r *response) Flush() { 118 if !r.wroteHeader { 119 r.WriteHeader(http.StatusOK) 120 } 121 r.w.Flush() 122 } 123 124 func (r *response) Close() error { 125 r.Flush() 126 return r.w.Close() 127 } 128 129 type child struct { 130 conn *conn 131 handler http.Handler 132 133 mu sync.Mutex // protects requests: 134 requests map[uint16]*request // keyed by request ID 135 } 136 137 func newChild(rwc io.ReadWriteCloser, handler http.Handler) *child { 138 return &child{ 139 conn: newConn(rwc), 140 handler: handler, 141 requests: make(map[uint16]*request), 142 } 143 } 144 145 func (c *child) serve() { 146 defer c.conn.Close() 147 defer c.cleanUp() 148 var rec record 149 for { 150 if err := rec.read(c.conn.rwc); err != nil { 151 return 152 } 153 if err := c.handleRecord(&rec); err != nil { 154 return 155 } 156 } 157 } 158 159 var errCloseConn = errors.New("fcgi: connection should be closed") 160 161 var emptyBody = ioutil.NopCloser(strings.NewReader("")) 162 163 // ErrRequestAborted is returned by Read when a handler attempts to read the 164 // body of a request that has been aborted by the web server. 165 var ErrRequestAborted = errors.New("fcgi: request aborted by web server") 166 167 // ErrConnClosed is returned by Read when a handler attempts to read the body of 168 // a request after the connection to the web server has been closed. 169 var ErrConnClosed = errors.New("fcgi: connection to web server closed") 170 171 func (c *child) handleRecord(rec *record) error { 172 c.mu.Lock() 173 req, ok := c.requests[rec.h.Id] 174 c.mu.Unlock() 175 if !ok && rec.h.Type != typeBeginRequest && rec.h.Type != typeGetValues { 176 // The spec says to ignore unknown request IDs. 177 return nil 178 } 179 180 switch rec.h.Type { 181 case typeBeginRequest: 182 if req != nil { 183 // The server is trying to begin a request with the same ID 184 // as an in-progress request. This is an error. 185 return errors.New("fcgi: received ID that is already in-flight") 186 } 187 188 var br beginRequest 189 if err := br.read(rec.content()); err != nil { 190 return err 191 } 192 if br.role != roleResponder { 193 c.conn.writeEndRequest(rec.h.Id, 0, statusUnknownRole) 194 return nil 195 } 196 req = newRequest(rec.h.Id, br.flags) 197 c.mu.Lock() 198 c.requests[rec.h.Id] = req 199 c.mu.Unlock() 200 return nil 201 case typeParams: 202 // NOTE(eds): Technically a key-value pair can straddle the boundary 203 // between two packets. We buffer until we've received all parameters. 204 if len(rec.content()) > 0 { 205 req.rawParams = append(req.rawParams, rec.content()...) 206 return nil 207 } 208 req.parseParams() 209 return nil 210 case typeStdin: 211 content := rec.content() 212 if req.pw == nil { 213 var body io.ReadCloser 214 if len(content) > 0 { 215 // body could be an io.LimitReader, but it shouldn't matter 216 // as long as both sides are behaving. 217 body, req.pw = io.Pipe() 218 } else { 219 body = emptyBody 220 } 221 go c.serveRequest(req, body) 222 } 223 if len(content) > 0 { 224 // TODO(eds): This blocks until the handler reads from the pipe. 225 // If the handler takes a long time, it might be a problem. 226 req.pw.Write(content) 227 } else if req.pw != nil { 228 req.pw.Close() 229 } 230 return nil 231 case typeGetValues: 232 values := map[string]string{"FCGI_MPXS_CONNS": "1"} 233 c.conn.writePairs(typeGetValuesResult, 0, values) 234 return nil 235 case typeData: 236 // If the filter role is implemented, read the data stream here. 237 return nil 238 case typeAbortRequest: 239 c.mu.Lock() 240 delete(c.requests, rec.h.Id) 241 c.mu.Unlock() 242 c.conn.writeEndRequest(rec.h.Id, 0, statusRequestComplete) 243 if req.pw != nil { 244 req.pw.CloseWithError(ErrRequestAborted) 245 } 246 if !req.keepConn { 247 // connection will close upon return 248 return errCloseConn 249 } 250 return nil 251 default: 252 b := make([]byte, 8) 253 b[0] = byte(rec.h.Type) 254 c.conn.writeRecord(typeUnknownType, 0, b) 255 return nil 256 } 257 } 258 259 func (c *child) serveRequest(req *request, body io.ReadCloser) { 260 r := newResponse(c, req) 261 httpReq, err := cgi.RequestFromMap(req.params) 262 if err != nil { 263 // there was an error reading the request 264 r.WriteHeader(http.StatusInternalServerError) 265 c.conn.writeRecord(typeStderr, req.reqId, []byte(err.Error())) 266 } else { 267 httpReq.Body = body 268 c.handler.ServeHTTP(r, httpReq) 269 } 270 r.Close() 271 c.mu.Lock() 272 delete(c.requests, req.reqId) 273 c.mu.Unlock() 274 c.conn.writeEndRequest(req.reqId, 0, statusRequestComplete) 275 276 // Consume the entire body, so the host isn't still writing to 277 // us when we close the socket below in the !keepConn case, 278 // otherwise we'd send a RST. (golang.org/issue/4183) 279 // TODO(bradfitz): also bound this copy in time. Or send 280 // some sort of abort request to the host, so the host 281 // can properly cut off the client sending all the data. 282 // For now just bound it a little and 283 io.CopyN(ioutil.Discard, body, 100<<20) 284 body.Close() 285 286 if !req.keepConn { 287 c.conn.Close() 288 } 289 } 290 291 func (c *child) cleanUp() { 292 c.mu.Lock() 293 defer c.mu.Unlock() 294 for _, req := range c.requests { 295 if req.pw != nil { 296 // race with call to Close in c.serveRequest doesn't matter because 297 // Pipe(Reader|Writer).Close are idempotent 298 req.pw.CloseWithError(ErrConnClosed) 299 } 300 } 301 } 302 303 // Serve accepts incoming FastCGI connections on the listener l, creating a new 304 // goroutine for each. The goroutine reads requests and then calls handler 305 // to reply to them. 306 // If l is nil, Serve accepts connections from os.Stdin. 307 // If handler is nil, http.DefaultServeMux is used. 308 func Serve(l net.Listener, handler http.Handler) error { 309 if l == nil { 310 var err error 311 l, err = net.FileListener(os.Stdin) 312 if err != nil { 313 return err 314 } 315 defer l.Close() 316 } 317 if handler == nil { 318 handler = http.DefaultServeMux 319 } 320 for { 321 rw, err := l.Accept() 322 if err != nil { 323 return err 324 } 325 c := newChild(rw, handler) 326 go c.serve() 327 } 328 } 329