Skip to content

Commit

Permalink
Reuse detected keepalive of http_parser as long as possible
Browse files Browse the repository at this point in the history
Motivation:

To detect if keepalive is used we need to search the headers (if HTTP/1.1 is used). This may be expensive depending how many headers are present. http_parser itself detects already if keep alive should be used and so we can re-use this as long as the user did not modify the headers.

Modifications:

Reuse keepalive parsed by http_parser as long as the headers were not modified.

Result:

Less overhead as long as the headers are not modified.
  • Loading branch information
normanmaurer committed Apr 10, 2018
1 parent d94ab56 commit 3e6ecc0
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 6 deletions.
2 changes: 1 addition & 1 deletion Sources/NIOHTTP1/HTTPDecoder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ public class HTTPDecoder<HTTPMessageT>: ByteToMessageDecoder, AnyHTTPDecoder {
private func newRequestHead(_ parser: UnsafeMutablePointer<http_parser>!) -> HTTPRequestHead {
let method = HTTPMethod.from(httpParserMethod: http_method(rawValue: parser.pointee.method))
let version = HTTPVersion(major: parser.pointee.http_major, minor: parser.pointee.http_minor)
let request = HTTPRequestHead(version: version, method: method, rawURI: state.currentURI!, headers: HTTPHeaders(buffer: cumulationBuffer!, headers: state.currentHeaders))
let request = HTTPRequestHead(version: version, method: method, rawURI: state.currentURI!, headers: HTTPHeaders(buffer: cumulationBuffer!, headers: state.currentHeaders), keepAlive: c_nio_http_should_keep_alive(parser) != 0)
self.state.currentHeaders.removeAll(keepingCapacity: true)
return request
}
Expand Down
38 changes: 33 additions & 5 deletions Sources/NIOHTTP1/HTTPTypes.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,17 @@ public struct HTTPRequestHead: Equatable {
/// The header fields for this HTTP request.
public var headers: HTTPHeaders

/// The keepAlive that may be parsed by http_parser before. This is only safe to be used as long as
/// the user did not modify the headers yet.
private let keepAlive: Bool?

/// Create a `HTTPRequestHead`
///
/// - Parameter version: The version for this HTTP request.
/// - Parameter method: The HTTP method for this request.
/// - Parameter uri: The URI used on this request.
public init(version: HTTPVersion, method: HTTPMethod, uri: String) {
self.init(version: version, method: method, rawURI: .string(uri), headers: HTTPHeaders())
self.init(version: version, method: method, rawURI: .string(uri), headers: HTTPHeaders(), keepAlive: nil)
}

/// Create a `HTTPRequestHead`
Expand All @@ -53,11 +57,13 @@ public struct HTTPRequestHead: Equatable {
/// - Parameter method: The HTTP method for this request.
/// - Parameter rawURI: The URI used on this request.
/// - Parameter headers: The headers for this HTTP request.
init(version: HTTPVersion, method: HTTPMethod, rawURI: URI, headers: HTTPHeaders) {
/// - Parameter keepAlive: Not nil if parsed previously.
init(version: HTTPVersion, method: HTTPMethod, rawURI: URI, headers: HTTPHeaders, keepAlive: Bool?) {
self.version = version
self.method = method
self.rawURI = rawURI
self.headers = headers
self.keepAlive = keepAlive
}

public static func ==(lhs: HTTPRequestHead, rhs: HTTPRequestHead) -> Bool {
Expand Down Expand Up @@ -125,6 +131,9 @@ extension HTTPRequestHead {
/// Whether this HTTP request is a keep-alive request: that is, whether the
/// connection should remain open after the request is complete.
public var isKeepAlive: Bool {
if headers.state == .notModified, let keepAlive = self.keepAlive {
return keepAlive
}
guard let connection = headers["connection"].first?.lowercased() else {
// HTTP 1.1 use keep-alive by default if not otherwise told.
return version.major == 1 && version.minor == 1
Expand Down Expand Up @@ -202,6 +211,11 @@ private extension UInt8 {
}
}

fileprivate enum HTTPHeadersModificationState {
case modifiedNonContinuous
case modified
case notModified
}

/// A representation of a block of HTTP header fields.
///
Expand All @@ -220,7 +234,20 @@ public struct HTTPHeaders: CustomStringConvertible {
// Because we use CoW implementations HTTPHeaders is also CoW
private var buffer: ByteBuffer
private var headers: [HTTPHeader]
private var continuous: Bool = true

fileprivate var state: HTTPHeadersModificationState = .notModified

/// Update the current state of the headers if needed.
private mutating func updateStateIfNeeded(newState: HTTPHeadersModificationState) {
switch newState {
case .modified where self.state == .notModified, .modifiedNonContinuous:
self.state = newState
case .notModified:
fatalError("Should never be called with \(newState)")
default:
break
}
}

/// Returns the `String` for the given `HTTPHeaderIndex`.
///
Expand Down Expand Up @@ -288,6 +315,7 @@ public struct HTTPHeaders: CustomStringConvertible {
let valueLength = self.buffer.write(string: value)!
self.headers.append(HTTPHeader(name: HTTPHeaderIndex(start: nameStart, length: nameLength), value: HTTPHeaderIndex(start: valueStart, length: valueLength)))
self.buffer.write(staticString: crlf)
self.updateStateIfNeeded(newState: .modified)
}

/// Add a header name/value pair to the block, replacing any previous values for the
Expand Down Expand Up @@ -335,7 +363,7 @@ public struct HTTPHeaders: CustomStringConvertible {
array.forEach {
self.headers.remove(at: $0)
}
self.continuous = false
self.updateStateIfNeeded(newState: .modifiedNonContinuous)
}

/// Retrieve all of the values for a give header field name from the block.
Expand Down Expand Up @@ -388,7 +416,7 @@ public struct HTTPHeaders: CustomStringConvertible {
/// - Parameter buffer: A buffer to write the serialized bytes into. Will increment
/// the writer index of this buffer.
func write(into: inout ByteBuffer) {
if self.continuous {
if self.state != .modifiedNonContinuous {
// Declare an extra variable so we not affect the readerIndex of the buffer itself.
var buf = self.buffer
into.write(buffer: &buf)
Expand Down

0 comments on commit 3e6ecc0

Please sign in to comment.