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