@@ -33,8 +33,8 @@ final class GRPCServerPipelineConfigurator: ChannelInboundHandler, RemovableChan
3333 /// The server configuration.
3434 private let configuration : Server . Configuration
3535
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 ?
3838
3939 /// The current state.
4040 private var state : State
@@ -212,13 +212,17 @@ final class GRPCServerPipelineConfigurator: ChannelInboundHandler, RemovableChan
212212 buffer: ByteBuffer ,
213213 context: ChannelHandlerContext
214214 ) {
215- if HTTPVersionParser . prefixedWithHTTP2ConnectionPreface ( buffer) {
215+ switch HTTPVersionParser . determineHTTPVersion ( buffer) {
216+ case . http2:
216217 self . configureHTTP2 ( context: context)
217- } else if HTTPVersionParser . prefixedWithHTTP1RequestLine ( buffer ) {
218+ case . http1 :
218219 self . configureHTTP1 ( context: context)
219- } else {
220+ case . unknown:
221+ // Neither H2 nor H1 or the length limit has been exceeded.
220222 self . configuration. logger. error ( " Unable to determine http version, closing " )
221223 context. close ( mode: . all, promise: nil )
224+ case . notEnoughBytes:
225+ ( ) // Try again with more bytes.
222226 }
223227 }
224228
@@ -268,13 +272,9 @@ final class GRPCServerPipelineConfigurator: ChannelInboundHandler, RemovableChan
268272
269273 /// Try to parse the buffered data to determine whether or not HTTP/2 or HTTP/1 should be used.
270274 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)
274277 }
275-
276- let buffer = self . unwrapInboundIn ( first)
277- self . determineHTTPVersionAndConfigurePipeline ( buffer: buffer, context: context)
278278 }
279279
280280 // MARK: - Channel Handler
@@ -312,7 +312,8 @@ final class GRPCServerPipelineConfigurator: ChannelInboundHandler, RemovableChan
312312 }
313313
314314 internal func channelRead( context: ChannelHandlerContext , data: NIOAny ) {
315- self . bufferedReads. append ( data)
315+ var buffer = self . unwrapInboundIn ( data)
316+ self . buffer. setOrWriteBuffer ( & buffer)
316317
317318 switch self . state {
318319 case . notConfigured( alpn: . notExpected) ,
@@ -335,8 +336,9 @@ final class GRPCServerPipelineConfigurator: ChannelInboundHandler, RemovableChan
335336 removalToken: ChannelHandlerContext . RemovalToken
336337 ) {
337338 // 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) )
340342 }
341343 context. leavePipeline ( removalToken: removalToken)
342344 }
@@ -375,16 +377,64 @@ struct HTTPVersionParser {
375377
376378 /// Determines whether the bytes in the `ByteBuffer` are prefixed with the HTTP/2 client
377379 /// connection preface.
378- static func prefixedWithHTTP2ConnectionPreface( _ buffer: ByteBuffer ) -> Bool {
380+ static func prefixedWithHTTP2ConnectionPreface( _ buffer: ByteBuffer ) -> SubParseResult {
379381 let view = buffer. readableBytesView
380382
381383 guard view. count >= HTTPVersionParser . http2ClientMagic. count else {
382384 // Not enough bytes.
383- return false
385+ return . notEnoughBytes
384386 }
385387
386388 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+ }
388438 }
389439
390440 private static let http1_1 = [
@@ -399,29 +449,59 @@ struct HTTPVersionParser {
399449 ]
400450
401451 /// 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 {
403453 var readableBytesView = buffer. readableBytesView
404454
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+
405458 // From RFC 2616 § 5.1:
406459 // Request-Line = Method SP Request-URI SP HTTP-Version CRLF
407460
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
412476 }
413477
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
417497 }
418498
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
422501 }
423502
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
426506 }
427507}
0 commit comments