summaryrefslogtreecommitdiff
path: root/src/pkg/http/cookie.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/pkg/http/cookie.go')
-rw-r--r--src/pkg/http/cookie.go117
1 files changed, 76 insertions, 41 deletions
diff --git a/src/pkg/http/cookie.go b/src/pkg/http/cookie.go
index 2c01826a1..eb61a7001 100644
--- a/src/pkg/http/cookie.go
+++ b/src/pkg/http/cookie.go
@@ -15,9 +15,9 @@ import (
"time"
)
-// This implementation is done according to IETF draft-ietf-httpstate-cookie-23, found at
+// This implementation is done according to RFC 6265:
//
-// http://tools.ietf.org/html/draft-ietf-httpstate-cookie-23
+// http://tools.ietf.org/html/rfc6265
// A Cookie represents an HTTP cookie as sent in the Set-Cookie header of an
// HTTP response or the Cookie header of an HTTP request.
@@ -81,12 +81,17 @@ func readSetCookies(h Header) []*Cookie {
if j := strings.Index(attr, "="); j >= 0 {
attr, val = attr[:j], attr[j+1:]
}
- val, success = parseCookieValue(val)
+ lowerAttr := strings.ToLower(attr)
+ parseCookieValueFn := parseCookieValue
+ if lowerAttr == "expires" {
+ parseCookieValueFn = parseCookieExpiresValue
+ }
+ val, success = parseCookieValueFn(val)
if !success {
c.Unparsed = append(c.Unparsed, parts[i])
continue
}
- switch strings.ToLower(attr) {
+ switch lowerAttr {
case "secure":
c.Secure = true
continue
@@ -112,8 +117,11 @@ func readSetCookies(h Header) []*Cookie {
c.RawExpires = val
exptime, err := time.Parse(time.RFC1123, val)
if err != nil {
- c.Expires = time.Time{}
- break
+ exptime, err = time.Parse("Mon, 02-Jan-2006 15:04:05 MST", val)
+ if err != nil {
+ c.Expires = time.Time{}
+ break
+ }
}
c.Expires = *exptime
continue
@@ -130,6 +138,37 @@ func readSetCookies(h Header) []*Cookie {
return cookies
}
+// SetCookie adds a Set-Cookie header to the provided ResponseWriter's headers.
+func SetCookie(w ResponseWriter, cookie *Cookie) {
+ var b bytes.Buffer
+ writeSetCookieToBuffer(&b, cookie)
+ w.Header().Add("Set-Cookie", b.String())
+}
+
+func writeSetCookieToBuffer(buf *bytes.Buffer, c *Cookie) {
+ fmt.Fprintf(buf, "%s=%s", sanitizeName(c.Name), sanitizeValue(c.Value))
+ if len(c.Path) > 0 {
+ fmt.Fprintf(buf, "; Path=%s", sanitizeValue(c.Path))
+ }
+ if len(c.Domain) > 0 {
+ fmt.Fprintf(buf, "; Domain=%s", sanitizeValue(c.Domain))
+ }
+ if len(c.Expires.Zone) > 0 {
+ fmt.Fprintf(buf, "; Expires=%s", c.Expires.Format(time.RFC1123))
+ }
+ if c.MaxAge > 0 {
+ fmt.Fprintf(buf, "; Max-Age=%d", c.MaxAge)
+ } else if c.MaxAge < 0 {
+ fmt.Fprintf(buf, "; Max-Age=0")
+ }
+ if c.HttpOnly {
+ fmt.Fprintf(buf, "; HttpOnly")
+ }
+ if c.Secure {
+ fmt.Fprintf(buf, "; Secure")
+ }
+}
+
// writeSetCookies writes the wire representation of the set-cookies
// to w. Each cookie is written on a separate "Set-Cookie: " line.
// This choice is made because HTTP parsers tend to have a limit on
@@ -142,27 +181,7 @@ func writeSetCookies(w io.Writer, kk []*Cookie) os.Error {
var b bytes.Buffer
for _, c := range kk {
b.Reset()
- fmt.Fprintf(&b, "%s=%s", sanitizeName(c.Name), sanitizeValue(c.Value))
- if len(c.Path) > 0 {
- fmt.Fprintf(&b, "; Path=%s", sanitizeValue(c.Path))
- }
- if len(c.Domain) > 0 {
- fmt.Fprintf(&b, "; Domain=%s", sanitizeValue(c.Domain))
- }
- if len(c.Expires.Zone) > 0 {
- fmt.Fprintf(&b, "; Expires=%s", c.Expires.Format(time.RFC1123))
- }
- if c.MaxAge > 0 {
- fmt.Fprintf(&b, "; Max-Age=%d", c.MaxAge)
- } else if c.MaxAge < 0 {
- fmt.Fprintf(&b, "; Max-Age=0")
- }
- if c.HttpOnly {
- fmt.Fprintf(&b, "; HttpOnly")
- }
- if c.Secure {
- fmt.Fprintf(&b, "; Secure")
- }
+ writeSetCookieToBuffer(&b, c)
lines = append(lines, "Set-Cookie: "+b.String()+"\r\n")
}
sort.SortStrings(lines)
@@ -218,22 +237,26 @@ func readCookies(h Header) []*Cookie {
return cookies
}
-// writeCookies writes the wire representation of the cookies
-// to w. Each cookie is written on a separate "Cookie: " line.
-// This choice is made because HTTP parsers tend to have a limit on
-// line-length, so it seems safer to place cookies on separate lines.
+// writeCookies writes the wire representation of the cookies to
+// w. According to RFC 6265 section 5.4, writeCookies does not
+// attach more than one Cookie header field. That means all
+// cookies, if any, are written into the same line, separated by
+// semicolon.
func writeCookies(w io.Writer, kk []*Cookie) os.Error {
- lines := make([]string, 0, len(kk))
- for _, c := range kk {
- lines = append(lines, fmt.Sprintf("Cookie: %s=%s\r\n", sanitizeName(c.Name), sanitizeValue(c.Value)))
+ if len(kk) == 0 {
+ return nil
}
- sort.SortStrings(lines)
- for _, l := range lines {
- if _, err := io.WriteString(w, l); err != nil {
- return err
+ var buf bytes.Buffer
+ fmt.Fprintf(&buf, "Cookie: ")
+ for i, c := range kk {
+ if i > 0 {
+ fmt.Fprintf(&buf, "; ")
}
+ fmt.Fprintf(&buf, "%s=%s", sanitizeName(c.Name), sanitizeValue(c.Value))
}
- return nil
+ fmt.Fprintf(&buf, "\r\n")
+ _, err := w.Write(buf.Bytes())
+ return err
}
func sanitizeName(n string) string {
@@ -257,7 +280,7 @@ func unquoteCookieValue(v string) string {
}
func isCookieByte(c byte) bool {
- switch true {
+ switch {
case c == 0x21, 0x23 <= c && c <= 0x2b, 0x2d <= c && c <= 0x3a,
0x3c <= c && c <= 0x5b, 0x5d <= c && c <= 0x7e:
return true
@@ -265,10 +288,22 @@ func isCookieByte(c byte) bool {
return false
}
+func isCookieExpiresByte(c byte) (ok bool) {
+ return isCookieByte(c) || c == ',' || c == ' '
+}
+
func parseCookieValue(raw string) (string, bool) {
+ return parseCookieValueUsing(raw, isCookieByte)
+}
+
+func parseCookieExpiresValue(raw string) (string, bool) {
+ return parseCookieValueUsing(raw, isCookieExpiresByte)
+}
+
+func parseCookieValueUsing(raw string, validByte func(byte) bool) (string, bool) {
raw = unquoteCookieValue(raw)
for i := 0; i < len(raw); i++ {
- if !isCookieByte(raw[i]) {
+ if !validByte(raw[i]) {
return "", false
}
}