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