@@ -293,16 +293,25 @@ private class BetterHTTPParser {
293
293
// does not meet the requirement of RFC 7230. This is an outstanding http_parser issue:
294
294
// https://github.com/nodejs/http-parser/issues/251. As a result, we check for these status
295
295
// codes and override http_parser's handling as well.
296
- guard let method = self . requestHeads. popFirst ( ) ? . method else {
296
+ guard ! self . requestHeads. isEmpty else {
297
297
self . richerError = NIOHTTPDecoderError . unsolicitedResponse
298
298
return . error( HPE_UNKNOWN)
299
299
}
300
-
301
- if method == . HEAD || method == . CONNECT {
302
- skipBody = true
303
- } else if statusCode / 100 == 1 || // 1XX codes
304
- statusCode == 204 || statusCode == 304 {
300
+
301
+ if ( HTTPResponseStatus . continue. code <= statusCode && statusCode < HTTPResponseStatus . ok. code)
302
+ && statusCode != HTTPResponseStatus . switchingProtocols. code {
303
+ // if the response status is in the range of 100..<200 but not 101 we don't want to
304
+ // pop the request method. The actual request head is expected with the next HTTP
305
+ // head.
305
306
skipBody = true
307
+ } else {
308
+ let method = self . requestHeads. removeFirst ( ) . method
309
+ if method == . HEAD || method == . CONNECT {
310
+ skipBody = true
311
+ } else if statusCode / 100 == 1 || // 1XX codes
312
+ statusCode == 204 || statusCode == 304 {
313
+ skipBody = true
314
+ }
306
315
}
307
316
}
308
317
@@ -473,15 +482,36 @@ public final class HTTPDecoder<In, Out>: ByteToMessageDecoder, HTTPDecoderDelega
473
482
// the actual state
474
483
private let parser : BetterHTTPParser
475
484
private let leftOverBytesStrategy : RemoveAfterUpgradeStrategy
485
+ private let informalResponseStrategy : InformalResponseStrategy
476
486
private let kind : HTTPDecoderKind
477
487
private var stopParsing = false // set on upgrade or HTTP version error
488
+ private var lastHeaderWasInformal = false
478
489
479
490
/// Creates a new instance of `HTTPDecoder`.
480
491
///
481
492
/// - parameters:
482
493
/// - leftOverBytesStrategy: The strategy to use when removing the decoder from the pipeline and an upgrade was,
483
494
/// detected. Note that this does not affect what happens on EOF.
484
- public init ( leftOverBytesStrategy: RemoveAfterUpgradeStrategy = . dropBytes) {
495
+ public convenience init ( leftOverBytesStrategy: RemoveAfterUpgradeStrategy = . dropBytes) {
496
+ self . init ( leftOverBytesStrategy: leftOverBytesStrategy, informalResponseStrategy: . drop)
497
+ }
498
+
499
+ /// Strategy to use when a HTTPDecoder receives an informal HTTP response (1xx except 101)
500
+ public enum InformalResponseStrategy {
501
+ /// Drop the informal response and only forward the "real" response
502
+ case drop
503
+ /// Forward the informal response and then forward the "real" response
504
+ case forward
505
+ }
506
+
507
+ /// Creates a new instance of `HTTPDecoder`.
508
+ ///
509
+ /// - parameters:
510
+ /// - leftOverBytesStrategy: The strategy to use when removing the decoder from the pipeline and an upgrade was,
511
+ /// detected. Note that this does not affect what happens on EOF.
512
+ /// - supportInformalResponses: Should informal responses (like http status 100) be forwarded or dropped. Default is `.drop`
513
+ /// This property is only respected when decoding responses.
514
+ public init ( leftOverBytesStrategy: RemoveAfterUpgradeStrategy = . dropBytes, informalResponseStrategy: InformalResponseStrategy = . drop) {
485
515
self . headers. reserveCapacity ( 16 )
486
516
if In . self == HTTPServerRequestPart . self {
487
517
self . kind = . request
@@ -492,6 +522,7 @@ public final class HTTPDecoder<In, Out>: ByteToMessageDecoder, HTTPDecoderDelega
492
522
}
493
523
self . parser = BetterHTTPParser ( kind: kind)
494
524
self . leftOverBytesStrategy = leftOverBytesStrategy
525
+ self . informalResponseStrategy = informalResponseStrategy
495
526
}
496
527
497
528
func didReceiveBody( _ bytes: UnsafeRawBufferPointer ) {
@@ -545,7 +576,7 @@ public final class HTTPDecoder<In, Out>: ByteToMessageDecoder, HTTPDecoderDelega
545
576
method: http_method ,
546
577
statusCode: Int ,
547
578
keepAliveState: KeepAliveState ) -> Bool {
548
- let message : NIOAny
579
+ let message : NIOAny ?
549
580
550
581
guard versionMajor == 1 else {
551
582
self . stopParsing = true
@@ -561,7 +592,23 @@ public final class HTTPDecoder<In, Out>: ByteToMessageDecoder, HTTPDecoderDelega
561
592
headers: HTTPHeaders ( self . headers,
562
593
keepAliveState: keepAliveState) )
563
594
message = NIOAny ( HTTPServerRequestPart . head ( reqHead) )
595
+
596
+ case . response where ( 100 ..< 200 ) . contains ( statusCode) && statusCode != 101 :
597
+ self . lastHeaderWasInformal = true
598
+
599
+ switch self . informalResponseStrategy {
600
+ case . forward:
601
+ let resHead : HTTPResponseHead = HTTPResponseHead ( version: . init( major: versionMajor, minor: versionMinor) ,
602
+ status: . init( statusCode: statusCode) ,
603
+ headers: HTTPHeaders ( self . headers,
604
+ keepAliveState: keepAliveState) )
605
+ message = NIOAny ( HTTPClientResponsePart . head ( resHead) )
606
+ case . drop:
607
+ message = nil
608
+ }
609
+
564
610
case . response:
611
+ self . lastHeaderWasInformal = false
565
612
let resHead : HTTPResponseHead = HTTPResponseHead ( version: . init( major: versionMajor, minor: versionMinor) ,
566
613
status: . init( statusCode: statusCode) ,
567
614
headers: HTTPHeaders ( self . headers,
@@ -570,7 +617,9 @@ public final class HTTPDecoder<In, Out>: ByteToMessageDecoder, HTTPDecoderDelega
570
617
}
571
618
self . url = nil
572
619
self . headers. removeAll ( keepingCapacity: true )
573
- self . context!. fireChannelRead ( message)
620
+ if let message = message {
621
+ self . context!. fireChannelRead ( message)
622
+ }
574
623
self . isUpgrade = isUpgrade
575
624
return true
576
625
}
@@ -582,7 +631,9 @@ public final class HTTPDecoder<In, Out>: ByteToMessageDecoder, HTTPDecoderDelega
582
631
case . request:
583
632
self . context!. fireChannelRead ( NIOAny ( HTTPServerRequestPart . end ( trailers. map ( HTTPHeaders . init) ) ) )
584
633
case . response:
585
- self . context!. fireChannelRead ( NIOAny ( HTTPClientResponsePart . end ( trailers. map ( HTTPHeaders . init) ) ) )
634
+ if !self . lastHeaderWasInformal {
635
+ self . context!. fireChannelRead ( NIOAny ( HTTPClientResponsePart . end ( trailers. map ( HTTPHeaders . init) ) ) )
636
+ }
586
637
}
587
638
self . stopParsing = self . isUpgrade!
588
639
self . isUpgrade = nil
0 commit comments