@@ -17,6 +17,7 @@ import OpenAPIRuntime
17
17
import HTTPTypes
18
18
import Vapor
19
19
import NIOFoundationCompat
20
+ import Atomics
20
21
21
22
public final class VaporTransport {
22
23
@@ -58,6 +59,7 @@ enum VaporTransportError: Error {
58
59
case unsupportedHTTPMethod( String )
59
60
case duplicatePathParameter( [ String ] )
60
61
case missingRequiredPathParameter( String )
62
+ case multipleBodyIteration
61
63
}
62
64
63
65
extension [ Vapor . PathComponent ] {
@@ -143,21 +145,26 @@ extension Vapor.Response.Body {
143
145
self = . empty
144
146
return
145
147
}
148
+ /// Used to guard the body from being iterated multiple times.
149
+ /// https://github.com/vapor/vapor/issues/3002
150
+ let iterated = ManagedAtomic ( false )
146
151
let stream : @Sendable ( any Vapor . BodyStreamWriter ) -> ( ) = { writer in
152
+ guard iterated. compareExchange (
153
+ expected: false ,
154
+ desired: true ,
155
+ ordering: . relaxed
156
+ ) . exchanged else {
157
+ _ = writer. write ( . error( VaporTransportError . multipleBodyIteration) )
158
+ return
159
+ }
147
160
_ = writer. eventLoop. makeFutureWithTask {
148
161
do {
149
162
for try await chunk in body {
150
- try await writer. eventLoop. flatSubmit {
151
- writer. write ( . buffer( ByteBuffer ( bytes: chunk) ) )
152
- } . get ( )
163
+ try await writer. write ( . buffer( ByteBuffer ( bytes: chunk) ) ) . get ( )
153
164
}
154
- try await writer. eventLoop. flatSubmit {
155
- writer. write ( . end)
156
- } . get ( )
165
+ try await writer. write ( . end) . get ( )
157
166
} catch {
158
- try await writer. eventLoop. flatSubmit {
159
- writer. write ( . error( error) )
160
- } . get ( )
167
+ try await writer. write ( . error( error) ) . get ( )
161
168
}
162
169
}
163
170
}
0 commit comments