HTTPHandler.swift 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. //////////////////////////////////////////////////////////////////////////////////////////////////
  2. //
  3. // HTTPHandler.swift
  4. // Starscream
  5. //
  6. // Created by Dalton Cherry on 1/24/19.
  7. // Copyright © 2019 Vluxe. All rights reserved.
  8. //
  9. // Licensed under the Apache License, Version 2.0 (the "License");
  10. // you may not use this file except in compliance with the License.
  11. // You may obtain a copy of the License at
  12. //
  13. // http://www.apache.org/licenses/LICENSE-2.0
  14. //
  15. // Unless required by applicable law or agreed to in writing, software
  16. // distributed under the License is distributed on an "AS IS" BASIS,
  17. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  18. // See the License for the specific language governing permissions and
  19. // limitations under the License.
  20. //
  21. //////////////////////////////////////////////////////////////////////////////////////////////////
  22. import Foundation
  23. public enum HTTPUpgradeError: Error {
  24. case notAnUpgrade(Int, [String: String])
  25. case invalidData
  26. }
  27. public struct HTTPWSHeader {
  28. static let upgradeName = "Upgrade"
  29. static let upgradeValue = "websocket"
  30. static let hostName = "Host"
  31. static let connectionName = "Connection"
  32. static let connectionValue = "Upgrade"
  33. static let protocolName = "Sec-WebSocket-Protocol"
  34. static let versionName = "Sec-WebSocket-Version"
  35. static let versionValue = "13"
  36. static let extensionName = "Sec-WebSocket-Extensions"
  37. static let keyName = "Sec-WebSocket-Key"
  38. static let originName = "Origin"
  39. static let acceptName = "Sec-WebSocket-Accept"
  40. static let switchProtocolCode = 101
  41. static let defaultSSLSchemes = ["wss", "https"]
  42. /// Creates a new URLRequest based off the source URLRequest.
  43. /// - Parameter request: the request to "upgrade" the WebSocket request by adding headers.
  44. /// - Parameter supportsCompression: set if the client support text compression.
  45. /// - Parameter secKeyName: the security key to use in the WebSocket request. https://tools.ietf.org/html/rfc6455#section-1.3
  46. /// - returns: A URLRequest request to be converted to data and sent to the server.
  47. public static func createUpgrade(request: URLRequest, supportsCompression: Bool, secKeyValue: String) -> URLRequest {
  48. guard let url = request.url, let parts = url.getParts() else {
  49. return request
  50. }
  51. var req = request
  52. if request.value(forHTTPHeaderField: HTTPWSHeader.originName) == nil {
  53. var origin = url.absoluteString
  54. if let hostUrl = URL (string: "/", relativeTo: url) {
  55. origin = hostUrl.absoluteString
  56. origin.remove(at: origin.index(before: origin.endIndex))
  57. }
  58. req.setValue(origin, forHTTPHeaderField: HTTPWSHeader.originName)
  59. }
  60. req.setValue(HTTPWSHeader.upgradeValue, forHTTPHeaderField: HTTPWSHeader.upgradeName)
  61. req.setValue(HTTPWSHeader.connectionValue, forHTTPHeaderField: HTTPWSHeader.connectionName)
  62. req.setValue(HTTPWSHeader.versionValue, forHTTPHeaderField: HTTPWSHeader.versionName)
  63. req.setValue(secKeyValue, forHTTPHeaderField: HTTPWSHeader.keyName)
  64. if req.allHTTPHeaderFields?["Cookie"] == nil {
  65. if let cookies = HTTPCookieStorage.shared.cookies(for: url), !cookies.isEmpty {
  66. let headers = HTTPCookie.requestHeaderFields(with: cookies)
  67. for (key, val) in headers {
  68. req.setValue(val, forHTTPHeaderField: key)
  69. }
  70. }
  71. }
  72. if supportsCompression {
  73. let val = "permessage-deflate; client_max_window_bits; server_max_window_bits=15"
  74. req.setValue(val, forHTTPHeaderField: HTTPWSHeader.extensionName)
  75. }
  76. let hostValue = req.allHTTPHeaderFields?[HTTPWSHeader.hostName] ?? "\(parts.host):\(parts.port)"
  77. req.setValue(hostValue, forHTTPHeaderField: HTTPWSHeader.hostName)
  78. return req
  79. }
  80. // generateWebSocketKey 16 random characters between a-z and return them as a base64 string
  81. public static func generateWebSocketKey() -> String {
  82. return Data((0..<16).map{ _ in UInt8.random(in: 97...122) }).base64EncodedString()
  83. }
  84. }
  85. public enum HTTPEvent {
  86. case success([String: String])
  87. case failure(Error)
  88. }
  89. public protocol HTTPHandlerDelegate: AnyObject {
  90. func didReceiveHTTP(event: HTTPEvent)
  91. }
  92. public protocol HTTPHandler {
  93. func register(delegate: HTTPHandlerDelegate)
  94. func convert(request: URLRequest) -> Data
  95. func parse(data: Data) -> Int
  96. }
  97. public protocol HTTPServerDelegate: AnyObject {
  98. func didReceive(event: HTTPEvent)
  99. }
  100. public protocol HTTPServerHandler {
  101. func register(delegate: HTTPServerDelegate)
  102. func parse(data: Data)
  103. func createResponse(headers: [String: String]) -> Data
  104. }
  105. public struct URLParts {
  106. let port: Int
  107. let host: String
  108. let isTLS: Bool
  109. }
  110. public extension URL {
  111. /// isTLSScheme returns true if the scheme is https or wss
  112. var isTLSScheme: Bool {
  113. guard let scheme = self.scheme else {
  114. return false
  115. }
  116. return HTTPWSHeader.defaultSSLSchemes.contains(scheme)
  117. }
  118. /// getParts pulls host and port from the url.
  119. func getParts() -> URLParts? {
  120. guard let host = self.host else {
  121. return nil // no host, this isn't a valid url
  122. }
  123. let isTLS = isTLSScheme
  124. var port = self.port ?? 0
  125. if self.port == nil {
  126. if isTLS {
  127. port = 443
  128. } else {
  129. port = 80
  130. }
  131. }
  132. return URLParts(port: port, host: host, isTLS: isTLS)
  133. }
  134. }