...
Source file src/pkg/net/http/cgi/child.go
1
2
3
4
5
6
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
26
27
28
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
51
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
85 for k, v := range params {
86 if !strings.HasPrefix(k, "HTTP_") || k == "HTTP_HOST" {
87 continue
88 }
89 r.Header.Add(strings.ReplaceAll(k[5:], "_", "-"), v)
90 }
91
92
93
94 uriStr := params["REQUEST_URI"]
95 if uriStr == "" {
96
97 uriStr = params["SCRIPT_NAME"] + params["PATH_INFO"]
98 s := params["QUERY_STRING"]
99 if s != "" {
100 uriStr += "?" + s
101 }
102 }
103
104
105
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
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
125
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
135
136 remotePort, _ := strconv.Atoi(params["REMOTE_PORT"])
137 r.RemoteAddr = net.JoinHostPort(params["REMOTE_ADDR"], strconv.Itoa(remotePort))
138
139 return r, nil
140 }
141
142
143
144
145
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)
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
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
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 }
207
View as plain text