Server.swift 3.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  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 io: IO
  11. public init(_ port: in_port_t = 8080, forceIPv4: Bool = false) throws {
  12. #if os(Linux)
  13. self.io = try LinuxIO(port, forceIPv4: forceIPv4)
  14. #else
  15. self.io = try MacOSIO(port, forceIPv4: forceIPv4)
  16. #endif
  17. }
  18. public func serve(_ callback: @escaping ((request: Request, responder: ((Response) -> Void))) -> Void) throws {
  19. try self.io.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 + response.body.count)
  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\r\n".utf8))
  35. data.append(contentsOf: response.body)
  36. do {
  37. try self.io.write(socket, data) {
  38. if let sucessor = response.processingSuccesor {
  39. self.processors[socket] = sucessor
  40. return .continue
  41. }
  42. return keepIOSession ? .continue : .terminate
  43. }
  44. } catch {
  45. self.processors.removeValue(forKey: socket)
  46. }
  47. }))
  48. }
  49. case .disconnect(_, let socket):
  50. self.processors.removeValue(forKey: socket)
  51. case .data(_, let socket, let chunk):
  52. do {
  53. try self.processors[socket]?.process(chunk)
  54. } catch {
  55. self.processors.removeValue(forKey: socket)
  56. self.io.finish(socket)
  57. }
  58. }
  59. }
  60. }
  61. private func supportsKeepAlive(_ headers: Array<(String, String)>) -> Bool {
  62. if let (_, value) = headers.filter({ $0.0 == "connection" }).first {
  63. return "keep-alive" == value.trimmingCharacters(in: CharacterSet.whitespaces)
  64. }
  65. return false
  66. }
  67. private func closeConnection(_ headers: Array<(String, String)>) -> Bool {
  68. if let (_, value) = headers.filter({ $0.0 == "connection" }).first {
  69. return "close" == value.trimmingCharacters(in: CharacterSet.whitespaces)
  70. }
  71. return false
  72. }
  73. }
  74. public protocol IncomingDataProcessor {
  75. func process(_ chunk: ArraySlice<UInt8>) throws
  76. }