// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package fcgi

// This file implements FastCGI from the perspective of a child process.

import (
	
	
	
	
	
	
	
	
	
	
)

// request holds the state for an in-progress request. As soon as it's complete,
// it's converted to an http.Request.
type request struct {
	pw        *io.PipeWriter
	reqId     uint16
	params    map[string]string
	buf       [1024]byte
	rawParams []byte
	keepConn  bool
}

// envVarsContextKey uniquely identifies a mapping of CGI
// environment variables to their values in a request context
type envVarsContextKey struct{}

func newRequest( uint16,  uint8) *request {
	 := &request{
		reqId:    ,
		params:   map[string]string{},
		keepConn: &flagKeepConn != 0,
	}
	.rawParams = .buf[:0]
	return 
}

// parseParams reads an encoded []byte into Params.
func ( *request) () {
	 := .rawParams
	.rawParams = nil
	for len() > 0 {
		,  := readSize()
		if  == 0 {
			return
		}
		 = [:]
		,  := readSize()
		if  == 0 {
			return
		}
		 = [:]
		if int()+int() > len() {
			return
		}
		 := readString(, )
		 = [:]
		 := readString(, )
		 = [:]
		.params[] = 
	}
}

// response implements http.ResponseWriter.
type response struct {
	req            *request
	header         http.Header
	code           int
	wroteHeader    bool
	wroteCGIHeader bool
	w              *bufWriter
}

func newResponse( *child,  *request) *response {
	return &response{
		req:    ,
		header: http.Header{},
		w:      newWriter(.conn, typeStdout, .reqId),
	}
}

func ( *response) () http.Header {
	return .header
}

func ( *response) ( []byte) ( int,  error) {
	if !.wroteHeader {
		.WriteHeader(http.StatusOK)
	}
	if !.wroteCGIHeader {
		.writeCGIHeader()
	}
	return .w.Write()
}

func ( *response) ( int) {
	if .wroteHeader {
		return
	}
	.wroteHeader = true
	.code = 
	if  == http.StatusNotModified {
		// Must not have body.
		.header.Del("Content-Type")
		.header.Del("Content-Length")
		.header.Del("Transfer-Encoding")
	}
	if .header.Get("Date") == "" {
		.header.Set("Date", time.Now().UTC().Format(http.TimeFormat))
	}
}

// writeCGIHeader finalizes the header sent to the client and writes it to the output.
// p is not written by writeHeader, but is the first chunk of the body
// that will be written. It is sniffed for a Content-Type if none is
// set explicitly.
func ( *response) ( []byte) {
	if .wroteCGIHeader {
		return
	}
	.wroteCGIHeader = true
	fmt.Fprintf(.w, "Status: %d %s\r\n", .code, http.StatusText(.code))
	if ,  := .header["Content-Type"]; .code != http.StatusNotModified && ! {
		.header.Set("Content-Type", http.DetectContentType())
	}
	.header.Write(.w)
	.w.WriteString("\r\n")
	.w.Flush()
}

func ( *response) () {
	if !.wroteHeader {
		.WriteHeader(http.StatusOK)
	}
	.w.Flush()
}

func ( *response) () error {
	.Flush()
	return .w.Close()
}

type child struct {
	conn    *conn
	handler http.Handler

	requests map[uint16]*request // keyed by request ID
}

func newChild( io.ReadWriteCloser,  http.Handler) *child {
	return &child{
		conn:     newConn(),
		handler:  ,
		requests: make(map[uint16]*request),
	}
}

func ( *child) () {
	defer .conn.Close()
	defer .cleanUp()
	var  record
	for {
		if  := .read(.conn.rwc);  != nil {
			return
		}
		if  := .handleRecord(&);  != nil {
			return
		}
	}
}

var errCloseConn = errors.New("fcgi: connection should be closed")

var emptyBody = io.NopCloser(strings.NewReader(""))

// ErrRequestAborted is returned by Read when a handler attempts to read the
// body of a request that has been aborted by the web server.
var ErrRequestAborted = errors.New("fcgi: request aborted by web server")

// ErrConnClosed is returned by Read when a handler attempts to read the body of
// a request after the connection to the web server has been closed.
var ErrConnClosed = errors.New("fcgi: connection to web server closed")

func ( *child) ( *record) error {
	,  := .requests[.h.Id]
	if ! && .h.Type != typeBeginRequest && .h.Type != typeGetValues {
		// The spec says to ignore unknown request IDs.
		return nil
	}

	switch .h.Type {
	case typeBeginRequest:
		if  != nil {
			// The server is trying to begin a request with the same ID
			// as an in-progress request. This is an error.
			return errors.New("fcgi: received ID that is already in-flight")
		}

		var  beginRequest
		if  := .read(.content());  != nil {
			return 
		}
		if .role != roleResponder {
			.conn.writeEndRequest(.h.Id, 0, statusUnknownRole)
			return nil
		}
		 = newRequest(.h.Id, .flags)
		.requests[.h.Id] = 
		return nil
	case typeParams:
		// NOTE(eds): Technically a key-value pair can straddle the boundary
		// between two packets. We buffer until we've received all parameters.
		if len(.content()) > 0 {
			.rawParams = append(.rawParams, .content()...)
			return nil
		}
		.parseParams()
		return nil
	case typeStdin:
		 := .content()
		if .pw == nil {
			var  io.ReadCloser
			if len() > 0 {
				// body could be an io.LimitReader, but it shouldn't matter
				// as long as both sides are behaving.
				, .pw = io.Pipe()
			} else {
				 = emptyBody
			}
			go .serveRequest(, )
		}
		if len() > 0 {
			// TODO(eds): This blocks until the handler reads from the pipe.
			// If the handler takes a long time, it might be a problem.
			.pw.Write()
		} else {
			delete(.requests, .reqId)
			if .pw != nil {
				.pw.Close()
			}
		}
		return nil
	case typeGetValues:
		 := map[string]string{"FCGI_MPXS_CONNS": "1"}
		.conn.writePairs(typeGetValuesResult, 0, )
		return nil
	case typeData:
		// If the filter role is implemented, read the data stream here.
		return nil
	case typeAbortRequest:
		delete(.requests, .h.Id)
		.conn.writeEndRequest(.h.Id, 0, statusRequestComplete)
		if .pw != nil {
			.pw.CloseWithError(ErrRequestAborted)
		}
		if !.keepConn {
			// connection will close upon return
			return errCloseConn
		}
		return nil
	default:
		 := make([]byte, 8)
		[0] = byte(.h.Type)
		.conn.writeRecord(typeUnknownType, 0, )
		return nil
	}
}

// filterOutUsedEnvVars returns a new map of env vars without the
// variables in the given envVars map that are read for creating each http.Request
func filterOutUsedEnvVars( map[string]string) map[string]string {
	 := make(map[string]string)
	for ,  := range  {
		if addFastCGIEnvToContext() {
			[] = 
		}
	}
	return 
}

func ( *child) ( *request,  io.ReadCloser) {
	 := newResponse(, )
	,  := cgi.RequestFromMap(.params)
	if  != nil {
		// there was an error reading the request
		.WriteHeader(http.StatusInternalServerError)
		.conn.writeRecord(typeStderr, .reqId, []byte(.Error()))
	} else {
		.Body = 
		 := filterOutUsedEnvVars(.params)
		 := context.WithValue(.Context(), envVarsContextKey{}, )
		 = .WithContext()
		.handler.ServeHTTP(, )
	}
	// Make sure we serve something even if nothing was written to r
	.Write(nil)
	.Close()
	.conn.writeEndRequest(.reqId, 0, statusRequestComplete)

	// Consume the entire body, so the host isn't still writing to
	// us when we close the socket below in the !keepConn case,
	// otherwise we'd send a RST. (golang.org/issue/4183)
	// TODO(bradfitz): also bound this copy in time. Or send
	// some sort of abort request to the host, so the host
	// can properly cut off the client sending all the data.
	// For now just bound it a little and
	io.CopyN(io.Discard, , 100<<20)
	.Close()

	if !.keepConn {
		.conn.Close()
	}
}

func ( *child) () {
	for ,  := range .requests {
		if .pw != nil {
			// race with call to Close in c.serveRequest doesn't matter because
			// Pipe(Reader|Writer).Close are idempotent
			.pw.CloseWithError(ErrConnClosed)
		}
	}
}

// Serve accepts incoming FastCGI connections on the listener l, creating a new
// goroutine for each. The goroutine reads requests and then calls handler
// to reply to them.
// If l is nil, Serve accepts connections from os.Stdin.
// If handler is nil, [http.DefaultServeMux] is used.
func ( net.Listener,  http.Handler) error {
	if  == nil {
		var  error
		,  = net.FileListener(os.Stdin)
		if  != nil {
			return 
		}
		defer .Close()
	}
	if  == nil {
		 = http.DefaultServeMux
	}
	for {
		,  := .Accept()
		if  != nil {
			return 
		}
		 := newChild(, )
		go .serve()
	}
}

// ProcessEnv returns FastCGI environment variables associated with the request r
// for which no effort was made to be included in the request itself - the data
// is hidden in the request's context. As an example, if REMOTE_USER is set for a
// request, it will not be found anywhere in r, but it will be included in
// ProcessEnv's response (via r's context).
func ( *http.Request) map[string]string {
	,  := .Context().Value(envVarsContextKey{}).(map[string]string)
	return 
}

// addFastCGIEnvToContext reports whether to include the FastCGI environment variable s
// in the http.Request.Context, accessible via ProcessEnv.
func addFastCGIEnvToContext( string) bool {
	// Exclude things supported by net/http natively:
	switch  {
	case "CONTENT_LENGTH", "CONTENT_TYPE", "HTTPS",
		"PATH_INFO", "QUERY_STRING", "REMOTE_ADDR",
		"REMOTE_HOST", "REMOTE_PORT", "REQUEST_METHOD",
		"REQUEST_URI", "SCRIPT_NAME", "SERVER_PROTOCOL":
		return false
	}
	if strings.HasPrefix(, "HTTP_") {
		return false
	}
	// Explicitly include FastCGI-specific things.
	// This list is redundant with the default "return true" below.
	// Consider this documentation of the sorts of things we expect
	// to maybe see.
	switch  {
	case "REMOTE_USER":
		return true
	}
	// Unknown, so include it to be safe.
	return true
}