@@ -392,6 +392,95 @@ async def test_max_response_bytes_bounds_answers_when_set(media):
392392 await server .stop (None )
393393
394394
395+ @pytest .mark .parametrize ("media" , ["application/json" , "application/connect+json" ])
396+ async def test_max_response_bytes_counts_the_json_the_face_sends (media ):
397+ # 2040 characters: 2043 bytes as proto, within 2048, and 2051 as JSON, past it.
398+ server = grpc .aio .server ()
399+ server .add_generic_rpc_handlers ((_handler (),))
400+ grpc_port = server .add_insecure_port ("127.0.0.1:0" )
401+ await server .start ()
402+ listener = await connect_gen .serve (
403+ 0 , grpc_port , host = "127.0.0.1" , max_message_bytes = 1024 , max_response_bytes = 2048
404+ )
405+ try :
406+ streaming = media .startswith ("application/connect+" )
407+ body = json .dumps ({"text" : "big" , "count" : 2040 }).encode ()
408+ async with httpx .AsyncClient () as client :
409+ proto = await client .post (
410+ _url (listener , "Say" ),
411+ content = Msg (text = "big" , count = 2040 ).SerializeToString (),
412+ headers = {"content-type" : "application/proto" },
413+ )
414+ r = await client .post (
415+ _url (listener , "Count" if streaming else "Say" ),
416+ content = envelope (body ) if streaming else body ,
417+ headers = {"content-type" : media },
418+ )
419+ assert proto .status_code == 200 and len (proto .content ) == 2043
420+ error = json .loads (frames (r .content )[- 1 ][1 ])["error" ] if streaming else r .json ()
421+ assert r .status_code == (200 if streaming else 400 )
422+ assert error == {
423+ "code" : "failed_precondition" ,
424+ "message" : (
425+ "the answer (2051 bytes) exceeds the Connect face's max_response_bytes (2048)"
426+ ),
427+ }
428+ finally :
429+ await listener .stop ()
430+ await server .stop (None )
431+
432+
433+ async def test_a_raised_message_bound_raises_the_answer_bound ():
434+ # max_response_bytes defaults to the larger of DEFAULT_MAX_RESPONSE_BYTES and
435+ # max_message_bytes: a service that raised its bound to 100 MiB answers 70 MiB.
436+ server = grpc .aio .server (options = [("grpc.max_send_message_length" , 100 * 2 ** 20 )])
437+ server .add_generic_rpc_handlers ((_handler (),))
438+ grpc_port = server .add_insecure_port ("127.0.0.1:0" )
439+ await server .start ()
440+ listener = await connect_gen .serve (
441+ 0 , grpc_port , host = "127.0.0.1" , max_message_bytes = 100 * 2 ** 20
442+ )
443+ try :
444+ size = 70 * 2 ** 20
445+ async with httpx .AsyncClient (timeout = 60 ) as client :
446+ r = await client .post (
447+ _url (listener , "Say" ),
448+ content = Msg (text = "big" , count = size ).SerializeToString (),
449+ headers = {"content-type" : "application/proto" },
450+ )
451+ assert r .status_code == 200 , r .text [:200 ]
452+ assert len (Msg .FromString (r .content ).text ) == size
453+ finally :
454+ await listener .stop ()
455+ await server .stop (None )
456+
457+
458+ async def test_the_default_answer_bound_is_16_mib ():
459+ server = grpc .aio .server ()
460+ server .add_generic_rpc_handlers ((_handler (),))
461+ grpc_port = server .add_insecure_port ("127.0.0.1:0" )
462+ await server .start ()
463+ listener = await connect_gen .serve (0 , grpc_port , host = "127.0.0.1" )
464+ try :
465+ async with httpx .AsyncClient (timeout = 60 ) as client :
466+ within = await client .post (
467+ _url (listener , "Say" ),
468+ content = Msg (text = "big" , count = 16 * 2 ** 20 - 8 ).SerializeToString (),
469+ headers = {"content-type" : "application/proto" },
470+ )
471+ past = await client .post (
472+ _url (listener , "Say" ),
473+ content = Msg (text = "big" , count = 16 * 2 ** 20 ).SerializeToString (),
474+ headers = {"content-type" : "application/proto" },
475+ )
476+ assert within .status_code == 200
477+ assert past .status_code == 400
478+ assert "max_response_bytes (16777216)" in past .json ()["message" ]
479+ finally :
480+ await listener .stop ()
481+ await server .stop (None )
482+
483+
395484async def test_one_connection_serves_several_calls (face ):
396485 reader , writer = await asyncio .open_connection ("127.0.0.1" , face .port )
397486 body = json .dumps ({"text" : "k" }).encode ()
@@ -655,89 +744,3 @@ async def test_the_relay_ends_when_the_grpc_server_hangs_up(silent_upstream):
655744 finally :
656745 writer .close ()
657746
658-
659- @pytest .mark .parametrize ("media" , ["application/json" , "application/connect+json" ])
660- async def test_max_response_bytes_counts_the_json_the_face_sends (media ):
661- # 2040 characters: 2043 bytes as proto, within 2048, and 2051 as JSON, past it.
662- server = grpc .aio .server ()
663- server .add_generic_rpc_handlers ((_handler (),))
664- grpc_port = server .add_insecure_port ("127.0.0.1:0" )
665- await server .start ()
666- listener = await connect_gen .serve (
667- 0 , grpc_port , host = "127.0.0.1" , max_message_bytes = 1024 , max_response_bytes = 2048
668- )
669- try :
670- streaming = media .startswith ("application/connect+" )
671- body = json .dumps ({"text" : "big" , "count" : 2040 }).encode ()
672- async with httpx .AsyncClient () as client :
673- proto = await client .post (
674- _url (listener , "Say" ),
675- content = Msg (text = "big" , count = 2040 ).SerializeToString (),
676- headers = {"content-type" : "application/proto" },
677- )
678- r = await client .post (
679- _url (listener , "Count" if streaming else "Say" ),
680- content = envelope (body ) if streaming else body ,
681- headers = {"content-type" : media },
682- )
683- assert proto .status_code == 200 and len (proto .content ) == 2043
684- error = json .loads (frames (r .content )[- 1 ][1 ])["error" ] if streaming else r .json ()
685- assert r .status_code == (200 if streaming else 400 )
686- assert error == {
687- "code" : "failed_precondition" ,
688- "message" : "the answer (2051 bytes) exceeds the Connect face's max_response_bytes (2048)" ,
689- }
690- finally :
691- await listener .stop ()
692- await server .stop (None )
693-
694-
695- async def test_a_raised_message_bound_raises_the_answer_bound ():
696- # max_response_bytes defaults to the larger of DEFAULT_MAX_RESPONSE_BYTES and
697- # max_message_bytes: a service that raised its bound to 100 MiB answers 70 MiB.
698- server = grpc .aio .server (options = [("grpc.max_send_message_length" , 100 * 2 ** 20 )])
699- server .add_generic_rpc_handlers ((_handler (),))
700- grpc_port = server .add_insecure_port ("127.0.0.1:0" )
701- await server .start ()
702- listener = await connect_gen .serve (
703- 0 , grpc_port , host = "127.0.0.1" , max_message_bytes = 100 * 2 ** 20
704- )
705- try :
706- size = 70 * 2 ** 20
707- async with httpx .AsyncClient (timeout = 60 ) as client :
708- r = await client .post (
709- _url (listener , "Say" ),
710- content = Msg (text = "big" , count = size ).SerializeToString (),
711- headers = {"content-type" : "application/proto" },
712- )
713- assert r .status_code == 200 , r .text [:200 ]
714- assert len (Msg .FromString (r .content ).text ) == size
715- finally :
716- await listener .stop ()
717- await server .stop (None )
718-
719-
720- async def test_the_default_answer_bound_is_16_mib ():
721- server = grpc .aio .server ()
722- server .add_generic_rpc_handlers ((_handler (),))
723- grpc_port = server .add_insecure_port ("127.0.0.1:0" )
724- await server .start ()
725- listener = await connect_gen .serve (0 , grpc_port , host = "127.0.0.1" )
726- try :
727- async with httpx .AsyncClient (timeout = 60 ) as client :
728- within = await client .post (
729- _url (listener , "Say" ),
730- content = Msg (text = "big" , count = 16 * 2 ** 20 - 8 ).SerializeToString (),
731- headers = {"content-type" : "application/proto" },
732- )
733- past = await client .post (
734- _url (listener , "Say" ),
735- content = Msg (text = "big" , count = 16 * 2 ** 20 ).SerializeToString (),
736- headers = {"content-type" : "application/proto" },
737- )
738- assert within .status_code == 200
739- assert past .status_code == 400
740- assert "max_response_bytes (16777216)" in past .json ()["message" ]
741- finally :
742- await listener .stop ()
743- await server .stop (None )
0 commit comments