Sergey Shepelev | 0112eff | 2017-05-05 06:46:43 +0300 | [diff] [blame^] | 1 | import email.utils |
| 2 | import httplib2 |
| 3 | import mock |
| 4 | import os |
| 5 | import pytest |
| 6 | import socket |
| 7 | import tests |
| 8 | from six.moves import http_client, urllib |
| 9 | |
| 10 | |
| 11 | dummy_url = 'http://127.0.0.1:1' |
| 12 | |
| 13 | |
| 14 | def test_connection_type(): |
| 15 | http = httplib2.Http() |
| 16 | http.force_exception_to_status_code = False |
| 17 | response, content = http.request(dummy_url, connection_type=tests.MockHTTPConnection) |
| 18 | assert response['content-location'] == dummy_url |
| 19 | assert content == b'the body' |
| 20 | |
| 21 | |
| 22 | def test_bad_status_line_retry(): |
| 23 | http = httplib2.Http() |
| 24 | old_retries = httplib2.RETRIES |
| 25 | httplib2.RETRIES = 1 |
| 26 | http.force_exception_to_status_code = False |
| 27 | try: |
| 28 | response, content = http.request(dummy_url, connection_type=tests.MockHTTPBadStatusConnection) |
| 29 | except http_client.BadStatusLine: |
| 30 | assert tests.MockHTTPBadStatusConnection.num_calls == 2 |
| 31 | httplib2.RETRIES = old_retries |
| 32 | |
| 33 | |
| 34 | def test_unknown_server(): |
| 35 | http = httplib2.Http() |
| 36 | http.force_exception_to_status_code = False |
| 37 | with tests.assert_raises(httplib2.ServerNotFoundError): |
| 38 | with mock.patch('socket.socket.connect', side_effect=socket.gaierror): |
| 39 | http.request("http://no-such-hostname./") |
| 40 | |
| 41 | # Now test with exceptions turned off |
| 42 | http.force_exception_to_status_code = True |
| 43 | response, content = http.request("http://no-such-hostname./") |
| 44 | assert response['content-type'] == 'text/plain' |
| 45 | assert content.startswith(b"Unable to find") |
| 46 | assert response.status == 400 |
| 47 | |
| 48 | |
| 49 | def test_connection_refused(): |
| 50 | http = httplib2.Http() |
| 51 | http.force_exception_to_status_code = False |
| 52 | with tests.assert_raises(socket.error): |
| 53 | http.request(dummy_url) |
| 54 | |
| 55 | # Now test with exceptions turned off |
| 56 | http.force_exception_to_status_code = True |
| 57 | response, content = http.request(dummy_url) |
| 58 | assert response['content-type'] == 'text/plain' |
| 59 | assert (b"Connection refused" in content or b"actively refused" in content) |
| 60 | assert response.status == 400 |
| 61 | |
| 62 | |
| 63 | def test_get_iri(): |
| 64 | http = httplib2.Http() |
| 65 | query = u'?a=\N{CYRILLIC CAPITAL LETTER DJE}' |
| 66 | with tests.server_reflect() as uri: |
| 67 | response, content = http.request(uri + query, 'GET') |
| 68 | assert response.status == 200 |
| 69 | reflected = tests.HttpRequest.from_bytes(content) |
| 70 | assert reflected.uri == '/?a=%D0%82' |
| 71 | |
| 72 | |
| 73 | def test_get_is_default_method(): |
| 74 | # Test that GET is the default method |
| 75 | http = httplib2.Http() |
| 76 | with tests.server_reflect() as uri: |
| 77 | response, content = http.request(uri) |
| 78 | assert response.status == 200 |
| 79 | reflected = tests.HttpRequest.from_bytes(content) |
| 80 | assert reflected.method == 'GET' |
| 81 | |
| 82 | |
| 83 | def test_different_methods(): |
| 84 | # Test that all methods can be used |
| 85 | http = httplib2.Http() |
| 86 | methods = ['GET', 'PUT', 'DELETE', 'POST', 'unknown'] |
| 87 | with tests.server_reflect(request_count=len(methods)) as uri: |
| 88 | for method in methods: |
| 89 | response, content = http.request(uri, method, body=b" ") |
| 90 | assert response.status == 200 |
| 91 | reflected = tests.HttpRequest.from_bytes(content) |
| 92 | assert reflected.method == method |
| 93 | |
| 94 | |
| 95 | def test_head_read(): |
| 96 | # Test that we don't try to read the response of a HEAD request |
| 97 | # since httplib blocks response.read() for HEAD requests. |
| 98 | http = httplib2.Http() |
| 99 | respond_with = b'HTTP/1.0 200 OK\r\ncontent-length: 14\r\n\r\nnon-empty-body' |
| 100 | with tests.server_const_bytes(respond_with) as uri: |
| 101 | response, content = http.request(uri, 'HEAD') |
| 102 | assert response.status == 200 |
| 103 | assert content == b"" |
| 104 | |
| 105 | |
| 106 | def test_get_no_cache(): |
| 107 | # Test that can do a GET w/o the cache turned on. |
| 108 | http = httplib2.Http() |
| 109 | with tests.server_const_http() as uri: |
| 110 | response, content = http.request(uri, 'GET') |
| 111 | assert response.status == 200 |
| 112 | assert response.previous is None |
| 113 | |
| 114 | |
| 115 | def test_user_agent(): |
| 116 | # Test that we provide a default user-agent |
| 117 | http = httplib2.Http() |
| 118 | with tests.server_reflect() as uri: |
| 119 | response, content = http.request(uri, 'GET') |
| 120 | assert response.status == 200 |
| 121 | reflected = tests.HttpRequest.from_bytes(content) |
| 122 | assert reflected.headers.get('user-agent', '').startswith('Python-httplib2/') |
| 123 | |
| 124 | |
| 125 | def test_user_agent_non_default(): |
| 126 | # Test that the default user-agent can be over-ridden |
| 127 | http = httplib2.Http() |
| 128 | with tests.server_reflect() as uri: |
| 129 | response, content = http.request(uri, 'GET', headers={'User-Agent': 'fred/1.0'}) |
| 130 | assert response.status == 200 |
| 131 | reflected = tests.HttpRequest.from_bytes(content) |
| 132 | assert reflected.headers.get('user-agent') == 'fred/1.0' |
| 133 | |
| 134 | |
| 135 | def test_get_300_with_location(): |
| 136 | # Test the we automatically follow 300 redirects if a Location: header is provided |
| 137 | http = httplib2.Http() |
| 138 | final_content = b'This is the final destination.\n' |
| 139 | routes = { |
| 140 | '/final': tests.http_response_bytes(body=final_content), |
| 141 | '': tests.http_response_bytes(status='300 Multiple Choices', headers={'location': '/final'}), |
| 142 | } |
| 143 | with tests.server_route(routes, request_count=2) as uri: |
| 144 | response, content = http.request(uri, 'GET') |
| 145 | assert response.status == 200 |
| 146 | assert content == final_content |
| 147 | assert response.previous.status == 300 |
| 148 | assert not response.previous.fromcache |
| 149 | |
| 150 | # Confirm that the intermediate 300 is not cached |
| 151 | with tests.server_route(routes, request_count=2) as uri: |
| 152 | response, content = http.request(uri, 'GET') |
| 153 | assert response.status == 200 |
| 154 | assert content == final_content |
| 155 | assert response.previous.status == 300 |
| 156 | assert not response.previous.fromcache |
| 157 | |
| 158 | |
| 159 | def test_get_300_with_location_noredirect(): |
| 160 | # Test the we automatically follow 300 redirects if a Location: header is provided |
| 161 | http = httplib2.Http() |
| 162 | http.follow_redirects = False |
| 163 | response = tests.http_response_bytes( |
| 164 | status='300 Multiple Choices', |
| 165 | headers={'location': '/final'}, |
| 166 | body=b'redirect body') |
| 167 | with tests.server_const_bytes(response) as uri: |
| 168 | response, content = http.request(uri, 'GET') |
| 169 | assert response.status == 300 |
| 170 | |
| 171 | |
| 172 | def test_get_300_without_location(): |
| 173 | # Not giving a Location: header in a 300 response is acceptable |
| 174 | # In which case we just return the 300 response |
| 175 | http = httplib2.Http() |
| 176 | with tests.server_const_http(status='300 Multiple Choices', body=b'redirect body') as uri: |
| 177 | response, content = http.request(uri, 'GET') |
| 178 | assert response.status == 300 |
| 179 | assert response.previous is None |
| 180 | assert content == b'redirect body' |
| 181 | |
| 182 | |
| 183 | def test_get_301(): |
| 184 | # Test that we automatically follow 301 redirects |
| 185 | # and that we cache the 301 response |
| 186 | http = httplib2.Http(cache=tests.get_cache_path()) |
| 187 | destination = '' |
| 188 | routes = { |
| 189 | '/final': tests.http_response_bytes(body=b'This is the final destination.\n'), |
| 190 | '': tests.http_response_bytes( |
| 191 | status='301 Now where did I leave that URL', headers={'location': '/final'}, body=b'redirect body'), |
| 192 | } |
| 193 | with tests.server_route(routes, request_count=3) as uri: |
| 194 | destination = urllib.parse.urljoin(uri, '/final') |
| 195 | response1, content1 = http.request(uri, 'GET') |
| 196 | response2, content2 = http.request(uri, 'GET') |
| 197 | assert response1.status == 200 |
| 198 | assert 'content-location' in response2 |
| 199 | assert response1['content-location'] == destination |
| 200 | assert content1 == b'This is the final destination.\n' |
| 201 | assert response1.previous.status == 301 |
| 202 | assert not response1.previous.fromcache |
| 203 | |
| 204 | assert response2.status == 200 |
| 205 | assert response2['content-location'] == destination |
| 206 | assert content2 == b'This is the final destination.\n' |
| 207 | assert response2.previous.status == 301 |
| 208 | assert response2.previous.fromcache |
| 209 | |
| 210 | |
| 211 | @pytest.mark.skip( |
| 212 | not os.environ.get('httplib2_test_still_run_skipped') and |
| 213 | os.environ.get('TRAVIS_PYTHON_VERSION') in ('2.7', 'pypy'), |
| 214 | reason='FIXME: timeout on Travis py27 and pypy, works elsewhere', |
| 215 | ) |
| 216 | def test_head_301(): |
| 217 | # Test that we automatically follow 301 redirects |
| 218 | http = httplib2.Http() |
| 219 | destination = '' |
| 220 | routes = { |
| 221 | '/final': tests.http_response_bytes(body=b'This is the final destination.\n'), |
| 222 | '': tests.http_response_bytes( |
| 223 | status='301 Now where did I leave that URL', headers={'location': '/final'}, body=b'redirect body'), |
| 224 | } |
| 225 | with tests.server_route(routes, request_count=2) as uri: |
| 226 | destination = urllib.parse.urljoin(uri, '/final') |
| 227 | response, content = http.request(uri, 'HEAD') |
| 228 | assert response.status == 200 |
| 229 | assert response['content-location'] == destination |
| 230 | assert response.previous.status == 301 |
| 231 | assert not response.previous.fromcache |
| 232 | |
| 233 | |
| 234 | @pytest.mark.xfail(reason='FIXME: 301 cache works only with follow_redirects, should work regardless') |
| 235 | def test_get_301_no_redirect(): |
| 236 | # Test that we cache the 301 response |
| 237 | http = httplib2.Http(cache=tests.get_cache_path(), timeout=0.5) |
| 238 | http.follow_redirects = False |
| 239 | response = tests.http_response_bytes( |
| 240 | status='301 Now where did I leave that URL', |
| 241 | headers={'location': '/final', 'cache-control': 'max-age=300'}, |
| 242 | body=b'redirect body', |
| 243 | add_date=True, |
| 244 | ) |
| 245 | with tests.server_const_bytes(response) as uri: |
| 246 | response, _ = http.request(uri, 'GET') |
| 247 | assert response.status == 301 |
| 248 | assert not response.fromcache |
| 249 | response, _ = http.request(uri, 'GET') |
| 250 | assert response.status == 301 |
| 251 | assert response.fromcache |
| 252 | |
| 253 | |
| 254 | def test_get_302(): |
| 255 | # Test that we automatically follow 302 redirects |
| 256 | # and that we DO NOT cache the 302 response |
| 257 | http = httplib2.Http(cache=tests.get_cache_path()) |
| 258 | second_url, final_url = '', '' |
| 259 | routes = { |
| 260 | '/final': tests.http_response_bytes(body=b'This is the final destination.\n'), |
| 261 | '/second': tests.http_response_bytes( |
| 262 | status='302 Found', headers={'location': '/final'}, body=b'second redirect'), |
| 263 | '': tests.http_response_bytes( |
| 264 | status='302 Found', headers={'location': '/second'}, body=b'redirect body'), |
| 265 | } |
| 266 | with tests.server_route(routes, request_count=7) as uri: |
| 267 | second_url = urllib.parse.urljoin(uri, '/second') |
| 268 | final_url = urllib.parse.urljoin(uri, '/final') |
| 269 | response1, content1 = http.request(second_url, 'GET') |
| 270 | response2, content2 = http.request(second_url, 'GET') |
| 271 | response3, content3 = http.request(uri, 'GET') |
| 272 | assert response1.status == 200 |
| 273 | assert response1['content-location'] == final_url |
| 274 | assert content1 == b'This is the final destination.\n' |
| 275 | assert response1.previous.status == 302 |
| 276 | assert not response1.previous.fromcache |
| 277 | |
| 278 | assert response2.status == 200 |
| 279 | # FIXME: |
| 280 | # assert response2.fromcache |
| 281 | assert response2['content-location'] == final_url |
| 282 | assert content2 == b'This is the final destination.\n' |
| 283 | assert response2.previous.status == 302 |
| 284 | assert not response2.previous.fromcache |
| 285 | assert response2.previous['content-location'] == second_url |
| 286 | |
| 287 | assert response3.status == 200 |
| 288 | # FIXME: |
| 289 | # assert response3.fromcache |
| 290 | assert content3 == b'This is the final destination.\n' |
| 291 | assert response3.previous.status == 302 |
| 292 | assert not response3.previous.fromcache |
| 293 | |
| 294 | |
| 295 | def test_get_302_redirection_limit(): |
| 296 | # Test that we can set a lower redirection limit |
| 297 | # and that we raise an exception when we exceed |
| 298 | # that limit. |
| 299 | http = httplib2.Http() |
| 300 | http.force_exception_to_status_code = False |
| 301 | routes = { |
| 302 | '/second': tests.http_response_bytes( |
| 303 | status='302 Found', headers={'location': '/final'}, body=b'second redirect'), |
| 304 | '': tests.http_response_bytes( |
| 305 | status='302 Found', headers={'location': '/second'}, body=b'redirect body'), |
| 306 | } |
| 307 | with tests.server_route(routes, request_count=4) as uri: |
| 308 | try: |
| 309 | http.request(uri, 'GET', redirections=1) |
| 310 | assert False, 'This should not happen' |
| 311 | except httplib2.RedirectLimit: |
| 312 | pass |
| 313 | except Exception: |
| 314 | assert False, 'Threw wrong kind of exception ' |
| 315 | |
| 316 | # Re-run the test with out the exceptions |
| 317 | http.force_exception_to_status_code = True |
| 318 | response, content = http.request(uri, 'GET', redirections=1) |
| 319 | |
| 320 | assert response.status == 500 |
| 321 | assert response.reason.startswith('Redirected more') |
| 322 | assert response['status'] == '302' |
| 323 | assert content == b'second redirect' |
| 324 | assert response.previous is not None |
| 325 | |
| 326 | |
| 327 | def test_get_302_no_location(): |
| 328 | # Test that we throw an exception when we get |
| 329 | # a 302 with no Location: header. |
| 330 | http = httplib2.Http() |
| 331 | http.force_exception_to_status_code = False |
| 332 | with tests.server_const_http(status='302 Found', request_count=2) as uri: |
| 333 | try: |
| 334 | http.request(uri, 'GET') |
| 335 | assert False, 'Should never reach here' |
| 336 | except httplib2.RedirectMissingLocation: |
| 337 | pass |
| 338 | except Exception: |
| 339 | assert False, 'Threw wrong kind of exception ' |
| 340 | |
| 341 | # Re-run the test with out the exceptions |
| 342 | http.force_exception_to_status_code = True |
| 343 | response, content = http.request(uri, 'GET') |
| 344 | |
| 345 | assert response.status == 500 |
| 346 | assert response.reason.startswith('Redirected but') |
| 347 | assert '302' == response['status'] |
| 348 | assert content == b'' |
| 349 | |
| 350 | |
| 351 | @pytest.mark.skip( |
| 352 | not os.environ.get('httplib2_test_still_run_skipped') and |
| 353 | os.environ.get('TRAVIS_PYTHON_VERSION') in ('2.7', 'pypy'), |
| 354 | reason='FIXME: timeout on Travis py27 and pypy, works elsewhere', |
| 355 | ) |
| 356 | def test_303(): |
| 357 | # Do a follow-up GET on a Location: header |
| 358 | # returned from a POST that gave a 303. |
| 359 | http = httplib2.Http() |
| 360 | routes = { |
| 361 | '/final': tests.make_http_reflect(), |
| 362 | '': tests.make_http_reflect(status='303 See Other', headers={'location': '/final'}), |
| 363 | } |
| 364 | with tests.server_route(routes, request_count=2) as uri: |
| 365 | response, content = http.request(uri, 'POST', " ") |
| 366 | assert response.status == 200 |
| 367 | reflected = tests.HttpRequest.from_bytes(content) |
| 368 | assert reflected.uri == '/final' |
| 369 | assert response.previous.status == 303 |
| 370 | |
| 371 | # Skip follow-up GET |
| 372 | http = httplib2.Http() |
| 373 | http.follow_redirects = False |
| 374 | with tests.server_route(routes, request_count=1) as uri: |
| 375 | response, content = http.request(uri, 'POST', " ") |
| 376 | assert response.status == 303 |
| 377 | |
| 378 | # All methods can be used |
| 379 | http = httplib2.Http() |
| 380 | cases = 'DELETE GET HEAD POST PUT EVEN_NEW_ONES'.split(' ') |
| 381 | with tests.server_route(routes, request_count=len(cases) * 2) as uri: |
| 382 | for method in cases: |
| 383 | response, content = http.request(uri, method, body=b'q q') |
| 384 | assert response.status == 200 |
| 385 | reflected = tests.HttpRequest.from_bytes(content) |
| 386 | assert reflected.method == 'GET' |
| 387 | |
| 388 | |
| 389 | def test_etag_used(): |
| 390 | # Test that we use ETags properly to validate our cache |
| 391 | cache_path = tests.get_cache_path() |
| 392 | http = httplib2.Http(cache=cache_path) |
| 393 | response_kwargs = dict( |
| 394 | add_date=True, |
| 395 | add_etag=True, |
| 396 | body=b'something', |
| 397 | headers={ |
| 398 | 'cache-control': 'public,max-age=300', |
| 399 | }, |
| 400 | ) |
| 401 | |
| 402 | def handler(request): |
| 403 | if request.headers.get('range'): |
| 404 | return tests.http_response_bytes(status=206, **response_kwargs) |
| 405 | return tests.http_response_bytes(**response_kwargs) |
| 406 | |
| 407 | with tests.server_request(handler, request_count=2) as uri: |
| 408 | response, _ = http.request(uri, 'GET', headers={'accept-encoding': 'identity'}) |
| 409 | assert response['etag'] == '"437b930db84b8079c2dd804a71936b5f"' |
| 410 | |
| 411 | http.request(uri, 'GET', headers={'accept-encoding': 'identity'}) |
| 412 | response, _ = http.request( |
| 413 | uri, 'GET', |
| 414 | headers={'accept-encoding': 'identity', 'cache-control': 'must-revalidate'}, |
| 415 | ) |
| 416 | assert response.status == 200 |
| 417 | assert response.fromcache |
| 418 | |
| 419 | # TODO: API to read cache item, at least internal to tests |
| 420 | cache_file_name = os.path.join(cache_path, httplib2.safename(httplib2.urlnorm(uri)[-1])) |
| 421 | with open(cache_file_name, 'r') as f: |
| 422 | status_line = f.readline() |
| 423 | assert status_line.startswith("status:") |
| 424 | |
| 425 | response, content = http.request(uri, 'HEAD', headers={'accept-encoding': 'identity'}) |
| 426 | assert response.status == 200 |
| 427 | assert response.fromcache |
| 428 | |
| 429 | response, content = http.request(uri, 'GET', headers={'accept-encoding': 'identity', 'range': 'bytes=0-0'}) |
| 430 | assert response.status == 206 |
| 431 | assert not response.fromcache |
| 432 | |
| 433 | |
| 434 | def test_etag_ignore(): |
| 435 | # Test that we can forcibly ignore ETags |
| 436 | http = httplib2.Http(cache=tests.get_cache_path()) |
| 437 | response_kwargs = dict( |
| 438 | add_date=True, |
| 439 | add_etag=True, |
| 440 | ) |
| 441 | with tests.server_reflect(request_count=3, **response_kwargs) as uri: |
| 442 | response, content = http.request(uri, 'GET', headers={'accept-encoding': 'identity'}) |
| 443 | assert response.status == 200 |
| 444 | assert response['etag'] != "" |
| 445 | |
| 446 | response, content = http.request( |
| 447 | uri, 'GET', |
| 448 | headers={'accept-encoding': 'identity', 'cache-control': 'max-age=0'}, |
| 449 | ) |
| 450 | reflected = tests.HttpRequest.from_bytes(content) |
| 451 | assert reflected.headers.get('if-none-match') |
| 452 | |
| 453 | http.ignore_etag = True |
| 454 | response, content = http.request( |
| 455 | uri, 'GET', |
| 456 | headers={'accept-encoding': 'identity', 'cache-control': 'max-age=0'}, |
| 457 | ) |
| 458 | assert not response.fromcache |
| 459 | reflected = tests.HttpRequest.from_bytes(content) |
| 460 | assert not reflected.headers.get('if-none-match') |
| 461 | |
| 462 | |
| 463 | def test_etag_override(): |
| 464 | # Test that we can forcibly ignore ETags |
| 465 | http = httplib2.Http(cache=tests.get_cache_path()) |
| 466 | response_kwargs = dict( |
| 467 | add_date=True, |
| 468 | add_etag=True, |
| 469 | ) |
| 470 | with tests.server_reflect(request_count=3, **response_kwargs) as uri: |
| 471 | response, _ = http.request(uri, 'GET', headers={'accept-encoding': 'identity'}) |
| 472 | assert response.status == 200 |
| 473 | assert response['etag'] != '' |
| 474 | |
| 475 | response, content = http.request( |
| 476 | uri, 'GET', |
| 477 | headers={'accept-encoding': 'identity', 'cache-control': 'max-age=0'}, |
| 478 | ) |
| 479 | assert response.status == 200 |
| 480 | reflected = tests.HttpRequest.from_bytes(content) |
| 481 | assert reflected.headers.get('if-none-match') |
| 482 | assert reflected.headers.get('if-none-match') != 'fred' |
| 483 | |
| 484 | response, content = http.request( |
| 485 | uri, 'GET', |
| 486 | headers={'accept-encoding': 'identity', 'cache-control': 'max-age=0', 'if-none-match': 'fred'}, |
| 487 | ) |
| 488 | assert response.status == 200 |
| 489 | reflected = tests.HttpRequest.from_bytes(content) |
| 490 | assert reflected.headers.get('if-none-match') == 'fred' |
| 491 | |
| 492 | |
| 493 | @pytest.mark.skip(reason='was commented in legacy code') |
| 494 | def test_get_304_end_to_end(): |
| 495 | pass |
| 496 | # Test that end to end headers get overwritten in the cache |
| 497 | # uri = urllib.parse.urljoin(base, "304/end2end.cgi") |
| 498 | # response, content = http.request(uri, 'GET') |
| 499 | # assertNotEqual(response['etag'], "") |
| 500 | # old_date = response['date'] |
| 501 | # time.sleep(2) |
| 502 | |
| 503 | # response, content = http.request(uri, 'GET', headers = {'Cache-Control': 'max-age=0'}) |
| 504 | # # The response should be from the cache, but the Date: header should be updated. |
| 505 | # new_date = response['date'] |
| 506 | # assert new_date != old_date |
| 507 | # assert response.status == 200 |
| 508 | # assert response.fromcache == True |
| 509 | |
| 510 | |
| 511 | def test_get_304_last_modified(): |
| 512 | # Test that we can still handle a 304 |
| 513 | # by only using the last-modified cache validator. |
| 514 | http = httplib2.Http(cache=tests.get_cache_path()) |
| 515 | date = email.utils.formatdate() |
| 516 | |
| 517 | def handler(read): |
| 518 | read() |
| 519 | yield tests.http_response_bytes( |
| 520 | status=200, |
| 521 | body=b'something', |
| 522 | headers={ |
| 523 | 'date': date, |
| 524 | 'last-modified': date, |
| 525 | }, |
| 526 | ) |
| 527 | |
| 528 | request2 = read() |
| 529 | assert request2.headers['if-modified-since'] == date |
| 530 | yield tests.http_response_bytes(status=304) |
| 531 | |
| 532 | with tests.server_yield(handler, request_count=2) as uri: |
| 533 | response, content = http.request(uri, 'GET') |
| 534 | assert response.get('last-modified') == date |
| 535 | |
| 536 | response, content = http.request(uri, 'GET') |
| 537 | assert response.status == 200 |
| 538 | assert response.fromcache |
| 539 | |
| 540 | |
| 541 | def test_get_307(): |
| 542 | # Test that we do follow 307 redirects but |
| 543 | # do not cache the 307 |
| 544 | http = httplib2.Http(cache=tests.get_cache_path(), timeout=1) |
| 545 | r307 = tests.http_response_bytes( |
| 546 | status=307, |
| 547 | headers={'location': '/final'}, |
| 548 | ) |
| 549 | r200 = tests.http_response_bytes( |
| 550 | status=200, |
| 551 | add_date=True, |
| 552 | body=b'final content\n', |
| 553 | headers={'cache-control': 'max-age=300'}, |
| 554 | ) |
| 555 | |
| 556 | with tests.server_list_http([r307, r200, r307]) as uri: |
| 557 | response, content = http.request(uri, 'GET') |
| 558 | assert response.previous.status == 307 |
| 559 | assert not response.previous.fromcache |
| 560 | assert response.status == 200 |
| 561 | assert not response.fromcache |
| 562 | assert content == b'final content\n' |
| 563 | |
| 564 | response, content = http.request(uri, 'GET') |
| 565 | assert response.previous.status == 307 |
| 566 | assert not response.previous.fromcache |
| 567 | assert response.status == 200 |
| 568 | assert response.fromcache |
| 569 | assert content == b'final content\n' |
| 570 | |
| 571 | |
| 572 | def test_get_410(): |
| 573 | # Test that we pass 410's through |
| 574 | http = httplib2.Http() |
| 575 | with tests.server_const_http(status=410) as uri: |
| 576 | response, content = http.request(uri, 'GET') |
| 577 | assert response.status == 410 |
| 578 | |
| 579 | |
| 580 | def test_get_duplicate_headers(): |
| 581 | # Test that duplicate headers get concatenated via ',' |
| 582 | http = httplib2.Http() |
| 583 | response = b'''HTTP/1.0 200 OK\r\n\ |
| 584 | Link: link1\r\n\ |
| 585 | Content-Length: 7\r\n\ |
| 586 | Link: link2\r\n\r\n\ |
| 587 | content''' |
| 588 | with tests.server_const_bytes(response) as uri: |
| 589 | response, content = http.request(uri, 'GET') |
| 590 | assert response.status == 200 |
| 591 | assert content == b"content" |
| 592 | assert response['link'], 'link1, link2' |