Skip to content

vkill/async-http-client

Repository files navigation

AsyncHTTPClient

This package provides simple HTTP Client library built on top of SwiftNIO.

This library provides the following:

  1. Asynchronous and non-blocking request methods
  2. Simple follow-redirects (cookie headers are dropped)
  3. Streaming body download
  4. TLS support
  5. Cookie parsing (but not storage)

NOTE: You will need Xcode 10.2 or Swift 5.0 to try out AsyncHTTPClient.


Getting Started

Adding the dependency

Add the following entry in your Package.swift to start using HTTPClient:

// it's early days here so we haven't tagged a version yet, but will soon .package(url:"https://github.com/swift-server/async-http-client.git",.branch("master"))

and AsyncHTTPClient dependency to your target:

.target(name:"MyApp", dependencies:["AsyncHTTPClient"]),

Request-Response API

The code snippet below illustrates how to make a simple GET request to a remote server:

import AsyncHTTPClient lethttpClient=HTTPClient(eventLoopGroupProvider:.createNew) httpClient.get(url:"https://swift.org").whenComplete{ result inswitch result {case.failure(let error): // process error case .success(let response):ifletresponse.status==.ok { // handle response }else{ // handle remote error }}}

It is important to close the client instance, for example in a defer statement, after use to cleanly shutdown the underlying NIO EventLoopGroup:

try? httpClient.syncShutdown()

Alternatively, you can provide shared EventLoopGroup:

lethttpClient=HTTPClient(eventLoopGroupProvider:.shared(userProvidedGroup))

In this case shutdown of the client is not neccecary.

Usage guide

Most common HTTP methods are supported out of the box. In case you need to have more control over the method, or you want to add headers or body, use the HTTPRequest struct:

import AsyncHTTPClient lethttpClient=HTTPClient(eventLoopGroupProvider:.createNew)defer{try? httpClient.syncShutdown()}varrequest=tryHTTPClient.HTTPRequest(url:"https://swift.org", method:.POST) request.headers.add(name:"User-Agent", value:"Swift HTTPClient") request.body =.string("some-body") httpClient.execute(request: request).whenComplete{ result inswitch result {case.failure(let error): // process error case .success(let response):ifletresponse.status==.ok { // handle response }else{ // handle remote error }}}

Redirects following

Enable follow-redirects behavior using the client configuration:

lethttpClient=HTTPClient(eventLoopGroupProvider:.createNew, configuration:HTTPClient.Configuration(followRedirects:true))

Timeouts

Timeouts (connect and read) can also be set using the client configuration:

lettimeout=HTTPClient.Timeout(connect:.seconds(1), read:.seconds(1))lethttpClient=HTTPClient(eventLoopGroupProvider:.createNew, configuration:HTTPClient.Configuration(timeout: timeout))

or on a per-request basis:

lettimeout=HTTPClient.Timeout(connect:.seconds(1), read:.seconds(1)) httpClient.execute(request: request, timeout: timeout)

Streaming

When dealing with larger amount of data, it's critical to stream the response body instead of aggregating in-memory. Handling a response stream is done using a delegate protocol. The following example demonstrates how to count the number of bytes in a streaming response body:

classCountingDelegate:HTTPClientResponseDelegate{typealiasResponse=Intvarcount=0func didSendRequestHead(task:HTTPClient.Task<Response>, _ head:HTTPRequestHead){ // this is executed right after request head was sent, called once }func didSendRequestPart(task:HTTPClient.Task<Response>, _ part:IOData){ // this is executed when request body part is sent, could be called zero or more times }func didSendRequest(task:HTTPClient.Task<Response>){ // this is executed when request is fully sent, called once }func didReceiveHead(task:HTTPClient.Task<Response>, _ head:HTTPResponseHead)->EventLoopFuture<Void>{ // this is executed when we receive HTTP Reponse head part of the request (it contains response code and headers), called once // in case backpressure is needed, all reads will be paused until returned future is resolved return task.eventLoop.makeSucceededFuture(())}func didReceivePart(task:HTTPClient.Task<Response>, _ buffer:ByteBuffer)->EventLoopFuture<Void>{ // this is executed when we receive parts of the response body, could be called zero or more times count += buffer.readableBytes // in case backpressure is needed, all reads will be paused until returned future is resolved return task.eventLoop.makeSucceededFuture(())}func didFinishRequest(task:HTTPClient.Task<Response>)throws->Int{ // this is called when the request is fully read, called once // this is where you return a result or throw any errors you require to propagate to the client return count }func didReceiveError(task:HTTPClient.Task<Response>, _ error:Error){ // this is called when we receive any network-related error, called once }}letrequest=tryHTTPClient.Request(url:"https://swift.org")letdelegate=CountingDelegate()try httpClient.execute(request: request, delegate: delegate).future.whenSuccess{ count inprint(count)}

About

HTTP client library built on SwiftNIO

Resources

License

Code of conduct

Stars

Watchers

Forks

Packages

No packages published

Languages

  • Swift87.5%
  • Shell7.1%
  • Ruby4.4%
  • Dockerfile1.0%