From eea1230d0d5b1534534b5e274d22861b72622660 Mon Sep 17 00:00:00 2001 From: gh-action-runner Date: Wed, 8 Jan 2025 22:26:36 +0000 Subject: [PATCH] Squashed 'apollo-ios/' changes from 39bf0726..c17bddf2 c17bddf2 fix: Multipart chunk content type (apollographql/apollo-ios-dev#572) a94ebd7a Update ROADMAP.md git-subtree-dir: apollo-ios git-subtree-split: c17bddf2f9ec626c70890fe203c382bd470749ec --- ROADMAP.md | 10 +--------- .../Apollo/MultipartResponseDeferParser.swift | 18 ++++++------------ .../MultipartResponseParsingInterceptor.swift | 12 ++++++++++++ .../MultipartResponseSubscriptionParser.swift | 16 ++++++---------- 4 files changed, 25 insertions(+), 31 deletions(-) diff --git a/ROADMAP.md b/ROADMAP.md index ad94dc4e1..1931b882b 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -1,6 +1,6 @@ # 🔮 Apollo iOS Roadmap -**Last updated: 2024-12-10** +**Last updated: 2025-01-07** For up to date release notes, refer to the project's [Changelog](https://github.com/apollographql/apollo-ios/blob/main/CHANGELOG.md). @@ -56,14 +56,6 @@ _Status: In design phase. Current RFC for design is available [here](https://git - ✅ [`ExistentialAny` upcoming feature](https://github.com/apollographql/apollo-ios/issues/3205) - (in progress) [`Sendable` types and `async/await` APIs](https://github.com/apollographql/apollo-ios/issues/3291) -### `@oneOf` Input Object Support - -_Status: Code complete. Will be included in the next minor version release (1.16.0)._ - -For more information on this feature, see the [RFC](https://github.com/graphql/graphql-spec/pull/825) for its addition to the GraphQL specification. - -Support for this feature is considered experimental and subject to change until the RFC is approved and merged into the GraphQL specification. - ### [Reduce generated schema types](https://github.com/apollographql/apollo-ios/milestone/71) _Status: API Design in progress_ diff --git a/Sources/Apollo/MultipartResponseDeferParser.swift b/Sources/Apollo/MultipartResponseDeferParser.swift index e0be7df81..e959be77a 100644 --- a/Sources/Apollo/MultipartResponseDeferParser.swift +++ b/Sources/Apollo/MultipartResponseDeferParser.swift @@ -23,7 +23,7 @@ struct MultipartResponseDeferParser: MultipartResponseSpecificationParser { } private enum DataLine { - case contentHeader(type: String) + case contentHeader(directives: [String]) case json(object: JSONObject) case unknown @@ -32,14 +32,8 @@ struct MultipartResponseDeferParser: MultipartResponseSpecificationParser { } private static func parse(_ dataLine: String) -> DataLine { - var contentTypeHeader: StaticString { "content-type:" } - - if dataLine.starts(with: contentTypeHeader.description) { - let contentType = (dataLine - .components(separatedBy: ":").last ?? dataLine - ).trimmingCharacters(in: .whitespaces) - - return .contentHeader(type: contentType) + if let directives = dataLine.parseContentTypeDirectives() { + return .contentHeader(directives: directives) } if @@ -58,9 +52,9 @@ struct MultipartResponseDeferParser: MultipartResponseSpecificationParser { static func parse(_ chunk: String) -> Result { for dataLine in chunk.components(separatedBy: Self.dataLineSeparator.description) { switch DataLine(dataLine.trimmingCharacters(in: .newlines)) { - case let .contentHeader(type): - guard type == "application/json" else { - return .failure(ParsingError.unsupportedContentType(type: type)) + case let .contentHeader(directives): + guard directives.contains("application/json") else { + return .failure(ParsingError.unsupportedContentType(type: directives.joined(separator: ";"))) } case let .json(object): diff --git a/Sources/Apollo/MultipartResponseParsingInterceptor.swift b/Sources/Apollo/MultipartResponseParsingInterceptor.swift index e7d38fe13..5975058fe 100644 --- a/Sources/Apollo/MultipartResponseParsingInterceptor.swift +++ b/Sources/Apollo/MultipartResponseParsingInterceptor.swift @@ -182,4 +182,16 @@ extension String { return nil } + + func parseContentTypeDirectives() -> [String]? { + var lowercasedContentTypeHeader: StaticString { "content-type:" } + + guard lowercased().starts(with: lowercasedContentTypeHeader.description) else { + return nil + } + + return dropFirst(lowercasedContentTypeHeader.description.count) + .components(separatedBy: ";") + .map({ $0.trimmingCharacters(in: .whitespaces) }) + } } diff --git a/Sources/Apollo/MultipartResponseSubscriptionParser.swift b/Sources/Apollo/MultipartResponseSubscriptionParser.swift index d16b14b27..72985d1c3 100644 --- a/Sources/Apollo/MultipartResponseSubscriptionParser.swift +++ b/Sources/Apollo/MultipartResponseSubscriptionParser.swift @@ -30,7 +30,7 @@ struct MultipartResponseSubscriptionParser: MultipartResponseSpecificationParser private enum DataLine { case heartbeat - case contentHeader(type: String) + case contentHeader(directives: [String]) case json(object: JSONObject) case unknown @@ -46,12 +46,8 @@ struct MultipartResponseSubscriptionParser: MultipartResponseSpecificationParser return .heartbeat } - if dataLine.lowercased().starts(with: contentTypeHeader.description) { - let contentType = (dataLine - .components(separatedBy: ":").last ?? dataLine - ).trimmingCharacters(in: .whitespaces) - - return .contentHeader(type: contentType) + if let directives = dataLine.parseContentTypeDirectives() { + return .contentHeader(directives: directives) } if @@ -74,9 +70,9 @@ struct MultipartResponseSubscriptionParser: MultipartResponseSpecificationParser // Periodically sent by the router - noop break - case let .contentHeader(type): - guard type == "application/json" else { - return .failure(ParsingError.unsupportedContentType(type: type)) + case let .contentHeader(directives): + guard directives.contains("application/json") else { + return .failure(ParsingError.unsupportedContentType(type: directives.joined(separator: ";"))) } case let .json(object):