mike-neckのブログ

Java or Groovy or Swift or Golang

Swift-NIO で HTTP Client を書いてみる

f:id:mike_neck:20180609045321p:plain

この前の土日に Swift-NIO を使って HTTP Client を書いてみたが、うまく動かなかった。


Bootstrap

基本的には Netty と同じ。 ClientBootstrap のイニシャライザーを呼び出して、 EventLoopGroupChannelOptionChannelInitializer(Channel -> EventLoopFuture のこと)を渡して、 connect(host:port:) すればつながるはず。

import NIO

let eventLoopGroup = MultiTHreadedEventLoopGroup(numberOfThreads: 1)
let bootstrap = ClientBootstrap(group:eventLoopGroup)
    .channelOption(ChannelOptions.socket(SocketOptionLevel(SOL_SOCKET), SO_REUSEADDR), value: 1)
    .channelInitializer(channelInitializer())
let future = bootstrap.connect(host: "api.github.com", port: 443)
future.then { channel in
    var httpHeadPart = HTTPRequestHead(version: HTTPVersion(major:1, minor: 1), method: HTTPMethod.GET, uri: "http://api.github.com/search/repositories?q=swift&per_page=3")
    httpHeadPart.headers = HTTPHeaders([
        ("Host", "api.github.com"),
        ("Connection", "close"),
        ("Accept-Encoding", "gzip"),
        ("User-Agent", "Swift-NIO"),
        ("Accept", "application/json")
    ])
    return channel.writeAndFlush(HTTPClientRequestPart.head(httpRequestPart))
}

ChannelInitializer

ここもわりとすんなり書けた。

Netty の HttpObjectAggregator に該当するものがないようにも思われるが、 ユーザーコードが受け取るのは HTTPClientResponsePart(= HTTPPart<HTTPResponseHead, ByteBuffer>) となる模様。型からすると、レスポンスが集約されているように思われる。

func channelInitializer() -> (Channel) -> EventLoopFuture<Swift.Void> {
  return { channel in
      let pipeline = channel.pipeline
      let sslHandler = try! OpenSSLClientHandler(context: sslContext)
      _ = pipeline.add(handler: sslHandler, first: true)
      _ = pipeline.addHTTPClientHandlers()
      return pipeline.add(handler: MyClientHandler(), first: false)
  }
}

これでいけるはずだと思っていたが、実際には TLS のところで unableToValiadteCertificate Error が発生してしまって、何もできない…