123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275 |
- package httphead
- import (
- "bufio"
- "bytes"
- )
- // Version contains protocol major and minor version.
- type Version struct {
- Major int
- Minor int
- }
- // RequestLine contains parameters parsed from the first request line.
- type RequestLine struct {
- Method []byte
- URI []byte
- Version Version
- }
- // ResponseLine contains parameters parsed from the first response line.
- type ResponseLine struct {
- Version Version
- Status int
- Reason []byte
- }
- // SplitRequestLine splits given slice of bytes into three chunks without
- // parsing.
- func SplitRequestLine(line []byte) (method, uri, version []byte) {
- return split3(line, ' ')
- }
- // ParseRequestLine parses http request line like "GET / HTTP/1.0".
- func ParseRequestLine(line []byte) (r RequestLine, ok bool) {
- var i int
- for i = 0; i < len(line); i++ {
- c := line[i]
- if !OctetTypes[c].IsToken() {
- if i > 0 && c == ' ' {
- break
- }
- return
- }
- }
- if i == len(line) {
- return
- }
- var proto []byte
- r.Method = line[:i]
- r.URI, proto = split2(line[i+1:], ' ')
- if len(r.URI) == 0 {
- return
- }
- if major, minor, ok := ParseVersion(proto); ok {
- r.Version.Major = major
- r.Version.Minor = minor
- return r, true
- }
- return r, false
- }
- // SplitResponseLine splits given slice of bytes into three chunks without
- // parsing.
- func SplitResponseLine(line []byte) (version, status, reason []byte) {
- return split3(line, ' ')
- }
- // ParseResponseLine parses first response line into ResponseLine struct.
- func ParseResponseLine(line []byte) (r ResponseLine, ok bool) {
- var (
- proto []byte
- status []byte
- )
- proto, status, r.Reason = split3(line, ' ')
- if major, minor, ok := ParseVersion(proto); ok {
- r.Version.Major = major
- r.Version.Minor = minor
- } else {
- return r, false
- }
- if n, ok := IntFromASCII(status); ok {
- r.Status = n
- } else {
- return r, false
- }
- // TODO(gobwas): parse here r.Reason fot TEXT rule:
- // TEXT = <any OCTET except CTLs,
- // but including LWS>
- return r, true
- }
- var (
- httpVersion10 = []byte("HTTP/1.0")
- httpVersion11 = []byte("HTTP/1.1")
- httpVersionPrefix = []byte("HTTP/")
- )
- // ParseVersion parses major and minor version of HTTP protocol.
- // It returns parsed values and true if parse is ok.
- func ParseVersion(bts []byte) (major, minor int, ok bool) {
- switch {
- case bytes.Equal(bts, httpVersion11):
- return 1, 1, true
- case bytes.Equal(bts, httpVersion10):
- return 1, 0, true
- case len(bts) < 8:
- return
- case !bytes.Equal(bts[:5], httpVersionPrefix):
- return
- }
- bts = bts[5:]
- dot := bytes.IndexByte(bts, '.')
- if dot == -1 {
- return
- }
- major, ok = IntFromASCII(bts[:dot])
- if !ok {
- return
- }
- minor, ok = IntFromASCII(bts[dot+1:])
- if !ok {
- return
- }
- return major, minor, true
- }
- // ReadLine reads line from br. It reads until '\n' and returns bytes without
- // '\n' or '\r\n' at the end.
- // It returns err if and only if line does not end in '\n'. Note that read
- // bytes returned in any case of error.
- //
- // It is much like the textproto/Reader.ReadLine() except the thing that it
- // returns raw bytes, instead of string. That is, it avoids copying bytes read
- // from br.
- //
- // textproto/Reader.ReadLineBytes() is also makes copy of resulting bytes to be
- // safe with future I/O operations on br.
- //
- // We could control I/O operations on br and do not need to make additional
- // copy for safety.
- func ReadLine(br *bufio.Reader) ([]byte, error) {
- var line []byte
- for {
- bts, err := br.ReadSlice('\n')
- if err == bufio.ErrBufferFull {
- // Copy bytes because next read will discard them.
- line = append(line, bts...)
- continue
- }
- // Avoid copy of single read.
- if line == nil {
- line = bts
- } else {
- line = append(line, bts...)
- }
- if err != nil {
- return line, err
- }
- // Size of line is at least 1.
- // In other case bufio.ReadSlice() returns error.
- n := len(line)
- // Cut '\n' or '\r\n'.
- if n > 1 && line[n-2] == '\r' {
- line = line[:n-2]
- } else {
- line = line[:n-1]
- }
- return line, nil
- }
- }
- // ParseHeaderLine parses HTTP header as key-value pair. It returns parsed
- // values and true if parse is ok.
- func ParseHeaderLine(line []byte) (k, v []byte, ok bool) {
- colon := bytes.IndexByte(line, ':')
- if colon == -1 {
- return
- }
- k = trim(line[:colon])
- for _, c := range k {
- if !OctetTypes[c].IsToken() {
- return nil, nil, false
- }
- }
- v = trim(line[colon+1:])
- return k, v, true
- }
- // IntFromASCII converts ascii encoded decimal numeric value from HTTP entities
- // to an integer.
- func IntFromASCII(bts []byte) (ret int, ok bool) {
- // ASCII numbers all start with the high-order bits 0011.
- // If you see that, and the next bits are 0-9 (0000 - 1001) you can grab those
- // bits and interpret them directly as an integer.
- var n int
- if n = len(bts); n < 1 {
- return 0, false
- }
- for i := 0; i < n; i++ {
- if bts[i]&0xf0 != 0x30 {
- return 0, false
- }
- ret += int(bts[i]&0xf) * pow(10, n-i-1)
- }
- return ret, true
- }
- const (
- toLower = 'a' - 'A' // for use with OR.
- toUpper = ^byte(toLower) // for use with AND.
- )
- // CanonicalizeHeaderKey is like standard textproto/CanonicalMIMEHeaderKey,
- // except that it operates with slice of bytes and modifies it inplace without
- // copying.
- func CanonicalizeHeaderKey(k []byte) {
- upper := true
- for i, c := range k {
- if upper && 'a' <= c && c <= 'z' {
- k[i] &= toUpper
- } else if !upper && 'A' <= c && c <= 'Z' {
- k[i] |= toLower
- }
- upper = c == '-'
- }
- }
- // pow for integers implementation.
- // See Donald Knuth, The Art of Computer Programming, Volume 2, Section 4.6.3
- func pow(a, b int) int {
- p := 1
- for b > 0 {
- if b&1 != 0 {
- p *= a
- }
- b >>= 1
- a *= a
- }
- return p
- }
- func split3(p []byte, sep byte) (p1, p2, p3 []byte) {
- a := bytes.IndexByte(p, sep)
- b := bytes.IndexByte(p[a+1:], sep)
- if a == -1 || b == -1 {
- return p, nil, nil
- }
- b += a + 1
- return p[:a], p[a+1 : b], p[b+1:]
- }
- func split2(p []byte, sep byte) (p1, p2 []byte) {
- i := bytes.IndexByte(p, sep)
- if i == -1 {
- return p, nil
- }
- return p[:i], p[i+1:]
- }
- func trim(p []byte) []byte {
- var i, j int
- for i = 0; i < len(p) && (p[i] == ' ' || p[i] == '\t'); {
- i++
- }
- for j = len(p); j > i && (p[j-1] == ' ' || p[j-1] == '\t'); {
- j--
- }
- return p[i:j]
- }
|