Handle nested comments in the User-Agent string. ... (quixote/http_request.py)
Neil Schemenauer <nascheme-fVcApmY9cLvQ3/1i3zOLAti2O/[email protected]> Mon, 30 Dec 2002 19:55:29 -0500
| Newsgroups | gmane.comp.web.quixote.cvs |
|---|---|
| Message-ID | <[email protected]> |
Update of /home/cvs/quixote
In directory hewson:/tmp/cvs-serv10362
Modified Files:
http_request.py
Log Message:
Handle nested comments in the User-Agent string. Some versions of MSIE
use that feature and it's allowed by the specification.
Index: http_request.py
===================================================================
RCS file: /home/cvs/quixote/http_request.py,v
retrieving revision 1.54
retrieving revision 1.55
diff -u -d -r1.54 -r1.55
--- http_request.py 20 Nov 2002 19:43:22 -0000 1.54
+++ http_request.py 31 Dec 2002 00:55:26 -0000 1.55
@@ -47,17 +47,11 @@
# CHAR = any 7-bit US ASCII character (0-127)
# separators are ( ) < > @ , ; : \ " / [ ] ? = { }
#
-# The comment RE is a simplification and not to spec -- it doesn't
-# allow for backslash-quoting within comments or nested comments!
-#
-# The user_agent RE is also a simplification; it only looks for
-# one "product", possibly followed by a comment.
+# The user_agent RE is a simplification; it only looks for one "product",
+# possibly followed by a comment.
_http_token_pat = r'[^\x00-\x20\(\)\<\>\@\,\;\:\\\"\/\[\]\?\=\{\}\x7F-\xFF]+'
_http_product_pat = r'(%s)(?:/(%s))?' % (_http_token_pat, _http_token_pat)
_http_product_re = re.compile(_http_product_pat)
-_http_comment_pat = r'\(([^\(\)]*)\)'
-_http_useragent_re = re.compile(r'%s(?:\s+%s)?'
- % (_http_product_pat, _http_comment_pat))
_comment_delim_re = re.compile(r';\s*')
@@ -381,26 +375,51 @@
return (None, None)
# The syntax for "User-Agent" in RFC 2616 is fairly simple:
- # User-Agent = "User-Agent" ":" 1*( product | comment )
- # product = token ["/" product-version]
- # product-version = token
- # token = 1*<any CHAR except CTLs or separators>
#
- # The _http_useragent_re handles the most-commonly-used
- # subset of this syntax, namely
+ # User-Agent = "User-Agent" ":" 1*( product | comment )
+ # product = token ["/" product-version ]
+ # product-version = token
+ # comment = "(" *( ctext | comment ) ")"
+ # ctext = <any TEXT excluding "(" and ")">
+ # token = 1*<any CHAR except CTLs or tspecials>
+ # tspecials = "(" | ")" | "<" | ">" | "@" | "," | ";" | ":" |
+ # "\" | <"> | "/" | "[" | "]" | "?" | "=" | "{" |
+ # "}" | SP | HT
+ #
+ # This function handles the most-commonly-used subset of this syntax,
+ # namely
# User-Agent = "User-Agent" ":" product 1*SP [comment]
# ie. one product string followed by an optional comment;
# anything after that first comment is ignored. This should be
# enough to distinguish Mozilla/Netscape, MSIE, Opera, and
# Konqueror.
- ua_match = _http_useragent_re.match(ua)
- if not ua_match:
+ m = _http_product_re.match(ua)
+ if not m:
import sys
sys.stderr.write("couldn't parse User-Agent header: %r\n" % ua)
return (None, None)
- (name, version, comment) = ua_match.groups()
+ name, version = m.groups()
+ ua = ua[m.end():].lstrip()
+
+ if ua.startswith('('):
+ # we need to handle nested comments since MSIE uses them
+ depth = 1
+ chars = []
+ for c in ua[1:]:
+ if c == '(':
+ depth += 1
+ elif c == ')':
+ depth -= 1
+ if depth == 0:
+ break
+ elif depth == 1:
+ # nested comments are discarded
+ chars.append(c)
+ comment = ''.join(chars)
+ else:
+ comment = ''
if comment:
comment_chunks = _comment_delim_re.split(comment)
else: