...

Source file src/net/textproto/reader.go

Documentation: net/textproto

     1  // Copyright 2010 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package textproto
     6  
     7  import (
     8  	"bufio"
     9  	"bytes"
    10  	"fmt"
    11  	"io"
    12  	"strconv"
    13  	"strings"
    14  	"sync"
    15  )
    16  
    17  // A Reader implements convenience methods for reading requests
    18  // or responses from a text protocol network connection.
    19  type Reader struct {
    20  	R   *bufio.Reader
    21  	dot *dotReader
    22  	buf []byte // a re-usable buffer for readContinuedLineSlice
    23  }
    24  
    25  // NewReader returns a new Reader reading from r.
    26  //
    27  // To avoid denial of service attacks, the provided bufio.Reader
    28  // should be reading from an io.LimitReader or similar Reader to bound
    29  // the size of responses.
    30  func NewReader(r *bufio.Reader) *Reader {
    31  	return &Reader{R: r}
    32  }
    33  
    34  // ReadLine reads a single line from r,
    35  // eliding the final \n or \r\n from the returned string.
    36  func (r *Reader) ReadLine() (string, error) {
    37  	line, err := r.readLineSlice()
    38  	return string(line), err
    39  }
    40  
    41  // ReadLineBytes is like ReadLine but returns a []byte instead of a string.
    42  func (r *Reader) ReadLineBytes() ([]byte, error) {
    43  	line, err := r.readLineSlice()
    44  	if line != nil {
    45  		buf := make([]byte, len(line))
    46  		copy(buf, line)
    47  		line = buf
    48  	}
    49  	return line, err
    50  }
    51  
    52  func (r *Reader) readLineSlice() ([]byte, error) {
    53  	r.closeDot()
    54  	var line []byte
    55  	for {
    56  		l, more, err := r.R.ReadLine()
    57  		if err != nil {
    58  			return nil, err
    59  		}
    60  		// Avoid the copy if the first call produced a full line.
    61  		if line == nil && !more {
    62  			return l, nil
    63  		}
    64  		line = append(line, l...)
    65  		if !more {
    66  			break
    67  		}
    68  	}
    69  	return line, nil
    70  }
    71  
    72  // ReadContinuedLine reads a possibly continued line from r,
    73  // eliding the final trailing ASCII white space.
    74  // Lines after the first are considered continuations if they
    75  // begin with a space or tab character. In the returned data,
    76  // continuation lines are separated from the previous line
    77  // only by a single space: the newline and leading white space
    78  // are removed.
    79  //
    80  // For example, consider this input:
    81  //
    82  //	Line 1
    83  //	  continued...
    84  //	Line 2
    85  //
    86  // The first call to ReadContinuedLine will return "Line 1 continued..."
    87  // and the second will return "Line 2".
    88  //
    89  // Empty lines are never continued.
    90  func (r *Reader) ReadContinuedLine() (string, error) {
    91  	line, err := r.readContinuedLineSlice(noValidation)
    92  	return string(line), err
    93  }
    94  
    95  // trim returns s with leading and trailing spaces and tabs removed.
    96  // It does not assume Unicode or UTF-8.
    97  func trim(s []byte) []byte {
    98  	i := 0
    99  	for i < len(s) && (s[i] == ' ' || s[i] == '\t') {
   100  		i++
   101  	}
   102  	n := len(s)
   103  	for n > i && (s[n-1] == ' ' || s[n-1] == '\t') {
   104  		n--
   105  	}
   106  	return s[i:n]
   107  }
   108  
   109  // ReadContinuedLineBytes is like ReadContinuedLine but
   110  // returns a []byte instead of a string.
   111  func (r *Reader) ReadContinuedLineBytes() ([]byte, error) {
   112  	line, err := r.readContinuedLineSlice(noValidation)
   113  	if line != nil {
   114  		buf := make([]byte, len(line))
   115  		copy(buf, line)
   116  		line = buf
   117  	}
   118  	return line, err
   119  }
   120  
   121  // readContinuedLineSlice reads continued lines from the reader buffer,
   122  // returning a byte slice with all lines. The validateFirstLine function
   123  // is run on the first read line, and if it returns an error then this
   124  // error is returned from readContinuedLineSlice.
   125  func (r *Reader) readContinuedLineSlice(validateFirstLine func([]byte) error) ([]byte, error) {
   126  	if validateFirstLine == nil {
   127  		return nil, fmt.Errorf("missing validateFirstLine func")
   128  	}
   129  
   130  	// Read the first line.
   131  	line, err := r.readLineSlice()
   132  	if err != nil {
   133  		return nil, err
   134  	}
   135  	if len(line) == 0 { // blank line - no continuation
   136  		return line, nil
   137  	}
   138  
   139  	if err := validateFirstLine(line); err != nil {
   140  		return nil, err
   141  	}
   142  
   143  	// Optimistically assume that we have started to buffer the next line
   144  	// and it starts with an ASCII letter (the next header key), or a blank
   145  	// line, so we can avoid copying that buffered data around in memory
   146  	// and skipping over non-existent whitespace.
   147  	if r.R.Buffered() > 1 {
   148  		peek, _ := r.R.Peek(2)
   149  		if len(peek) > 0 && (isASCIILetter(peek[0]) || peek[0] == '\n') ||
   150  			len(peek) == 2 && peek[0] == '\r' && peek[1] == '\n' {
   151  			return trim(line), nil
   152  		}
   153  	}
   154  
   155  	// ReadByte or the next readLineSlice will flush the read buffer;
   156  	// copy the slice into buf.
   157  	r.buf = append(r.buf[:0], trim(line)...)
   158  
   159  	// Read continuation lines.
   160  	for r.skipSpace() > 0 {
   161  		line, err := r.readLineSlice()
   162  		if err != nil {
   163  			break
   164  		}
   165  		r.buf = append(r.buf, ' ')
   166  		r.buf = append(r.buf, trim(line)...)
   167  	}
   168  	return r.buf, nil
   169  }
   170  
   171  // skipSpace skips R over all spaces and returns the number of bytes skipped.
   172  func (r *Reader) skipSpace() int {
   173  	n := 0
   174  	for {
   175  		c, err := r.R.ReadByte()
   176  		if err != nil {
   177  			// Bufio will keep err until next read.
   178  			break
   179  		}
   180  		if c != ' ' && c != '\t' {
   181  			r.R.UnreadByte()
   182  			break
   183  		}
   184  		n++
   185  	}
   186  	return n
   187  }
   188  
   189  func (r *Reader) readCodeLine(expectCode int) (code int, continued bool, message string, err error) {
   190  	line, err := r.ReadLine()
   191  	if err != nil {
   192  		return
   193  	}
   194  	return parseCodeLine(line, expectCode)
   195  }
   196  
   197  func parseCodeLine(line string, expectCode int) (code int, continued bool, message string, err error) {
   198  	if len(line) < 4 || line[3] != ' ' && line[3] != '-' {
   199  		err = ProtocolError("short response: " + line)
   200  		return
   201  	}
   202  	continued = line[3] == '-'
   203  	code, err = strconv.Atoi(line[0:3])
   204  	if err != nil || code < 100 {
   205  		err = ProtocolError("invalid response code: " + line)
   206  		return
   207  	}
   208  	message = line[4:]
   209  	if 1 <= expectCode && expectCode < 10 && code/100 != expectCode ||
   210  		10 <= expectCode && expectCode < 100 && code/10 != expectCode ||
   211  		100 <= expectCode && expectCode < 1000 && code != expectCode {
   212  		err = &Error{code, message}
   213  	}
   214  	return
   215  }
   216  
   217  // ReadCodeLine reads a response code line of the form
   218  //
   219  //	code message
   220  //
   221  // where code is a three-digit status code and the message
   222  // extends to the rest of the line. An example of such a line is:
   223  //
   224  //	220 plan9.bell-labs.com ESMTP
   225  //
   226  // If the prefix of the status does not match the digits in expectCode,
   227  // ReadCodeLine returns with err set to &Error{code, message}.
   228  // For example, if expectCode is 31, an error will be returned if
   229  // the status is not in the range [310,319].
   230  //
   231  // If the response is multi-line, ReadCodeLine returns an error.
   232  //
   233  // An expectCode <= 0 disables the check of the status code.
   234  func (r *Reader) ReadCodeLine(expectCode int) (code int, message string, err error) {
   235  	code, continued, message, err := r.readCodeLine(expectCode)
   236  	if err == nil && continued {
   237  		err = ProtocolError("unexpected multi-line response: " + message)
   238  	}
   239  	return
   240  }
   241  
   242  // ReadResponse reads a multi-line response of the form:
   243  //
   244  //	code-message line 1
   245  //	code-message line 2
   246  //	...
   247  //	code message line n
   248  //
   249  // where code is a three-digit status code. The first line starts with the
   250  // code and a hyphen. The response is terminated by a line that starts
   251  // with the same code followed by a space. Each line in message is
   252  // separated by a newline (\n).
   253  //
   254  // See page 36 of RFC 959 (https://www.ietf.org/rfc/rfc959.txt) for
   255  // details of another form of response accepted:
   256  //
   257  //	code-message line 1
   258  //	message line 2
   259  //	...
   260  //	code message line n
   261  //
   262  // If the prefix of the status does not match the digits in expectCode,
   263  // ReadResponse returns with err set to &Error{code, message}.
   264  // For example, if expectCode is 31, an error will be returned if
   265  // the status is not in the range [310,319].
   266  //
   267  // An expectCode <= 0 disables the check of the status code.
   268  func (r *Reader) ReadResponse(expectCode int) (code int, message string, err error) {
   269  	code, continued, message, err := r.readCodeLine(expectCode)
   270  	multi := continued
   271  	for continued {
   272  		line, err := r.ReadLine()
   273  		if err != nil {
   274  			return 0, "", err
   275  		}
   276  
   277  		var code2 int
   278  		var moreMessage string
   279  		code2, continued, moreMessage, err = parseCodeLine(line, 0)
   280  		if err != nil || code2 != code {
   281  			message += "\n" + strings.TrimRight(line, "\r\n")
   282  			continued = true
   283  			continue
   284  		}
   285  		message += "\n" + moreMessage
   286  	}
   287  	if err != nil && multi && message != "" {
   288  		// replace one line error message with all lines (full message)
   289  		err = &Error{code, message}
   290  	}
   291  	return
   292  }
   293  
   294  // DotReader returns a new Reader that satisfies Reads using the
   295  // decoded text of a dot-encoded block read from r.
   296  // The returned Reader is only valid until the next call
   297  // to a method on r.
   298  //
   299  // Dot encoding is a common framing used for data blocks
   300  // in text protocols such as SMTP.  The data consists of a sequence
   301  // of lines, each of which ends in "\r\n".  The sequence itself
   302  // ends at a line containing just a dot: ".\r\n".  Lines beginning
   303  // with a dot are escaped with an additional dot to avoid
   304  // looking like the end of the sequence.
   305  //
   306  // The decoded form returned by the Reader's Read method
   307  // rewrites the "\r\n" line endings into the simpler "\n",
   308  // removes leading dot escapes if present, and stops with error io.EOF
   309  // after consuming (and discarding) the end-of-sequence line.
   310  func (r *Reader) DotReader() io.Reader {
   311  	r.closeDot()
   312  	r.dot = &dotReader{r: r}
   313  	return r.dot
   314  }
   315  
   316  type dotReader struct {
   317  	r     *Reader
   318  	state int
   319  }
   320  
   321  // Read satisfies reads by decoding dot-encoded data read from d.r.
   322  func (d *dotReader) Read(b []byte) (n int, err error) {
   323  	// Run data through a simple state machine to
   324  	// elide leading dots, rewrite trailing \r\n into \n,
   325  	// and detect ending .\r\n line.
   326  	const (
   327  		stateBeginLine = iota // beginning of line; initial state; must be zero
   328  		stateDot              // read . at beginning of line
   329  		stateDotCR            // read .\r at beginning of line
   330  		stateCR               // read \r (possibly at end of line)
   331  		stateData             // reading data in middle of line
   332  		stateEOF              // reached .\r\n end marker line
   333  	)
   334  	br := d.r.R
   335  	for n < len(b) && d.state != stateEOF {
   336  		var c byte
   337  		c, err = br.ReadByte()
   338  		if err != nil {
   339  			if err == io.EOF {
   340  				err = io.ErrUnexpectedEOF
   341  			}
   342  			break
   343  		}
   344  		switch d.state {
   345  		case stateBeginLine:
   346  			if c == '.' {
   347  				d.state = stateDot
   348  				continue
   349  			}
   350  			if c == '\r' {
   351  				d.state = stateCR
   352  				continue
   353  			}
   354  			d.state = stateData
   355  
   356  		case stateDot:
   357  			if c == '\r' {
   358  				d.state = stateDotCR
   359  				continue
   360  			}
   361  			if c == '\n' {
   362  				d.state = stateEOF
   363  				continue
   364  			}
   365  			d.state = stateData
   366  
   367  		case stateDotCR:
   368  			if c == '\n' {
   369  				d.state = stateEOF
   370  				continue
   371  			}
   372  			// Not part of .\r\n.
   373  			// Consume leading dot and emit saved \r.
   374  			br.UnreadByte()
   375  			c = '\r'
   376  			d.state = stateData
   377  
   378  		case stateCR:
   379  			if c == '\n' {
   380  				d.state = stateBeginLine
   381  				break
   382  			}
   383  			// Not part of \r\n. Emit saved \r
   384  			br.UnreadByte()
   385  			c = '\r'
   386  			d.state = stateData
   387  
   388  		case stateData:
   389  			if c == '\r' {
   390  				d.state = stateCR
   391  				continue
   392  			}
   393  			if c == '\n' {
   394  				d.state = stateBeginLine
   395  			}
   396  		}
   397  		b[n] = c
   398  		n++
   399  	}
   400  	if err == nil && d.state == stateEOF {
   401  		err = io.EOF
   402  	}
   403  	if err != nil && d.r.dot == d {
   404  		d.r.dot = nil
   405  	}
   406  	return
   407  }
   408  
   409  // closeDot drains the current DotReader if any,
   410  // making sure that it reads until the ending dot line.
   411  func (r *Reader) closeDot() {
   412  	if r.dot == nil {
   413  		return
   414  	}
   415  	buf := make([]byte, 128)
   416  	for r.dot != nil {
   417  		// When Read reaches EOF or an error,
   418  		// it will set r.dot == nil.
   419  		r.dot.Read(buf)
   420  	}
   421  }
   422  
   423  // ReadDotBytes reads a dot-encoding and returns the decoded data.
   424  //
   425  // See the documentation for the DotReader method for details about dot-encoding.
   426  func (r *Reader) ReadDotBytes() ([]byte, error) {
   427  	return io.ReadAll(r.DotReader())
   428  }
   429  
   430  // ReadDotLines reads a dot-encoding and returns a slice
   431  // containing the decoded lines, with the final \r\n or \n elided from each.
   432  //
   433  // See the documentation for the DotReader method for details about dot-encoding.
   434  func (r *Reader) ReadDotLines() ([]string, error) {
   435  	// We could use ReadDotBytes and then Split it,
   436  	// but reading a line at a time avoids needing a
   437  	// large contiguous block of memory and is simpler.
   438  	var v []string
   439  	var err error
   440  	for {
   441  		var line string
   442  		line, err = r.ReadLine()
   443  		if err != nil {
   444  			if err == io.EOF {
   445  				err = io.ErrUnexpectedEOF
   446  			}
   447  			break
   448  		}
   449  
   450  		// Dot by itself marks end; otherwise cut one dot.
   451  		if len(line) > 0 && line[0] == '.' {
   452  			if len(line) == 1 {
   453  				break
   454  			}
   455  			line = line[1:]
   456  		}
   457  		v = append(v, line)
   458  	}
   459  	return v, err
   460  }
   461  
   462  var colon = []byte(":")
   463  
   464  // ReadMIMEHeader reads a MIME-style header from r.
   465  // The header is a sequence of possibly continued Key: Value lines
   466  // ending in a blank line.
   467  // The returned map m maps CanonicalMIMEHeaderKey(key) to a
   468  // sequence of values in the same order encountered in the input.
   469  //
   470  // For example, consider this input:
   471  //
   472  //	My-Key: Value 1
   473  //	Long-Key: Even
   474  //	       Longer Value
   475  //	My-Key: Value 2
   476  //
   477  // Given that input, ReadMIMEHeader returns the map:
   478  //
   479  //	map[string][]string{
   480  //		"My-Key": {"Value 1", "Value 2"},
   481  //		"Long-Key": {"Even Longer Value"},
   482  //	}
   483  func (r *Reader) ReadMIMEHeader() (MIMEHeader, error) {
   484  	// Avoid lots of small slice allocations later by allocating one
   485  	// large one ahead of time which we'll cut up into smaller
   486  	// slices. If this isn't big enough later, we allocate small ones.
   487  	var strs []string
   488  	hint := r.upcomingHeaderNewlines()
   489  	if hint > 0 {
   490  		strs = make([]string, hint)
   491  	}
   492  
   493  	m := make(MIMEHeader, hint)
   494  
   495  	// The first line cannot start with a leading space.
   496  	if buf, err := r.R.Peek(1); err == nil && (buf[0] == ' ' || buf[0] == '\t') {
   497  		line, err := r.readLineSlice()
   498  		if err != nil {
   499  			return m, err
   500  		}
   501  		return m, ProtocolError("malformed MIME header initial line: " + string(line))
   502  	}
   503  
   504  	for {
   505  		kv, err := r.readContinuedLineSlice(mustHaveFieldNameColon)
   506  		if len(kv) == 0 {
   507  			return m, err
   508  		}
   509  
   510  		// Key ends at first colon.
   511  		k, v, ok := bytes.Cut(kv, colon)
   512  		if !ok {
   513  			return m, ProtocolError("malformed MIME header line: " + string(kv))
   514  		}
   515  		key := canonicalMIMEHeaderKey(k)
   516  
   517  		// As per RFC 7230 field-name is a token, tokens consist of one or more chars.
   518  		// We could return a ProtocolError here, but better to be liberal in what we
   519  		// accept, so if we get an empty key, skip it.
   520  		if key == "" {
   521  			continue
   522  		}
   523  
   524  		// Skip initial spaces in value.
   525  		value := strings.TrimLeft(string(v), " \t")
   526  
   527  		vv := m[key]
   528  		if vv == nil && len(strs) > 0 {
   529  			// More than likely this will be a single-element key.
   530  			// Most headers aren't multi-valued.
   531  			// Set the capacity on strs[0] to 1, so any future append
   532  			// won't extend the slice into the other strings.
   533  			vv, strs = strs[:1:1], strs[1:]
   534  			vv[0] = value
   535  			m[key] = vv
   536  		} else {
   537  			m[key] = append(vv, value)
   538  		}
   539  
   540  		if err != nil {
   541  			return m, err
   542  		}
   543  	}
   544  }
   545  
   546  // noValidation is a no-op validation func for readContinuedLineSlice
   547  // that permits any lines.
   548  func noValidation(_ []byte) error { return nil }
   549  
   550  // mustHaveFieldNameColon ensures that, per RFC 7230, the
   551  // field-name is on a single line, so the first line must
   552  // contain a colon.
   553  func mustHaveFieldNameColon(line []byte) error {
   554  	if bytes.IndexByte(line, ':') < 0 {
   555  		return ProtocolError(fmt.Sprintf("malformed MIME header: missing colon: %q", line))
   556  	}
   557  	return nil
   558  }
   559  
   560  var nl = []byte("\n")
   561  
   562  // upcomingHeaderNewlines returns an approximation of the number of newlines
   563  // that will be in this header. If it gets confused, it returns 0.
   564  func (r *Reader) upcomingHeaderNewlines() (n int) {
   565  	// Try to determine the 'hint' size.
   566  	r.R.Peek(1) // force a buffer load if empty
   567  	s := r.R.Buffered()
   568  	if s == 0 {
   569  		return
   570  	}
   571  	peek, _ := r.R.Peek(s)
   572  	return bytes.Count(peek, nl)
   573  }
   574  
   575  // CanonicalMIMEHeaderKey returns the canonical format of the
   576  // MIME header key s. The canonicalization converts the first
   577  // letter and any letter following a hyphen to upper case;
   578  // the rest are converted to lowercase. For example, the
   579  // canonical key for "accept-encoding" is "Accept-Encoding".
   580  // MIME header keys are assumed to be ASCII only.
   581  // If s contains a space or invalid header field bytes, it is
   582  // returned without modifications.
   583  func CanonicalMIMEHeaderKey(s string) string {
   584  	// Quick check for canonical encoding.
   585  	upper := true
   586  	for i := 0; i < len(s); i++ {
   587  		c := s[i]
   588  		if !validHeaderFieldByte(c) {
   589  			return s
   590  		}
   591  		if upper && 'a' <= c && c <= 'z' {
   592  			return canonicalMIMEHeaderKey([]byte(s))
   593  		}
   594  		if !upper && 'A' <= c && c <= 'Z' {
   595  			return canonicalMIMEHeaderKey([]byte(s))
   596  		}
   597  		upper = c == '-'
   598  	}
   599  	return s
   600  }
   601  
   602  const toLower = 'a' - 'A'
   603  
   604  // validHeaderFieldByte reports whether b is a valid byte in a header
   605  // field name. RFC 7230 says:
   606  //
   607  //	header-field   = field-name ":" OWS field-value OWS
   608  //	field-name     = token
   609  //	tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" / "+" / "-" / "." /
   610  //	        "^" / "_" / "`" / "|" / "~" / DIGIT / ALPHA
   611  //	token = 1*tchar
   612  func validHeaderFieldByte(b byte) bool {
   613  	return int(b) < len(isTokenTable) && isTokenTable[b]
   614  }
   615  
   616  // canonicalMIMEHeaderKey is like CanonicalMIMEHeaderKey but is
   617  // allowed to mutate the provided byte slice before returning the
   618  // string.
   619  //
   620  // For invalid inputs (if a contains spaces or non-token bytes), a
   621  // is unchanged and a string copy is returned.
   622  func canonicalMIMEHeaderKey(a []byte) string {
   623  	// See if a looks like a header key. If not, return it unchanged.
   624  	for _, c := range a {
   625  		if validHeaderFieldByte(c) {
   626  			continue
   627  		}
   628  		// Don't canonicalize.
   629  		return string(a)
   630  	}
   631  
   632  	upper := true
   633  	for i, c := range a {
   634  		// Canonicalize: first letter upper case
   635  		// and upper case after each dash.
   636  		// (Host, User-Agent, If-Modified-Since).
   637  		// MIME headers are ASCII only, so no Unicode issues.
   638  		if upper && 'a' <= c && c <= 'z' {
   639  			c -= toLower
   640  		} else if !upper && 'A' <= c && c <= 'Z' {
   641  			c += toLower
   642  		}
   643  		a[i] = c
   644  		upper = c == '-' // for next time
   645  	}
   646  	commonHeaderOnce.Do(initCommonHeader)
   647  	// The compiler recognizes m[string(byteSlice)] as a special
   648  	// case, so a copy of a's bytes into a new string does not
   649  	// happen in this map lookup:
   650  	if v := commonHeader[string(a)]; v != "" {
   651  		return v
   652  	}
   653  	return string(a)
   654  }
   655  
   656  // commonHeader interns common header strings.
   657  var commonHeader map[string]string
   658  
   659  var commonHeaderOnce sync.Once
   660  
   661  func initCommonHeader() {
   662  	commonHeader = make(map[string]string)
   663  	for _, v := range []string{
   664  		"Accept",
   665  		"Accept-Charset",
   666  		"Accept-Encoding",
   667  		"Accept-Language",
   668  		"Accept-Ranges",
   669  		"Cache-Control",
   670  		"Cc",
   671  		"Connection",
   672  		"Content-Id",
   673  		"Content-Language",
   674  		"Content-Length",
   675  		"Content-Transfer-Encoding",
   676  		"Content-Type",
   677  		"Cookie",
   678  		"Date",
   679  		"Dkim-Signature",
   680  		"Etag",
   681  		"Expires",
   682  		"From",
   683  		"Host",
   684  		"If-Modified-Since",
   685  		"If-None-Match",
   686  		"In-Reply-To",
   687  		"Last-Modified",
   688  		"Location",
   689  		"Message-Id",
   690  		"Mime-Version",
   691  		"Pragma",
   692  		"Received",
   693  		"Return-Path",
   694  		"Server",
   695  		"Set-Cookie",
   696  		"Subject",
   697  		"To",
   698  		"User-Agent",
   699  		"Via",
   700  		"X-Forwarded-For",
   701  		"X-Imforwards",
   702  		"X-Powered-By",
   703  	} {
   704  		commonHeader[v] = v
   705  	}
   706  }
   707  
   708  // isTokenTable is a copy of net/http/lex.go's isTokenTable.
   709  // See https://httpwg.github.io/specs/rfc7230.html#rule.token.separators
   710  var isTokenTable = [127]bool{
   711  	'!':  true,
   712  	'#':  true,
   713  	'$':  true,
   714  	'%':  true,
   715  	'&':  true,
   716  	'\'': true,
   717  	'*':  true,
   718  	'+':  true,
   719  	'-':  true,
   720  	'.':  true,
   721  	'0':  true,
   722  	'1':  true,
   723  	'2':  true,
   724  	'3':  true,
   725  	'4':  true,
   726  	'5':  true,
   727  	'6':  true,
   728  	'7':  true,
   729  	'8':  true,
   730  	'9':  true,
   731  	'A':  true,
   732  	'B':  true,
   733  	'C':  true,
   734  	'D':  true,
   735  	'E':  true,
   736  	'F':  true,
   737  	'G':  true,
   738  	'H':  true,
   739  	'I':  true,
   740  	'J':  true,
   741  	'K':  true,
   742  	'L':  true,
   743  	'M':  true,
   744  	'N':  true,
   745  	'O':  true,
   746  	'P':  true,
   747  	'Q':  true,
   748  	'R':  true,
   749  	'S':  true,
   750  	'T':  true,
   751  	'U':  true,
   752  	'W':  true,
   753  	'V':  true,
   754  	'X':  true,
   755  	'Y':  true,
   756  	'Z':  true,
   757  	'^':  true,
   758  	'_':  true,
   759  	'`':  true,
   760  	'a':  true,
   761  	'b':  true,
   762  	'c':  true,
   763  	'd':  true,
   764  	'e':  true,
   765  	'f':  true,
   766  	'g':  true,
   767  	'h':  true,
   768  	'i':  true,
   769  	'j':  true,
   770  	'k':  true,
   771  	'l':  true,
   772  	'm':  true,
   773  	'n':  true,
   774  	'o':  true,
   775  	'p':  true,
   776  	'q':  true,
   777  	'r':  true,
   778  	's':  true,
   779  	't':  true,
   780  	'u':  true,
   781  	'v':  true,
   782  	'w':  true,
   783  	'x':  true,
   784  	'y':  true,
   785  	'z':  true,
   786  	'|':  true,
   787  	'~':  true,
   788  }
   789  

View as plain text