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

Subversion Repositories openrisc

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

Go to most recent revision | 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.

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

package cgi

import (
        "bufio"
        "crypto/tls"
        "errors"
        "fmt"
        "io"
        "io/ioutil"
        "net"
        "net/http"
        "net/url"
        "os"
        "strconv"
        "strings"
)

// Request returns the HTTP request as represented in the current
// environment. This assumes the current program is being run
// by a web server in a CGI environment.
// The returned Request's Body is populated, if applicable.
func Request() (*http.Request, error) {
        r, err := RequestFromMap(envMap(os.Environ()))
        if err != nil {
                return nil, err
        }
        if r.ContentLength > 0 {
                r.Body = ioutil.NopCloser(io.LimitReader(os.Stdin, r.ContentLength))
        }
        return r, nil
}

func envMap(env []string) map[string]string {
        m := make(map[string]string)
        for _, kv := range env {
                if idx := strings.Index(kv, "="); idx != -1 {
                        m[kv[:idx]] = kv[idx+1:]
                }
        }
        return m
}

// RequestFromMap creates an http.Request from CGI variables.
// The returned Request's Body field is not populated.
func RequestFromMap(params map[string]string) (*http.Request, error) {
        r := new(http.Request)
        r.Method = params["REQUEST_METHOD"]
        if r.Method == "" {
                return nil, errors.New("cgi: no REQUEST_METHOD in environment")
        }

        r.Proto = params["SERVER_PROTOCOL"]
        var ok bool
        r.ProtoMajor, r.ProtoMinor, ok = http.ParseHTTPVersion(r.Proto)
        if !ok {
                return nil, errors.New("cgi: invalid SERVER_PROTOCOL version")
        }

        r.Close = true
        r.Trailer = http.Header{}
        r.Header = http.Header{}

        r.Host = params["HTTP_HOST"]

        if lenstr := params["CONTENT_LENGTH"]; lenstr != "" {
                clen, err := strconv.ParseInt(lenstr, 10, 64)
                if err != nil {
                        return nil, errors.New("cgi: bad CONTENT_LENGTH in environment: " + lenstr)
                }
                r.ContentLength = clen
        }

        if ct := params["CONTENT_TYPE"]; ct != "" {
                r.Header.Set("Content-Type", ct)
        }

        // Copy "HTTP_FOO_BAR" variables to "Foo-Bar" Headers
        for k, v := range params {
                if !strings.HasPrefix(k, "HTTP_") || k == "HTTP_HOST" {
                        continue
                }
                r.Header.Add(strings.Replace(k[5:], "_", "-", -1), v)
        }

        // TODO: cookies.  parsing them isn't exported, though.

        if r.Host != "" {
                // Hostname is provided, so we can reasonably construct a URL,
                // even if we have to assume 'http' for the scheme.
                rawurl := "http://" + r.Host + params["REQUEST_URI"]
                url, err := url.Parse(rawurl)
                if err != nil {
                        return nil, errors.New("cgi: failed to parse host and REQUEST_URI into a URL: " + rawurl)
                }
                r.URL = url
        }
        // Fallback logic if we don't have a Host header or the URL
        // failed to parse
        if r.URL == nil {
                uriStr := params["REQUEST_URI"]
                url, err := url.Parse(uriStr)
                if err != nil {
                        return nil, errors.New("cgi: failed to parse REQUEST_URI into a URL: " + uriStr)
                }
                r.URL = url
        }

        // There's apparently a de-facto standard for this.
        // http://docstore.mik.ua/orelly/linux/cgi/ch03_02.htm#ch03-35636
        if s := params["HTTPS"]; s == "on" || s == "ON" || s == "1" {
                r.TLS = &tls.ConnectionState{HandshakeComplete: true}
        }

        // Request.RemoteAddr has its port set by Go's standard http
        // server, so we do here too. We don't have one, though, so we
        // use a dummy one.
        r.RemoteAddr = net.JoinHostPort(params["REMOTE_ADDR"], "0")

        return r, nil
}

// Serve executes the provided Handler on the currently active CGI
// request, if any. If there's no current CGI environment
// an error is returned. The provided handler may be nil to use
// http.DefaultServeMux.
func Serve(handler http.Handler) error {
        req, err := Request()
        if err != nil {
                return err
        }
        if handler == nil {
                handler = http.DefaultServeMux
        }
        rw := &response{
                req:    req,
                header: make(http.Header),
                bufw:   bufio.NewWriter(os.Stdout),
        }
        handler.ServeHTTP(rw, req)
        if err = rw.bufw.Flush(); err != nil {
                return err
        }
        return nil
}

type response struct {
        req        *http.Request
        header     http.Header
        bufw       *bufio.Writer
        headerSent bool
}

func (r *response) Flush() {
        r.bufw.Flush()
}

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

func (r *response) Write(p []byte) (n int, err error) {
        if !r.headerSent {
                r.WriteHeader(http.StatusOK)
        }
        return r.bufw.Write(p)
}

func (r *response) WriteHeader(code int) {
        if r.headerSent {
                // Note: explicitly using Stderr, as Stdout is our HTTP output.
                fmt.Fprintf(os.Stderr, "CGI attempted to write header twice on request for %s", r.req.URL)
                return
        }
        r.headerSent = true
        fmt.Fprintf(r.bufw, "Status: %d %s\r\n", code, http.StatusText(code))

        // Set a default Content-Type
        if _, hasType := r.header["Content-Type"]; !hasType {
                r.header.Add("Content-Type", "text/html; charset=utf-8")
        }

        r.header.Write(r.bufw)
        r.bufw.WriteString("\r\n")
        r.bufw.Flush()
}

Go to most recent revision | 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.