<commit_before>/*
 * Copyright IBM Corporation 2016
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import Foundation
import Socket
import LoggerAPI

// MARK: HTTPServerRequest

/// This class implements the `ServerRequest` protocol for incoming sockets that
/// are communicating via the HTTP protocol. 
public class HTTPServerRequest: ServerRequest {
    
    /// HTTP Status code if this message is a response
    @available(*, deprecated, message:
    "This method never worked on Server Requests and was inherited incorrectly from a super class")
    public private(set) var httpStatusCode: HTTPStatusCode = .unknown
    
    /// Client connection socket
    private let socket: Socket
    
    /// Server IP address pulled from socket.
    public var remoteAddress: String {
        return socket.remoteHostname
    }
    
    /// HTTP Method of the request.
    public var method: String { return httpParser?.method ?? ""}
    
    /// Major version of HTTP of the request
    public var httpVersionMajor: UInt16? { return httpParser?.httpVersionMajor ?? 0}
    
    /// Minor version of HTTP of the request
    public var httpVersionMinor: UInt16? { return httpParser?.httpVersionMinor ?? 0}
    
    /// Set of HTTP headers of the request.
    public var headers: HeadersContainer { return httpParser?.headers ?? HeadersContainer() }
    
    /// socket signature of the request.
    public var signature: Socket.Signature? { return socket.signature }
    
    private var _url: URL?
    
    public var urlURL: URL {
        if let _url = _url {
            return _url
        }
        
        var url = ""
        var proto: String?
        if let isSecure = signature?.isSecure {
            proto = (isSecure ? "https" : "http")
            url.append(proto! + "://")
        } else {
            url.append("http://")
            Log.error("Socket signature not initialized, using http")
        }
        
        if let host = headers["Host"]?[0] {
            url.append(host)
        } else {
            url.append("Host_Not_Available")
            Log.error("Host header not received")
        }
        
        url.append(httpParser?.urlString ?? "")
        
        if let urlURL = URL(string: url) {
            self._url = urlURL
        } else {
            Log.error("URL init failed from: \(url)")
            self._url = URL(string: "http://not_available/")!
        }
        
        return self._url!
    }
    
    private var urlc: URLComponents?
    
    /// The URL from the request as URLComponents
    /// URLComponents has a memory leak on linux as of swift 3.0.1. Use 'urlURL' instead
    @available(*, deprecated, message:
    "URLComponents has a memory leak on linux as of swift 3.0.1. use 'urlURL' instead")
    public var urlComponents: URLComponents {
        if let urlc = self.urlc {
            return urlc
        }
        let urlc = URLComponents(url: self.urlURL, resolvingAgainstBaseURL: false) ?? URLComponents()
        self.urlc = urlc
        return urlc
    }
    
    /// The URL from the request in string form
    /// This contains just the path and query parameters starting with '/'
    /// Use 'urlURL' for the full URL
    @available(*, deprecated, message:
    "This contains just the path and query parameters starting with '/'. use 'urlURL' instead")
    public var urlString : String { return httpParser?.urlString ?? ""}
    
    /// The URL from the request in UTF-8 form
    /// This contains just the path and query parameters starting with '/'
    /// Use 'urlURL' for the full URL
    public var url : Data {
        if let httpParser = httpParser {
            return  Data(bytes: httpParser.url.bytes, count: httpParser.url.length)
        }
        return Data()
    }
    
    // Private
    
    /// Default buffer size used for creating a BufferList
    private static let bufferSize = 2000
    
    /// The http_parser Swift wrapper
    weak var httpParser: HTTPParser?
    
    /// State of parsing the request
    private var status = HTTPParserStatus()
    
    private var buffer = Data(capacity: bufferSize)
    
    /// Initializes a new `HTTPServerRequest`
    ///
    /// - Parameter socket: The Socket object associated with this request
    /// - Parameter httpParser: The `HTTPParser` object used to parse the incoming request
    ///
    /// - Returns: an HTTPServerRequest instance
    init (socket: Socket, httpParser: HTTPParser?) {
        self.socket = socket
        self.httpParser = httpParser
    }
    
    /// Read a chunk of the body of the request.
    ///
    /// - Parameter into: An NSMutableData to hold the data in the request.
    /// - Throws: if an error occurs while reading the body.
    /// - Returns: the number of bytes read.
    public func read(into data: inout Data) throws -> Int {
        guard let httpParser = httpParser else {
            return 0
        }
        
        let count = httpParser.bodyChunk.fill(data: &data)
        return count
    }
    
    /// Read the whole body of the request.
    ///
    /// - Parameter into: An NSMutableData to hold the data in the request.
    /// - Throws: if an error occurs while reading the data.
    /// - Returns: the number of bytes read.
    @discardableResult
    public func readAllData(into data: inout Data) throws -> Int {
        var length = try read(into: &data)
        var bytesRead = length
        while length > 0 {
            length = try read(into: &data)
            bytesRead += length
        }
        return bytesRead
    }
    
    /// Read a chunk of the body and return it as a String.
    ///
    /// - Throws: if an error occurs while reading the data.
    /// - Returns: an Optional string.
    public func readString() throws -> String? {
        buffer.count = 0
        let length = try read(into: &buffer)
        if length > 0 {
            return String(data: buffer, encoding: .utf8)
        }
        else {
            return nil
        }
    }
    
    /// Extra handling performed when a message is completely parsed
    func parsingCompleted() {<commit_msg>Updated HTTPServerRequest docs<commit_after>/*
 * Copyright IBM Corporation 2016
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import Foundation
import Socket
import LoggerAPI

// MARK: HTTPServerRequest

/**
This class implements the `ServerRequest` protocol for incoming sockets that are communicating via the HTTP protocol. Data and Strings can be read in.

### Usage Example: ###
````swift
 func handlePost(request: ServerRequest, response: ServerResponse) {
     var body = Data()
     do {
         let length = try request.readAllData(into: &body)
         let result = "Read \(length) bytes"
         response.headers["Content-Type"] = ["text/plain"]
         response.headers["Content-Length"] = ["\(result.count)"]
 
         try response.end(text: result)
     }
     catch {
         print("Error reading body or writing response")
     }
 }
````
*/
public class HTTPServerRequest: ServerRequest {
    
    /**
     HTTP Status code if this message is a response
     
     ### Usage Example: ###
     ````swift
     response!.httpStatusCode
     ````
     */
    @available(*, deprecated, message:
    "This method never worked on Server Requests and was inherited incorrectly from a super class")
    public private(set) var httpStatusCode: HTTPStatusCode = .unknown
    
    /// Client connection socket
    private let socket: Socket
    
    /**
     Server IP address pulled from socket.
     
     ### Usage Example: ###
     ````swift
     request.remoteAddress
     ````
     */
    public var remoteAddress: String {
        return socket.remoteHostname
    }
    
    /**
     HTTP Method of the request.
     
     ### Usage Example: ###
     ````swift
     request.method.lowercased()
     ````
     */
    public var method: String { return httpParser?.method ?? ""}
    
    /**
     Major version of HTTP of the request
     
     ### Usage Example: ###
     ````swift
     print(String(describing: request.httpVersionMajor))
     ````
     */
    public var httpVersionMajor: UInt16? { return httpParser?.httpVersionMajor ?? 0}
    
    /**
     Minor version of HTTP of the request
     
     ### Usage Example: ###
     ````swift
     print(String(describing: request.httpVersionMinor))
     ````
     */
    public var httpVersionMinor: UInt16? { return httpParser?.httpVersionMinor ?? 0}
    
    /**
     Set of HTTP headers of the request.
     
     ### Usage Example: ###
     ````swift
     let protocols = request.headers["Upgrade"]
     ````
     */
    public var headers: HeadersContainer { return httpParser?.headers ?? HeadersContainer() }
    
    /**
     socket signature of the request.
     
     ### Usage Example: ###
     ````swift
     public var signature: Socket.Signature? { return socket.signature }
     ````
     */
    public var signature: Socket.Signature? { return socket.signature }
    
    private var _url: URL?
    
    /**
     Create and validate the full URL.
     
     ### Usage Example: ###
     ````swift
     print(request.urlURL)
     ````
     */
    public var urlURL: URL {
        if let _url = _url {
            return _url
        }
        
        var url = ""
        var proto: String?
        if let isSecure = signature?.isSecure {
            proto = (isSecure ? "https" : "http")
            url.append(proto! + "://")
        } else {
            url.append("http://")
            Log.error("Socket signature not initialized, using http")
        }
        
        if let host = headers["Host"]?[0] {
            url.append(host)
        } else {
            url.append("Host_Not_Available")
            Log.error("Host header not received")
        }
        
        url.append(httpParser?.urlString ?? "")
        
        if let urlURL = URL(string: url) {
            self._url = urlURL
        } else {
            Log.error("URL init failed from: \(url)")
            self._url = URL(string: "http://not_available/")!
        }
        
        return self._url!
    }
    
    private var urlc: URLComponents?
    
    /**
     The URL from the request as URLComponents
     URLComponents has a memory leak on linux as of swift 3.0.1. Use 'urlURL' instead
     
     ### Usage Example: ###
     ````swift
     print(request.urlComponents)
     ````
     */
    @available(*, deprecated, message:
    "URLComponents has a memory leak on linux as of swift 3.0.1. use 'urlURL' instead")
    public var urlComponents: URLComponents {
        if let urlc = self.urlc {
            return urlc
        }
        let urlc = URLComponents(url: self.urlURL, resolvingAgainstBaseURL: false) ?? URLComponents()
        self.urlc = urlc
        return urlc
    }
    
    /**
     The URL from the request in string form
     This contains just the path and query parameters starting with '/'
     Use 'urlURL' for the full URL
     
     ### Usage Example: ###
     ````swift
     print(request.urlString)
     ````
     */
    @available(*, deprecated, message:
    "This contains just the path and query parameters starting with '/'. use 'urlURL' instead")
    public var urlString : String { return httpParser?.urlString ?? ""}
    
    /**
     The URL from the request in UTF-8 form
     This contains just the path and query parameters starting with '/'
     Use 'urlURL' for the full URL
     
     ### Usage Example: ###
     ````swift
     print(request.url)
     ````
     */
    public var url : Data {
        if let httpParser = httpParser {
            return  Data(bytes: httpParser.url.bytes, count: httpParser.url.length)
        }
        return Data()
    }
    
    // Private
    
    /// Default buffer size used for creating a BufferList
    private static let bufferSize = 2000
    
    /// The http_parser Swift wrapper
    weak var httpParser: HTTPParser?
    
    /// State of parsing the request
    private var status = HTTPParserStatus()
    
    private var buffer = Data(capacity: bufferSize)
    
    /// Initializes a new `HTTPServerRequest`
    ///
    /// - Parameter socket: The Socket object associated with this request
    /// - Parameter httpParser: The `HTTPParser` object used to parse the incoming request
    ///
    /// - Returns: an HTTPServerRequest instance
    init (socket: Socket, httpParser: HTTPParser?) {
        self.socket = socket
        self.httpParser = httpParser
    }
    
    /**
     Read a chunk of the body of the request.
     
     - Parameter into: An NSMutableData to hold the data in the request.
     - Throws: if an error occurs while reading the body.
     - Returns: the number of bytes read.
     
     ### Usage Example: ###
     ````swift
     let readData = try self.read(into: data)
     ````
     */
    public func read(into data: inout Data) throws -> Int {
        guard let httpParser = httpParser else {
            return 0
        }
        
        let count = httpParser.bodyChunk.fill(data: &data)
        return count
    }
    
    /**
     Read the whole body of the request.
     
     - Parameter into: An NSMutableData to hold the data in the request.
     - Throws: if an error occurs while reading the data.
     - Returns: the number of bytes read.
     
     ### Usage Example: ###
     ````swift
     let length = try request.readAllData(into: &body)
     ````
     */
    @discardableResult
    public func readAllData(into data: inout Data) throws -> Int {
        var length = try read(into: &data)
        var bytesRead = length
        while length > 0 {
            length = try read(into: &data)
            bytesRead += length
        }
        return bytesRead
    }
    
    /**
     Read a chunk of the body and return it as a String.
     
     - Throws: if an error occurs while reading the data.
     - Returns: an Optional string.
     
     ### Usage Example: ###
     ````swift
     let body = try request.readString()
     ````
     */
    public func readString() throws -> String? {
        buffer.count = 0
        let length = try read(into: &buffer)
        if length > 0 {
            return String(data: buffer, encoding: .utf8)
        }
        else {
            return nil
        }
    }
    
    /// Extra handling performed when a message is completely parsed
    func parsingCompleted() {
{'fraction_non_alphanumeric': 0.09762464108587836, 'fraction_numerical': 0.004045941007569825, 'mean_word_length': 2.0358557844690965, 'pattern_counts': {'":': 2, '<': 3, '<?xml version=': 0, '>': 13, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6711138', 'n_tokens_mistral': 4135, 'n_tokens_neox': 4031, 'n_words': 1507}