// ReadRequestHeader reads an HTTP request header from r. func ReadRequestHeader(r xo.Reader) (*Request, error) { var req = new(Request) // Fetch the whole Request-Line. buf, err := xo.PeekTo(r, '\n', 0) if err != nil { return nil, err } method, rest := strtok(buf, ' ') if len(method) == 0 || rest == nil { return nil, ErrRequestHeader } uri, rest := strtok(rest, ' ') if len(uri) == 0 || rest == nil { return nil, ErrRequestHeader } // Trim trailing CRLF/LF. if len(rest) < 2 { return nil, ErrRequestHeader } else if rest[len(rest)-2] == '\r' { rest = rest[:len(rest)-2] } else { rest = rest[:len(rest)-1] } req.Major, req.Minor, err = parseHTTPVersion(rest) if err != nil { return nil, ErrRequestVersion } req.Method = stringify(method) req.URI = string(uri) // Consume the Request-Line. if err := r.Consume(len(buf)); err != nil { return nil, err } // Read header fields. req.Fields, err = readHeader(r) if err != nil { if err == errMalformedHeader { err = ErrRequestHeader } return nil, err } return req, nil }
func readHeader(r xo.Reader) (Fields, error) { var fields Fields for { buf, err := xo.PeekTo(r, '\n', 0) if err != nil { return nil, err } if c := buf[0]; c == '\n' || (c == '\r' && len(buf) == 2) { if err := r.Consume(len(buf)); err != nil { return nil, err } else { return fields, nil } } else if c == ' ' || c == '\t' { // Because the loop below will consume all continuation lines, // taking this branch must mean that the first fields field has // leading whitespace, which is illegal. return nil, errMalformedHeader } colon := bytes.IndexByte(buf, ':') if colon == -1 { return nil, errMalformedHeader } // Lines beginning with horizontal whitespace are continuations of // the field value on the previous line, meaning we have to read all // of them before we have a full field value. for off := len(buf); ; off = len(buf) { peek, err := xo.PeekTo(r, '\n', off) if err != nil { return nil, err } if c := peek[off]; c == ' ' || c == '\t' { buf = peek } else { break } } // Trim the field's name and value. The shrinkValue call will modify // buf in place, which is referencing the xo.Reader's internal storage. // This isn't ideal, but it will only matter if the Consume call fails, // which is impossible for correct xo.Readers. name := shrinkName(buf[:colon]) if len(name) == 0 { return nil, errMalformedHeader } value := shrinkValue(buf[colon+1:]) fields = append(fields, Field{ Name: stringify(name), Value: string(value), }) // Consume the bytes we just parsed. if err := r.Consume(len(buf)); err != nil { return nil, err } } }