Repository: go
Issue: proposal: x/net/http/httpguts: add IsToken
Labels: Proposal

### Proposal Details

According to [RFC 9110][rfc-9110] and [RFC 6265][rfc-6265], [header-field names][field-names], [methods][methods], and [cookie names][cookies] all share the same production: [_token_][token]. However, the logic for validating tokens is duplicated across `x/net/http/httpguts` and `net/http`:

1. `net/http` relies on [`httpguts.ValidHeaderFieldName`][ValidHeaderFieldName] for validating header names in [transport.go][transport.go].
2. `net/http` relies on a combination of [`strings.IndexFunc`][IndexFunc] and [`httpguts.IsTokenRune`][IsTokenRune] for validating HTTP methods in [method.go][method.go]; and
3. `net/http` relies on that same combination for validating cookie names in [cookie.go][cookie.go].

---

/cc @neild 

I propose the addition, in `x/net/http/httpguts`, of a function unifying the logic for validating tokens:

```go
func IsToken(string) bool
```
Its implementation would be identical to [`httpguts.ValidHeaderFieldName`'s implementation at tip][tip].

Then, both method validation and cookie-name validation could simply call `httpguts.IsToken`. 

`httpguts.ValidHeaderFieldName` would simply delegate to `httpguts.IsToken`. The former would then become redundant; perhaps it could be marked for deprecation, but I'm not sure what the best course of action is in this regard.

(Of course, a simple alternative to all this would be to add no function to `httpguts` and instead rely on `httpguts.ValidHeaderFieldName` for all three types of validation; but such an approach feels semantically dishonest.)

---

Beside the argument for reducing logic duplication, benchmarks indicate that the validation logic for methods and cookie names is not as fast as it could be (due in part to unnecessary UTF-8 decoding) and would benefit (at no extra cost) from [`httpguts.ValidHeaderFieldName`'s recent speedup](https://github.com/golang/go/issues/66700):

```text
goos: darwin
goarch: amd64
pkg: REDACTED
cpu: Intel(R) Core(TM) i7-6700HQ CPU @ 2.60GHz
                       │     std      │               jub0bs                │
                       │    sec/op    │   sec/op     vs base                │
IsCookieNameValid-8      1665.0n ± 1%   377.0n ± 0%  -77.36% (p=0.000 n=20)

                       │     std      │               jub0bs                │
                       │     B/op     │    B/op     vs base                 │
IsCookieNameValid-8      0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=20)

                       │     std      │               jub0bs                │
                       │  allocs/op   │ allocs/op   vs base                 │
IsCookieNameValid-8      0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=20)
```

Although a speedup of method validation is unlikely to be noticeable in practice, a speedup of cookie-name validation may be noticeable in cases of requests containing many cookies and/or cookies with long names (esp. those that use [cookie name prefixes][prefixes] like `__Secure-` and `__Host-`).

[ValidHeaderFieldName]: https://pkg.go.dev/golang.org/x/net/http/httpguts#ValidHeaderFieldName
[cookies]: https://www.rfc-editor.org/rfc/rfc6265.html#section-4.1.1
[field-names]: https://httpwg.org/specs/rfc9110.html#fields.names
[isNotToken]: https://github.com/golang/go/blob/2e064cf14441460290fd25d9d61f02a9d0bae671/src/net/http/http.go#L61
[methods]: https://httpwg.org/specs/rfc9110.html#method.overview
[rfc-6265]: https://www.rfc-editor.org/rfc/rfc6265.html
[rfc-9110]: https://httpwg.org/specs/rfc9110.html
[token]: https://www.rfc-editor.org/rfc/rfc2616#section-2.2
[transport.go]: https://cs.opensource.google/go/go/+/refs/tags/go1.22.2:src/net/http/transport.go;l=534
[method.go]: https://cs.opensource.google/go/go/+/refs/tags/go1.22.2:src/net/http/request.go;l=837
[cookie.go]: https://cs.opensource.google/go/go/+/refs/tags/go1.22.2:src/net/http/cookie.go;l=463
[IndexFunc]: https://pkg.go.dev/strings#IndexFunc
[IsTokenRune]: https://pkg.go.dev/golang.org/x/net/http/httpguts#IsTokenRune
[tip]: https://cs.opensource.google/go/x/net/+/0a24555f5cc06e8caf23d84a4f8b7102dcab838e:http/httpguts/httplex.go;l=196
[prefixes]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#sect2