593f74bbab
From-SVN: r185010
137 lines
2.9 KiB
Go
137 lines
2.9 KiB
Go
// Copyright 2009 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package http
|
|
|
|
// This file deals with lexical matters of HTTP
|
|
|
|
func isSeparator(c byte) bool {
|
|
switch c {
|
|
case '(', ')', '<', '>', '@', ',', ';', ':', '\\', '"', '/', '[', ']', '?', '=', '{', '}', ' ', '\t':
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
func isCtl(c byte) bool { return (0 <= c && c <= 31) || c == 127 }
|
|
|
|
func isChar(c byte) bool { return 0 <= c && c <= 127 }
|
|
|
|
func isAnyText(c byte) bool { return !isCtl(c) }
|
|
|
|
func isQdText(c byte) bool { return isAnyText(c) && c != '"' }
|
|
|
|
func isToken(c byte) bool { return isChar(c) && !isCtl(c) && !isSeparator(c) }
|
|
|
|
// Valid escaped sequences are not specified in RFC 2616, so for now, we assume
|
|
// that they coincide with the common sense ones used by GO. Malformed
|
|
// characters should probably not be treated as errors by a robust (forgiving)
|
|
// parser, so we replace them with the '?' character.
|
|
func httpUnquotePair(b byte) byte {
|
|
// skip the first byte, which should always be '\'
|
|
switch b {
|
|
case 'a':
|
|
return '\a'
|
|
case 'b':
|
|
return '\b'
|
|
case 'f':
|
|
return '\f'
|
|
case 'n':
|
|
return '\n'
|
|
case 'r':
|
|
return '\r'
|
|
case 't':
|
|
return '\t'
|
|
case 'v':
|
|
return '\v'
|
|
case '\\':
|
|
return '\\'
|
|
case '\'':
|
|
return '\''
|
|
case '"':
|
|
return '"'
|
|
}
|
|
return '?'
|
|
}
|
|
|
|
// raw must begin with a valid quoted string. Only the first quoted string is
|
|
// parsed and is unquoted in result. eaten is the number of bytes parsed, or -1
|
|
// upon failure.
|
|
func httpUnquote(raw []byte) (eaten int, result string) {
|
|
buf := make([]byte, len(raw))
|
|
if raw[0] != '"' {
|
|
return -1, ""
|
|
}
|
|
eaten = 1
|
|
j := 0 // # of bytes written in buf
|
|
for i := 1; i < len(raw); i++ {
|
|
switch b := raw[i]; b {
|
|
case '"':
|
|
eaten++
|
|
buf = buf[0:j]
|
|
return i + 1, string(buf)
|
|
case '\\':
|
|
if len(raw) < i+2 {
|
|
return -1, ""
|
|
}
|
|
buf[j] = httpUnquotePair(raw[i+1])
|
|
eaten += 2
|
|
j++
|
|
i++
|
|
default:
|
|
if isQdText(b) {
|
|
buf[j] = b
|
|
} else {
|
|
buf[j] = '?'
|
|
}
|
|
eaten++
|
|
j++
|
|
}
|
|
}
|
|
return -1, ""
|
|
}
|
|
|
|
// This is a best effort parse, so errors are not returned, instead not all of
|
|
// the input string might be parsed. result is always non-nil.
|
|
func httpSplitFieldValue(fv string) (eaten int, result []string) {
|
|
result = make([]string, 0, len(fv))
|
|
raw := []byte(fv)
|
|
i := 0
|
|
chunk := ""
|
|
for i < len(raw) {
|
|
b := raw[i]
|
|
switch {
|
|
case b == '"':
|
|
eaten, unq := httpUnquote(raw[i:len(raw)])
|
|
if eaten < 0 {
|
|
return i, result
|
|
} else {
|
|
i += eaten
|
|
chunk += unq
|
|
}
|
|
case isSeparator(b):
|
|
if chunk != "" {
|
|
result = result[0 : len(result)+1]
|
|
result[len(result)-1] = chunk
|
|
chunk = ""
|
|
}
|
|
i++
|
|
case isToken(b):
|
|
chunk += string(b)
|
|
i++
|
|
case b == '\n' || b == '\r':
|
|
i++
|
|
default:
|
|
chunk += "?"
|
|
i++
|
|
}
|
|
}
|
|
if chunk != "" {
|
|
result = result[0 : len(result)+1]
|
|
result[len(result)-1] = chunk
|
|
chunk = ""
|
|
}
|
|
return i, result
|
|
}
|