Skip to content

Commit b7d663f

Browse files
committed
fix(graphql): fix multipart file upload in CancellableHttpLink
For MultipartRequest, headers must be copied after finalize() is called because finalize() sets the Content-Type header with the boundary parameter. Previously headers were copied before finalize(), causing the boundary to be missing and file uploads to fail. Also adds comprehensive tests for CancellableHttpLink: - File upload (multipart) works correctly - File upload can be cancelled - Pre-cancelled token immediately fails - Server errors pass through (not transformed to CancelledException) - Cancelled request behavior at server level - Multiple concurrent requests - cancel one, others complete - Connection reuse verification
1 parent c3084a3 commit b7d663f

2 files changed

Lines changed: 461 additions & 6 deletions

File tree

packages/graphql/lib/src/links/cancellable_http_link/_cancellable_io_client.dart

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -60,22 +60,35 @@ class CancellableHttpClientImpl implements CancellableHttpClient {
6060
return completer.future;
6161
}
6262

63-
// Set headers
64-
request.headers.forEach((name, value) {
65-
httpClientRequest!.headers.add(name, value);
66-
});
67-
6863
// Handle different request types
6964
if (request is http.Request) {
65+
// Set headers
66+
request.headers.forEach((name, value) {
67+
httpClientRequest!.headers.add(name, value);
68+
});
7069
httpClientRequest.contentLength = request.bodyBytes.length;
7170
httpClientRequest.add(request.bodyBytes);
7271
} else if (request is http.MultipartRequest) {
73-
// For multipart requests, we need to finalize and stream the body
72+
// For multipart requests, finalize() sets up the Content-Type header
73+
// with the boundary, so we need to finalize first, then copy headers
7474
final stream = request.finalize();
75+
// Now headers includes Content-Type with boundary
76+
request.headers.forEach((name, value) {
77+
httpClientRequest!.headers.set(name, value);
78+
});
7579
httpClientRequest.contentLength = request.contentLength;
7680
await httpClientRequest.addStream(stream);
7781
} else if (request is http.StreamedRequest) {
82+
// Set headers
83+
request.headers.forEach((name, value) {
84+
httpClientRequest!.headers.add(name, value);
85+
});
7886
await httpClientRequest.addStream(request.finalize());
87+
} else {
88+
// Generic BaseRequest - set headers
89+
request.headers.forEach((name, value) {
90+
httpClientRequest!.headers.add(name, value);
91+
});
7992
}
8093

8194
// Check cancellation before sending

0 commit comments

Comments
 (0)