Source file src/pkg/net/http/fcgi/child.go
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 "net" 14 "net/http" 15 "net/http/cgi" 16 "os" 17 "time" 18 ) 19 20 // request holds the state for an in-progress request. As soon as it's complete, 21 // it's converted to an http.Request. 22 type request struct { 23 pw *io.PipeWriter 24 reqId uint16 25 params map[string]string 26 buf [1024]byte 27 rawParams []byte 28 keepConn bool 29 } 30 31 func newRequest(reqId uint16, flags uint8) *request { 32 r := &request{ 33 reqId: reqId, 34 params: map[string]string{}, 35 keepConn: flags&flagKeepConn != 0, 36 } 37 r.rawParams = r.buf[:0] 38 return r 39 } 40 41 // parseParams reads an encoded []byte into Params. 42 func (r *request) parseParams() { 43 text := r.rawParams 44 r.rawParams = nil 45 for len(text) > 0 { 46 keyLen, n := readSize(text) 47 if n == 0 { 48 return 49 } 50 text = text[n:] 51 valLen, n := readSize(text) 52 if n == 0 { 53 return 54 } 55 text = text[n:] 56 key := readString(text, keyLen) 57 text = text[keyLen:] 58 val := readString(text, valLen) 59 text = text[valLen:] 60 r.params[key] = val 61 } 62 } 63 64 // response implements http.ResponseWriter. 65 type response struct { 66 req *request 67 header http.Header 68 w *bufWriter 69 wroteHeader bool 70 } 71 72 func newResponse(c *child, req *request) *response { 73 return &response{ 74 req: req, 75 header: http.Header{}, 76 w: newWriter(c.conn, typeStdout, req.reqId), 77 } 78 } 79 80 func (r *response) Header() http.Header { 81 return r.header 82 } 83 84 func (r *response) Write(data []byte) (int, error) { 85 if !r.wroteHeader { 86 r.WriteHeader(http.StatusOK) 87 } 88 return r.w.Write(data) 89 } 90 91 func (r *response) WriteHeader(code int) { 92 if r.wroteHeader { 93 return 94 } 95 r.wroteHeader = true 96 if code == http.StatusNotModified { 97 // Must not have body. 98 r.header.Del("Content-Type") 99 r.header.Del("Content-Length") 100 r.header.Del("Transfer-Encoding") 101 } else if r.header.Get("Content-Type") == "" { 102 r.header.Set("Content-Type", "text/html; charset=utf-8") 103 } 104 105 if r.header.Get("Date") == "" { 106 r.header.Set("Date", time.Now().UTC().Format(http.TimeFormat)) 107 } 108 109 fmt.Fprintf(r.w, "Status: %d %s\r\n", code, http.StatusText(code)) 110 r.header.Write(r.w) 111 r.w.WriteString("\r\n") 112 } 113 114 func (r *response) Flush() { 115 if !r.wroteHeader { 116 r.WriteHeader(http.StatusOK) 117 } 118 r.w.Flush() 119 } 120 121 func (r *response) Close() error { 122 r.Flush() 123 return r.w.Close() 124 } 125 126 type child struct { 127 conn *conn 128 handler http.Handler 129 requests map[uint16]*request // keyed by request ID 130 } 131 132 func newChild(rwc io.ReadWriteCloser, handler http.Handler) *child { 133 return &child{ 134 conn: newConn(rwc), 135 handler: handler, 136 requests: make(map[uint16]*request), 137 } 138 } 139 140 func (c *child) serve() { 141 defer c.conn.Close() 142 var rec record 143 for { 144 if err := rec.read(c.conn.rwc); err != nil { 145 return 146 } 147 if err := c.handleRecord(&rec); err != nil { 148 return 149 } 150 } 151 } 152 153 var errCloseConn = errors.New("fcgi: connection should be closed") 154 155 func (c *child) handleRecord(rec *record) error { 156 req, ok := c.requests[rec.h.Id] 157 if !ok && rec.h.Type != typeBeginRequest && rec.h.Type != typeGetValues { 158 // The spec says to ignore unknown request IDs. 159 return nil 160 } 161 if ok && rec.h.Type == typeBeginRequest { 162 // The server is trying to begin a request with the same ID 163 // as an in-progress request. This is an error. 164 return errors.New("fcgi: received ID that is already in-flight") 165 } 166 167 switch rec.h.Type { 168 case typeBeginRequest: 169 var br beginRequest 170 if err := br.read(rec.content()); err != nil { 171 return err 172 } 173 if br.role != roleResponder { 174 c.conn.writeEndRequest(rec.h.Id, 0, statusUnknownRole) 175 return nil 176 } 177 c.requests[rec.h.Id] = newRequest(rec.h.Id, br.flags) 178 case typeParams: 179 // NOTE(eds): Technically a key-value pair can straddle the boundary 180 // between two packets. We buffer until we've received all parameters. 181 if len(rec.content()) > 0 { 182 req.rawParams = append(req.rawParams, rec.content()...) 183 return nil 184 } 185 req.parseParams() 186 case typeStdin: 187 content := rec.content() 188 if req.pw == nil { 189 var body io.ReadCloser 190 if len(content) > 0 { 191 // body could be an io.LimitReader, but it shouldn't matter 192 // as long as both sides are behaving. 193 body, req.pw = io.Pipe() 194 } 195 go c.serveRequest(req, body) 196 } 197 if len(content) > 0 { 198 // TODO(eds): This blocks until the handler reads from the pipe. 199 // If the handler takes a long time, it might be a problem. 200 req.pw.Write(content) 201 } else if req.pw != nil { 202 req.pw.Close() 203 } 204 case typeGetValues: 205 values := map[string]string{"FCGI_MPXS_CONNS": "1"} 206 c.conn.writePairs(typeGetValuesResult, 0, values) 207 case typeData: 208 // If the filter role is implemented, read the data stream here. 209 case typeAbortRequest: 210 delete(c.requests, rec.h.Id) 211 c.conn.writeEndRequest(rec.h.Id, 0, statusRequestComplete) 212 if !req.keepConn { 213 // connection will close upon return 214 return errCloseConn 215 } 216 default: 217 b := make([]byte, 8) 218 b[0] = byte(rec.h.Type) 219 c.conn.writeRecord(typeUnknownType, 0, b) 220 } 221 return nil 222 } 223 224 func (c *child) serveRequest(req *request, body io.ReadCloser) { 225 r := newResponse(c, req) 226 httpReq, err := cgi.RequestFromMap(req.params) 227 if err != nil { 228 // there was an error reading the request 229 r.WriteHeader(http.StatusInternalServerError) 230 c.conn.writeRecord(typeStderr, req.reqId, []byte(err.Error())) 231 } else { 232 httpReq.Body = body 233 c.handler.ServeHTTP(r, httpReq) 234 } 235 if body != nil { 236 body.Close() 237 } 238 r.Close() 239 c.conn.writeEndRequest(req.reqId, 0, statusRequestComplete) 240 if !req.keepConn { 241 c.conn.Close() 242 } 243 } 244 245 // Serve accepts incoming FastCGI connections on the listener l, creating a new 246 // goroutine for each. The goroutine reads requests and then calls handler 247 // to reply to them. 248 // If l is nil, Serve accepts connections from os.Stdin. 249 // If handler is nil, http.DefaultServeMux is used. 250 func Serve(l net.Listener, handler http.Handler) error { 251 if l == nil { 252 var err error 253 l, err = net.FileListener(os.Stdin) 254 if err != nil { 255 return err 256 } 257 defer l.Close() 258 } 259 if handler == nil { 260 handler = http.DefaultServeMux 261 } 262 for { 263 rw, err := l.Accept() 264 if err != nil { 265 return err 266 } 267 c := newChild(rw, handler) 268 go c.serve() 269 } 270 panic("unreachable") 271 }