| // 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 | |
| } |