~nskaggs/+junk/xenial-test

« back to all changes in this revision

Viewing changes to src/github.com/Azure/azure-sdk-for-go/core/http/cgi/child.go

  • Committer: Nicholas Skaggs
  • Date: 2016-10-24 20:56:05 UTC
  • Revision ID: nicholas.skaggs@canonical.com-20161024205605-z8lta0uvuhtxwzwl
Initi with beta15

Show diffs side-by-side

added added

removed removed

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