@@ -85,8 +85,14 @@ public struct MultipartResponseParsingInterceptor: ApolloInterceptor {
85
85
return
86
86
}
87
87
88
- for chunk in dataString. components ( separatedBy: " -- \( boundary) " ) {
89
- if chunk. isEmpty || chunk. isBoundaryMarker { continue }
88
+ // Parsing Notes:
89
+ //
90
+ // Multipart messages arriving here may consist of more than one chunk, but they are always
91
+ // expected to be complete chunks. Downstream protocol specification parsers are only built
92
+ // to handle the protocol specific message formats, i.e.: data between the multipart delimiter.
93
+ let boundaryDelimiter = Self . boundaryDelimiter ( with: boundary)
94
+ for chunk in dataString. components ( separatedBy: boundaryDelimiter) {
95
+ if chunk. isEmpty || chunk. isDashBoundaryPrefix || chunk. isMultipartNewLine { continue }
90
96
91
97
switch parser. parse ( chunk) {
92
98
case let . success( data) :
@@ -119,6 +125,8 @@ public struct MultipartResponseParsingInterceptor: ApolloInterceptor {
119
125
}
120
126
}
121
127
128
+ // MARK: Specification Parser Protocol
129
+
122
130
/// A protocol that multipart response parsers must conform to in order to be added to the list of
123
131
/// available response specification parsers.
124
132
protocol MultipartResponseSpecificationParser {
@@ -140,6 +148,38 @@ extension MultipartResponseSpecificationParser {
140
148
static var dataLineSeparator : StaticString { " \r \n \r \n " }
141
149
}
142
150
143
- fileprivate extension String {
144
- var isBoundaryMarker : Bool { self == " -- " }
151
+ // MARK: Helpers
152
+
153
+ extension MultipartResponseParsingInterceptor {
154
+ static func boundaryDelimiter( with boundary: String ) -> String {
155
+ " \r \n -- \( boundary) "
156
+ }
157
+
158
+ static func closeBoundaryDelimiter( with boundary: String ) -> String {
159
+ boundaryDelimiter ( with: boundary) + " -- "
160
+ }
161
+ }
162
+
163
+ extension String {
164
+ fileprivate var isDashBoundaryPrefix : Bool { self == " -- " }
165
+ fileprivate var isMultipartNewLine : Bool { self == " \r \n " }
166
+
167
+ /// Returns the range of a complete multipart chunk.
168
+ func multipartRange( using boundary: String ) -> String . Index ? {
169
+ // The end boundary marker indicates that no further chunks will follow so if this delimiter
170
+ // if found then include the delimiter in the index. Search for this first.
171
+ let closeBoundaryDelimiter = MultipartResponseParsingInterceptor . closeBoundaryDelimiter ( with: boundary)
172
+ if let endIndex = range ( of: closeBoundaryDelimiter, options: . backwards) ? . upperBound {
173
+ return endIndex
174
+ }
175
+
176
+ // A chunk boundary indicates there may still be more chunks to follow so the index need not
177
+ // include the chunk boundary in the index.
178
+ let boundaryDelimiter = MultipartResponseParsingInterceptor . boundaryDelimiter ( with: boundary)
179
+ if let chunkIndex = range ( of: boundaryDelimiter, options: . backwards) ? . lowerBound {
180
+ return chunkIndex
181
+ }
182
+
183
+ return nil
184
+ }
145
185
}
0 commit comments