@@ -140,18 +140,17 @@ async def subscribe(
140140 if self .session is None :
141141 raise TransportClosed ("Transport is not connected" )
142142
143- # Prepare the request payload
144143 payload = request .payload
145144
146- # Log the request
147- if log .isEnabledFor (logging .DEBUG ):
148- log .debug (">>> %s" , self .json_serialize (payload ))
145+ if log .isEnabledFor (logging .DEBUG ): # pragma: no cover
146+ log .debug (">>> %s" , self .json_serialize (payload )) # pragma: no cover
149147
150- # Set headers to accept multipart responses
151- # The multipart subscription protocol requires subscriptionSpec parameter
152148 headers = {
153149 "Content-Type" : "application/json" ,
154- "Accept" : 'multipart/mixed;subscriptionSpec="1.0", application/json' ,
150+ "Accept" : (
151+ "multipart/mixed;boundary=graphql;"
152+ "subscriptionSpec=1.0,application/json"
153+ ),
155154 }
156155
157156 try :
@@ -175,17 +174,14 @@ async def subscribe(
175174
176175 content_type = response .headers .get ("Content-Type" , "" )
177176
178- # Check if response is multipart
179- if "multipart/mixed" not in content_type :
177+ if "application/json" not in content_type :
180178 raise TransportProtocolError (
181- f"Expected multipart/mixed response, got { content_type } . "
179+ f"Expected application/json response, got { content_type } . "
182180 "Server may not support the multipart subscription protocol."
183181 )
184182
185183 # Parse multipart response
186- async for result in self ._parse_multipart_response (
187- response , content_type
188- ):
184+ async for result in self ._parse_multipart_response (response ):
189185 yield result
190186
191187 except (TransportServerError , TransportProtocolError ):
@@ -196,27 +192,17 @@ async def subscribe(
196192 async def _parse_multipart_response (
197193 self ,
198194 response : aiohttp .ClientResponse ,
199- content_type : str ,
200195 ) -> AsyncGenerator [ExecutionResult , None ]:
201196 """
202- Parse a multipart/mixed response and yield execution results.
197+ Parse a multipart response stream and yield execution results.
198+
199+ The boundary is always "graphql" per the protocol specification.
203200
204201 :param response: The aiohttp response object
205- :param content_type: The Content-Type header value
206202 :yields: ExecutionResult objects
207203 """
208- # Extract boundary from Content-Type header
209- # Format: multipart/mixed; boundary="---"
210- boundary = None
211- for part in content_type .split (";" ):
212- part = part .strip ()
213- if part .startswith ("boundary=" ):
214- boundary = part .split ("=" , 1 )[1 ].strip ('"' )
215- break
216-
217- if not boundary :
218- raise TransportProtocolError ("No boundary found in multipart response" )
219-
204+ # The multipart subscription protocol requires boundary to always be "graphql"
205+ boundary = "graphql"
220206 log .debug ("Parsing multipart response with boundary: %s" , boundary )
221207
222208 # Read and parse the multipart stream
@@ -287,18 +273,16 @@ def _parse_multipart_part(self, part_data: bytes) -> Optional[ExecutionResult]:
287273 # No headers separator found, treat entire content as body
288274 parts = ["" , part_str ]
289275
290- if len (parts ) < 2 :
291- return None
292-
276+ assert len (parts ) == 2 , "parts should always have exactly 2 elements"
293277 headers_str , body = parts
294278 body = body .strip ()
295279
296280 if not body :
297281 return None
298282
299283 # Log the received data
300- if log .isEnabledFor (logging .DEBUG ):
301- log .debug ("<<< %s" , body )
284+ if log .isEnabledFor (logging .DEBUG ): # pragma: no cover
285+ log .debug ("<<< %s" , body ) # pragma: no cover
302286
303287 try :
304288 # Parse JSON body
0 commit comments