@@ -540,70 +540,73 @@ async def _parse_multipart_part(
540540 :param part: aiohttp BodyPartReader for the part
541541 :return: ExecutionResult or None if part is empty/heartbeat
542542 """
543- # Verify the part has the correct content type
543+ try :
544+ body = await part .text ()
545+ body = body .strip ()
546+ except UnicodeDecodeError as e :
547+ log .warning (f"Failed to decode part: { ascii (e )} " )
548+ return None
549+
544550 content_type = part .headers .get (aiohttp .hdrs .CONTENT_TYPE , "" )
551+
552+ if not content_type and not body :
553+ log .debug ("Skipping part with no content-type and no body" )
554+ return None
555+
556+ # Verify the part has the correct content type
545557 if not content_type .startswith ("application/json" ):
546558 raise TransportProtocolError (
547559 f"Unexpected part content-type: { content_type } . "
548560 "Expected 'application/json'."
549561 )
550562
551- try :
552- # Read the part content as text
553- body = await part .text ()
554- body = body .strip ()
555-
556- if log .isEnabledFor (logging .DEBUG ):
557- log .debug ("<<< %s" , ascii (body or "(empty body, skipping)" ))
563+ if log .isEnabledFor (logging .DEBUG ):
564+ log .debug ("<<< %s" , ascii (body or "(empty body, skipping)" ))
558565
559- if not body :
560- return None
566+ if not body :
567+ return None
561568
562- # Parse JSON body using custom deserializer
569+ try :
563570 data = self .json_deserialize (body )
564-
565- # Handle heartbeats - empty JSON objects
566- if not data :
567- log .debug ("Received heartbeat, ignoring" )
568- return None
569-
570- # The multipart subscription protocol wraps data in a "payload" property
571- if "payload" not in data :
572- log .warning ("Invalid response: missing 'payload' field" )
573- return None
574-
575- payload = data ["payload" ]
576-
577- # Check for transport-level errors (payload is null)
578- if payload is None :
579- # If there are errors, this is a transport-level error
580- errors = data .get ("errors" )
581- if errors :
582- error_messages = [
583- error .get ("message" , "Unknown transport error" )
584- for error in errors
585- ]
586-
587- for message in error_messages :
588- log .error (f"Transport error: { message } " )
589-
590- raise TransportServerError ("\n \n " .join (error_messages ))
591- else :
592- # Null payload without errors - just skip this part
593- return None
594-
595- # Extract GraphQL data from payload
596- return ExecutionResult (
597- data = payload .get ("data" ),
598- errors = payload .get ("errors" ),
599- extensions = payload .get ("extensions" ),
600- )
601571 except json .JSONDecodeError as e :
602572 log .warning (
603573 f"Failed to parse JSON: { ascii (e )} , "
604574 f"body: { ascii (body [:100 ]) if body else '' } "
605575 )
606576 return None
607- except UnicodeDecodeError as e :
608- log .warning (f"Failed to decode part: { ascii (e )} " )
577+
578+ # Handle heartbeats - empty JSON objects
579+ if not data :
580+ log .debug ("Received heartbeat, ignoring" )
581+ return None
582+
583+ # The multipart subscription protocol wraps data in a "payload" property
584+ if "payload" not in data :
585+ log .warning ("Invalid response: missing 'payload' field" )
609586 return None
587+
588+ payload = data ["payload" ]
589+
590+ # Check for transport-level errors (payload is null)
591+ if payload is None :
592+ # If there are errors, this is a transport-level error
593+ errors = data .get ("errors" )
594+ if errors :
595+ error_messages = [
596+ error .get ("message" , "Unknown transport error" ) for error in errors
597+ ]
598+
599+ for message in error_messages :
600+ log .error (f"Transport error: { message } " )
601+
602+ raise TransportServerError ("\n \n " .join (error_messages ))
603+ else :
604+ # Null payload without errors - just skip this part
605+ return None
606+
607+ # Extract GraphQL data from payload
608+ return ExecutionResult (
609+ data = payload .get ("data" ),
610+ errors = payload .get ("errors" ),
611+ extensions = payload .get ("extensions" ),
612+ )
0 commit comments