OSDN Git Service

Update Go library to r60.
[pf3gnuchains/gcc-fork.git] / libgo / go / http / cgi / child.go
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 // This file implements CGI from the perspective of a child
6 // process.
7
8 package cgi
9
10 import (
11         "bufio"
12         "crypto/tls"
13         "fmt"
14         "http"
15         "io"
16         "io/ioutil"
17         "net"
18         "os"
19         "strconv"
20         "strings"
21         "url"
22 )
23
24 // Request returns the HTTP request as represented in the current
25 // environment. This assumes the current program is being run
26 // by a web server in a CGI environment.
27 // The returned Request's Body is populated, if applicable.
28 func Request() (*http.Request, os.Error) {
29         r, err := RequestFromMap(envMap(os.Environ()))
30         if err != nil {
31                 return nil, err
32         }
33         if r.ContentLength > 0 {
34                 r.Body = ioutil.NopCloser(io.LimitReader(os.Stdin, r.ContentLength))
35         }
36         return r, nil
37 }
38
39 func envMap(env []string) map[string]string {
40         m := make(map[string]string)
41         for _, kv := range env {
42                 if idx := strings.Index(kv, "="); idx != -1 {
43                         m[kv[:idx]] = kv[idx+1:]
44                 }
45         }
46         return m
47 }
48
49 // RequestFromMap creates an http.Request from CGI variables.
50 // The returned Request's Body field is not populated.
51 func RequestFromMap(params map[string]string) (*http.Request, os.Error) {
52         r := new(http.Request)
53         r.Method = params["REQUEST_METHOD"]
54         if r.Method == "" {
55                 return nil, os.NewError("cgi: no REQUEST_METHOD in environment")
56         }
57
58         r.Proto = params["SERVER_PROTOCOL"]
59         var ok bool
60         r.ProtoMajor, r.ProtoMinor, ok = http.ParseHTTPVersion(r.Proto)
61         if !ok {
62                 return nil, os.NewError("cgi: invalid SERVER_PROTOCOL version")
63         }
64
65         r.Close = true
66         r.Trailer = http.Header{}
67         r.Header = http.Header{}
68
69         r.Host = params["HTTP_HOST"]
70
71         if lenstr := params["CONTENT_LENGTH"]; lenstr != "" {
72                 clen, err := strconv.Atoi64(lenstr)
73                 if err != nil {
74                         return nil, os.NewError("cgi: bad CONTENT_LENGTH in environment: " + lenstr)
75                 }
76                 r.ContentLength = clen
77         }
78
79         if ct := params["CONTENT_TYPE"]; ct != "" {
80                 r.Header.Set("Content-Type", ct)
81         }
82
83         // Copy "HTTP_FOO_BAR" variables to "Foo-Bar" Headers
84         for k, v := range params {
85                 if !strings.HasPrefix(k, "HTTP_") || k == "HTTP_HOST" {
86                         continue
87                 }
88                 r.Header.Add(strings.Replace(k[5:], "_", "-", -1), v)
89         }
90
91         // TODO: cookies.  parsing them isn't exported, though.
92
93         if r.Host != "" {
94                 // Hostname is provided, so we can reasonably construct a URL,
95                 // even if we have to assume 'http' for the scheme.
96                 r.RawURL = "http://" + r.Host + params["REQUEST_URI"]
97                 url, err := url.Parse(r.RawURL)
98                 if err != nil {
99                         return nil, os.NewError("cgi: failed to parse host and REQUEST_URI into a URL: " + r.RawURL)
100                 }
101                 r.URL = url
102         }
103         // Fallback logic if we don't have a Host header or the URL
104         // failed to parse
105         if r.URL == nil {
106                 r.RawURL = params["REQUEST_URI"]
107                 url, err := url.Parse(r.RawURL)
108                 if err != nil {
109                         return nil, os.NewError("cgi: failed to parse REQUEST_URI into a URL: " + r.RawURL)
110                 }
111                 r.URL = url
112         }
113
114         // There's apparently a de-facto standard for this.
115         // http://docstore.mik.ua/orelly/linux/cgi/ch03_02.htm#ch03-35636
116         if s := params["HTTPS"]; s == "on" || s == "ON" || s == "1" {
117                 r.TLS = &tls.ConnectionState{HandshakeComplete: true}
118         }
119
120         // Request.RemoteAddr has its port set by Go's standard http
121         // server, so we do here too. We don't have one, though, so we
122         // use a dummy one.
123         r.RemoteAddr = net.JoinHostPort(params["REMOTE_ADDR"], "0")
124
125         return r, nil
126 }
127
128 // Serve executes the provided Handler on the currently active CGI
129 // request, if any. If there's no current CGI environment
130 // an error is returned. The provided handler may be nil to use
131 // http.DefaultServeMux.
132 func Serve(handler http.Handler) os.Error {
133         req, err := Request()
134         if err != nil {
135                 return err
136         }
137         if handler == nil {
138                 handler = http.DefaultServeMux
139         }
140         rw := &response{
141                 req:    req,
142                 header: make(http.Header),
143                 bufw:   bufio.NewWriter(os.Stdout),
144         }
145         handler.ServeHTTP(rw, req)
146         if err = rw.bufw.Flush(); err != nil {
147                 return err
148         }
149         return nil
150 }
151
152 type response struct {
153         req        *http.Request
154         header     http.Header
155         bufw       *bufio.Writer
156         headerSent bool
157 }
158
159 func (r *response) Flush() {
160         r.bufw.Flush()
161 }
162
163 func (r *response) Header() http.Header {
164         return r.header
165 }
166
167 func (r *response) Write(p []byte) (n int, err os.Error) {
168         if !r.headerSent {
169                 r.WriteHeader(http.StatusOK)
170         }
171         return r.bufw.Write(p)
172 }
173
174 func (r *response) WriteHeader(code int) {
175         if r.headerSent {
176                 // Note: explicitly using Stderr, as Stdout is our HTTP output.
177                 fmt.Fprintf(os.Stderr, "CGI attempted to write header twice on request for %s", r.req.URL)
178                 return
179         }
180         r.headerSent = true
181         fmt.Fprintf(r.bufw, "Status: %d %s\r\n", code, http.StatusText(code))
182
183         // Set a default Content-Type
184         if _, hasType := r.header["Content-Type"]; !hasType {
185                 r.header.Add("Content-Type", "text/html; charset=utf-8")
186         }
187
188         r.header.Write(r.bufw)
189         r.bufw.WriteString("\r\n")
190         r.bufw.Flush()
191 }