Special Headers¶
pyheaderparse automatically parses certain headers into structured formats for easier access.
Content-Type with Parameters¶
Content-Type headers are parsed into HeaderValue objects with separate value and parameters:
from parser_header import HeaderParser
parser = HeaderParser()
parser.set('content-type', 'text/html; charset=utf-8; boundary=something')
ct = parser.get('content-type')
print(f"Type: {type(ct)}")
# Output: Type: <class 'parser_header.parser.HeaderValue'>
print(f"Value: {ct.value}")
# Output: Value: text/html
print(f"Params: {ct.params}")
# Output: Params: {'charset': 'utf-8', 'boundary': 'something'}
print(f"Charset: {ct.params.get('charset')}")
# Output: Charset: utf-8
# The content_type property returns just the value
print(f"Property: {parser.content_type}")
# Output: Property: text/html
# String representation
print(f"String: {str(ct)}")
# Output: String: text/html; charset=utf-8; boundary=something
Content-Disposition¶
Similar to Content-Type, parsed with parameters:
parser = HeaderParser()
parser.set('content-disposition', 'attachment; filename="report.pdf"; size=1024')
cd = parser.get('content-disposition')
print(f"Value: {cd.value}")
# Output: Value: attachment
print(f"Filename: {cd.params.get('filename')}")
# Output: Filename: report.pdf
print(f"Size: {cd.params.get('size')}")
# Output: Size: 1024
Accept Header¶
Accept headers are parsed into a list of media types with quality values, sorted by quality (descending):
parser = HeaderParser()
parser.set('accept', 'text/html,application/json;q=0.9,*/*;q=0.8,text/plain;q=0.7')
accept = parser.get('accept')
print(accept)
# Output (sorted by quality):
# [
# {'type': 'text/html', 'q': 1.0},
# {'type': 'application/json', 'q': 0.9},
# {'type': '*/*', 'q': 0.8},
# {'type': 'text/plain', 'q': 0.7}
# ]
# Get preferred type
print(f"Preferred: {accept[0]['type']}")
# Output: Preferred: text/html
# Check if JSON is accepted
json_accepted = any(a['type'] == 'application/json' for a in accept)
print(f"JSON accepted: {json_accepted}")
# Output: JSON accepted: True
Special case - wildcard:
parser = HeaderParser()
parser.set('accept', '*/*')
print(parser.get('accept'))
# Output: [{'type': '*/*', 'q': 1.0}]
Accept-Language¶
Accept-Language is parsed similarly with language codes and quality values:
parser = HeaderParser()
parser.set('accept-language', 'en-US,en;q=0.9,es;q=0.8,fr;q=0.7,de;q=0.5')
langs = parser.get('accept-language')
print(langs)
# Output (sorted by quality):
# [
# {'lang': 'en-US', 'q': 1.0},
# {'lang': 'en', 'q': 0.9},
# {'lang': 'es', 'q': 0.8},
# {'lang': 'fr', 'q': 0.7},
# {'lang': 'de', 'q': 0.5}
# ]
# Get preferred language
print(f"Preferred: {langs[0]['lang']}")
# Output: Preferred: en-US
# Get all accepted languages
all_langs = [l['lang'] for l in langs]
print(f"All: {all_langs}")
# Output: All: ['en-US', 'en', 'es', 'fr', 'de']
Accept-Encoding¶
Accept-Encoding is parsed into a simple list:
parser = HeaderParser()
parser.set('accept-encoding', 'gzip, deflate, br, zstd')
encodings = parser.get('accept-encoding')
print(encodings)
# Output: ['gzip', 'deflate', 'br', 'zstd']
# Check support
print(f"Supports gzip: {'gzip' in encodings}")
# Output: Supports gzip: True
print(f"Supports brotli: {'br' in encodings}")
# Output: Supports brotli: True
Cache-Control¶
Cache-Control directives are parsed into a dictionary:
parser = HeaderParser()
parser.set('cache-control', 'max-age=3600, public, no-transform, must-revalidate')
cache = parser.get('cache-control')
print(cache)
# Output:
# {
# 'max-age': 3600, # Numeric values parsed as int
# 'public': True, # Flags parsed as True
# 'no-transform': True,
# 'must-revalidate': True
# }
# Access directives
print(f"Max age: {cache.get('max-age')} seconds")
# Output: Max age: 3600 seconds
print(f"Is public: {cache.get('public', False)}")
# Output: Is public: True
print(f"No cache: {cache.get('no-cache', False)}")
# Output: No cache: False
Boolean Headers¶
Certain headers are automatically parsed as boolean values:
DNT(Do Not Track)Sec-GPC(Global Privacy Control)Upgrade-Insecure-Requests
parser = HeaderParser()
parser.set('dnt', '1')
parser.set('sec-gpc', '1')
parser.set('upgrade-insecure-requests', '1')
print(f"DNT: {parser.get('dnt')}")
# Output: DNT: True
print(f"Sec-GPC: {parser.get('sec-gpc')}")
# Output: Sec-GPC: True
print(f"UIR: {parser.get('upgrade-insecure-requests')}")
# Output: UIR: True
# Type check
print(f"Type: {type(parser.get('dnt'))}")
# Output: Type: <class 'bool'>
Recognized true values: 1, true, yes, ?1
Client Hints (Sec-CH-UA-*)¶
Modern browsers send Client Hints for feature detection. These are parsed specially:
Sec-CH-UA (User Agent Brands)¶
parser = HeaderParser()
parser.set('sec-ch-ua', '"Chrome";v="144", "Not A Brand";v="8", "Chromium";v="144"')
ua = parser.get('sec-ch-ua')
print(ua)
# Output:
# [
# {'brand': 'Chrome', 'version': '144'},
# {'brand': 'Not A Brand', 'version': '8'},
# {'brand': 'Chromium', 'version': '144'}
# ]
# Find Chrome version
chrome = next((b for b in ua if b['brand'] == 'Chrome'), None)
if chrome:
print(f"Chrome version: {chrome['version']}")
# Output: Chrome version: 144
Sec-CH-UA-Mobile¶
parser = HeaderParser()
# Mobile device
parser.set('sec-ch-ua-mobile', '?1')
print(f"Is mobile: {parser.get('sec-ch-ua-mobile')}")
# Output: Is mobile: True
# Desktop
parser.set('sec-ch-ua-mobile', '?0')
print(f"Is mobile: {parser.get('sec-ch-ua-mobile')}")
# Output: Is mobile: False
Other Client Hints¶
String values have quotes stripped automatically:
raw = '''sec-ch-ua-platform: "Windows"
sec-ch-ua-arch: "x86"
sec-ch-ua-bitness: "64"
sec-ch-ua-model: ""
sec-ch-ua-full-version: "144.0.7524.3"
sec-ch-ua-platform-version: "15.0.0"
'''
parser = HeaderParser(raw)
print(f"Platform: {parser.get('sec-ch-ua-platform')}")
# Output: Platform: Windows (quotes stripped)
print(f"Arch: {parser.get('sec-ch-ua-arch')}")
# Output: Arch: x86
print(f"Bitness: {parser.get('sec-ch-ua-bitness')}")
# Output: Bitness: 64
print(f"Model: {parser.get('sec-ch-ua-model')}")
# Output: Model: (empty string)
print(f"Full Version: {parser.get('sec-ch-ua-full-version')}")
# Output: Full Version: 144.0.7524.3
Get All Client Hints¶
raw = '''sec-ch-ua: "Chrome";v="144"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
sec-ch-ua-arch: "x86"
'''
parser = HeaderParser(raw)
hints = parser.get_client_hints()
print(hints)
# Output:
# {
# 'sec-ch-ua': [{'brand': 'Chrome', 'version': '144'}],
# 'sec-ch-ua-mobile': False,
# 'sec-ch-ua-platform': 'Windows',
# 'sec-ch-ua-arch': 'x86'
# }
Sec-CH-UA-Full-Version-List¶
parser = HeaderParser()
parser.set(
'sec-ch-ua-full-version-list',
'"Not(A:Brand";v="8.0.0.0", "Chromium";v="144.0.7524.3", "Google Chrome";v="144.0.7524.3"'
)
versions = parser.get('sec-ch-ua-full-version-list')
print(versions)
# Output:
# [
# {'brand': 'Not(A:Brand', 'version': '8.0.0.0'},
# {'brand': 'Chromium', 'version': '144.0.7524.3'},
# {'brand': 'Google Chrome', 'version': '144.0.7524.3'}
# ]
Fetch Metadata (Sec-Fetch-*)¶
Sec-Fetch headers provide context about how a request was initiated:
raw = '''sec-fetch-site: same-origin
sec-fetch-mode: cors
sec-fetch-dest: empty
sec-fetch-user: ?1
'''
parser = HeaderParser(raw)
# Individual access
print(f"Site: {parser.get('sec-fetch-site')}")
# Output: Site: same-origin
print(f"Mode: {parser.get('sec-fetch-mode')}")
# Output: Mode: cors
print(f"Dest: {parser.get('sec-fetch-dest')}")
# Output: Dest: empty
# Get all at once
metadata = parser.get_sec_fetch_metadata()
print(metadata)
# Output:
# {
# 'site': 'same-origin',
# 'mode': 'cors',
# 'dest': 'empty',
# 'user': '?1'
# }
Common Values:
sec-fetch-site:same-origin,same-site,cross-site,nonesec-fetch-mode:navigate,cors,no-cors,same-origin,websocketsec-fetch-dest:document,script,style,image,font,empty, etc.
Priority Header¶
The Priority header (HTTP/2 and HTTP/3) is parsed into a dictionary:
parser = HeaderParser()
parser.set('priority', 'u=1, i')
priority = parser.get('priority')
print(priority)
# Output: {'u': '1', 'i': True}
# u = urgency (0-7, lower is more urgent)
# i = incremental (boolean flag)
print(f"Urgency: {priority.get('u')}")
# Output: Urgency: 1
print(f"Incremental: {priority.get('i')}")
# Output: Incremental: True
Content-Length¶
Content-Length is automatically parsed as an integer:
parser = HeaderParser()
parser.set('content-length', '1024')
length = parser.get('content-length')
print(f"Value: {length}")
# Output: Value: 1024
print(f"Type: {type(length)}")
# Output: Type: <class 'int'>
# Also via property
print(f"Property: {parser.content_length}")
# Output: Property: 1024
Custom/Unknown Headers¶
Headers that don’t have special parsing are stored as plain strings:
parser = HeaderParser()
parser.set('x-custom-header', 'some value here')
parser.set('x-request-id', 'req-12345-abcde')
parser.set('authorization', 'Bearer eyJhbGciOiJIUzI1NiIs...')
print(parser.get('x-custom-header'))
# Output: some value here
print(parser.get('x-request-id'))
# Output: req-12345-abcde
print(parser.get('authorization'))
# Output: Bearer eyJhbGciOiJIUzI1NiIs...
Using set_raw() to Skip Parsing¶
If you want to store a value without any parsing:
parser = HeaderParser()
# With set() - value is parsed
parser.set('cache-control', 'max-age=3600')
print(parser.get('cache-control'))
# Output: {'max-age': 3600}
# With set_raw() - value stored as-is
parser.set_raw('cache-control', 'max-age=3600')
print(parser.get('cache-control'))
# Output: max-age=3600 (string)