Skip to content

Commit b2888a3

Browse files
committed
merging fixes for weekly gc.2011-04-04
2 parents 05550ee + 843f22e commit b2888a3

File tree

5 files changed

+212
-68
lines changed

5 files changed

+212
-68
lines changed

Makefile

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ TARG=web
44
GOFMT=gofmt -s -spaces=true -tabindent=false -tabwidth=4
55

66
GOFILES=\
7+
cookie.go\
78
fcgi.go\
89
request.go\
910
scgi.go\
@@ -14,13 +15,7 @@ GOFILES=\
1415
include $(GOROOT)/src/Make.pkg
1516

1617
format:
17-
${GOFMT} -w fcgi.go
18-
${GOFMT} -w request.go
19-
${GOFMT} -w scgi.go
20-
${GOFMT} -w servefile.go
21-
${GOFMT} -w status.go
22-
${GOFMT} -w web.go
23-
${GOFMT} -w web_test.go
18+
${GOFMT} -w ${GOFILES}
2419
${GOFMT} -w examples/arcchallenge.go
2520
${GOFMT} -w examples/hello.go
2621
${GOFMT} -w examples/methodhandler.go

cookie.go

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
// Copyright 2009 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+
package web
6+
7+
import (
8+
"bytes"
9+
"fmt"
10+
"http"
11+
"io"
12+
"os"
13+
"sort"
14+
"strings"
15+
"time"
16+
)
17+
18+
// writeSetCookies writes the wire representation of the set-cookies
19+
// to w. Each cookie is written on a separate "Set-Cookie: " line.
20+
// This choice is made because HTTP parsers tend to have a limit on
21+
// line-length, so it seems safer to place cookies on separate lines.
22+
func writeSetCookies(w io.Writer, kk []*http.Cookie) os.Error {
23+
if kk == nil {
24+
return nil
25+
}
26+
lines := make([]string, 0, len(kk))
27+
var b bytes.Buffer
28+
for _, c := range kk {
29+
b.Reset()
30+
// TODO(petar): c.Value (below) should be unquoted if it is recognized as quoted
31+
fmt.Fprintf(&b, "%s=%s", http.CanonicalHeaderKey(c.Name), c.Value)
32+
if len(c.Path) > 0 {
33+
fmt.Fprintf(&b, "; Path=%s", http.URLEscape(c.Path))
34+
}
35+
if len(c.Domain) > 0 {
36+
fmt.Fprintf(&b, "; Domain=%s", http.URLEscape(c.Domain))
37+
}
38+
if len(c.Expires.Zone) > 0 {
39+
fmt.Fprintf(&b, "; Expires=%s", c.Expires.Format(time.RFC1123))
40+
}
41+
if c.MaxAge >= 0 {
42+
fmt.Fprintf(&b, "; Max-Age=%d", c.MaxAge)
43+
}
44+
if c.HttpOnly {
45+
fmt.Fprintf(&b, "; HttpOnly")
46+
}
47+
if c.Secure {
48+
fmt.Fprintf(&b, "; Secure")
49+
}
50+
lines = append(lines, "Set-Cookie: "+b.String()+"\r\n")
51+
}
52+
sort.SortStrings(lines)
53+
for _, l := range lines {
54+
if _, err := io.WriteString(w, l); err != nil {
55+
return err
56+
}
57+
}
58+
return nil
59+
}
60+
61+
// writeCookies writes the wire representation of the cookies
62+
// to w. Each cookie is written on a separate "Cookie: " line.
63+
// This choice is made because HTTP parsers tend to have a limit on
64+
// line-length, so it seems safer to place cookies on separate lines.
65+
func writeCookies(w io.Writer, kk []*http.Cookie) os.Error {
66+
lines := make([]string, 0, len(kk))
67+
var b bytes.Buffer
68+
for _, c := range kk {
69+
b.Reset()
70+
n := c.Name
71+
// TODO(petar): c.Value (below) should be unquoted if it is recognized as quoted
72+
fmt.Fprintf(&b, "%s=%s", http.CanonicalHeaderKey(n), c.Value)
73+
if len(c.Path) > 0 {
74+
fmt.Fprintf(&b, "; $Path=%s", http.URLEscape(c.Path))
75+
}
76+
if len(c.Domain) > 0 {
77+
fmt.Fprintf(&b, "; $Domain=%s", http.URLEscape(c.Domain))
78+
}
79+
if c.HttpOnly {
80+
fmt.Fprintf(&b, "; $HttpOnly")
81+
}
82+
lines = append(lines, "Cookie: "+b.String()+"\r\n")
83+
}
84+
sort.SortStrings(lines)
85+
for _, l := range lines {
86+
if _, err := io.WriteString(w, l); err != nil {
87+
return err
88+
}
89+
}
90+
return nil
91+
}
92+
93+
// readCookies parses all "Cookie" values from
94+
// the header h, removes the successfully parsed values from the
95+
// "Cookie" key in h and returns the parsed Cookies.
96+
func readCookies(h http.Header) []*http.Cookie {
97+
cookies := []*http.Cookie{}
98+
lines, ok := h["Cookie"]
99+
if !ok {
100+
return cookies
101+
}
102+
unparsedLines := []string{}
103+
for _, line := range lines {
104+
parts := strings.Split(strings.TrimSpace(line), ";", -1)
105+
if len(parts) == 1 && parts[0] == "" {
106+
continue
107+
}
108+
// Per-line attributes
109+
var lineCookies = make(map[string]string)
110+
var path string
111+
var domain string
112+
var httponly bool
113+
for i := 0; i < len(parts); i++ {
114+
parts[i] = strings.TrimSpace(parts[i])
115+
if len(parts[i]) == 0 {
116+
continue
117+
}
118+
attr, val := parts[i], ""
119+
var err os.Error
120+
if j := strings.Index(attr, "="); j >= 0 {
121+
attr, val = attr[:j], attr[j+1:]
122+
val, err = http.URLUnescape(val)
123+
if err != nil {
124+
continue
125+
}
126+
}
127+
switch strings.ToLower(attr) {
128+
case "$httponly":
129+
httponly = true
130+
case "$domain":
131+
domain = val
132+
// TODO: Add domain parsing
133+
case "$path":
134+
path = val
135+
// TODO: Add path parsing
136+
default:
137+
lineCookies[attr] = val
138+
}
139+
}
140+
if len(lineCookies) == 0 {
141+
unparsedLines = append(unparsedLines, line)
142+
}
143+
for n, v := range lineCookies {
144+
cookies = append(cookies, &http.Cookie{
145+
Name: n,
146+
Value: v,
147+
Path: path,
148+
Domain: domain,
149+
HttpOnly: httponly,
150+
MaxAge: -1,
151+
Raw: line,
152+
})
153+
}
154+
}
155+
h["Cookie"] = unparsedLines, len(unparsedLines) > 0
156+
return cookies
157+
}

request.go

Lines changed: 9 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ type Request struct {
3838
Params map[string]string
3939
ParamData []byte
4040
Cookies map[string]string
41+
Cookie []*http.Cookie
4142
Files map[string]filedata
4243
RemoteAddr string
4344
RemotePort int
@@ -79,6 +80,7 @@ func newRequest(hr *http.Request, hc http.ResponseWriter) *Request {
7980
Referer: hr.Referer,
8081
UserAgent: hr.UserAgent,
8182
FullParams: hr.Form,
83+
Cookie: hr.Cookie,
8284
RemoteAddr: remoteAddr.IP.String(),
8385
RemotePort: remoteAddr.Port,
8486
}
@@ -116,6 +118,9 @@ func newRequestCgi(headers http.Header, body io.Reader) *Request {
116118
httpheader["Content-Length"] = clength
117119
}
118120
}
121+
122+
//read the cookies
123+
cookies := readCookies(httpheader)
119124

120125
req := Request{
121126
Method: method,
@@ -128,6 +133,7 @@ func newRequestCgi(headers http.Header, body io.Reader) *Request {
128133
Headers: httpheader,
129134
RemoteAddr: remoteAddr,
130135
RemotePort: remotePort,
136+
Cookie: cookies,
131137
}
132138

133139
return &req
@@ -217,16 +223,16 @@ func (r *Request) parseParams() (err os.Error) {
217223
data, _ := ioutil.ReadAll(part)
218224
//check for the 'filename' param
219225
v := part.Header.Get("Content-Disposition")
220-
if len(v)==0 {
221-
continue
226+
if v == "" {
227+
continue
222228
}
223229
name := part.FormName()
224230
d, params := mime.ParseMediaType(v)
225231
if d != "form-data" {
226232
continue
227233
}
228234
if params["filename"] != "" {
229-
r.Files[name] = filedata{name, data}
235+
r.Files[name] = filedata{params["filename"], data}
230236
} else {
231237
var params vector.StringVector = r.FullParams[name]
232238
params.Push(string(data))
@@ -256,30 +262,6 @@ func (r *Request) parseParams() (err os.Error) {
256262
return nil
257263
}
258264

259-
func (r *Request) parseCookies() (err os.Error) {
260-
if r.Cookies != nil {
261-
return
262-
}
263-
264-
r.Cookies = make(map[string]string)
265-
266-
if va, ok := r.Headers["Cookie"]; ok {
267-
for _, v := range va {
268-
cookies := strings.Split(v, ";", -1)
269-
for _, cookie := range cookies {
270-
cookie = strings.TrimSpace(cookie)
271-
parts := strings.Split(cookie, "=", 2)
272-
if len(parts) != 2 {
273-
continue
274-
}
275-
r.Cookies[parts[0]] = parts[1]
276-
}
277-
}
278-
}
279-
280-
return nil
281-
}
282-
283265
func (r *Request) HasFile(name string) bool {
284266
if r.Files == nil || len(r.Files) == 0 {
285267
return false

web.go

Lines changed: 21 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -130,32 +130,34 @@ func (ctx *Context) SetSecureCookie(name string, val string, age int64) {
130130
}
131131

132132
func (ctx *Context) GetSecureCookie(name string) (string, bool) {
133-
cookie, ok := ctx.Request.Cookies[name]
134-
if !ok {
135-
return "", false
136-
}
133+
for _, cookie := range ctx.Request.Cookie {
134+
if cookie.Name != name {
135+
continue
136+
}
137137

138-
parts := strings.Split(cookie, "|", 3)
138+
parts := strings.Split(cookie.Value, "|", 3)
139139

140-
val := parts[0]
141-
timestamp := parts[1]
142-
sig := parts[2]
140+
val := parts[0]
141+
timestamp := parts[1]
142+
sig := parts[2]
143143

144-
if getCookieSig(ctx.Server.Config.CookieSecret, []byte(val), timestamp) != sig {
145-
return "", false
146-
}
144+
if getCookieSig(ctx.Server.Config.CookieSecret, []byte(val), timestamp) != sig {
145+
return "", false
146+
}
147147

148-
ts, _ := strconv.Atoi64(timestamp)
148+
ts, _ := strconv.Atoi64(timestamp)
149149

150-
if time.Seconds()-31*86400 > ts {
151-
return "", false
152-
}
150+
if time.Seconds()-31*86400 > ts {
151+
return "", false
152+
}
153153

154-
buf := bytes.NewBufferString(val)
155-
encoder := base64.NewDecoder(base64.StdEncoding, buf)
154+
buf := bytes.NewBufferString(val)
155+
encoder := base64.NewDecoder(base64.StdEncoding, buf)
156156

157-
res, _ := ioutil.ReadAll(encoder)
158-
return string(res), true
157+
res, _ := ioutil.ReadAll(encoder)
158+
return string(res), true
159+
}
160+
return "", false
159161
}
160162

161163
// small optimization: cache the context type instead of repeteadly calling reflect.Typeof
@@ -316,12 +318,6 @@ func (s *Server) routeHandler(req *Request, c conn) {
316318
s.Logger.Printf("Failed to parse form data %q\n", perr.String())
317319
}
318320

319-
//parse the cookies
320-
perr = req.parseCookies()
321-
if perr != nil {
322-
s.Logger.Printf("Failed to parse cookies %q", perr.String())
323-
}
324-
325321
ctx := Context{req, s, c, false}
326322

327323
//set some default headers

0 commit comments

Comments
 (0)