github.com/ader1990/go@v0.0.0-20140630135419-8c24447fa791/src/pkg/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 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 var rec record 148 for { 149 if err := rec.read(c.conn.rwc); err != nil { 150 return 151 } 152 if err := c.handleRecord(&rec); err != nil { 153 return 154 } 155 } 156 } 157 158 var errCloseConn = errors.New("fcgi: connection should be closed") 159 160 var emptyBody = ioutil.NopCloser(strings.NewReader("")) 161 162 func (c *child) handleRecord(rec *record) error { 163 c.mu.Lock() 164 req, ok := c.requests[rec.h.Id] 165 c.mu.Unlock() 166 if !ok && rec.h.Type != typeBeginRequest && rec.h.Type != typeGetValues { 167 // The spec says to ignore unknown request IDs. 168 return nil 169 } 170 171 switch rec.h.Type { 172 case typeBeginRequest: 173 if req != nil { 174 // The server is trying to begin a request with the same ID 175 // as an in-progress request. This is an error. 176 return errors.New("fcgi: received ID that is already in-flight") 177 } 178 179 var br beginRequest 180 if err := br.read(rec.content()); err != nil { 181 return err 182 } 183 if br.role != roleResponder { 184 c.conn.writeEndRequest(rec.h.Id, 0, statusUnknownRole) 185 return nil 186 } 187 req = newRequest(rec.h.Id, br.flags) 188 c.mu.Lock() 189 c.requests[rec.h.Id] = req 190 c.mu.Unlock() 191 return nil 192 case typeParams: 193 // NOTE(eds): Technically a key-value pair can straddle the boundary 194 // between two packets. We buffer until we've received all parameters. 195 if len(rec.content()) > 0 { 196 req.rawParams = append(req.rawParams, rec.content()...) 197 return nil 198 } 199 req.parseParams() 200 return nil 201 case typeStdin: 202 content := rec.content() 203 if req.pw == nil { 204 var body io.ReadCloser 205 if len(content) > 0 { 206 // body could be an io.LimitReader, but it shouldn't matter 207 // as long as both sides are behaving. 208 body, req.pw = io.Pipe() 209 } else { 210 body = emptyBody 211 } 212 go c.serveRequest(req, body) 213 } 214 if len(content) > 0 { 215 // TODO(eds): This blocks until the handler reads from the pipe. 216 // If the handler takes a long time, it might be a problem. 217 req.pw.Write(content) 218 } else if req.pw != nil { 219 req.pw.Close() 220 } 221 return nil 222 case typeGetValues: 223 values := map[string]string{"FCGI_MPXS_CONNS": "1"} 224 c.conn.writePairs(typeGetValuesResult, 0, values) 225 return nil 226 case typeData: 227 // If the filter role is implemented, read the data stream here. 228 return nil 229 case typeAbortRequest: 230 println("abort") 231 c.mu.Lock() 232 delete(c.requests, rec.h.Id) 233 c.mu.Unlock() 234 c.conn.writeEndRequest(rec.h.Id, 0, statusRequestComplete) 235 if !req.keepConn { 236 // connection will close upon return 237 return errCloseConn 238 } 239 return nil 240 default: 241 b := make([]byte, 8) 242 b[0] = byte(rec.h.Type) 243 c.conn.writeRecord(typeUnknownType, 0, b) 244 return nil 245 } 246 } 247 248 func (c *child) serveRequest(req *request, body io.ReadCloser) { 249 r := newResponse(c, req) 250 httpReq, err := cgi.RequestFromMap(req.params) 251 if err != nil { 252 // there was an error reading the request 253 r.WriteHeader(http.StatusInternalServerError) 254 c.conn.writeRecord(typeStderr, req.reqId, []byte(err.Error())) 255 } else { 256 httpReq.Body = body 257 c.handler.ServeHTTP(r, httpReq) 258 } 259 r.Close() 260 c.mu.Lock() 261 delete(c.requests, req.reqId) 262 c.mu.Unlock() 263 c.conn.writeEndRequest(req.reqId, 0, statusRequestComplete) 264 265 // Consume the entire body, so the host isn't still writing to 266 // us when we close the socket below in the !keepConn case, 267 // otherwise we'd send a RST. (golang.org/issue/4183) 268 // TODO(bradfitz): also bound this copy in time. Or send 269 // some sort of abort request to the host, so the host 270 // can properly cut off the client sending all the data. 271 // For now just bound it a little and 272 io.CopyN(ioutil.Discard, body, 100<<20) 273 body.Close() 274 275 if !req.keepConn { 276 c.conn.Close() 277 } 278 } 279 280 // Serve accepts incoming FastCGI connections on the listener l, creating a new 281 // goroutine for each. The goroutine reads requests and then calls handler 282 // to reply to them. 283 // If l is nil, Serve accepts connections from os.Stdin. 284 // If handler is nil, http.DefaultServeMux is used. 285 func Serve(l net.Listener, handler http.Handler) error { 286 if l == nil { 287 var err error 288 l, err = net.FileListener(os.Stdin) 289 if err != nil { 290 return err 291 } 292 defer l.Close() 293 } 294 if handler == nil { 295 handler = http.DefaultServeMux 296 } 297 for { 298 rw, err := l.Accept() 299 if err != nil { 300 return err 301 } 302 c := newChild(rw, handler) 303 go c.serve() 304 } 305 }