1
0

HttpParser.swift 2.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364
  1. //
  2. // HttpParser.swift
  3. // Swifter
  4. //
  5. // Copyright (c) 2014-2016 Damian Kołakowski. All rights reserved.
  6. //
  7. import Foundation
  8. enum HttpParserError: Error, Equatable {
  9. case invalidStatusLine(String)
  10. case negativeContentLength
  11. }
  12. public class HttpParser {
  13. public init() { }
  14. public func readHttpRequest(_ socket: Socket) throws -> HttpRequest {
  15. let statusLine = try socket.readLine()
  16. let statusLineTokens = statusLine.components(separatedBy: " ")
  17. if statusLineTokens.count < 3 {
  18. throw HttpParserError.invalidStatusLine(statusLine)
  19. }
  20. let request = HttpRequest()
  21. request.method = statusLineTokens[0]
  22. let encodedPath = statusLineTokens[1].addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? statusLineTokens[1]
  23. let urlComponents = URLComponents(string: encodedPath)
  24. request.path = urlComponents?.path ?? ""
  25. request.queryParams = urlComponents?.queryItems?.map { ($0.name, $0.value ?? "") } ?? []
  26. request.headers = try readHeaders(socket)
  27. if let contentLength = request.headers["content-length"], let contentLengthValue = Int(contentLength) {
  28. // Prevent a buffer overflow and runtime error trying to create an `UnsafeMutableBufferPointer` with
  29. // a negative length
  30. guard contentLengthValue >= 0 else {
  31. throw HttpParserError.negativeContentLength
  32. }
  33. request.body = try readBody(socket, size: contentLengthValue)
  34. }
  35. return request
  36. }
  37. private func readBody(_ socket: Socket, size: Int) throws -> [UInt8] {
  38. return try socket.read(length: size)
  39. }
  40. private func readHeaders(_ socket: Socket) throws -> [String: String] {
  41. var headers = [String: String]()
  42. while case let headerLine = try socket.readLine(), !headerLine.isEmpty {
  43. let headerTokens = headerLine.split(separator: ":", maxSplits: 1, omittingEmptySubsequences: true).map(String.init)
  44. if let name = headerTokens.first, let value = headerTokens.last {
  45. headers[name.lowercased()] = value.trimmingCharacters(in: .whitespaces)
  46. }
  47. }
  48. return headers
  49. }
  50. func supportsKeepAlive(_ headers: [String: String]) -> Bool {
  51. if let value = headers["connection"] {
  52. return "keep-alive" == value.trimmingCharacters(in: .whitespaces)
  53. }
  54. return false
  55. }
  56. }