diff options
Diffstat (limited to 'src/pkg/http/request.go')
-rw-r--r-- | src/pkg/http/request.go | 246 |
1 files changed, 123 insertions, 123 deletions
diff --git a/src/pkg/http/request.go b/src/pkg/http/request.go index 83374a549..bf1e299d7 100644 --- a/src/pkg/http/request.go +++ b/src/pkg/http/request.go @@ -10,50 +10,50 @@ package http import ( - "bufio"; - "bytes"; - "container/vector"; - "fmt"; - "io"; - "io/ioutil"; - "os"; - "strconv"; - "strings"; + "bufio" + "bytes" + "container/vector" + "fmt" + "io" + "io/ioutil" + "os" + "strconv" + "strings" ) const ( - maxLineLength = 1024; // assumed < bufio.DefaultBufSize - maxValueLength = 1024; - maxHeaderLines = 1024; - chunkSize = 4 << 10; // 4 KB chunks + maxLineLength = 1024 // assumed < bufio.DefaultBufSize + maxValueLength = 1024 + maxHeaderLines = 1024 + chunkSize = 4 << 10 // 4 KB chunks ) // HTTP request parsing errors. type ProtocolError struct { - os.ErrorString; + os.ErrorString } var ( - ErrLineTooLong = &ProtocolError{"header line too long"}; - ErrHeaderTooLong = &ProtocolError{"header too long"}; - ErrShortBody = &ProtocolError{"entity body too short"}; + ErrLineTooLong = &ProtocolError{"header line too long"} + ErrHeaderTooLong = &ProtocolError{"header too long"} + ErrShortBody = &ProtocolError{"entity body too short"} ) type badStringError struct { - what string; - str string; + what string + str string } -func (e *badStringError) String() string { return fmt.Sprintf("%s %q", e.what, e.str) } +func (e *badStringError) String() string { return fmt.Sprintf("%s %q", e.what, e.str) } // A Request represents a parsed HTTP request header. type Request struct { - Method string; // GET, POST, PUT, etc. - RawURL string; // The raw URL given in the request. - URL *URL; // Parsed URL. - Proto string; // "HTTP/1.0" - ProtoMajor int; // 1 - ProtoMinor int; // 0 + Method string // GET, POST, PUT, etc. + RawURL string // The raw URL given in the request. + URL *URL // Parsed URL. + Proto string // "HTTP/1.0" + ProtoMajor int // 1 + ProtoMinor int // 0 // A header mapping request lines to their values. // If the header says @@ -74,18 +74,18 @@ type Request struct { // The request parser implements this by canonicalizing the // name, making the first character and any characters // following a hyphen uppercase and the rest lowercase. - Header map[string]string; + Header map[string]string // The message body. - Body io.Reader; + Body io.Reader // Whether to close the connection after replying to this request. - Close bool; + Close bool // The host on which the URL is sought. // Per RFC 2616, this is either the value of the Host: header // or the host name given in the URL itself. - Host string; + Host string // The referring URL, if sent in the request. // @@ -97,13 +97,13 @@ type Request struct { // can diagnose programs that use the alternate // (correct English) spelling req.Referrer but cannot // diagnose programs that use Header["Referrer"]. - Referer string; + Referer string // The User-Agent: header string, if sent in the request. - UserAgent string; + UserAgent string // The parsed form. Only available after ParseForm is called. - Form map[string][]string; + Form map[string][]string } // ProtoAtLeast returns whether the HTTP protocol used @@ -118,7 +118,7 @@ func valueOrDefault(value, def string) string { if value != "" { return value } - return def; + return def } const defaultUserAgent = "Go http package" @@ -134,14 +134,14 @@ const defaultUserAgent = "Go http package" // // If Body is present, "Transfer-Encoding: chunked" is forced as a header. func (req *Request) Write(w io.Writer) os.Error { - uri := URLEscape(req.URL.Path); + uri := URLEscape(req.URL.Path) if req.URL.RawQuery != "" { uri += "?" + req.URL.RawQuery } - fmt.Fprintf(w, "%s %s HTTP/1.1\r\n", valueOrDefault(req.Method, "GET"), uri); - fmt.Fprintf(w, "Host: %s\r\n", req.URL.Host); - fmt.Fprintf(w, "User-Agent: %s\r\n", valueOrDefault(req.UserAgent, defaultUserAgent)); + fmt.Fprintf(w, "%s %s HTTP/1.1\r\n", valueOrDefault(req.Method, "GET"), uri) + fmt.Fprintf(w, "Host: %s\r\n", req.URL.Host) + fmt.Fprintf(w, "User-Agent: %s\r\n", valueOrDefault(req.UserAgent, defaultUserAgent)) if req.Referer != "" { fmt.Fprintf(w, "Referer: %s\r\n", req.Referer) @@ -163,19 +163,19 @@ func (req *Request) Write(w io.Writer) os.Error { io.WriteString(w, k+": "+v+"\r\n") } - io.WriteString(w, "\r\n"); + io.WriteString(w, "\r\n") if req.Body != nil { - buf := make([]byte, chunkSize); + buf := make([]byte, chunkSize) Loop: for { - var nr, nw int; - var er, ew os.Error; + var nr, nw int + var er, ew os.Error if nr, er = req.Body.Read(buf); nr > 0 { if er == nil || er == os.EOF { - fmt.Fprintf(w, "%x\r\n", nr); - nw, ew = w.Write(buf[0:nr]); - fmt.Fprint(w, "\r\n"); + fmt.Fprintf(w, "%x\r\n", nr) + nw, ew = w.Write(buf[0:nr]) + fmt.Fprint(w, "\r\n") } } switch { @@ -183,7 +183,7 @@ func (req *Request) Write(w io.Writer) os.Error { if er == os.EOF { break Loop } - return er; + return er case ew != nil: return ew case nw < nr: @@ -191,10 +191,10 @@ func (req *Request) Write(w io.Writer) os.Error { } } // last-chunk CRLF - fmt.Fprint(w, "0\r\n\r\n"); + fmt.Fprint(w, "0\r\n\r\n") } - return nil; + return nil } // Read a line of bytes (up to \n) from b. @@ -208,29 +208,29 @@ func readLineBytes(b *bufio.Reader) (p []byte, err os.Error) { if err == os.EOF { err = io.ErrUnexpectedEOF } - return nil, err; + return nil, err } if len(p) >= maxLineLength { return nil, ErrLineTooLong } // Chop off trailing white space. - var i int; + var i int for i = len(p); i > 0; i-- { if c := p[i-1]; c != ' ' && c != '\r' && c != '\t' && c != '\n' { break } } - return p[0:i], nil; + return p[0:i], nil } // readLineBytes, but convert the bytes into a string. func readLine(b *bufio.Reader) (s string, err os.Error) { - p, e := readLineBytes(b); + p, e := readLineBytes(b) if e != nil { return "", e } - return string(p), nil; + return string(p), nil } var colon = []byte{':'} @@ -240,7 +240,7 @@ var colon = []byte{':'} // and the Value can continue on multiple lines if each continuation line // starts with a space. func readKeyValue(b *bufio.Reader) (key, value string, err os.Error) { - line, e := readLineBytes(b); + line, e := readLineBytes(b) if e != nil { return "", "", e } @@ -249,12 +249,12 @@ func readKeyValue(b *bufio.Reader) (key, value string, err os.Error) { } // Scan first line for colon. - i := bytes.Index(line, colon); + i := bytes.Index(line, colon) if i < 0 { goto Malformed } - key = string(line[0:i]); + key = string(line[0:i]) if strings.Index(key, " ") >= 0 { // Key field has space - no good. goto Malformed @@ -266,16 +266,16 @@ func readKeyValue(b *bufio.Reader) (key, value string, err os.Error) { break } } - value = string(line[i:]); + value = string(line[i:]) // Look for extension lines, which must begin with space. for { - c, e := b.ReadByte(); + c, e := b.ReadByte() if c != ' ' { if e != os.EOF { b.UnreadByte() } - break; + break } // Eat leading space. @@ -284,43 +284,43 @@ func readKeyValue(b *bufio.Reader) (key, value string, err os.Error) { if e == os.EOF { e = io.ErrUnexpectedEOF } - return "", "", e; + return "", "", e } } - b.UnreadByte(); + b.UnreadByte() // Read the rest of the line and add to value. if line, e = readLineBytes(b); e != nil { return "", "", e } - value += " " + string(line); + value += " " + string(line) if len(value) >= maxValueLength { return "", "", &badStringError{"value too long for key", key} } } - return key, value, nil; + return key, value, nil Malformed: - return "", "", &badStringError{"malformed header line", string(line)}; + return "", "", &badStringError{"malformed header line", string(line)} } // Convert decimal at s[i:len(s)] to integer, // returning value, string position where the digits stopped, // and whether there was a valid number (digits, not too big). func atoi(s string, i int) (n, i1 int, ok bool) { - const Big = 1000000; + const Big = 1000000 if i >= len(s) || s[i] < '0' || s[i] > '9' { return 0, 0, false } - n = 0; + n = 0 for ; i < len(s) && '0' <= s[i] && s[i] <= '9'; i++ { - n = n*10 + int(s[i]-'0'); + n = n*10 + int(s[i]-'0') if n > Big { return 0, 0, false } } - return n, i, true; + return n, i, true } // Parse HTTP version: "HTTP/1.2" -> (1, 2, true). @@ -328,16 +328,16 @@ func parseHTTPVersion(vers string) (int, int, bool) { if vers[0:5] != "HTTP/" { return 0, 0, false } - major, i, ok := atoi(vers, 5); + major, i, ok := atoi(vers, 5) if !ok || i >= len(vers) || vers[i] != '.' { return 0, 0, false } - var minor int; - minor, i, ok = atoi(vers, i+1); + var minor int + minor, i, ok = atoi(vers, i+1) if !ok || i != len(vers) { return 0, 0, false } - return major, minor, true; + return major, minor, true } var cmap = make(map[string]string) @@ -356,8 +356,8 @@ func CanonicalHeaderKey(s string) string { // and upper case after each dash. // (Host, User-Agent, If-Modified-Since). // HTTP headers are ASCII only, so no Unicode issues. - a := strings.Bytes(s); - upper := true; + a := strings.Bytes(s) + upper := true for i, v := range a { if upper && 'a' <= v && v <= 'z' { a[i] = v + 'A' - 'a' @@ -365,20 +365,20 @@ func CanonicalHeaderKey(s string) string { if !upper && 'A' <= v && v <= 'Z' { a[i] = v + 'a' - 'A' } - upper = false; + upper = false if v == '-' { upper = true } } - t := string(a); - cmap[s] = t; - return t; + t := string(a) + cmap[s] = t + return t } type chunkedReader struct { - r *bufio.Reader; - n uint64; // unread bytes in chunk - err os.Error; + r *bufio.Reader + n uint64 // unread bytes in chunk + err os.Error } func newChunkedReader(r *bufio.Reader) *chunkedReader { @@ -387,19 +387,19 @@ func newChunkedReader(r *bufio.Reader) *chunkedReader { func (cr *chunkedReader) beginChunk() { // chunk-size CRLF - var line string; - line, cr.err = readLine(cr.r); + var line string + line, cr.err = readLine(cr.r) if cr.err != nil { return } - cr.n, cr.err = strconv.Btoui64(line, 16); + cr.n, cr.err = strconv.Btoui64(line, 16) if cr.err != nil { return } if cr.n == 0 { // trailer CRLF for { - line, cr.err = readLine(cr.r); + line, cr.err = readLine(cr.r) if cr.err != nil { return } @@ -407,7 +407,7 @@ func (cr *chunkedReader) beginChunk() { break } } - cr.err = os.EOF; + cr.err = os.EOF } } @@ -416,7 +416,7 @@ func (cr *chunkedReader) Read(b []uint8) (n int, err os.Error) { return 0, cr.err } if cr.n == 0 { - cr.beginChunk(); + cr.beginChunk() if cr.err != nil { return 0, cr.err } @@ -424,36 +424,36 @@ func (cr *chunkedReader) Read(b []uint8) (n int, err os.Error) { if uint64(len(b)) > cr.n { b = b[0:cr.n] } - n, cr.err = cr.r.Read(b); - cr.n -= uint64(n); + n, cr.err = cr.r.Read(b) + cr.n -= uint64(n) if cr.n == 0 && cr.err == nil { // end of chunk (CRLF) - b := make([]byte, 2); + b := make([]byte, 2) if _, cr.err = io.ReadFull(cr.r, b); cr.err == nil { if b[0] != '\r' || b[1] != '\n' { cr.err = os.NewError("malformed chunked encoding") } } } - return n, cr.err; + return n, cr.err } // ReadRequest reads and parses a request from b. func ReadRequest(b *bufio.Reader) (req *Request, err os.Error) { - req = new(Request); + req = new(Request) // First line: GET /index.html HTTP/1.0 - var s string; + var s string if s, err = readLine(b); err != nil { return nil, err } - var f []string; + var f []string if f = strings.Split(s, " ", 3); len(f) < 3 { return nil, &badStringError{"malformed HTTP request", s} } - req.Method, req.RawURL, req.Proto = f[0], f[1], f[2]; - var ok bool; + req.Method, req.RawURL, req.Proto = f[0], f[1], f[2] + var ok bool if req.ProtoMajor, req.ProtoMinor, ok = parseHTTPVersion(req.Proto); !ok { return nil, &badStringError{"malformed HTTP version", req.Proto} } @@ -463,10 +463,10 @@ func ReadRequest(b *bufio.Reader) (req *Request, err os.Error) { } // Subsequent lines: Key: value. - nheader := 0; - req.Header = make(map[string]string); + nheader := 0 + req.Header = make(map[string]string) for { - var key, value string; + var key, value string if key, value, err = readKeyValue(b); err != nil { return nil, err } @@ -477,12 +477,12 @@ func ReadRequest(b *bufio.Reader) (req *Request, err os.Error) { return nil, ErrHeaderTooLong } - key = CanonicalHeaderKey(key); + key = CanonicalHeaderKey(key) // RFC 2616 says that if you send the same header key // multiple times, it has to be semantically equivalent // to concatenating the values separated by commas. - oldvalue, present := req.Header[key]; + oldvalue, present := req.Header[key] if present { req.Header[key] = oldvalue + "," + value } else { @@ -561,30 +561,30 @@ func ReadRequest(b *bufio.Reader) (req *Request, err os.Error) { if v, present := req.Header["Transfer-Encoding"]; present && v == "chunked" { req.Body = newChunkedReader(b) } else if v, present := req.Header["Content-Length"]; present { - length, err := strconv.Btoui64(v, 10); + length, err := strconv.Btoui64(v, 10) if err != nil { return nil, &badStringError{"invalid Content-Length", v} } // TODO: limit the Content-Length. This is an easy DoS vector. - raw := make([]byte, length); - n, err := b.Read(raw); + raw := make([]byte, length) + n, err := b.Read(raw) if err != nil || uint64(n) < length { return nil, ErrShortBody } - req.Body = bytes.NewBuffer(raw); + req.Body = bytes.NewBuffer(raw) } - return req, nil; + return req, nil } func parseForm(m map[string][]string, query string) (err os.Error) { - data := make(map[string]*vector.StringVector); + data := make(map[string]*vector.StringVector) for _, kv := range strings.Split(query, "&", 0) { - kvPair := strings.Split(kv, "=", 2); + kvPair := strings.Split(kv, "=", 2) - var key, value string; - var e os.Error; - key, e = URLUnescape(kvPair[0]); + var key, value string + var e os.Error + key, e = URLUnescape(kvPair[0]) if e == nil && len(kvPair) > 1 { value, e = URLUnescape(kvPair[1]) } @@ -592,19 +592,19 @@ func parseForm(m map[string][]string, query string) (err os.Error) { err = e } - vec, ok := data[key]; + vec, ok := data[key] if !ok { - vec = new(vector.StringVector); - data[key] = vec; + vec = new(vector.StringVector) + data[key] = vec } - vec.Push(value); + vec.Push(value) } for k, vec := range data { m[k] = vec.Data() } - return; + return } // ParseForm parses the request body as a form for POST requests, or the raw query for GET requests. @@ -613,9 +613,9 @@ func (r *Request) ParseForm() (err os.Error) { if r.Form != nil { return } - r.Form = make(map[string][]string); + r.Form = make(map[string][]string) - var query string; + var query string switch r.Method { case "GET": query = r.URL.RawQuery @@ -623,20 +623,20 @@ func (r *Request) ParseForm() (err os.Error) { if r.Body == nil { return os.ErrorString("missing form body") } - ct, _ := r.Header["Content-Type"]; + ct, _ := r.Header["Content-Type"] switch strings.Split(ct, ";", 2)[0] { case "text/plain", "application/x-www-form-urlencoded", "": - var b []byte; + var b []byte if b, err = ioutil.ReadAll(r.Body); err != nil { return err } - query = string(b); + query = string(b) // TODO(dsymonds): Handle multipart/form-data default: return &badStringError{"unknown Content-Type", ct} } } - return parseForm(r.Form, query); + return parseForm(r.Form, query) } // FormValue returns the first value for the named component of the query. @@ -648,5 +648,5 @@ func (r *Request) FormValue(key string) string { if vs, ok := r.Form[key]; ok && len(vs) > 0 { return vs[0] } - return ""; + return "" } |