@@ -311,3 +311,291 @@ def test_header_tuples_are_decoded_push_promise(self,
311311
312312 assert isinstance (event , h2 .events .PushedStreamReceived )
313313 assert_header_blocks_actually_equal (headers , event .headers )
314+
315+
316+ class TestSecureHeaders (object ):
317+ """
318+ Certain headers should always be transformed to their never-indexed form.
319+ """
320+ example_request_headers = [
321+ (u':authority' , u'example.com' ),
322+ (u':path' , u'/' ),
323+ (u':scheme' , u'https' ),
324+ (u':method' , u'GET' ),
325+ ]
326+ bytes_example_request_headers = [
327+ (b':authority' , b'example.com' ),
328+ (b':path' , b'/' ),
329+ (b':scheme' , b'https' ),
330+ (b':method' , b'GET' ),
331+ ]
332+ possible_auth_headers = [
333+ (u'authorization' , u'test' ),
334+ (u'Authorization' , u'test' ),
335+ (u'authorization' , u'really long test' ),
336+ HeaderTuple (u'authorization' , u'test' ),
337+ HeaderTuple (u'Authorization' , u'test' ),
338+ HeaderTuple (u'authorization' , u'really long test' ),
339+ NeverIndexedHeaderTuple (u'authorization' , u'test' ),
340+ NeverIndexedHeaderTuple (u'Authorization' , u'test' ),
341+ NeverIndexedHeaderTuple (u'authorization' , u'really long test' ),
342+ (b'authorization' , b'test' ),
343+ (b'Authorization' , b'test' ),
344+ (b'authorization' , b'really long test' ),
345+ HeaderTuple (b'authorization' , b'test' ),
346+ HeaderTuple (b'Authorization' , b'test' ),
347+ HeaderTuple (b'authorization' , b'really long test' ),
348+ NeverIndexedHeaderTuple (b'authorization' , b'test' ),
349+ NeverIndexedHeaderTuple (b'Authorization' , b'test' ),
350+ NeverIndexedHeaderTuple (b'authorization' , b'really long test' ),
351+ ]
352+ secured_cookie_headers = [
353+ (u'cookie' , u'short' ),
354+ (u'Cookie' , u'short' ),
355+ (u'cookie' , u'nineteen byte cooki' ),
356+ HeaderTuple (u'cookie' , u'short' ),
357+ HeaderTuple (u'Cookie' , u'short' ),
358+ HeaderTuple (u'cookie' , u'nineteen byte cooki' ),
359+ NeverIndexedHeaderTuple (u'cookie' , u'short' ),
360+ NeverIndexedHeaderTuple (u'Cookie' , u'short' ),
361+ NeverIndexedHeaderTuple (u'cookie' , u'nineteen byte cooki' ),
362+ NeverIndexedHeaderTuple (u'cookie' , u'longer manually secured cookie' ),
363+ (b'cookie' , b'short' ),
364+ (b'Cookie' , b'short' ),
365+ (b'cookie' , b'nineteen byte cooki' ),
366+ HeaderTuple (b'cookie' , b'short' ),
367+ HeaderTuple (b'Cookie' , b'short' ),
368+ HeaderTuple (b'cookie' , b'nineteen byte cooki' ),
369+ NeverIndexedHeaderTuple (b'cookie' , b'short' ),
370+ NeverIndexedHeaderTuple (b'Cookie' , b'short' ),
371+ NeverIndexedHeaderTuple (b'cookie' , b'nineteen byte cooki' ),
372+ NeverIndexedHeaderTuple (b'cookie' , b'longer manually secured cookie' ),
373+ ]
374+ unsecured_cookie_headers = [
375+ (u'cookie' , u'twenty byte cookie!!' ),
376+ (u'Cookie' , u'twenty byte cookie!!' ),
377+ (u'cookie' , u'substantially longer than 20 byte cookie' ),
378+ HeaderTuple (u'cookie' , u'twenty byte cookie!!' ),
379+ HeaderTuple (u'cookie' , u'twenty byte cookie!!' ),
380+ HeaderTuple (u'Cookie' , u'twenty byte cookie!!' ),
381+ (b'cookie' , b'twenty byte cookie!!' ),
382+ (b'Cookie' , b'twenty byte cookie!!' ),
383+ (b'cookie' , b'substantially longer than 20 byte cookie' ),
384+ HeaderTuple (b'cookie' , b'twenty byte cookie!!' ),
385+ HeaderTuple (b'cookie' , b'twenty byte cookie!!' ),
386+ HeaderTuple (b'Cookie' , b'twenty byte cookie!!' ),
387+ ]
388+
389+ @pytest .mark .parametrize (
390+ 'headers' , (example_request_headers , bytes_example_request_headers )
391+ )
392+ @pytest .mark .parametrize ('auth_header' , possible_auth_headers )
393+ def test_authorization_headers_never_indexed (self ,
394+ headers ,
395+ auth_header ,
396+ frame_factory ):
397+ """
398+ Authorization headers are always forced to be never-indexed, regardless
399+ of their form.
400+ """
401+ # Regardless of what we send, we expect it to be never indexed.
402+ send_headers = headers + [auth_header ]
403+ expected_headers = headers + [
404+ NeverIndexedHeaderTuple (auth_header [0 ].lower (), auth_header [1 ])
405+ ]
406+
407+ c = h2 .connection .H2Connection ()
408+ c .initiate_connection ()
409+
410+ # Clear the data, then send headers.
411+ c .clear_outbound_data_buffer ()
412+ c .send_headers (1 , send_headers )
413+
414+ f = frame_factory .build_headers_frame (headers = expected_headers )
415+ assert c .data_to_send () == f .serialize ()
416+
417+ @pytest .mark .parametrize (
418+ 'headers' , (example_request_headers , bytes_example_request_headers )
419+ )
420+ @pytest .mark .parametrize ('auth_header' , possible_auth_headers )
421+ def test_authorization_headers_never_indexed_push (self ,
422+ headers ,
423+ auth_header ,
424+ frame_factory ):
425+ """
426+ Authorization headers are always forced to be never-indexed, regardless
427+ of their form, when pushed by a server.
428+ """
429+ # Regardless of what we send, we expect it to be never indexed.
430+ send_headers = headers + [auth_header ]
431+ expected_headers = headers + [
432+ NeverIndexedHeaderTuple (auth_header [0 ].lower (), auth_header [1 ])
433+ ]
434+
435+ c = h2 .connection .H2Connection (client_side = False )
436+ c .receive_data (frame_factory .preamble ())
437+
438+ # We can use normal headers for the request.
439+ f = frame_factory .build_headers_frame (
440+ self .example_request_headers
441+ )
442+ c .receive_data (f .serialize ())
443+
444+ frame_factory .refresh_encoder ()
445+ expected_frame = frame_factory .build_push_promise_frame (
446+ stream_id = 1 ,
447+ promised_stream_id = 2 ,
448+ headers = expected_headers ,
449+ flags = ['END_HEADERS' ],
450+ )
451+
452+ c .clear_outbound_data_buffer ()
453+ c .push_stream (
454+ stream_id = 1 ,
455+ promised_stream_id = 2 ,
456+ request_headers = send_headers
457+ )
458+
459+ assert c .data_to_send () == expected_frame .serialize ()
460+
461+ @pytest .mark .parametrize (
462+ 'headers' , (example_request_headers , bytes_example_request_headers )
463+ )
464+ @pytest .mark .parametrize ('cookie_header' , secured_cookie_headers )
465+ def test_short_cookie_headers_never_indexed (self ,
466+ headers ,
467+ cookie_header ,
468+ frame_factory ):
469+ """
470+ Short cookie headers, and cookies provided as NeverIndexedHeaderTuple,
471+ are never indexed.
472+ """
473+ # Regardless of what we send, we expect it to be never indexed.
474+ send_headers = headers + [cookie_header ]
475+ expected_headers = headers + [
476+ NeverIndexedHeaderTuple (cookie_header [0 ].lower (), cookie_header [1 ])
477+ ]
478+
479+ c = h2 .connection .H2Connection ()
480+ c .initiate_connection ()
481+
482+ # Clear the data, then send headers.
483+ c .clear_outbound_data_buffer ()
484+ c .send_headers (1 , send_headers )
485+
486+ f = frame_factory .build_headers_frame (headers = expected_headers )
487+ assert c .data_to_send () == f .serialize ()
488+
489+ @pytest .mark .parametrize (
490+ 'headers' , (example_request_headers , bytes_example_request_headers )
491+ )
492+ @pytest .mark .parametrize ('cookie_header' , secured_cookie_headers )
493+ def test_short_cookie_headers_never_indexed_push (self ,
494+ headers ,
495+ cookie_header ,
496+ frame_factory ):
497+ """
498+ Short cookie headers, and cookies provided as NeverIndexedHeaderTuple,
499+ are never indexed when pushed by servers.
500+ """
501+ # Regardless of what we send, we expect it to be never indexed.
502+ send_headers = headers + [cookie_header ]
503+ expected_headers = headers + [
504+ NeverIndexedHeaderTuple (cookie_header [0 ].lower (), cookie_header [1 ])
505+ ]
506+
507+ c = h2 .connection .H2Connection (client_side = False )
508+ c .receive_data (frame_factory .preamble ())
509+
510+ # We can use normal headers for the request.
511+ f = frame_factory .build_headers_frame (
512+ self .example_request_headers
513+ )
514+ c .receive_data (f .serialize ())
515+
516+ frame_factory .refresh_encoder ()
517+ expected_frame = frame_factory .build_push_promise_frame (
518+ stream_id = 1 ,
519+ promised_stream_id = 2 ,
520+ headers = expected_headers ,
521+ flags = ['END_HEADERS' ],
522+ )
523+
524+ c .clear_outbound_data_buffer ()
525+ c .push_stream (
526+ stream_id = 1 ,
527+ promised_stream_id = 2 ,
528+ request_headers = send_headers
529+ )
530+
531+ assert c .data_to_send () == expected_frame .serialize ()
532+
533+ @pytest .mark .parametrize (
534+ 'headers' , (example_request_headers , bytes_example_request_headers )
535+ )
536+ @pytest .mark .parametrize ('cookie_header' , unsecured_cookie_headers )
537+ def test_long_cookie_headers_can_be_indexed (self ,
538+ headers ,
539+ cookie_header ,
540+ frame_factory ):
541+ """
542+ Longer cookie headers can be indexed.
543+ """
544+ # Regardless of what we send, we expect it to be indexed.
545+ send_headers = headers + [cookie_header ]
546+ expected_headers = headers + [
547+ HeaderTuple (cookie_header [0 ].lower (), cookie_header [1 ])
548+ ]
549+
550+ c = h2 .connection .H2Connection ()
551+ c .initiate_connection ()
552+
553+ # Clear the data, then send headers.
554+ c .clear_outbound_data_buffer ()
555+ c .send_headers (1 , send_headers )
556+
557+ f = frame_factory .build_headers_frame (headers = expected_headers )
558+ assert c .data_to_send () == f .serialize ()
559+
560+ @pytest .mark .parametrize (
561+ 'headers' , (example_request_headers , bytes_example_request_headers )
562+ )
563+ @pytest .mark .parametrize ('cookie_header' , unsecured_cookie_headers )
564+ def test_long_cookie_headers_can_be_indexed_push (self ,
565+ headers ,
566+ cookie_header ,
567+ frame_factory ):
568+ """
569+ Longer cookie headers can be indexed.
570+ """
571+ # Regardless of what we send, we expect it to be never indexed.
572+ send_headers = headers + [cookie_header ]
573+ expected_headers = headers + [
574+ HeaderTuple (cookie_header [0 ].lower (), cookie_header [1 ])
575+ ]
576+
577+ c = h2 .connection .H2Connection (client_side = False )
578+ c .receive_data (frame_factory .preamble ())
579+
580+ # We can use normal headers for the request.
581+ f = frame_factory .build_headers_frame (
582+ self .example_request_headers
583+ )
584+ c .receive_data (f .serialize ())
585+
586+ frame_factory .refresh_encoder ()
587+ expected_frame = frame_factory .build_push_promise_frame (
588+ stream_id = 1 ,
589+ promised_stream_id = 2 ,
590+ headers = expected_headers ,
591+ flags = ['END_HEADERS' ],
592+ )
593+
594+ c .clear_outbound_data_buffer ()
595+ c .push_stream (
596+ stream_id = 1 ,
597+ promised_stream_id = 2 ,
598+ request_headers = send_headers
599+ )
600+
601+ assert c .data_to_send () == expected_frame .serialize ()
0 commit comments