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