HttpParser.swift 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. //
  2. // HttpParser.swift
  3. // Swifter
  4. // Copyright (c) 2015 Damian Kołakowski. All rights reserved.
  5. //
  6. import Foundation
  7. enum HttpParserError : ErrorType {
  8. case RecvFailed(String?)
  9. case ReadBodyFailed(String?)
  10. case InvalidStatusLine(String)
  11. }
  12. class HttpParser {
  13. func nextHttpRequest(socket: CInt) throws -> HttpRequest {
  14. let statusLine = try nextLine(socket)
  15. let statusLineTokens = statusLine.componentsSeparatedByString(" ")
  16. print(statusLineTokens)
  17. if statusLineTokens.count < 3 {
  18. throw HttpParserError.InvalidStatusLine(statusLine)
  19. }
  20. let method = statusLineTokens[0]
  21. let path = statusLineTokens[1]
  22. let urlParams = extractUrlParams(path)
  23. let headers = try nextHeaders(socket)
  24. if let contentLength = headers["content-length"], let contentLengthValue = Int(contentLength) {
  25. let body = try nextBody(socket, size: contentLengthValue)
  26. return HttpRequest(url: path, urlParams: urlParams, method: method, headers: headers, body: body, capturedUrlGroups: [], address: nil)
  27. }
  28. return HttpRequest(url: path, urlParams: urlParams, method: method, headers: headers, body: nil, capturedUrlGroups: [], address: nil)
  29. }
  30. private func extractUrlParams(url: String) -> [(String, String)] {
  31. guard let query = url.componentsSeparatedByString("?").last else {
  32. return []
  33. }
  34. return query.componentsSeparatedByString("&").map { (param:String) -> (String, String) in
  35. let tokens = param.componentsSeparatedByString("=")
  36. guard tokens.count >= 2 else {
  37. return ("", "")
  38. }
  39. guard let k = tokens[0].stringByRemovingPercentEncoding, v = tokens[1].stringByRemovingPercentEncoding else {
  40. return ("", "")
  41. }
  42. return (k, v)
  43. }
  44. }
  45. private func nextBody(socket: CInt, size: Int) throws -> String {
  46. var body = ""
  47. var counter = 0;
  48. while counter < size {
  49. let c = nextInt8(socket)
  50. if c < 0 {
  51. throw HttpParserError.ReadBodyFailed(String.fromCString(UnsafePointer(strerror(errno))))
  52. }
  53. body.append(UnicodeScalar(c))
  54. counter++;
  55. }
  56. return body
  57. }
  58. private func nextHeaders(socket: CInt) throws -> [String: String] {
  59. var requestHeaders = [String: String]()
  60. repeat {
  61. let headerLine = try nextLine(socket)
  62. if headerLine.isEmpty {
  63. return requestHeaders
  64. }
  65. let headerTokens = headerLine.componentsSeparatedByString(":")
  66. if headerTokens.count >= 2 {
  67. // RFC 2616 - "Hypertext Transfer Protocol -- HTTP/1.1", paragraph 4.2, "Message Headers":
  68. // "Each header field consists of a name followed by a colon (":") and the field value. Field names are case-insensitive."
  69. // We will keep lower case version.
  70. let headerName = headerTokens[0].lowercaseString
  71. let headerValue = headerTokens[1].stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet())
  72. if !headerName.isEmpty && !headerValue.isEmpty {
  73. requestHeaders.updateValue(headerValue, forKey: headerName)
  74. }
  75. }
  76. } while true
  77. }
  78. private func nextInt8(socket: CInt) -> Int {
  79. var buffer = [UInt8](count: 1, repeatedValue: 0);
  80. let next = recv(socket as Int32, &buffer, Int(buffer.count), 0)
  81. if next <= 0 {
  82. return next
  83. }
  84. return Int(buffer[0])
  85. }
  86. private func nextLine(socket: CInt) throws -> String {
  87. var characters: String = ""
  88. var n = 0
  89. repeat {
  90. n = nextInt8(socket)
  91. if ( n > 13 /* CR */ ) { characters.append(Character(UnicodeScalar(n))) }
  92. } while n > 0 && n != 10 /* NL */
  93. if n == -1 {
  94. throw HttpParserError.RecvFailed(String.fromCString(UnsafePointer(strerror(errno))))
  95. }
  96. return characters
  97. }
  98. func supportsKeepAlive(headers: [String: String]) -> Bool {
  99. if let value = headers["connection"] {
  100. return "keep-alive" == value.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet()).lowercaseString
  101. }
  102. return false
  103. }
  104. }