blob: 2bee6ca7145f86e5c8ee3b73f69922ec01a53b1c [file] [log] [blame]
Alex Yuaa1b95b2018-07-26 23:23:35 -04001from __future__ import absolute_import
2from __future__ import division
3from __future__ import print_function
4
Sergey Shepelev0112eff2017-05-05 06:46:43 +03005import email.utils
Alex Yuaa1b95b2018-07-26 23:23:35 -04006import errno
Sergey Shepelev0112eff2017-05-05 06:46:43 +03007import httplib2
8import mock
9import os
10import pytest
Alex Yuaa1b95b2018-07-26 23:23:35 -040011from six.moves import http_client, urllib
Sergey Shepelev0112eff2017-05-05 06:46:43 +030012import socket
13import tests
Alex Yuaa1b95b2018-07-26 23:23:35 -040014
15DUMMY_URL = "http://127.0.0.1:1"
Sergey Shepelev0112eff2017-05-05 06:46:43 +030016
17
Alex Yuaa1b95b2018-07-26 23:23:35 -040018def _raise_connection_refused_exception(*args, **kwargs):
19 raise socket.error(errno.ECONNREFUSED, "Connection refused.")
Sergey Shepelev0112eff2017-05-05 06:46:43 +030020
21
22def test_connection_type():
23 http = httplib2.Http()
24 http.force_exception_to_status_code = False
Alex Yuaa1b95b2018-07-26 23:23:35 -040025 response, content = http.request(
26 DUMMY_URL, connection_type=tests.MockHTTPConnection
27 )
28 assert response["content-location"] == DUMMY_URL
29 assert content == b"the body"
Sergey Shepelev0112eff2017-05-05 06:46:43 +030030
31
32def test_bad_status_line_retry():
33 http = httplib2.Http()
34 old_retries = httplib2.RETRIES
35 httplib2.RETRIES = 1
36 http.force_exception_to_status_code = False
37 try:
Alex Yuaa1b95b2018-07-26 23:23:35 -040038 response, content = http.request(
39 DUMMY_URL, connection_type=tests.MockHTTPBadStatusConnection
40 )
Sergey Shepelev0112eff2017-05-05 06:46:43 +030041 except http_client.BadStatusLine:
42 assert tests.MockHTTPBadStatusConnection.num_calls == 2
43 httplib2.RETRIES = old_retries
44
45
46def test_unknown_server():
47 http = httplib2.Http()
48 http.force_exception_to_status_code = False
49 with tests.assert_raises(httplib2.ServerNotFoundError):
Alex Yuaa1b95b2018-07-26 23:23:35 -040050 with mock.patch("socket.socket.connect", side_effect=socket.gaierror):
Sergey Shepelev0112eff2017-05-05 06:46:43 +030051 http.request("http://no-such-hostname./")
52
53 # Now test with exceptions turned off
54 http.force_exception_to_status_code = True
55 response, content = http.request("http://no-such-hostname./")
Alex Yuaa1b95b2018-07-26 23:23:35 -040056 assert response["content-type"] == "text/plain"
Sergey Shepelev0112eff2017-05-05 06:46:43 +030057 assert content.startswith(b"Unable to find")
58 assert response.status == 400
59
60
Alex Yuaa1b95b2018-07-26 23:23:35 -040061@pytest.mark.skipif(
62 os.environ.get("TRAVIS_PYTHON_VERSION") in ("2.7", "pypy"),
63 reason="Fails on Travis py27/pypy, works elsewhere. "
64 "See https://travis-ci.org/httplib2/httplib2/jobs/408769880.",
65)
66@mock.patch("socket.socket.connect", spec=True)
67def test_connection_refused_raises_exception(mock_socket_connect):
68 mock_socket_connect.side_effect = _raise_connection_refused_exception
Sergey Shepelev0112eff2017-05-05 06:46:43 +030069 http = httplib2.Http()
70 http.force_exception_to_status_code = False
71 with tests.assert_raises(socket.error):
Alex Yuaa1b95b2018-07-26 23:23:35 -040072 http.request(DUMMY_URL)
Sergey Shepelev0112eff2017-05-05 06:46:43 +030073
Alex Yuaa1b95b2018-07-26 23:23:35 -040074
75@pytest.mark.skipif(
76 os.environ.get("TRAVIS_PYTHON_VERSION") in ("2.7", "pypy"),
77 reason="Fails on Travis py27/pypy, works elsewhere. "
78 "See https://travis-ci.org/httplib2/httplib2/jobs/408769880.",
79)
80@mock.patch("socket.socket.connect", spec=True)
81def test_connection_refused_returns_response(mock_socket_connect):
82 mock_socket_connect.side_effect = _raise_connection_refused_exception
83 http = httplib2.Http()
Sergey Shepelev0112eff2017-05-05 06:46:43 +030084 http.force_exception_to_status_code = True
Alex Yuaa1b95b2018-07-26 23:23:35 -040085 response, content = http.request(DUMMY_URL)
86 content = content.lower()
87 assert response["content-type"] == "text/plain"
88 assert (
89 b"connection refused" in content
90 or b"actively refused" in content
91 or b"socket is not connected" in content
92 )
Sergey Shepelev0112eff2017-05-05 06:46:43 +030093 assert response.status == 400
94
95
96def test_get_iri():
97 http = httplib2.Http()
Alex Yuaa1b95b2018-07-26 23:23:35 -040098 query = u"?a=\N{CYRILLIC CAPITAL LETTER DJE}"
Sergey Shepelev0112eff2017-05-05 06:46:43 +030099 with tests.server_reflect() as uri:
Alex Yuaa1b95b2018-07-26 23:23:35 -0400100 response, content = http.request(uri + query, "GET")
Sergey Shepelev0112eff2017-05-05 06:46:43 +0300101 assert response.status == 200
102 reflected = tests.HttpRequest.from_bytes(content)
Alex Yuaa1b95b2018-07-26 23:23:35 -0400103 assert reflected.uri == "/?a=%D0%82"
Sergey Shepelev0112eff2017-05-05 06:46:43 +0300104
105
106def test_get_is_default_method():
107 # Test that GET is the default method
108 http = httplib2.Http()
109 with tests.server_reflect() as uri:
110 response, content = http.request(uri)
111 assert response.status == 200
112 reflected = tests.HttpRequest.from_bytes(content)
Alex Yuaa1b95b2018-07-26 23:23:35 -0400113 assert reflected.method == "GET"
Sergey Shepelev0112eff2017-05-05 06:46:43 +0300114
115
116def test_different_methods():
117 # Test that all methods can be used
118 http = httplib2.Http()
Alex Yuaa1b95b2018-07-26 23:23:35 -0400119 methods = ["GET", "PUT", "DELETE", "POST", "unknown"]
Sergey Shepelev0112eff2017-05-05 06:46:43 +0300120 with tests.server_reflect(request_count=len(methods)) as uri:
121 for method in methods:
122 response, content = http.request(uri, method, body=b" ")
123 assert response.status == 200
124 reflected = tests.HttpRequest.from_bytes(content)
125 assert reflected.method == method
126
127
128def test_head_read():
129 # Test that we don't try to read the response of a HEAD request
130 # since httplib blocks response.read() for HEAD requests.
131 http = httplib2.Http()
Alex Yuaa1b95b2018-07-26 23:23:35 -0400132 respond_with = b"HTTP/1.0 200 OK\r\ncontent-length: " b"14\r\n\r\nnon-empty-body"
Sergey Shepelev0112eff2017-05-05 06:46:43 +0300133 with tests.server_const_bytes(respond_with) as uri:
Alex Yuaa1b95b2018-07-26 23:23:35 -0400134 response, content = http.request(uri, "HEAD")
Sergey Shepelev0112eff2017-05-05 06:46:43 +0300135 assert response.status == 200
136 assert content == b""
137
138
139def test_get_no_cache():
140 # Test that can do a GET w/o the cache turned on.
141 http = httplib2.Http()
142 with tests.server_const_http() as uri:
Alex Yuaa1b95b2018-07-26 23:23:35 -0400143 response, content = http.request(uri, "GET")
Sergey Shepelev0112eff2017-05-05 06:46:43 +0300144 assert response.status == 200
145 assert response.previous is None
146
147
148def test_user_agent():
149 # Test that we provide a default user-agent
150 http = httplib2.Http()
151 with tests.server_reflect() as uri:
Alex Yuaa1b95b2018-07-26 23:23:35 -0400152 response, content = http.request(uri, "GET")
Sergey Shepelev0112eff2017-05-05 06:46:43 +0300153 assert response.status == 200
154 reflected = tests.HttpRequest.from_bytes(content)
Alex Yuaa1b95b2018-07-26 23:23:35 -0400155 assert reflected.headers.get("user-agent", "").startswith("Python-httplib2/")
Sergey Shepelev0112eff2017-05-05 06:46:43 +0300156
157
158def test_user_agent_non_default():
159 # Test that the default user-agent can be over-ridden
160 http = httplib2.Http()
161 with tests.server_reflect() as uri:
Alex Yuaa1b95b2018-07-26 23:23:35 -0400162 response, content = http.request(uri, "GET", headers={"User-Agent": "fred/1.0"})
Sergey Shepelev0112eff2017-05-05 06:46:43 +0300163 assert response.status == 200
164 reflected = tests.HttpRequest.from_bytes(content)
Alex Yuaa1b95b2018-07-26 23:23:35 -0400165 assert reflected.headers.get("user-agent") == "fred/1.0"
Sergey Shepelev0112eff2017-05-05 06:46:43 +0300166
167
168def test_get_300_with_location():
169 # Test the we automatically follow 300 redirects if a Location: header is provided
170 http = httplib2.Http()
Alex Yuaa1b95b2018-07-26 23:23:35 -0400171 final_content = b"This is the final destination.\n"
Sergey Shepelev0112eff2017-05-05 06:46:43 +0300172 routes = {
Alex Yuaa1b95b2018-07-26 23:23:35 -0400173 "/final": tests.http_response_bytes(body=final_content),
174 "": tests.http_response_bytes(
175 status="300 Multiple Choices", headers={"location": "/final"}
176 ),
Sergey Shepelev0112eff2017-05-05 06:46:43 +0300177 }
178 with tests.server_route(routes, request_count=2) as uri:
Alex Yuaa1b95b2018-07-26 23:23:35 -0400179 response, content = http.request(uri, "GET")
Sergey Shepelev0112eff2017-05-05 06:46:43 +0300180 assert response.status == 200
181 assert content == final_content
182 assert response.previous.status == 300
183 assert not response.previous.fromcache
184
185 # Confirm that the intermediate 300 is not cached
186 with tests.server_route(routes, request_count=2) as uri:
Alex Yuaa1b95b2018-07-26 23:23:35 -0400187 response, content = http.request(uri, "GET")
Sergey Shepelev0112eff2017-05-05 06:46:43 +0300188 assert response.status == 200
189 assert content == final_content
190 assert response.previous.status == 300
191 assert not response.previous.fromcache
192
193
194def test_get_300_with_location_noredirect():
195 # Test the we automatically follow 300 redirects if a Location: header is provided
196 http = httplib2.Http()
197 http.follow_redirects = False
198 response = tests.http_response_bytes(
Alex Yuaa1b95b2018-07-26 23:23:35 -0400199 status="300 Multiple Choices",
200 headers={"location": "/final"},
201 body=b"redirect body",
202 )
Sergey Shepelev0112eff2017-05-05 06:46:43 +0300203 with tests.server_const_bytes(response) as uri:
Alex Yuaa1b95b2018-07-26 23:23:35 -0400204 response, content = http.request(uri, "GET")
Sergey Shepelev0112eff2017-05-05 06:46:43 +0300205 assert response.status == 300
206
207
208def test_get_300_without_location():
209 # Not giving a Location: header in a 300 response is acceptable
210 # In which case we just return the 300 response
211 http = httplib2.Http()
Alex Yuaa1b95b2018-07-26 23:23:35 -0400212 with tests.server_const_http(
213 status="300 Multiple Choices", body=b"redirect body"
214 ) as uri:
215 response, content = http.request(uri, "GET")
Sergey Shepelev0112eff2017-05-05 06:46:43 +0300216 assert response.status == 300
217 assert response.previous is None
Alex Yuaa1b95b2018-07-26 23:23:35 -0400218 assert content == b"redirect body"
Sergey Shepelev0112eff2017-05-05 06:46:43 +0300219
220
221def test_get_301():
222 # Test that we automatically follow 301 redirects
223 # and that we cache the 301 response
224 http = httplib2.Http(cache=tests.get_cache_path())
Alex Yuaa1b95b2018-07-26 23:23:35 -0400225 destination = ""
Sergey Shepelev0112eff2017-05-05 06:46:43 +0300226 routes = {
Alex Yuaa1b95b2018-07-26 23:23:35 -0400227 "/final": tests.http_response_bytes(body=b"This is the final destination.\n"),
228 "": tests.http_response_bytes(
229 status="301 Now where did I leave that URL",
230 headers={"location": "/final"},
231 body=b"redirect body",
232 ),
Sergey Shepelev0112eff2017-05-05 06:46:43 +0300233 }
234 with tests.server_route(routes, request_count=3) as uri:
Alex Yuaa1b95b2018-07-26 23:23:35 -0400235 destination = urllib.parse.urljoin(uri, "/final")
236 response1, content1 = http.request(uri, "GET")
237 response2, content2 = http.request(uri, "GET")
Sergey Shepelev0112eff2017-05-05 06:46:43 +0300238 assert response1.status == 200
Alex Yuaa1b95b2018-07-26 23:23:35 -0400239 assert "content-location" in response2
240 assert response1["content-location"] == destination
241 assert content1 == b"This is the final destination.\n"
Sergey Shepelev0112eff2017-05-05 06:46:43 +0300242 assert response1.previous.status == 301
243 assert not response1.previous.fromcache
244
245 assert response2.status == 200
Alex Yuaa1b95b2018-07-26 23:23:35 -0400246 assert response2["content-location"] == destination
247 assert content2 == b"This is the final destination.\n"
Sergey Shepelev0112eff2017-05-05 06:46:43 +0300248 assert response2.previous.status == 301
249 assert response2.previous.fromcache
250
251
252@pytest.mark.skip(
Alex Yuaa1b95b2018-07-26 23:23:35 -0400253 not os.environ.get("httplib2_test_still_run_skipped")
254 and os.environ.get("TRAVIS_PYTHON_VERSION") in ("2.7", "pypy"),
255 reason="FIXME: timeout on Travis py27 and pypy, works elsewhere",
Sergey Shepelev0112eff2017-05-05 06:46:43 +0300256)
257def test_head_301():
258 # Test that we automatically follow 301 redirects
259 http = httplib2.Http()
Alex Yuaa1b95b2018-07-26 23:23:35 -0400260 destination = ""
Sergey Shepelev0112eff2017-05-05 06:46:43 +0300261 routes = {
Alex Yuaa1b95b2018-07-26 23:23:35 -0400262 "/final": tests.http_response_bytes(body=b"This is the final destination.\n"),
263 "": tests.http_response_bytes(
264 status="301 Now where did I leave that URL",
265 headers={"location": "/final"},
266 body=b"redirect body",
267 ),
Sergey Shepelev0112eff2017-05-05 06:46:43 +0300268 }
269 with tests.server_route(routes, request_count=2) as uri:
Alex Yuaa1b95b2018-07-26 23:23:35 -0400270 destination = urllib.parse.urljoin(uri, "/final")
271 response, content = http.request(uri, "HEAD")
Sergey Shepelev0112eff2017-05-05 06:46:43 +0300272 assert response.status == 200
Alex Yuaa1b95b2018-07-26 23:23:35 -0400273 assert response["content-location"] == destination
Sergey Shepelev0112eff2017-05-05 06:46:43 +0300274 assert response.previous.status == 301
275 assert not response.previous.fromcache
276
277
Alex Yuaa1b95b2018-07-26 23:23:35 -0400278@pytest.mark.xfail(
279 reason=(
280 "FIXME: 301 cache works only with follow_redirects, should work " "regardless"
281 )
282)
Sergey Shepelev0112eff2017-05-05 06:46:43 +0300283def test_get_301_no_redirect():
284 # Test that we cache the 301 response
285 http = httplib2.Http(cache=tests.get_cache_path(), timeout=0.5)
286 http.follow_redirects = False
287 response = tests.http_response_bytes(
Alex Yuaa1b95b2018-07-26 23:23:35 -0400288 status="301 Now where did I leave that URL",
289 headers={"location": "/final", "cache-control": "max-age=300"},
290 body=b"redirect body",
Sergey Shepelev0112eff2017-05-05 06:46:43 +0300291 add_date=True,
292 )
293 with tests.server_const_bytes(response) as uri:
Alex Yuaa1b95b2018-07-26 23:23:35 -0400294 response, _ = http.request(uri, "GET")
Sergey Shepelev0112eff2017-05-05 06:46:43 +0300295 assert response.status == 301
296 assert not response.fromcache
Alex Yuaa1b95b2018-07-26 23:23:35 -0400297 response, _ = http.request(uri, "GET")
Sergey Shepelev0112eff2017-05-05 06:46:43 +0300298 assert response.status == 301
299 assert response.fromcache
300
301
302def test_get_302():
303 # Test that we automatically follow 302 redirects
304 # and that we DO NOT cache the 302 response
305 http = httplib2.Http(cache=tests.get_cache_path())
Alex Yuaa1b95b2018-07-26 23:23:35 -0400306 second_url, final_url = "", ""
Sergey Shepelev0112eff2017-05-05 06:46:43 +0300307 routes = {
Alex Yuaa1b95b2018-07-26 23:23:35 -0400308 "/final": tests.http_response_bytes(body=b"This is the final destination.\n"),
309 "/second": tests.http_response_bytes(
310 status="302 Found", headers={"location": "/final"}, body=b"second redirect"
311 ),
312 "": tests.http_response_bytes(
313 status="302 Found", headers={"location": "/second"}, body=b"redirect body"
314 ),
Sergey Shepelev0112eff2017-05-05 06:46:43 +0300315 }
316 with tests.server_route(routes, request_count=7) as uri:
Alex Yuaa1b95b2018-07-26 23:23:35 -0400317 second_url = urllib.parse.urljoin(uri, "/second")
318 final_url = urllib.parse.urljoin(uri, "/final")
319 response1, content1 = http.request(second_url, "GET")
320 response2, content2 = http.request(second_url, "GET")
321 response3, content3 = http.request(uri, "GET")
Sergey Shepelev0112eff2017-05-05 06:46:43 +0300322 assert response1.status == 200
Alex Yuaa1b95b2018-07-26 23:23:35 -0400323 assert response1["content-location"] == final_url
324 assert content1 == b"This is the final destination.\n"
Sergey Shepelev0112eff2017-05-05 06:46:43 +0300325 assert response1.previous.status == 302
326 assert not response1.previous.fromcache
327
328 assert response2.status == 200
329 # FIXME:
330 # assert response2.fromcache
Alex Yuaa1b95b2018-07-26 23:23:35 -0400331 assert response2["content-location"] == final_url
332 assert content2 == b"This is the final destination.\n"
Sergey Shepelev0112eff2017-05-05 06:46:43 +0300333 assert response2.previous.status == 302
334 assert not response2.previous.fromcache
Alex Yuaa1b95b2018-07-26 23:23:35 -0400335 assert response2.previous["content-location"] == second_url
Sergey Shepelev0112eff2017-05-05 06:46:43 +0300336
337 assert response3.status == 200
338 # FIXME:
339 # assert response3.fromcache
Alex Yuaa1b95b2018-07-26 23:23:35 -0400340 assert content3 == b"This is the final destination.\n"
Sergey Shepelev0112eff2017-05-05 06:46:43 +0300341 assert response3.previous.status == 302
342 assert not response3.previous.fromcache
343
344
345def test_get_302_redirection_limit():
346 # Test that we can set a lower redirection limit
347 # and that we raise an exception when we exceed
348 # that limit.
349 http = httplib2.Http()
350 http.force_exception_to_status_code = False
351 routes = {
Alex Yuaa1b95b2018-07-26 23:23:35 -0400352 "/second": tests.http_response_bytes(
353 status="302 Found", headers={"location": "/final"}, body=b"second redirect"
354 ),
355 "": tests.http_response_bytes(
356 status="302 Found", headers={"location": "/second"}, body=b"redirect body"
357 ),
Sergey Shepelev0112eff2017-05-05 06:46:43 +0300358 }
359 with tests.server_route(routes, request_count=4) as uri:
360 try:
Alex Yuaa1b95b2018-07-26 23:23:35 -0400361 http.request(uri, "GET", redirections=1)
362 assert False, "This should not happen"
Sergey Shepelev0112eff2017-05-05 06:46:43 +0300363 except httplib2.RedirectLimit:
364 pass
365 except Exception:
Alex Yuaa1b95b2018-07-26 23:23:35 -0400366 assert False, "Threw wrong kind of exception "
Sergey Shepelev0112eff2017-05-05 06:46:43 +0300367
368 # Re-run the test with out the exceptions
369 http.force_exception_to_status_code = True
Alex Yuaa1b95b2018-07-26 23:23:35 -0400370 response, content = http.request(uri, "GET", redirections=1)
Sergey Shepelev0112eff2017-05-05 06:46:43 +0300371
372 assert response.status == 500
Alex Yuaa1b95b2018-07-26 23:23:35 -0400373 assert response.reason.startswith("Redirected more")
374 assert response["status"] == "302"
375 assert content == b"second redirect"
Sergey Shepelev0112eff2017-05-05 06:46:43 +0300376 assert response.previous is not None
377
378
379def test_get_302_no_location():
380 # Test that we throw an exception when we get
381 # a 302 with no Location: header.
382 http = httplib2.Http()
383 http.force_exception_to_status_code = False
Alex Yuaa1b95b2018-07-26 23:23:35 -0400384 with tests.server_const_http(status="302 Found", request_count=2) as uri:
Sergey Shepelev0112eff2017-05-05 06:46:43 +0300385 try:
Alex Yuaa1b95b2018-07-26 23:23:35 -0400386 http.request(uri, "GET")
387 assert False, "Should never reach here"
Sergey Shepelev0112eff2017-05-05 06:46:43 +0300388 except httplib2.RedirectMissingLocation:
389 pass
390 except Exception:
Alex Yuaa1b95b2018-07-26 23:23:35 -0400391 assert False, "Threw wrong kind of exception "
Sergey Shepelev0112eff2017-05-05 06:46:43 +0300392
393 # Re-run the test with out the exceptions
394 http.force_exception_to_status_code = True
Alex Yuaa1b95b2018-07-26 23:23:35 -0400395 response, content = http.request(uri, "GET")
Sergey Shepelev0112eff2017-05-05 06:46:43 +0300396
397 assert response.status == 500
Alex Yuaa1b95b2018-07-26 23:23:35 -0400398 assert response.reason.startswith("Redirected but")
399 assert "302" == response["status"]
400 assert content == b""
Sergey Shepelev0112eff2017-05-05 06:46:43 +0300401
402
403@pytest.mark.skip(
Alex Yuaa1b95b2018-07-26 23:23:35 -0400404 not os.environ.get("httplib2_test_still_run_skipped")
405 and os.environ.get("TRAVIS_PYTHON_VERSION") in ("2.7", "pypy"),
406 reason="FIXME: timeout on Travis py27 and pypy, works elsewhere",
Sergey Shepelev0112eff2017-05-05 06:46:43 +0300407)
408def test_303():
409 # Do a follow-up GET on a Location: header
410 # returned from a POST that gave a 303.
411 http = httplib2.Http()
412 routes = {
Alex Yuaa1b95b2018-07-26 23:23:35 -0400413 "/final": tests.make_http_reflect(),
414 "": tests.make_http_reflect(
415 status="303 See Other", headers={"location": "/final"}
416 ),
Sergey Shepelev0112eff2017-05-05 06:46:43 +0300417 }
418 with tests.server_route(routes, request_count=2) as uri:
Alex Yuaa1b95b2018-07-26 23:23:35 -0400419 response, content = http.request(uri, "POST", " ")
Sergey Shepelev0112eff2017-05-05 06:46:43 +0300420 assert response.status == 200
421 reflected = tests.HttpRequest.from_bytes(content)
Alex Yuaa1b95b2018-07-26 23:23:35 -0400422 assert reflected.uri == "/final"
Sergey Shepelev0112eff2017-05-05 06:46:43 +0300423 assert response.previous.status == 303
424
425 # Skip follow-up GET
426 http = httplib2.Http()
427 http.follow_redirects = False
428 with tests.server_route(routes, request_count=1) as uri:
Alex Yuaa1b95b2018-07-26 23:23:35 -0400429 response, content = http.request(uri, "POST", " ")
Sergey Shepelev0112eff2017-05-05 06:46:43 +0300430 assert response.status == 303
431
432 # All methods can be used
433 http = httplib2.Http()
Alex Yuaa1b95b2018-07-26 23:23:35 -0400434 cases = "DELETE GET HEAD POST PUT EVEN_NEW_ONES".split(" ")
Sergey Shepelev0112eff2017-05-05 06:46:43 +0300435 with tests.server_route(routes, request_count=len(cases) * 2) as uri:
436 for method in cases:
Alex Yuaa1b95b2018-07-26 23:23:35 -0400437 response, content = http.request(uri, method, body=b"q q")
Sergey Shepelev0112eff2017-05-05 06:46:43 +0300438 assert response.status == 200
439 reflected = tests.HttpRequest.from_bytes(content)
Alex Yuaa1b95b2018-07-26 23:23:35 -0400440 assert reflected.method == "GET"
Sergey Shepelev0112eff2017-05-05 06:46:43 +0300441
442
443def test_etag_used():
444 # Test that we use ETags properly to validate our cache
445 cache_path = tests.get_cache_path()
446 http = httplib2.Http(cache=cache_path)
447 response_kwargs = dict(
448 add_date=True,
449 add_etag=True,
Alex Yuaa1b95b2018-07-26 23:23:35 -0400450 body=b"something",
451 headers={"cache-control": "public,max-age=300"},
Sergey Shepelev0112eff2017-05-05 06:46:43 +0300452 )
453
454 def handler(request):
Alex Yuaa1b95b2018-07-26 23:23:35 -0400455 if request.headers.get("range"):
Sergey Shepelev0112eff2017-05-05 06:46:43 +0300456 return tests.http_response_bytes(status=206, **response_kwargs)
457 return tests.http_response_bytes(**response_kwargs)
458
459 with tests.server_request(handler, request_count=2) as uri:
Alex Yuaa1b95b2018-07-26 23:23:35 -0400460 response, _ = http.request(uri, "GET", headers={"accept-encoding": "identity"})
461 assert response["etag"] == '"437b930db84b8079c2dd804a71936b5f"'
Sergey Shepelev0112eff2017-05-05 06:46:43 +0300462
Alex Yuaa1b95b2018-07-26 23:23:35 -0400463 http.request(uri, "GET", headers={"accept-encoding": "identity"})
Sergey Shepelev0112eff2017-05-05 06:46:43 +0300464 response, _ = http.request(
Alex Yuaa1b95b2018-07-26 23:23:35 -0400465 uri,
466 "GET",
467 headers={"accept-encoding": "identity", "cache-control": "must-revalidate"},
Sergey Shepelev0112eff2017-05-05 06:46:43 +0300468 )
469 assert response.status == 200
470 assert response.fromcache
471
472 # TODO: API to read cache item, at least internal to tests
Alex Yuaa1b95b2018-07-26 23:23:35 -0400473 cache_file_name = os.path.join(
474 cache_path, httplib2.safename(httplib2.urlnorm(uri)[-1])
475 )
476 with open(cache_file_name, "r") as f:
Sergey Shepelev0112eff2017-05-05 06:46:43 +0300477 status_line = f.readline()
478 assert status_line.startswith("status:")
479
Alex Yuaa1b95b2018-07-26 23:23:35 -0400480 response, content = http.request(
481 uri, "HEAD", headers={"accept-encoding": "identity"}
482 )
Sergey Shepelev0112eff2017-05-05 06:46:43 +0300483 assert response.status == 200
484 assert response.fromcache
485
Alex Yuaa1b95b2018-07-26 23:23:35 -0400486 response, content = http.request(
487 uri, "GET", headers={"accept-encoding": "identity", "range": "bytes=0-0"}
488 )
Sergey Shepelev0112eff2017-05-05 06:46:43 +0300489 assert response.status == 206
490 assert not response.fromcache
491
492
493def test_etag_ignore():
494 # Test that we can forcibly ignore ETags
495 http = httplib2.Http(cache=tests.get_cache_path())
Alex Yuaa1b95b2018-07-26 23:23:35 -0400496 response_kwargs = dict(add_date=True, add_etag=True)
Sergey Shepelev0112eff2017-05-05 06:46:43 +0300497 with tests.server_reflect(request_count=3, **response_kwargs) as uri:
Alex Yuaa1b95b2018-07-26 23:23:35 -0400498 response, content = http.request(
499 uri, "GET", headers={"accept-encoding": "identity"}
500 )
Sergey Shepelev0112eff2017-05-05 06:46:43 +0300501 assert response.status == 200
Alex Yuaa1b95b2018-07-26 23:23:35 -0400502 assert response["etag"] != ""
Sergey Shepelev0112eff2017-05-05 06:46:43 +0300503
504 response, content = http.request(
Alex Yuaa1b95b2018-07-26 23:23:35 -0400505 uri,
506 "GET",
507 headers={"accept-encoding": "identity", "cache-control": "max-age=0"},
Sergey Shepelev0112eff2017-05-05 06:46:43 +0300508 )
509 reflected = tests.HttpRequest.from_bytes(content)
Alex Yuaa1b95b2018-07-26 23:23:35 -0400510 assert reflected.headers.get("if-none-match")
Sergey Shepelev0112eff2017-05-05 06:46:43 +0300511
512 http.ignore_etag = True
513 response, content = http.request(
Alex Yuaa1b95b2018-07-26 23:23:35 -0400514 uri,
515 "GET",
516 headers={"accept-encoding": "identity", "cache-control": "max-age=0"},
Sergey Shepelev0112eff2017-05-05 06:46:43 +0300517 )
518 assert not response.fromcache
519 reflected = tests.HttpRequest.from_bytes(content)
Alex Yuaa1b95b2018-07-26 23:23:35 -0400520 assert not reflected.headers.get("if-none-match")
Sergey Shepelev0112eff2017-05-05 06:46:43 +0300521
522
523def test_etag_override():
524 # Test that we can forcibly ignore ETags
525 http = httplib2.Http(cache=tests.get_cache_path())
Alex Yuaa1b95b2018-07-26 23:23:35 -0400526 response_kwargs = dict(add_date=True, add_etag=True)
Sergey Shepelev0112eff2017-05-05 06:46:43 +0300527 with tests.server_reflect(request_count=3, **response_kwargs) as uri:
Alex Yuaa1b95b2018-07-26 23:23:35 -0400528 response, _ = http.request(uri, "GET", headers={"accept-encoding": "identity"})
Sergey Shepelev0112eff2017-05-05 06:46:43 +0300529 assert response.status == 200
Alex Yuaa1b95b2018-07-26 23:23:35 -0400530 assert response["etag"] != ""
Sergey Shepelev0112eff2017-05-05 06:46:43 +0300531
532 response, content = http.request(
Alex Yuaa1b95b2018-07-26 23:23:35 -0400533 uri,
534 "GET",
535 headers={"accept-encoding": "identity", "cache-control": "max-age=0"},
Sergey Shepelev0112eff2017-05-05 06:46:43 +0300536 )
537 assert response.status == 200
538 reflected = tests.HttpRequest.from_bytes(content)
Alex Yuaa1b95b2018-07-26 23:23:35 -0400539 assert reflected.headers.get("if-none-match")
540 assert reflected.headers.get("if-none-match") != "fred"
Sergey Shepelev0112eff2017-05-05 06:46:43 +0300541
542 response, content = http.request(
Alex Yuaa1b95b2018-07-26 23:23:35 -0400543 uri,
544 "GET",
545 headers={
546 "accept-encoding": "identity",
547 "cache-control": "max-age=0",
548 "if-none-match": "fred",
549 },
Sergey Shepelev0112eff2017-05-05 06:46:43 +0300550 )
551 assert response.status == 200
552 reflected = tests.HttpRequest.from_bytes(content)
Alex Yuaa1b95b2018-07-26 23:23:35 -0400553 assert reflected.headers.get("if-none-match") == "fred"
Sergey Shepelev0112eff2017-05-05 06:46:43 +0300554
555
Alex Yuaa1b95b2018-07-26 23:23:35 -0400556@pytest.mark.skip(reason="was commented in legacy code")
Sergey Shepelev0112eff2017-05-05 06:46:43 +0300557def test_get_304_end_to_end():
558 pass
559 # Test that end to end headers get overwritten in the cache
560 # uri = urllib.parse.urljoin(base, "304/end2end.cgi")
561 # response, content = http.request(uri, 'GET')
562 # assertNotEqual(response['etag'], "")
563 # old_date = response['date']
564 # time.sleep(2)
565
566 # response, content = http.request(uri, 'GET', headers = {'Cache-Control': 'max-age=0'})
567 # # The response should be from the cache, but the Date: header should be updated.
568 # new_date = response['date']
569 # assert new_date != old_date
570 # assert response.status == 200
571 # assert response.fromcache == True
572
573
574def test_get_304_last_modified():
575 # Test that we can still handle a 304
576 # by only using the last-modified cache validator.
577 http = httplib2.Http(cache=tests.get_cache_path())
578 date = email.utils.formatdate()
579
580 def handler(read):
581 read()
582 yield tests.http_response_bytes(
Alex Yuaa1b95b2018-07-26 23:23:35 -0400583 status=200, body=b"something", headers={"date": date, "last-modified": date}
Sergey Shepelev0112eff2017-05-05 06:46:43 +0300584 )
585
586 request2 = read()
Alex Yuaa1b95b2018-07-26 23:23:35 -0400587 assert request2.headers["if-modified-since"] == date
Sergey Shepelev0112eff2017-05-05 06:46:43 +0300588 yield tests.http_response_bytes(status=304)
589
590 with tests.server_yield(handler, request_count=2) as uri:
Alex Yuaa1b95b2018-07-26 23:23:35 -0400591 response, content = http.request(uri, "GET")
592 assert response.get("last-modified") == date
Sergey Shepelev0112eff2017-05-05 06:46:43 +0300593
Alex Yuaa1b95b2018-07-26 23:23:35 -0400594 response, content = http.request(uri, "GET")
Sergey Shepelev0112eff2017-05-05 06:46:43 +0300595 assert response.status == 200
596 assert response.fromcache
597
598
599def test_get_307():
600 # Test that we do follow 307 redirects but
601 # do not cache the 307
602 http = httplib2.Http(cache=tests.get_cache_path(), timeout=1)
Alex Yuaa1b95b2018-07-26 23:23:35 -0400603 r307 = tests.http_response_bytes(status=307, headers={"location": "/final"})
Sergey Shepelev0112eff2017-05-05 06:46:43 +0300604 r200 = tests.http_response_bytes(
605 status=200,
606 add_date=True,
Alex Yuaa1b95b2018-07-26 23:23:35 -0400607 body=b"final content\n",
608 headers={"cache-control": "max-age=300"},
Sergey Shepelev0112eff2017-05-05 06:46:43 +0300609 )
610
611 with tests.server_list_http([r307, r200, r307]) as uri:
Alex Yuaa1b95b2018-07-26 23:23:35 -0400612 response, content = http.request(uri, "GET")
Sergey Shepelev0112eff2017-05-05 06:46:43 +0300613 assert response.previous.status == 307
614 assert not response.previous.fromcache
615 assert response.status == 200
616 assert not response.fromcache
Alex Yuaa1b95b2018-07-26 23:23:35 -0400617 assert content == b"final content\n"
Sergey Shepelev0112eff2017-05-05 06:46:43 +0300618
Alex Yuaa1b95b2018-07-26 23:23:35 -0400619 response, content = http.request(uri, "GET")
Sergey Shepelev0112eff2017-05-05 06:46:43 +0300620 assert response.previous.status == 307
621 assert not response.previous.fromcache
622 assert response.status == 200
623 assert response.fromcache
Alex Yuaa1b95b2018-07-26 23:23:35 -0400624 assert content == b"final content\n"
Sergey Shepelev0112eff2017-05-05 06:46:43 +0300625
626
627def test_get_410():
628 # Test that we pass 410's through
629 http = httplib2.Http()
630 with tests.server_const_http(status=410) as uri:
Alex Yuaa1b95b2018-07-26 23:23:35 -0400631 response, content = http.request(uri, "GET")
Sergey Shepelev0112eff2017-05-05 06:46:43 +0300632 assert response.status == 410
633
634
635def test_get_duplicate_headers():
636 # Test that duplicate headers get concatenated via ','
637 http = httplib2.Http()
Alex Yuaa1b95b2018-07-26 23:23:35 -0400638 response = b"""HTTP/1.0 200 OK\r\n\
Sergey Shepelev0112eff2017-05-05 06:46:43 +0300639Link: link1\r\n\
640Content-Length: 7\r\n\
641Link: link2\r\n\r\n\
Alex Yuaa1b95b2018-07-26 23:23:35 -0400642content"""
Sergey Shepelev0112eff2017-05-05 06:46:43 +0300643 with tests.server_const_bytes(response) as uri:
Alex Yuaa1b95b2018-07-26 23:23:35 -0400644 response, content = http.request(uri, "GET")
Sergey Shepelev0112eff2017-05-05 06:46:43 +0300645 assert response.status == 200
646 assert content == b"content"
Alex Yuaa1b95b2018-07-26 23:23:35 -0400647 assert response["link"], "link1, link2"