gitee.com/ks-custle/core-gm@v0.0.0-20230922171213-b83bdd97b62c/gmhttp/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 "context" 11 "errors" 12 "fmt" 13 "io" 14 "net" 15 "os" 16 "strings" 17 "time" 18 19 http "gitee.com/ks-custle/core-gm/gmhttp" 20 "gitee.com/ks-custle/core-gm/gmhttp/cgi" 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 // envVarsContextKey uniquely identifies a mapping of CGI 35 // environment variables to their values in a request context 36 type envVarsContextKey struct{} 37 38 func newRequest(reqId uint16, flags uint8) *request { 39 r := &request{ 40 reqId: reqId, 41 params: map[string]string{}, 42 keepConn: flags&flagKeepConn != 0, 43 } 44 r.rawParams = r.buf[:0] 45 return r 46 } 47 48 // parseParams reads an encoded []byte into Params. 49 func (r *request) parseParams() { 50 text := r.rawParams 51 r.rawParams = nil 52 for len(text) > 0 { 53 keyLen, n := readSize(text) 54 if n == 0 { 55 return 56 } 57 text = text[n:] 58 valLen, n := readSize(text) 59 if n == 0 { 60 return 61 } 62 text = text[n:] 63 if int(keyLen)+int(valLen) > len(text) { 64 return 65 } 66 key := readString(text, keyLen) 67 text = text[keyLen:] 68 val := readString(text, valLen) 69 text = text[valLen:] 70 r.params[key] = val 71 } 72 } 73 74 // response implements http.ResponseWriter. 75 type response struct { 76 req *request 77 header http.Header 78 code int 79 wroteHeader bool 80 wroteCGIHeader bool 81 w *bufWriter 82 } 83 84 func newResponse(c *child, req *request) *response { 85 return &response{ 86 req: req, 87 header: http.Header{}, 88 w: newWriter(c.conn, typeStdout, req.reqId), 89 } 90 } 91 92 func (r *response) Header() http.Header { 93 return r.header 94 } 95 96 func (r *response) Write(p []byte) (n int, err error) { 97 if !r.wroteHeader { 98 r.WriteHeader(http.StatusOK) 99 } 100 if !r.wroteCGIHeader { 101 r.writeCGIHeader(p) 102 } 103 return r.w.Write(p) 104 } 105 106 func (r *response) WriteHeader(code int) { 107 if r.wroteHeader { 108 return 109 } 110 r.wroteHeader = true 111 r.code = code 112 if code == http.StatusNotModified { 113 // Must not have body. 114 r.header.Del("Content-Type") 115 r.header.Del("Content-Length") 116 r.header.Del("Transfer-Encoding") 117 } 118 if r.header.Get("Date") == "" { 119 r.header.Set("Date", time.Now().UTC().Format(http.TimeFormat)) 120 } 121 } 122 123 // writeCGIHeader finalizes the header sent to the client and writes it to the output. 124 // p is not written by writeHeader, but is the first chunk of the body 125 // that will be written. It is sniffed for a Content-Type if none is 126 // set explicitly. 127 func (r *response) writeCGIHeader(p []byte) { 128 if r.wroteCGIHeader { 129 return 130 } 131 r.wroteCGIHeader = true 132 _, _ = fmt.Fprintf(r.w, "Status: %d %s\r\n", r.code, http.StatusText(r.code)) 133 if _, hasType := r.header["Content-Type"]; r.code != http.StatusNotModified && !hasType { 134 r.header.Set("Content-Type", http.DetectContentType(p)) 135 } 136 _ = r.header.Write(r.w) 137 _, _ = r.w.WriteString("\r\n") 138 _ = r.w.Flush() 139 } 140 141 func (r *response) Flush() { 142 if !r.wroteHeader { 143 r.WriteHeader(http.StatusOK) 144 } 145 _ = r.w.Flush() 146 } 147 148 func (r *response) Close() error { 149 r.Flush() 150 return r.w.Close() 151 } 152 153 type child struct { 154 conn *conn 155 handler http.Handler 156 157 requests map[uint16]*request // keyed by request ID 158 } 159 160 func newChild(rwc io.ReadWriteCloser, handler http.Handler) *child { 161 return &child{ 162 conn: newConn(rwc), 163 handler: handler, 164 requests: make(map[uint16]*request), 165 } 166 } 167 168 func (c *child) serve() { 169 defer func(conn *conn) { 170 _ = conn.Close() 171 }(c.conn) 172 defer c.cleanUp() 173 var rec record 174 for { 175 if err := rec.read(c.conn.rwc); err != nil { 176 return 177 } 178 if err := c.handleRecord(&rec); err != nil { 179 return 180 } 181 } 182 } 183 184 var errCloseConn = errors.New("fcgi: connection should be closed") 185 186 var emptyBody = io.NopCloser(strings.NewReader("")) 187 188 // ErrRequestAborted is returned by Read when a handler attempts to read the 189 // body of a request that has been aborted by the web server. 190 var ErrRequestAborted = errors.New("fcgi: request aborted by web server") 191 192 // ErrConnClosed is returned by Read when a handler attempts to read the body of 193 // a request after the connection to the web server has been closed. 194 var ErrConnClosed = errors.New("fcgi: connection to web server closed") 195 196 func (c *child) handleRecord(rec *record) error { 197 req, ok := c.requests[rec.h.Id] 198 if !ok && rec.h.Type != typeBeginRequest && rec.h.Type != typeGetValues { 199 // The spec says to ignore unknown request IDs. 200 return nil 201 } 202 203 switch rec.h.Type { 204 case typeBeginRequest: 205 if req != nil { 206 // The server is trying to begin a request with the same ID 207 // as an in-progress request. This is an error. 208 return errors.New("fcgi: received ID that is already in-flight") 209 } 210 211 var br beginRequest 212 if err := br.read(rec.content()); err != nil { 213 return err 214 } 215 if br.role != roleResponder { 216 _ = c.conn.writeEndRequest(rec.h.Id, 0, statusUnknownRole) 217 return nil 218 } 219 req = newRequest(rec.h.Id, br.flags) 220 c.requests[rec.h.Id] = req 221 return nil 222 case typeParams: 223 // NOTE(eds): Technically a key-value pair can straddle the boundary 224 // between two packets. We buffer until we've received all parameters. 225 if len(rec.content()) > 0 { 226 req.rawParams = append(req.rawParams, rec.content()...) 227 return nil 228 } 229 req.parseParams() 230 return nil 231 case typeStdin: 232 content := rec.content() 233 if req.pw == nil { 234 var body io.ReadCloser 235 if len(content) > 0 { 236 // body could be an io.LimitReader, but it shouldn't matter 237 // as long as both sides are behaving. 238 body, req.pw = io.Pipe() 239 } else { 240 body = emptyBody 241 } 242 go c.serveRequest(req, body) 243 } 244 if len(content) > 0 { 245 // TODO(eds): This blocks until the handler reads from the pipe. 246 // If the handler takes a long time, it might be a problem. 247 _, _ = req.pw.Write(content) 248 } else { 249 delete(c.requests, req.reqId) 250 if req.pw != nil { 251 _ = req.pw.Close() 252 } 253 } 254 return nil 255 case typeGetValues: 256 values := map[string]string{"FCGI_MPXS_CONNS": "1"} 257 _ = c.conn.writePairs(typeGetValuesResult, 0, values) 258 return nil 259 case typeData: 260 // If the filter role is implemented, read the data stream here. 261 return nil 262 case typeAbortRequest: 263 delete(c.requests, rec.h.Id) 264 _ = c.conn.writeEndRequest(rec.h.Id, 0, statusRequestComplete) 265 if req.pw != nil { 266 _ = req.pw.CloseWithError(ErrRequestAborted) 267 } 268 if !req.keepConn { 269 // connection will close upon return 270 return errCloseConn 271 } 272 return nil 273 default: 274 b := make([]byte, 8) 275 b[0] = byte(rec.h.Type) 276 _ = c.conn.writeRecord(typeUnknownType, 0, b) 277 return nil 278 } 279 } 280 281 // filterOutUsedEnvVars returns a new map of env vars without the 282 // variables in the given envVars map that are read for creating each http.Request 283 func filterOutUsedEnvVars(envVars map[string]string) map[string]string { 284 withoutUsedEnvVars := make(map[string]string) 285 for k, v := range envVars { 286 if addFastCGIEnvToContext(k) { 287 withoutUsedEnvVars[k] = v 288 } 289 } 290 return withoutUsedEnvVars 291 } 292 293 func (c *child) serveRequest(req *request, body io.ReadCloser) { 294 r := newResponse(c, req) 295 httpReq, err := cgi.RequestFromMap(req.params) 296 if err != nil { 297 // there was an error reading the request 298 r.WriteHeader(http.StatusInternalServerError) 299 _ = c.conn.writeRecord(typeStderr, req.reqId, []byte(err.Error())) 300 } else { 301 httpReq.Body = body 302 withoutUsedEnvVars := filterOutUsedEnvVars(req.params) 303 envVarCtx := context.WithValue(httpReq.Context(), envVarsContextKey{}, withoutUsedEnvVars) 304 httpReq = httpReq.WithContext(envVarCtx) 305 c.handler.ServeHTTP(r, httpReq) 306 } 307 // Make sure we serve something even if nothing was written to r 308 _, _ = r.Write(nil) 309 _ = r.Close() 310 _ = c.conn.writeEndRequest(req.reqId, 0, statusRequestComplete) 311 312 // Consume the entire body, so the host isn't still writing to 313 // us when we close the socket below in the !keepConn case, 314 // otherwise we'd send a RST. (golang.org/issue/4183) 315 // TODO(bradfitz): also bound this copy in time. Or send 316 // some sort of abort request to the host, so the host 317 // can properly cut off the client sending all the data. 318 // For now just bound it a little and 319 _, _ = io.CopyN(io.Discard, body, 100<<20) 320 _ = body.Close() 321 322 if !req.keepConn { 323 _ = c.conn.Close() 324 } 325 } 326 327 func (c *child) cleanUp() { 328 for _, req := range c.requests { 329 if req.pw != nil { 330 // race with call to Close in c.serveRequest doesn't matter because 331 // Pipe(Reader|Writer).Close are idempotent 332 _ = req.pw.CloseWithError(ErrConnClosed) 333 } 334 } 335 } 336 337 // Serve accepts incoming FastCGI connections on the listener l, creating a new 338 // goroutine for each. The goroutine reads requests and then calls handler 339 // to reply to them. 340 // If l is nil, Serve accepts connections from os.Stdin. 341 // If handler is nil, http.DefaultServeMux is used. 342 // 343 //goland:noinspection GoUnusedExportedFunction 344 func Serve(l net.Listener, handler http.Handler) error { 345 if l == nil { 346 var err error 347 l, err = net.FileListener(os.Stdin) 348 if err != nil { 349 return err 350 } 351 defer func(l net.Listener) { 352 _ = l.Close() 353 }(l) 354 } 355 if handler == nil { 356 handler = http.DefaultServeMux 357 } 358 for { 359 rw, err := l.Accept() 360 if err != nil { 361 return err 362 } 363 c := newChild(rw, handler) 364 go c.serve() 365 } 366 } 367 368 // ProcessEnv returns FastCGI environment variables associated with the request r 369 // for which no effort was made to be included in the request itself - the data 370 // is hidden in the request's context. As an example, if REMOTE_USER is set for a 371 // request, it will not be found anywhere in r, but it will be included in 372 // ProcessEnv's response (via r's context). 373 func ProcessEnv(r *http.Request) map[string]string { 374 env, _ := r.Context().Value(envVarsContextKey{}).(map[string]string) 375 return env 376 } 377 378 // addFastCGIEnvToContext reports whether to include the FastCGI environment variable s 379 // in the http.Request.Context, accessible via ProcessEnv. 380 func addFastCGIEnvToContext(s string) bool { 381 // Exclude things supported by net/http natively: 382 switch s { 383 case "CONTENT_LENGTH", "CONTENT_TYPE", "HTTPS", 384 "PATH_INFO", "QUERY_STRING", "REMOTE_ADDR", 385 "REMOTE_HOST", "REMOTE_PORT", "REQUEST_METHOD", 386 "REQUEST_URI", "SCRIPT_NAME", "SERVER_PROTOCOL": 387 return false 388 } 389 if strings.HasPrefix(s, "HTTP_") { 390 return false 391 } 392 // Explicitly include FastCGI-specific things. 393 // This list is redundant with the default "return true" below. 394 // Consider this documentation of the sorts of things we expect 395 // to maybe see. 396 switch s { 397 case "REMOTE_USER": 398 return true 399 } 400 // Unknown, so include it to be safe. 401 return true 402 }