main.swift 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. import Foundation
  2. import NIO
  3. import NIOHTTP1
  4. struct JSONTestResponse: Encodable {
  5. let message = "Hello, World!"
  6. }
  7. enum Constants {
  8. static let httpVersion = HTTPVersion(major: 1, minor: 1)
  9. static let serverName = "SwiftNIO"
  10. static let plainTextResponse: StaticString = "Hello, World!"
  11. static let plainTextResponseLength = plainTextResponse.utf8CodeUnitCount
  12. static let plainTextResponseLengthString = String(plainTextResponseLength)
  13. static let jsonResponseLength = try! JSONEncoder().encode(JSONTestResponse()).count
  14. static let jsonResponseLengthString = String(jsonResponseLength)
  15. }
  16. private final class HTTPHandler: ChannelInboundHandler {
  17. public typealias InboundIn = HTTPServerRequestPart
  18. public typealias OutboundOut = HTTPServerResponsePart
  19. let jsonEncoder: JSONEncoder
  20. let dateCache: RFC1123DateCache
  21. var plaintextBuffer: ByteBuffer
  22. var jsonBuffer: ByteBuffer
  23. init(channel: Channel) {
  24. let allocator = ByteBufferAllocator()
  25. self.plaintextBuffer = allocator.buffer(capacity: Constants.plainTextResponseLength)
  26. self.plaintextBuffer.writeStaticString(Constants.plainTextResponse)
  27. self.jsonBuffer = allocator.buffer(capacity: Constants.jsonResponseLength)
  28. self.jsonEncoder = .init()
  29. self.dateCache = .on(channel.eventLoop)
  30. }
  31. func channelRead(context: ChannelHandlerContext, data: NIOAny) {
  32. switch self.unwrapInboundIn(data) {
  33. case .head(let request):
  34. switch request.uri {
  35. case "/plaintext":
  36. self.processPlaintext(context: context)
  37. case "/json":
  38. do {
  39. try self.processJSON(context: context)
  40. } catch {
  41. context.close(promise: nil)
  42. }
  43. default:
  44. context.close(promise: nil)
  45. }
  46. case .body:
  47. break
  48. case .end:
  49. context.write(self.wrapOutboundOut(.end(nil)), promise: nil)
  50. }
  51. }
  52. func channelReadComplete(context: ChannelHandlerContext) {
  53. context.flush()
  54. context.fireChannelReadComplete()
  55. }
  56. private func processPlaintext(context: ChannelHandlerContext) {
  57. let responseHead = self.responseHead(contentType: "text/plain", contentLength: Constants.plainTextResponseLengthString)
  58. context.write(self.wrapOutboundOut(.head(responseHead)), promise: nil)
  59. context.write(self.wrapOutboundOut(.body(.byteBuffer(self.plaintextBuffer))), promise: nil)
  60. }
  61. private func processJSON(context: ChannelHandlerContext) throws {
  62. let responseHead = self.responseHead(contentType: "application/json", contentLength: Constants.jsonResponseLengthString)
  63. context.write(self.wrapOutboundOut(.head(responseHead)), promise: nil)
  64. self.jsonBuffer.clear()
  65. try self.jsonBuffer.writeBytes(self.jsonEncoder.encode(JSONTestResponse()))
  66. context.write(self.wrapOutboundOut(.body(.byteBuffer(self.jsonBuffer))), promise: nil)
  67. }
  68. private func responseHead(contentType: String, contentLength: String) -> HTTPResponseHead {
  69. var headers = HTTPHeaders()
  70. headers.add(name: "content-type", value: contentType)
  71. headers.add(name: "content-length", value: contentLength)
  72. headers.add(name: "server", value: Constants.serverName)
  73. headers.add(name: "date", value: self.dateCache.currentTimestamp())
  74. return HTTPResponseHead(
  75. version: Constants.httpVersion,
  76. status: .ok,
  77. headers: headers
  78. )
  79. }
  80. }
  81. let group = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount)
  82. let bootstrap = ServerBootstrap(group: group)
  83. .serverChannelOption(ChannelOptions.backlog, value: 8192)
  84. .serverChannelOption(ChannelOptions.socket(SocketOptionLevel(SOL_SOCKET), SO_REUSEADDR), value: 1)
  85. .childChannelInitializer { channel in
  86. channel.pipeline.configureHTTPServerPipeline(withPipeliningAssistance: false).flatMap {
  87. channel.pipeline.addHandler(HTTPHandler(channel: channel))
  88. }
  89. }
  90. .childChannelOption(ChannelOptions.socket(SocketOptionLevel(SOL_SOCKET), SO_REUSEADDR), value: 1)
  91. .childChannelOption(ChannelOptions.maxMessagesPerRead, value: 16)
  92. defer {
  93. try! group.syncShutdownGracefully()
  94. }
  95. let channel = try bootstrap.bind(host: "0.0.0.0", port: 8080).wait()
  96. guard let localAddress = channel.localAddress else {
  97. fatalError("Address was unable to bind. Please check that the socket was not closed or that the address family was understood.")
  98. }
  99. try channel.closeFuture.wait()