Files
async-http-client/Tests/AsyncHTTPClientTests/HTTPClientInternalTests.swift
T

197 lines
8.1 KiB
Swift

//===----------------------------------------------------------------------===//
//
// This source file is part of the AsyncHTTPClient open source project
//
// Copyright (c) 2018-2019 Swift Server Working Group and the AsyncHTTPClient project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
@testable import AsyncHTTPClient
import NIO
import NIOConcurrencyHelpers
import NIOHTTP1
import XCTest
class HTTPClientInternalTests: XCTestCase {
typealias Request = HTTPClient.Request
typealias Task = HTTPClient.Task
func testHTTPPartsHandler() throws {
let channel = EmbeddedChannel()
let recorder = RecordingHandler<HTTPClientResponsePart, HTTPClientRequestPart>()
let task = Task<Void>(eventLoop: channel.eventLoop)
try channel.pipeline.addHandler(recorder).wait()
try channel.pipeline.addHandler(TaskHandler(task: task, delegate: TestHTTPDelegate(), redirectHandler: nil, ignoreUncleanSSLShutdown: false)).wait()
var request = try Request(url: "http://localhost/get")
request.headers.add(name: "X-Test-Header", value: "X-Test-Value")
request.body = .string("1234")
XCTAssertNoThrow(try channel.writeOutbound(request))
XCTAssertEqual(3, recorder.writes.count)
var head = HTTPRequestHead(version: HTTPVersion(major: 1, minor: 1), method: .GET, uri: "/get")
head.headers.add(name: "X-Test-Header", value: "X-Test-Value")
head.headers.add(name: "Host", value: "localhost")
head.headers.add(name: "Content-Length", value: "4")
head.headers.add(name: "Connection", value: "close")
XCTAssertEqual(HTTPClientRequestPart.head(head), recorder.writes[0])
let buffer = ByteBuffer.of(string: "1234")
XCTAssertEqual(HTTPClientRequestPart.body(.byteBuffer(buffer)), recorder.writes[1])
XCTAssertNoThrow(try channel.writeInbound(HTTPClientResponsePart.head(HTTPResponseHead(version: HTTPVersion(major: 1, minor: 1), status: HTTPResponseStatus.ok))))
XCTAssertNoThrow(try channel.writeInbound(HTTPClientResponsePart.end(nil)))
}
func testHTTPPartsHandlerMultiBody() throws {
let channel = EmbeddedChannel()
let delegate = TestHTTPDelegate()
let task = Task<Void>(eventLoop: channel.eventLoop)
let handler = TaskHandler(task: task, delegate: delegate, redirectHandler: nil, ignoreUncleanSSLShutdown: false)
try channel.pipeline.addHandler(handler).wait()
handler.state = .sent
var body = channel.allocator.buffer(capacity: 4)
body.writeStaticString("1234")
XCTAssertNoThrow(try channel.writeInbound(HTTPClientResponsePart.head(HTTPResponseHead(version: HTTPVersion(major: 1, minor: 1), status: HTTPResponseStatus.ok))))
XCTAssertNoThrow(try channel.writeInbound(HTTPClientResponsePart.body(body)))
XCTAssertNoThrow(try channel.writeInbound(HTTPClientResponsePart.body(body)))
switch delegate.state {
case .body(_, let body):
XCTAssertEqual(8, body.readableBytes)
default:
XCTFail("Expecting .body")
}
}
func testProxyStreaming() throws {
let httpBin = HTTPBin()
let httpClient = HTTPClient(eventLoopGroupProvider: .createNew)
defer {
XCTAssertNoThrow(try httpClient.syncShutdown())
XCTAssertNoThrow(try httpBin.shutdown())
}
let body: HTTPClient.Body = .stream(length: 50) { writer in
do {
var request = try Request(url: "http://localhost:\(httpBin.port)/events/10/1")
request.headers.add(name: "Accept", value: "text/event-stream")
let delegate = HTTPClientCopyingDelegate { part in
writer.write(.byteBuffer(part))
}
return httpClient.execute(request: request, delegate: delegate).futureResult
} catch {
return httpClient.eventLoopGroup.next().makeFailedFuture(error)
}
}
let upload = try! httpClient.post(url: "http://localhost:\(httpBin.port)/post", body: body).wait()
let bytes = upload.body.flatMap { $0.getData(at: 0, length: $0.readableBytes) }
let data = try! JSONDecoder().decode(RequestInfo.self, from: bytes!)
XCTAssertEqual(.ok, upload.status)
XCTAssertEqual("id: 0id: 1id: 2id: 3id: 4id: 5id: 6id: 7id: 8id: 9", data.data)
}
func testProxyStreamingFailure() throws {
let httpBin = HTTPBin()
let httpClient = HTTPClient(eventLoopGroupProvider: .createNew)
defer {
XCTAssertNoThrow(try httpClient.syncShutdown())
XCTAssertNoThrow(try httpBin.shutdown())
}
var body: HTTPClient.Body = .stream(length: 50) { _ in
httpClient.eventLoopGroup.next().makeFailedFuture(HTTPClientError.invalidProxyResponse)
}
XCTAssertThrowsError(try httpClient.post(url: "http://localhost:\(httpBin.port)/post", body: body).wait())
body = .stream(length: 50) { _ in
do {
var request = try Request(url: "http://localhost:\(httpBin.port)/events/10/1")
request.headers.add(name: "Accept", value: "text/event-stream")
let delegate = HTTPClientCopyingDelegate { _ in
httpClient.eventLoopGroup.next().makeFailedFuture(HTTPClientError.invalidProxyResponse)
}
return httpClient.execute(request: request, delegate: delegate).futureResult
} catch {
return httpClient.eventLoopGroup.next().makeFailedFuture(error)
}
}
XCTAssertThrowsError(try httpClient.post(url: "http://localhost:\(httpBin.port)/post", body: body).wait())
}
func testUploadStreamingBackpressure() throws {
class BackpressureTestDelegate: HTTPClientResponseDelegate {
typealias Response = Void
var _reads = 0
let lock: Lock
let promise: EventLoopPromise<Void>
init(promise: EventLoopPromise<Void>) {
self.lock = Lock()
self.promise = promise
}
var reads: Int {
return self.lock.withLock {
self._reads
}
}
func didReceiveBodyPart(task: HTTPClient.Task<Response>, _ buffer: ByteBuffer) -> EventLoopFuture<Void> {
self.lock.withLockVoid {
self._reads += 1
}
return self.promise.futureResult
}
func didFinishRequest(task: HTTPClient.Task<Response>) throws {}
}
let httpClient = HTTPClient(eventLoopGroupProvider: .createNew)
let promise: EventLoopPromise<Channel> = httpClient.eventLoopGroup.next().makePromise()
let httpBin = HTTPBin(channelPromise: promise)
defer {
XCTAssertNoThrow(try httpClient.syncShutdown())
XCTAssertNoThrow(try httpBin.shutdown())
}
let request = try Request(url: "http://localhost:\(httpBin.port)/custom")
let delegate = BackpressureTestDelegate(promise: httpClient.eventLoopGroup.next().makePromise())
let future = httpClient.execute(request: request, delegate: delegate).futureResult
let channel = try promise.futureResult.wait()
// Send 3 parts, but only one should be received until the future is complete
let buffer = ByteBuffer.of(string: "1234")
try channel.writeAndFlush(HTTPServerResponsePart.body(.byteBuffer(buffer))).wait()
try channel.writeAndFlush(HTTPServerResponsePart.body(.byteBuffer(buffer))).wait()
try channel.writeAndFlush(HTTPServerResponsePart.body(.byteBuffer(buffer))).wait()
XCTAssertEqual(delegate.reads, 1)
delegate.promise.succeed(())
try channel.writeAndFlush(HTTPServerResponsePart.end(nil)).wait()
try future.wait()
XCTAssertEqual(delegate.reads, 3)
}
}