Repository: twisted/twisted

Issue:
Typo in `_istoken` causes valid HTTP requests to be rejected.
# The bug
In f4fd67430494a79fef8727529858681ed14cdf39, a typo was made that causes tokens containing `&` to be incorrectly rejected. This causes, for example, requests with methods containing `&` to be rejected.

The current definition of `_istoken` looks like this:
```python3
def _istoken(b: bytes) -> bool:
    """
    Is the string a token per RFC 9110 section 5.6.2?
    """
    for c in b:
        if c not in (
            b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"  # ALPHA
            b"0123456789"  # DIGIT
            b"!#$%^'*+-.^_`|~"
        ):
            return False
    return b != b""
```
See how `^` is listed twice? The first one of those was probably intended to be a `&`. This would give the same order of characters listed in the `tchar` ABNF rule from the RFC.

Discussion:
codspeed-hq[bot]: ## [CodSpeed Performance Report](https://codspeed.io/twisted/twisted/branches/kenballus:trunk)

### Merging #12302 will **not alter performance**

<sub>Comparing <code>kenballus:trunk</code> (f0f5ba8) with <code>trunk</code> (f4fd674)</sub>



### Summary

`✅ 23` untouched benchmarks





kenballus: please review

Patch:
diff --git a/src/twisted/web/_abnf.py b/src/twisted/web/_abnf.py
index a9fd5a48de..8029943bea 100644
--- a/src/twisted/web/_abnf.py
+++ b/src/twisted/web/_abnf.py
@@ -15,7 +15,7 @@ def _istoken(b: bytes) -> bool:
         if c not in (
             b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"  # ALPHA
             b"0123456789"  # DIGIT
-            b"!#$%^'*+-.^_`|~"
+            b"!#$%&'*+-.^_`|~"
         ):
             return False
     return b != b""
diff --git a/src/twisted/web/newsfragments/12301.bugfix b/src/twisted/web/newsfragments/12301.bugfix
new file mode 100644
index 0000000000..9ac16288fa
--- /dev/null
+++ b/src/twisted/web/newsfragments/12301.bugfix
@@ -0,0 +1,1 @@
+twisted.web's HTTP/1.1 server now accepts '&' within tokens (methods, header field names, etc.), in compliance with RFC 9110.


Test patch:
diff --git a/src/twisted/web/test/test_abnf.py b/src/twisted/web/test/test_abnf.py
index 68a3403f5b..7d6c4d9bbf 100644
--- a/src/twisted/web/test/test_abnf.py
+++ b/src/twisted/web/test/test_abnf.py
@@ -18,6 +18,7 @@ class IsTokenTests(unittest.SynchronousTestCase):
         for b in (
             b"GET",
             b"Cache-Control",
+            b"&",
         ):
             self.assertTrue(_istoken(b))