Skip to content

Commit

Permalink
Fix parsing of responses to HEAD requests with non-zero Content-Length (
Browse files Browse the repository at this point in the history
  • Loading branch information
tunniclm committed Oct 27, 2017
1 parent e04ee59 commit 5fd7b5c
Show file tree
Hide file tree
Showing 4 changed files with 35 additions and 17 deletions.
3 changes: 2 additions & 1 deletion Sources/KituraNet/ClientRequest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -336,7 +336,8 @@ public class ClientRequest {

let invoker = CurlInvoker(handle: handle!, maxRedirects: maxRedirects)
invoker.delegate = self
response = ClientResponse()
let skipBody = (method.uppercased() == "HEAD")
response = ClientResponse(skipBody: skipBody)

var code = invoker.invoke()
guard code == CURLE_OK else {
Expand Down
7 changes: 4 additions & 3 deletions Sources/KituraNet/ClientResponse.swift
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public class ClientResponse {
private static let bufferSize = 2000

/// The http_parser Swift wrapper
private var httpParser = HTTPParser(isRequest: false)
private var httpParser: HTTPParser

/// State of response parsing
private var parserStatus = HTTPParserStatus()
Expand All @@ -71,7 +71,7 @@ public class ClientResponse {
if parserStatus.state == .reset {
reset()
}

let bytes = buffer.bytes.assumingMemoryBound(to: Int8.self) + from
let (numberParsed, _) = httpParser.execute(bytes, length: length)

Expand Down Expand Up @@ -155,7 +155,8 @@ public class ClientResponse {
}

/// Initializes a `ClientResponse` instance
init() {
init(skipBody: Bool = false) {
httpParser = HTTPParser(isRequest: false, skipBody: skipBody)
}

/// The HTTP Status code, as an Int, sent in the response by the remote server.
Expand Down
38 changes: 28 additions & 10 deletions Sources/KituraNet/HTTPParser/HTTPParser.swift
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,10 @@ class HTTPParser {
/// Initializes a HTTPParser instance
///
/// - Parameter isRequest: whether or not this HTTP message is a request
/// - Parameter skipBody: whether parser should skip body content (ie when parsing the response to a HEAD request)
///
/// - Returns: an HTTPParser instance
init(isRequest: Bool) {
init(isRequest: Bool, skipBody: Bool = false) {

self.isRequest = isRequest

Expand Down Expand Up @@ -99,14 +100,21 @@ class HTTPParser {
return 0
}

settings.on_headers_complete = { (parser) -> Int32 in
let method = String(cString: get_method(parser))

let results = getResults(parser)

results?.onHeadersComplete(method: method, versionMajor: (parser?.pointee.http_major)!,
versionMinor: (parser?.pointee.http_minor)!)
return 0
// Callback should return 1 when instructing the C HTTP parser
// to skip body content. This closure is bound to a C function
// pointer and cannot capture values (including self.skipBody)
// so instead we choose which closure to assign outside the
// closure.
if skipBody {
settings.on_headers_complete = { (parser) -> Int32 in
onHeadersComplete(parser)
return 1
}
} else {
settings.on_headers_complete = { (parser) -> Int32 in
onHeadersComplete(parser)
return 0
}
}

settings.on_message_complete = { (parser) -> Int32 in
Expand All @@ -116,7 +124,7 @@ class HTTPParser {

reset()
}

/// Executes the parsing on the byte array
///
/// - Parameter data: pointer to a byte array
Expand Down Expand Up @@ -146,6 +154,16 @@ class HTTPParser {
}
}

fileprivate func onHeadersComplete(_ parser: UnsafeMutableRawPointer?) {
let httpParser = parser?.assumingMemoryBound(to: http_parser.self)
let method = String(cString: get_method(httpParser!))

let results = getResults(httpParser)

results?.onHeadersComplete(method: method, versionMajor: (httpParser?.pointee.http_major)!,
versionMinor: (httpParser?.pointee.http_minor)!)
}

fileprivate func getResults(_ parser: UnsafeMutableRawPointer?) -> ParseResults? {
let httpParser = parser?.assumingMemoryBound(to: http_parser.self)
let httpParserData = httpParser?.pointee.data
Expand Down
4 changes: 1 addition & 3 deletions Tests/KituraNetTests/ClientE2ETests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -311,9 +311,7 @@ class ClientE2ETests: KituraNetTest {
response.statusCode = .OK
XCTAssertEqual(response.statusCode, .OK, "Set response status code wasn't .OK, it was \(String(describing: response.statusCode))")
response.headers["Content-Type"] = ["text/plain"]
if request.method.lowercased() != "head" {
response.headers["Content-Length"] = ["\(result.characters.count)"]
}
response.headers["Content-Length"] = ["\(result.characters.count)"]

try response.end(text: result)
}
Expand Down

0 comments on commit 5fd7b5c

Please sign in to comment.