@@ -33,8 +33,8 @@ final class GRPCServerPipelineConfigurator: ChannelInboundHandler, RemovableChan
33
33
/// The server configuration.
34
34
private let configuration : Server . Configuration
35
35
36
- /// Reads which we're holding on to before the pipeline is configured .
37
- private var bufferedReads = CircularBuffer < NIOAny > ( )
36
+ /// A buffer containing the buffered bytes .
37
+ private var buffer : ByteBuffer ?
38
38
39
39
/// The current state.
40
40
private var state : State
@@ -212,13 +212,17 @@ final class GRPCServerPipelineConfigurator: ChannelInboundHandler, RemovableChan
212
212
buffer: ByteBuffer ,
213
213
context: ChannelHandlerContext
214
214
) {
215
- if HTTPVersionParser . prefixedWithHTTP2ConnectionPreface ( buffer) {
215
+ switch HTTPVersionParser . determineHTTPVersion ( buffer) {
216
+ case . http2:
216
217
self . configureHTTP2 ( context: context)
217
- } else if HTTPVersionParser . prefixedWithHTTP1RequestLine ( buffer ) {
218
+ case . http1 :
218
219
self . configureHTTP1 ( context: context)
219
- } else {
220
+ case . unknown:
221
+ // Neither H2 nor H1 or the length limit has been exceeded.
220
222
self . configuration. logger. error ( " Unable to determine http version, closing " )
221
223
context. close ( mode: . all, promise: nil )
224
+ case . notEnoughBytes:
225
+ ( ) // Try again with more bytes.
222
226
}
223
227
}
224
228
@@ -268,13 +272,9 @@ final class GRPCServerPipelineConfigurator: ChannelInboundHandler, RemovableChan
268
272
269
273
/// Try to parse the buffered data to determine whether or not HTTP/2 or HTTP/1 should be used.
270
274
private func tryParsingBufferedData( context: ChannelHandlerContext ) {
271
- guard let first = self . bufferedReads. first else {
272
- // No data buffered yet. We'll try when we read.
273
- return
275
+ if let buffer = self . buffer {
276
+ self . determineHTTPVersionAndConfigurePipeline ( buffer: buffer, context: context)
274
277
}
275
-
276
- let buffer = self . unwrapInboundIn ( first)
277
- self . determineHTTPVersionAndConfigurePipeline ( buffer: buffer, context: context)
278
278
}
279
279
280
280
// MARK: - Channel Handler
@@ -312,7 +312,8 @@ final class GRPCServerPipelineConfigurator: ChannelInboundHandler, RemovableChan
312
312
}
313
313
314
314
internal func channelRead( context: ChannelHandlerContext , data: NIOAny ) {
315
- self . bufferedReads. append ( data)
315
+ var buffer = self . unwrapInboundIn ( data)
316
+ self . buffer. setOrWriteBuffer ( & buffer)
316
317
317
318
switch self . state {
318
319
case . notConfigured( alpn: . notExpected) ,
@@ -335,8 +336,9 @@ final class GRPCServerPipelineConfigurator: ChannelInboundHandler, RemovableChan
335
336
removalToken: ChannelHandlerContext . RemovalToken
336
337
) {
337
338
// Forward any buffered reads.
338
- while let read = self . bufferedReads. popFirst ( ) {
339
- context. fireChannelRead ( read)
339
+ if let buffer = self . buffer {
340
+ self . buffer = nil
341
+ context. fireChannelRead ( self . wrapInboundOut ( buffer) )
340
342
}
341
343
context. leavePipeline ( removalToken: removalToken)
342
344
}
@@ -375,16 +377,64 @@ struct HTTPVersionParser {
375
377
376
378
/// Determines whether the bytes in the `ByteBuffer` are prefixed with the HTTP/2 client
377
379
/// connection preface.
378
- static func prefixedWithHTTP2ConnectionPreface( _ buffer: ByteBuffer ) -> Bool {
380
+ static func prefixedWithHTTP2ConnectionPreface( _ buffer: ByteBuffer ) -> SubParseResult {
379
381
let view = buffer. readableBytesView
380
382
381
383
guard view. count >= HTTPVersionParser . http2ClientMagic. count else {
382
384
// Not enough bytes.
383
- return false
385
+ return . notEnoughBytes
384
386
}
385
387
386
388
let slice = view [ view. startIndex ..< view. startIndex. advanced ( by: self . http2ClientMagic. count) ]
387
- return slice. elementsEqual ( HTTPVersionParser . http2ClientMagic)
389
+ return slice. elementsEqual ( HTTPVersionParser . http2ClientMagic) ? . accepted : . rejected
390
+ }
391
+
392
+ enum ParseResult : Hashable {
393
+ case http1
394
+ case http2
395
+ case unknown
396
+ case notEnoughBytes
397
+ }
398
+
399
+ enum SubParseResult : Hashable {
400
+ case accepted
401
+ case rejected
402
+ case notEnoughBytes
403
+ }
404
+
405
+ private static let maxLengthToCheck = 1024
406
+
407
+ static func determineHTTPVersion( _ buffer: ByteBuffer ) -> ParseResult {
408
+ switch Self . prefixedWithHTTP2ConnectionPreface ( buffer) {
409
+ case . accepted:
410
+ return . http2
411
+
412
+ case . notEnoughBytes:
413
+ switch Self . prefixedWithHTTP1RequestLine ( buffer) {
414
+ case . accepted:
415
+ // Not enough bytes to check H2, but enough to confirm H1.
416
+ return . http1
417
+ case . notEnoughBytes:
418
+ // Not enough bytes to check H2 or H1.
419
+ return . notEnoughBytes
420
+ case . rejected:
421
+ // Not enough bytes to check H2 and definitely not H1.
422
+ return . notEnoughBytes
423
+ }
424
+
425
+ case . rejected:
426
+ switch Self . prefixedWithHTTP1RequestLine ( buffer) {
427
+ case . accepted:
428
+ // Not H2, but H1 is confirmed.
429
+ return . http1
430
+ case . notEnoughBytes:
431
+ // Not H2, but not enough bytes to reject H1 yet.
432
+ return . notEnoughBytes
433
+ case . rejected:
434
+ // Not H2 or H1.
435
+ return . unknown
436
+ }
437
+ }
388
438
}
389
439
390
440
private static let http1_1 = [
@@ -399,29 +449,59 @@ struct HTTPVersionParser {
399
449
]
400
450
401
451
/// Determines whether the bytes in the `ByteBuffer` are prefixed with an HTTP/1.1 request line.
402
- static func prefixedWithHTTP1RequestLine( _ buffer: ByteBuffer ) -> Bool {
452
+ static func prefixedWithHTTP1RequestLine( _ buffer: ByteBuffer ) -> SubParseResult {
403
453
var readableBytesView = buffer. readableBytesView
404
454
455
+ // We don't need to validate the request line, only determine whether we think it's an HTTP1
456
+ // request line. Another handler will parse it properly.
457
+
405
458
// From RFC 2616 § 5.1:
406
459
// Request-Line = Method SP Request-URI SP HTTP-Version CRLF
407
460
408
- // Read off the Method and Request-URI (and spaces).
409
- guard readableBytesView. trimPrefix ( to: UInt8 ( ascii: " " ) ) != nil ,
410
- readableBytesView. trimPrefix ( to: UInt8 ( ascii: " " ) ) != nil else {
411
- return false
461
+ // Get through the first space.
462
+ guard readableBytesView. dropPrefix ( through: UInt8 ( ascii: " " ) ) != nil else {
463
+ let tooLong = buffer. readableBytes > Self . maxLengthToCheck
464
+ return tooLong ? . rejected : . notEnoughBytes
465
+ }
466
+
467
+ // Get through the second space.
468
+ guard readableBytesView. dropPrefix ( through: UInt8 ( ascii: " " ) ) != nil else {
469
+ let tooLong = buffer. readableBytes > Self . maxLengthToCheck
470
+ return tooLong ? . rejected : . notEnoughBytes
471
+ }
472
+
473
+ // +2 for \r\n
474
+ guard readableBytesView. count >= ( Self . http1_1. count + 2 ) else {
475
+ return . notEnoughBytes
412
476
}
413
477
414
- // Read off the HTTP-Version and CR.
415
- guard let versionView = readableBytesView. trimPrefix ( to: UInt8 ( ascii: " \r " ) ) else {
416
- return false
478
+ guard let version = readableBytesView. dropPrefix ( through: UInt8 ( ascii: " \r " ) ) ,
479
+ readableBytesView. first == UInt8 ( ascii: " \n " ) else {
480
+ // If we didn't drop the prefix OR we did and the next byte wasn't '\n', then we had enough
481
+ // bytes but the '\r\n' wasn't present: reject this as being HTTP1.
482
+ return . rejected
483
+ }
484
+
485
+ return version. elementsEqual ( Self . http1_1) ? . accepted : . rejected
486
+ }
487
+ }
488
+
489
+ extension Collection where Self == Self . SubSequence , Self. Element: Equatable {
490
+ /// Drops the prefix off the collection up to and including the first `separator`
491
+ /// only if that separator appears in the collection.
492
+ ///
493
+ /// Returns the prefix up to but not including the separator if it was found, nil otherwise.
494
+ mutating func dropPrefix( through separator: Element ) -> SubSequence ? {
495
+ if self . isEmpty {
496
+ return nil
417
497
}
418
498
419
- // Check that the LF followed the CR.
420
- guard readableBytesView. first == UInt8 ( ascii: " \n " ) else {
421
- return false
499
+ guard let separatorIndex = self . firstIndex ( of: separator) else {
500
+ return nil
422
501
}
423
502
424
- // Now check the HTTP version.
425
- return versionView. elementsEqual ( HTTPVersionParser . http1_1)
503
+ let prefix = self [ ..< separatorIndex]
504
+ self = self [ self . index ( after: separatorIndex) ... ]
505
+ return prefix
426
506
}
427
507
}
0 commit comments