summaryrefslogtreecommitdiff
path: root/src/pkg/http/request.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/pkg/http/request.go')
-rw-r--r--src/pkg/http/request.go246
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 ""
}