| // 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 isSpace(c byte) bool { |
| switch c { |
| case ' ', '\t', '\r', '\n': |
| 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 |
| } |