OpenCores
URL https://opencores.org/ocsvn/openrisc/openrisc/trunk

Subversion Repositories openrisc

[/] [openrisc/] [trunk/] [gnu-dev/] [or1k-gcc/] [libgo/] [go/] [net/] [http/] [fcgi/] [child.go] - Rev 747

Compare with Previous | Blame | View Log

// 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 (
        "errors"
        "fmt"
        "io"
        "net"
        "net/http"
        "net/http/cgi"
        "os"
        "time"
)

// 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
}

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

// parseParams reads an encoded []byte into Params.
func (r *request) parseParams() {
        text := r.rawParams
        r.rawParams = nil
        for len(text) > 0 {
                keyLen, n := readSize(text)
                if n == 0 {
                        return
                }
                text = text[n:]
                valLen, n := readSize(text)
                if n == 0 {
                        return
                }
                text = text[n:]
                key := readString(text, keyLen)
                text = text[keyLen:]
                val := readString(text, valLen)
                text = text[valLen:]
                r.params[key] = val
        }
}

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

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

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

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

func (r *response) WriteHeader(code int) {
        if r.wroteHeader {
                return
        }
        r.wroteHeader = true
        if code == http.StatusNotModified {
                // Must not have body.
                r.header.Del("Content-Type")
                r.header.Del("Content-Length")
                r.header.Del("Transfer-Encoding")
        } else if r.header.Get("Content-Type") == "" {
                r.header.Set("Content-Type", "text/html; charset=utf-8")
        }

        if r.header.Get("Date") == "" {
                r.header.Set("Date", time.Now().UTC().Format(http.TimeFormat))
        }

        fmt.Fprintf(r.w, "Status: %d %s\r\n", code, http.StatusText(code))
        r.header.Write(r.w)
        r.w.WriteString("\r\n")
}

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

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

type child struct {
        conn     *conn
        handler  http.Handler
        requests map[uint16]*request // keyed by request ID
}

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

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

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

func (c *child) handleRecord(rec *record) error {
        req, ok := c.requests[rec.h.Id]
        if !ok && rec.h.Type != typeBeginRequest && rec.h.Type != typeGetValues {
                // The spec says to ignore unknown request IDs.
                return nil
        }
        if ok && rec.h.Type == typeBeginRequest {
                // 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")
        }

        switch rec.h.Type {
        case typeBeginRequest:
                var br beginRequest
                if err := br.read(rec.content()); err != nil {
                        return err
                }
                if br.role != roleResponder {
                        c.conn.writeEndRequest(rec.h.Id, 0, statusUnknownRole)
                        return nil
                }
                c.requests[rec.h.Id] = newRequest(rec.h.Id, br.flags)
        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(rec.content()) > 0 {
                        req.rawParams = append(req.rawParams, rec.content()...)
                        return nil
                }
                req.parseParams()
        case typeStdin:
                content := rec.content()
                if req.pw == nil {
                        var body io.ReadCloser
                        if len(content) > 0 {
                                // body could be an io.LimitReader, but it shouldn't matter
                                // as long as both sides are behaving.
                                body, req.pw = io.Pipe()
                        }
                        go c.serveRequest(req, body)
                }
                if len(content) > 0 {
                        // TODO(eds): This blocks until the handler reads from the pipe.
                        // If the handler takes a long time, it might be a problem.
                        req.pw.Write(content)
                } else if req.pw != nil {
                        req.pw.Close()
                }
        case typeGetValues:
                values := map[string]string{"FCGI_MPXS_CONNS": "1"}
                c.conn.writePairs(typeGetValuesResult, 0, values)
        case typeData:
                // If the filter role is implemented, read the data stream here.
        case typeAbortRequest:
                delete(c.requests, rec.h.Id)
                c.conn.writeEndRequest(rec.h.Id, 0, statusRequestComplete)
                if !req.keepConn {
                        // connection will close upon return
                        return errCloseConn
                }
        default:
                b := make([]byte, 8)
                b[0] = byte(rec.h.Type)
                c.conn.writeRecord(typeUnknownType, 0, b)
        }
        return nil
}

func (c *child) serveRequest(req *request, body io.ReadCloser) {
        r := newResponse(c, req)
        httpReq, err := cgi.RequestFromMap(req.params)
        if err != nil {
                // there was an error reading the request
                r.WriteHeader(http.StatusInternalServerError)
                c.conn.writeRecord(typeStderr, req.reqId, []byte(err.Error()))
        } else {
                httpReq.Body = body
                c.handler.ServeHTTP(r, httpReq)
        }
        if body != nil {
                body.Close()
        }
        r.Close()
        c.conn.writeEndRequest(req.reqId, 0, statusRequestComplete)
        if !req.keepConn {
                c.conn.Close()
        }
}

// Serve accepts incoming FastCGI connections on the listener l, creating a new
// service thread for each. The service threads read requests and then call handler
// to reply to them.
// If l is nil, Serve accepts connections on stdin.
// If handler is nil, http.DefaultServeMux is used.
func Serve(l net.Listener, handler http.Handler) error {
        if l == nil {
                var err error
                l, err = net.FileListener(os.Stdin)
                if err != nil {
                        return err
                }
                defer l.Close()
        }
        if handler == nil {
                handler = http.DefaultServeMux
        }
        for {
                rw, err := l.Accept()
                if err != nil {
                        return err
                }
                c := newChild(rw, handler)
                go c.serve()
        }
        panic("unreachable")
}

Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.