0
# Request and Response Processing
1
2
HTTP request and response representation with case-insensitive headers, body processing, and URI parsing capabilities. VCR.py provides internal models for capturing and manipulating HTTP interactions.
3
4
## Capabilities
5
6
### Request Class
7
8
VCR's representation of an HTTP request with automatic body and header processing.
9
10
```python { .api }
11
class Request:
12
"""
13
VCR's representation of an HTTP request.
14
15
Args:
16
method (str): HTTP method (GET, POST, etc.)
17
uri (str): Full request URI
18
body: Request body (str, bytes, file-like object, or iterable)
19
headers: Request headers (dict or HeadersDict)
20
"""
21
def __init__(self, method: str, uri: str, body, headers): ...
22
```
23
24
### Request Properties
25
26
Properties providing parsed access to request components.
27
28
```python { .api }
29
@property
30
def headers(self) -> HeadersDict:
31
"""Case-insensitive dictionary of request headers."""
32
33
@property
34
def body(self):
35
"""
36
Request body, returned in appropriate format:
37
- File-like objects: Returns BytesIO
38
- Iterables: Returns iterator
39
- Other types: Returns as-is
40
"""
41
42
@property
43
def method(self) -> str:
44
"""HTTP method (GET, POST, PUT, DELETE, etc.)"""
45
46
@property
47
def uri(self) -> str:
48
"""Complete request URI"""
49
50
@property
51
def scheme(self) -> str:
52
"""URI scheme (http, https)"""
53
54
@property
55
def host(self) -> str:
56
"""Request host/domain"""
57
58
@property
59
def port(self) -> int:
60
"""Request port number"""
61
62
@property
63
def path(self) -> str:
64
"""URI path component"""
65
66
@property
67
def query(self) -> str:
68
"""Query string component"""
69
```
70
71
### HeadersDict Class
72
73
Case-insensitive dictionary implementation for HTTP headers.
74
75
```python { .api }
76
class HeadersDict(dict):
77
"""
78
Case-insensitive dictionary for HTTP headers.
79
80
Allows access to headers regardless of case:
81
headers['Content-Type'] == headers['content-type']
82
"""
83
def __init__(self, data=None): ...
84
```
85
86
## Usage Examples
87
88
### Creating Requests
89
90
```python
91
from vcr.request import Request, HeadersDict
92
93
# Basic request creation
94
request = Request(
95
method='GET',
96
uri='https://api.example.com/users?page=1',
97
body=None,
98
headers={'User-Agent': 'MyApp/1.0', 'Accept': 'application/json'}
99
)
100
101
# POST request with body
102
post_request = Request(
103
method='POST',
104
uri='https://api.example.com/users',
105
body='{"name": "John", "email": "john@example.com"}',
106
headers={
107
'Content-Type': 'application/json',
108
'Authorization': 'Bearer token123'
109
}
110
)
111
```
112
113
### Accessing Request Properties
114
115
```python
116
# URI components
117
print(request.scheme) # 'https'
118
print(request.host) # 'api.example.com'
119
print(request.port) # 443 (default for https)
120
print(request.path) # '/users'
121
print(request.query) # 'page=1'
122
123
# Headers (case-insensitive access)
124
print(request.headers['User-Agent']) # 'MyApp/1.0'
125
print(request.headers['user-agent']) # 'MyApp/1.0' (same value)
126
print(request.headers.get('Accept')) # 'application/json'
127
```
128
129
### Body Processing
130
131
```python
132
import io
133
134
# String body
135
request = Request(
136
method='POST',
137
uri='https://api.example.com/data',
138
body='{"key": "value"}',
139
headers={}
140
)
141
print(request.body) # '{"key": "value"}'
142
143
# File-like body
144
file_obj = io.BytesIO(b'file content')
145
request = Request(
146
method='PUT',
147
uri='https://api.example.com/upload',
148
body=file_obj,
149
headers={}
150
)
151
# Request automatically reads and stores file content
152
# request.body returns BytesIO with the content
153
154
# Iterable body
155
request = Request(
156
method='POST',
157
uri='https://api.example.com/stream',
158
body=[b'chunk1', b'chunk2', b'chunk3'],
159
headers={}
160
)
161
# Request converts iterable to list
162
# request.body returns iterator over the chunks
163
```
164
165
### HeadersDict Usage
166
167
```python
168
from vcr.request import HeadersDict
169
170
# Create headers dict
171
headers = HeadersDict({
172
'Content-Type': 'application/json',
173
'Authorization': 'Bearer token123',
174
'X-Custom-Header': 'custom-value'
175
})
176
177
# Case-insensitive access
178
print(headers['content-type']) # 'application/json'
179
print(headers['AUTHORIZATION']) # 'Bearer token123'
180
print(headers['x-custom-header']) # 'custom-value'
181
182
# Standard dict operations work
183
headers['New-Header'] = 'new-value'
184
del headers['X-Custom-Header']
185
186
# Iteration preserves original case
187
for key, value in headers.items():
188
print(f"{key}: {value}")
189
```
190
191
### Request Modification
192
193
```python
194
# Modifying request components
195
request = Request('GET', 'https://api.example.com/data', None, {})
196
197
# Update headers (creates new HeadersDict if needed)
198
request.headers['Authorization'] = 'Bearer new-token'
199
request.headers['Accept'] = 'application/xml'
200
201
# Body modification
202
request.body = '{"updated": "data"}'
203
204
# Note: URI components are read-only properties
205
# To change URI, create a new Request object
206
```
207
208
### Integration with VCR Filters
209
210
```python
211
def sanitize_request(request):
212
"""Example filter function that modifies requests before recording"""
213
# Remove sensitive headers
214
if 'authorization' in request.headers:
215
request.headers['authorization'] = 'REDACTED'
216
217
# Modify query parameters by reconstructing URI
218
if 'api_key' in request.uri:
219
# Custom logic to remove api_key from URI
220
pass
221
222
return request
223
224
# Use with VCR configuration
225
my_vcr = vcr.VCR(before_record_request=sanitize_request)
226
```
227
228
## Advanced Usage
229
230
### Custom Header Processing
231
232
```python
233
from vcr.request import HeadersDict
234
235
def normalize_headers(headers_dict):
236
"""Normalize header values for consistent recording"""
237
normalized = HeadersDict()
238
239
for key, value in headers_dict.items():
240
if key.lower() == 'user-agent':
241
# Normalize user agent strings
242
normalized[key] = 'Normalized-User-Agent/1.0'
243
elif key.lower() == 'date':
244
# Remove timestamp headers for deterministic recording
245
continue
246
else:
247
normalized[key] = value
248
249
return normalized
250
251
# Apply to requests during processing
252
request.headers = normalize_headers(request.headers)
253
```
254
255
### Body Content Analysis
256
257
```python
258
import json
259
from urllib.parse import parse_qs
260
261
def analyze_request_body(request):
262
"""Analyze and potentially modify request body"""
263
if request.headers.get('content-type', '').startswith('application/json'):
264
try:
265
# Parse and potentially modify JSON body
266
data = json.loads(request.body)
267
# Remove sensitive fields
268
data.pop('password', None)
269
data.pop('api_secret', None)
270
request.body = json.dumps(data)
271
except (json.JSONDecodeError, TypeError):
272
pass
273
274
elif request.headers.get('content-type') == 'application/x-www-form-urlencoded':
275
# Handle form data
276
try:
277
data = parse_qs(request.body)
278
# Process form fields
279
if 'password' in data:
280
data['password'] = ['REDACTED']
281
request.body = '&'.join(f"{k}={v[0]}" for k, v in data.items())
282
except (ValueError, TypeError):
283
pass
284
285
return request
286
```