Server.swift 3.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. //
  2. // Misc.swift
  3. // Swifter
  4. //
  5. // Copyright © 2017 Damian Kołakowski. All rights reserved.
  6. //
  7. import Foundation
  8. public class Server {
  9. private var processors = [Int32 : IncomingDataProcessor]()
  10. private let server: TcpServer
  11. public init(_ port: in_port_t = 8080, forceIPv4: Bool = false) throws {
  12. #if os(Linux)
  13. self.server = try LinuxAsyncServer(port)
  14. #else
  15. self.server = try MacOSAsyncTCPServer(port, forceIPv4: forceIPv4)
  16. #endif
  17. }
  18. public func serve(_ callback: @escaping ((request: Request, responder: @escaping ((Response) -> Void))) -> Void) throws {
  19. try self.server.wait { event in
  20. switch event {
  21. case .connect(_, let socket):
  22. self.processors[socket] = HttpIncomingDataPorcessor(socket) { request in
  23. callback((request, { response in
  24. let keepIOSession = self.supportsKeepAlive(request.headers) || request.httpVersion == .http11
  25. var data = [UInt8]()
  26. data.reserveCapacity(1024)
  27. data.append(contentsOf: [UInt8]("HTTP/\(request.httpVersion == .http10 ? "1.0" : "1.1") \(response.status) OK\r\n".utf8))
  28. for (name, value) in response.headers {
  29. data.append(contentsOf: [UInt8]("\(name): \(value)\r\n".utf8))
  30. }
  31. if (keepIOSession) {
  32. data.append(contentsOf: [UInt8]("Connection: keep-alive\r\n".utf8))
  33. }
  34. data.append(contentsOf: [UInt8]("Content-Length: \(response.body.count)\r\n".utf8))
  35. data.append(contentsOf: [13, 10])
  36. data.append(contentsOf: response.body)
  37. do {
  38. try self.server.write(socket, data) {
  39. if let sucessor = response.processingSuccesor {
  40. self.processors[socket] = sucessor
  41. return .continue
  42. }
  43. return keepIOSession ? .continue : .terminate
  44. }
  45. } catch {
  46. self.processors.removeValue(forKey: socket)
  47. }
  48. }))
  49. }
  50. case .disconnect(_, let socket):
  51. self.processors.removeValue(forKey: socket)
  52. case .data(_, let socket, let chunk):
  53. do {
  54. try self.processors[socket]?.process(chunk)
  55. } catch {
  56. self.processors.removeValue(forKey: socket)
  57. self.server.finish(socket)
  58. }
  59. }
  60. }
  61. }
  62. private func supportsKeepAlive(_ headers: Array<(String, String)>) -> Bool {
  63. if let (_, value) = headers.filter({ $0.0 == "connection" }).first {
  64. return "keep-alive" == value.trimmingCharacters(in: CharacterSet.whitespaces)
  65. }
  66. return false
  67. }
  68. private func closeConnection(_ headers: Array<(String, String)>) -> Bool {
  69. if let (_, value) = headers.filter({ $0.0 == "connection" }).first {
  70. return "close" == value.trimmingCharacters(in: CharacterSet.whitespaces)
  71. }
  72. return false
  73. }
  74. }
  75. public protocol IncomingDataProcessor {
  76. func process(_ chunk: ArraySlice<UInt8>) throws
  77. }