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