1
0

HttpParser.swift 3.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  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 ReadBodyFailed(String)
  9. case InvalidStatusLine(String)
  10. }
  11. class HttpParser {
  12. func readHttpRequest(socket: Socket) throws -> HttpRequest {
  13. let statusLine = try socket.readLine()
  14. let statusLineTokens = statusLine.componentsSeparatedByString(" ")
  15. print(statusLineTokens)
  16. if statusLineTokens.count < 3 {
  17. throw HttpParserError.InvalidStatusLine(statusLine)
  18. }
  19. let method = statusLineTokens[0]
  20. let path = statusLineTokens[1]
  21. let urlParams = extractUrlParams(path)
  22. let headers = try readHeaders(socket)
  23. if let contentLength = headers["content-length"], let contentLengthValue = Int(contentLength) {
  24. let body = try readBody(socket, size: contentLengthValue)
  25. return HttpRequest(url: path, urlParams: urlParams, method: method, headers: headers, body: body, capturedUrlGroups: [], address: nil)
  26. }
  27. return HttpRequest(url: path, urlParams: urlParams, method: method, headers: headers, body: nil, capturedUrlGroups: [], address: nil)
  28. }
  29. private func extractUrlParams(url: String) -> [(String, String)] {
  30. guard let query = url.componentsSeparatedByString("?").last else {
  31. return []
  32. }
  33. return query.componentsSeparatedByString("&").map { (param:String) -> (String, String) in
  34. let tokens = param.componentsSeparatedByString("=")
  35. guard tokens.count >= 2 else {
  36. return ("", "")
  37. }
  38. guard let k = tokens[0].stringByRemovingPercentEncoding, v = tokens[1].stringByRemovingPercentEncoding else {
  39. return ("", "")
  40. }
  41. return (k, v)
  42. }
  43. }
  44. private func readBody(socket: Socket, size: Int) throws -> String {
  45. var body = ""
  46. var counter = 0;
  47. while counter < size {
  48. let c = socket.read()
  49. if c < 0 {
  50. throw HttpParserError.ReadBodyFailed(String.fromCString(UnsafePointer(strerror(errno))) ?? "Error: \(errno)")
  51. }
  52. body.append(UnicodeScalar(c))
  53. counter++;
  54. }
  55. return body
  56. }
  57. private func readHeaders(socket: Socket) throws -> [String: String] {
  58. var requestHeaders = [String: String]()
  59. repeat {
  60. let headerLine = try socket.readLine()
  61. if headerLine.isEmpty {
  62. return requestHeaders
  63. }
  64. let headerTokens = headerLine.componentsSeparatedByString(":")
  65. if headerTokens.count >= 2 {
  66. // RFC 2616 - "Hypertext Transfer Protocol -- HTTP/1.1", paragraph 4.2, "Message Headers":
  67. // "Each header field consists of a name followed by a colon (":") and the field value. Field names are case-insensitive."
  68. // We will keep lower case version.
  69. let headerName = headerTokens[0].lowercaseString
  70. let headerValue = headerTokens[1].stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet())
  71. if !headerName.isEmpty && !headerValue.isEmpty {
  72. requestHeaders.updateValue(headerValue, forKey: headerName)
  73. }
  74. }
  75. } while true
  76. }
  77. func supportsKeepAlive(headers: [String: String]) -> Bool {
  78. if let value = headers["connection"] {
  79. return "keep-alive" == value.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet()).lowercaseString
  80. }
  81. return false
  82. }
  83. }