<commit_before>// Foundation/NSURLSession/EasyHandle.swift - NSURLSession & libcurl
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
// -----------------------------------------------------------------------------
///
/// libcurl *easy handle* wrapper.
/// These are libcurl helpers for the URLSession API code.
/// - SeeAlso: https://curl.haxx.se/libcurl/c/
/// - SeeAlso: NSURLSession.swift
///
// -----------------------------------------------------------------------------

import CoreFoundation
import Dispatch



/// Minimal wrapper around the [curl easy interface](https://curl.haxx.se/libcurl/c/)
///
/// An *easy handle* manages the state of a transfer inside libcurl.
///
/// As such the easy handle's responsibility is implementing the HTTP
/// protocol while the *multi handle* is in charge of managing sockets and
/// reading from / writing to these sockets.
///
/// An easy handle is added to a multi handle in order to associate it with
/// an actual socket. The multi handle will then feed bytes into the easy
/// handle and read bytes from the easy handle. But this process is opaque
/// to use. It is further worth noting, that with HTTP/1.1 persistent
/// connections and with HTTP/2 there's a 1-to-many relationship between
/// TCP streams and HTTP transfers / easy handles. A single TCP stream and
/// its socket may be shared by multiple easy handles.
///
/// A single HTTP request-response exchange (refered to here as a
/// *transfer*) corresponds directly to an easy handle. Hence anything that
/// needs to be configured for a specific transfer (e.g. the URL) will be
/// configured on an easy handle.
///
/// A single `URLSessionTask` may do multiple, sonecutive transfers, and
/// as a result it will have to reconfigure it's easy handle between
/// transfers. An easy handle can be re-used once its transfer has
/// completed.
///
/// - Note: All code assumes that it is being called on a single thread /
/// `Dispatch` only -- it is intentionally **not** thread safe.
internal final class _EasyHandle {
    let rawHandle = CFURLSessionEasyHandleInit()
    weak var delegate: _EasyHandleDelegate?
    fileprivate var headerList: _CurlStringList?
    fileprivate var pauseState: _PauseState = []
    internal var fileLength: Int64 = 0
    internal var timeoutTimer: _TimeoutSource!

    init(delegate: _EasyHandleDelegate) {
        self.delegate = delegate
        setupCallbacks()
    }
    deinit {
        CFURLSessionEasyHandleDeinit(rawHandle)
    }
}

extension _EasyHandle: Equatable {}
    internal func ==(lhs: _EasyHandle, rhs: _EasyHandle) -> Bool {
        return lhs.rawHandle == rhs.rawHandle
}

extension _EasyHandle {
    enum _Action {
        case abort
        case proceed
        case pause
    }
    enum _WriteBufferResult {
        case abort
        case pause
        /// Write the given number of bytes into the buffer
        case bytes(Int)
    }
}

internal extension _EasyHandle {
    func completedTransfer(withErrorCode errorCode: Int?) {
        delegate?.transferCompleted(withErrorCode: errorCode)
    }
}
internal protocol _EasyHandleDelegate: class {
    /// Handle data read from the network.
    /// - returns: the action to be taken: abort, proceed, or pause.
    func didReceive(data: Data) -> _EasyHandle._Action
    /// Handle header data read from the network.
    /// - returns: the action to be taken: abort, proceed, or pause.
    func didReceive(headerData data: Data) -> _EasyHandle._Action
    /// Fill a buffer with data to be sent.
    ///
    /// - parameter data: The buffer to fill
    /// - returns: the number of bytes written to the `data` buffer, or `nil` to stop the current transfer immediately.
    func fill(writeBuffer buffer: UnsafeMutableBufferPointer<Int8>) -> _EasyHandle._WriteBufferResult
    /// The transfer for this handle completed.
    /// - parameter errorCode: An NSURLError code, or `nil` if no error occured.
    func transferCompleted(withErrorCode errorCode: Int?)
    /// Seek the input stream to the given position
    func seekInputStream(to position: UInt64) throws
    /// Gets called during the transfer to update progress.
    func updateProgressMeter(with propgress: _EasyHandle._Progress)
}
extension _EasyHandle {
    func set(verboseModeOn flag: Bool) {
        try! CFURLSession_easy_setopt_long(rawHandle, CFURLSessionOptionVERBOSE, flag ? 1 : 0).asError()
    }
    /// - SeeAlso: https://curl.haxx.se/libcurl/c/CFURLSessionOptionDEBUGFUNCTION.html
    func set(debugOutputOn flag: Bool, task: URLSessionTask) {
        if flag {
            try! CFURLSession_easy_setopt_ptr(rawHandle, CFURLSessionOptionDEBUGDATA, UnsafeMutableRawPointer(Unmanaged.passUnretained(task).toOpaque())).asError()
            try! CFURLSession_easy_setopt_dc(rawHandle, CFURLSessionOptionDEBUGFUNCTION, printLibcurlDebug(handle:type:data:size:userInfo:)).asError()
        } else {
            try! CFURLSession_easy_setopt_ptr(rawHandle, CFURLSessionOptionDEBUGDATA, nil).asError()
            try! CFURLSession_easy_setopt_ptr(rawHandle, CFURLSessionOptionDEBUGFUNCTION, nil).asError()
        }
    }
    func set(passHeadersToDataStream flag: Bool) {
        try! CFURLSession_easy_setopt_long(rawHandle, CFURLSessionOptionHEADER, flag ? 1 : 0).asError()
    }
    /// Follow any Location: header that the server sends as part of a HTTP header in a 3xx response
    func set(followLocation flag: Bool) {
        try! CFURLSession_easy_setopt_long(rawHandle, CFURLSessionOptionFOLLOWLOCATION, flag ? 1 : 0).asError()
    }
    /// Switch off the progress meter. It will also prevent the CFURLSessionOptionPROGRESSFUNCTION from getting called.
    func set(progressMeterOff flag: Bool) {
        try! CFURLSession_easy_setopt_long(rawHandle, CFURLSessionOptionNOPROGRESS, flag ? 1 : 0).asError()
    }
    /// Skip all signal handling
    /// - SeeAlso: https://curl.haxx.se/libcurl/c/CURLOPT_NOSIGNAL.html
    func set(skipAllSignalHandling flag: Bool) {
        try! CFURLSession_easy_setopt_long(rawHandle, CFURLSessionOptionNOSIGNAL, flag ? 1 : 0).asError()
    }
    /// Set error buffer for error messages
    /// - SeeAlso: https://curl.haxx.se/libcurl/c/CURLOPT_ERRORBUFFER.html
    func set(errorBuffer buffer: UnsafeMutableBufferPointer<UInt8>?) {
        try! CFURLSession_easy_setopt_ptr(rawHandle, CFURLSessionOptionERRORBUFFER, buffer?.baseAddress ?? nil).asError()
    }
    /// Request failure on HTTP response >= 400
    func set(failOnHTTPErrorCode flag: Bool) {
        try! CFURLSession_easy_setopt_long(rawHandle, CFURLSessionOptionFAILONERROR, flag ? 1 : 0).asError()
    }
    /// URL to use in the request
    /// - SeeAlso: https://curl.haxx.se/libcurl/c/CURLOPT_URL.html
    func set(url: URL) {
        url.absoluteString.withCString {
            try! CFURLSession_easy_setopt_ptr(rawHandle, CFURLSessionOptionURL, UnsafeMutablePointer(mutating: $0)).asError()
        }
    }
    /// Set allowed protocols
    ///
    /// - Note: This has security implications. Not limiting this, someone could
    /// redirect a HTTP request into one of the many other protocols that libcurl
    /// supports.
    /// - SeeAlso: https://curl.haxx.se/libcurl/c/CURLOPT_PROTOCOLS.html
    /// - SeeAlso: https://curl.haxx.se/libcurl/c/CURLOPT_REDIR_PROTOCOLS.html
    func setAllowedProtocolsToHTTPAndHTTPS() {
        let protocols = (CFURLSessionProtocolHTTP | CFURLSessionProtocolHTTPS)
        try! CFURLSession_easy_setopt_long(rawHandle, CFURLSessionOptionPROTOCOLS, protocols).asError()
        try! CFURLSession_easy_setopt_long(rawHandle, CFURLSessionOptionREDIR_PROTOCOLS, protocols).asError()
        #if os(Android)
            // See https://curl.haxx.se/docs/sslcerts.html
            // For SSL to work you need "cacert.pem" to be accessable
            // at the path pointed to by the URLSessionCAInfo env var.
            // Downloadable here: https://curl.haxx.se/ca/cacert.pem
            if let caInfo = getenv("URLSessionCAInfo")  {
                if String(cString: caInfo) == "UNSAFE_NOVERIFY" {
                    try! CFURLSession_easy_setopt_int(rawHandle, CFURLSessionOptionSSL_VERIFYPEER, 0).asError()
                }
                else {
                    try! CFURLSession_easy_setopt_ptr(rawHandle, CFURLSessionOptionCAINFO, caInfo).asError()
                }
            }
        #endif
        //TODO: Added in libcurl 7.45.0
        //TODO: Set default protocol for schemeless URLs
        //CURLOPT_DEFAULT_PROTOCOL available only in libcurl 7.45.0
    }
    
    //TODO: Proxy setting, namely CFURLSessionOptionPROXY, CFURLSessionOptionPROXYPORT,
    // CFURLSessionOptionPROXYTYPE, CFURLSessionOptionNOPROXY, CFURLSessionOptionHTTPPROXYTUNNEL, CFURLSessionOptionPROXYHEADER,
    // CFURLSessionOptionHEADEROPT, etc.
    
    /// set preferred receive buffer size
    /// - SeeAlso: https://curl.haxx.se/libcurl/c/CURLOPT_BUFFERSIZE.html
    func set(preferredReceiveBufferSize size: Int) {
        try! CFURLSession_easy_setopt_long(rawHandle, CFURLSessionOptionBUFFERSIZE, min(size, Int(CFURLSessionMaxWriteSize))).asError()
    }
    /// Set custom HTTP headers
    /// - SeeAlso: https://curl.haxx.se/libcurl/c/CURLOPT_HTTPHEADER.html
    func set(customHeaders headers: [String]) {
        let list = _CurlStringList(headers)
        try! CFURLSession_easy_setopt_ptr(rawHandle, CFURLSessionOptionHTTPHEADER, list.asUnsafeMutablePointer).asError()
        // We need to retain the list for as long as the rawHandle is in use.
        headerList = list
    }
    ///TODO: Wait for pipelining/multiplexing. Unavailable on Ubuntu 14.0<commit_msg>UNSAFE_SSL_NOVERIFY<commit_after>// Foundation/NSURLSession/EasyHandle.swift - NSURLSession & libcurl
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
// -----------------------------------------------------------------------------
///
/// libcurl *easy handle* wrapper.
/// These are libcurl helpers for the URLSession API code.
/// - SeeAlso: https://curl.haxx.se/libcurl/c/
/// - SeeAlso: NSURLSession.swift
///
// -----------------------------------------------------------------------------

import CoreFoundation
import Dispatch



/// Minimal wrapper around the [curl easy interface](https://curl.haxx.se/libcurl/c/)
///
/// An *easy handle* manages the state of a transfer inside libcurl.
///
/// As such the easy handle's responsibility is implementing the HTTP
/// protocol while the *multi handle* is in charge of managing sockets and
/// reading from / writing to these sockets.
///
/// An easy handle is added to a multi handle in order to associate it with
/// an actual socket. The multi handle will then feed bytes into the easy
/// handle and read bytes from the easy handle. But this process is opaque
/// to use. It is further worth noting, that with HTTP/1.1 persistent
/// connections and with HTTP/2 there's a 1-to-many relationship between
/// TCP streams and HTTP transfers / easy handles. A single TCP stream and
/// its socket may be shared by multiple easy handles.
///
/// A single HTTP request-response exchange (refered to here as a
/// *transfer*) corresponds directly to an easy handle. Hence anything that
/// needs to be configured for a specific transfer (e.g. the URL) will be
/// configured on an easy handle.
///
/// A single `URLSessionTask` may do multiple, sonecutive transfers, and
/// as a result it will have to reconfigure it's easy handle between
/// transfers. An easy handle can be re-used once its transfer has
/// completed.
///
/// - Note: All code assumes that it is being called on a single thread /
/// `Dispatch` only -- it is intentionally **not** thread safe.
internal final class _EasyHandle {
    let rawHandle = CFURLSessionEasyHandleInit()
    weak var delegate: _EasyHandleDelegate?
    fileprivate var headerList: _CurlStringList?
    fileprivate var pauseState: _PauseState = []
    internal var fileLength: Int64 = 0
    internal var timeoutTimer: _TimeoutSource!

    init(delegate: _EasyHandleDelegate) {
        self.delegate = delegate
        setupCallbacks()
    }
    deinit {
        CFURLSessionEasyHandleDeinit(rawHandle)
    }
}

extension _EasyHandle: Equatable {}
    internal func ==(lhs: _EasyHandle, rhs: _EasyHandle) -> Bool {
        return lhs.rawHandle == rhs.rawHandle
}

extension _EasyHandle {
    enum _Action {
        case abort
        case proceed
        case pause
    }
    enum _WriteBufferResult {
        case abort
        case pause
        /// Write the given number of bytes into the buffer
        case bytes(Int)
    }
}

internal extension _EasyHandle {
    func completedTransfer(withErrorCode errorCode: Int?) {
        delegate?.transferCompleted(withErrorCode: errorCode)
    }
}
internal protocol _EasyHandleDelegate: class {
    /// Handle data read from the network.
    /// - returns: the action to be taken: abort, proceed, or pause.
    func didReceive(data: Data) -> _EasyHandle._Action
    /// Handle header data read from the network.
    /// - returns: the action to be taken: abort, proceed, or pause.
    func didReceive(headerData data: Data) -> _EasyHandle._Action
    /// Fill a buffer with data to be sent.
    ///
    /// - parameter data: The buffer to fill
    /// - returns: the number of bytes written to the `data` buffer, or `nil` to stop the current transfer immediately.
    func fill(writeBuffer buffer: UnsafeMutableBufferPointer<Int8>) -> _EasyHandle._WriteBufferResult
    /// The transfer for this handle completed.
    /// - parameter errorCode: An NSURLError code, or `nil` if no error occured.
    func transferCompleted(withErrorCode errorCode: Int?)
    /// Seek the input stream to the given position
    func seekInputStream(to position: UInt64) throws
    /// Gets called during the transfer to update progress.
    func updateProgressMeter(with propgress: _EasyHandle._Progress)
}
extension _EasyHandle {
    func set(verboseModeOn flag: Bool) {
        try! CFURLSession_easy_setopt_long(rawHandle, CFURLSessionOptionVERBOSE, flag ? 1 : 0).asError()
    }
    /// - SeeAlso: https://curl.haxx.se/libcurl/c/CFURLSessionOptionDEBUGFUNCTION.html
    func set(debugOutputOn flag: Bool, task: URLSessionTask) {
        if flag {
            try! CFURLSession_easy_setopt_ptr(rawHandle, CFURLSessionOptionDEBUGDATA, UnsafeMutableRawPointer(Unmanaged.passUnretained(task).toOpaque())).asError()
            try! CFURLSession_easy_setopt_dc(rawHandle, CFURLSessionOptionDEBUGFUNCTION, printLibcurlDebug(handle:type:data:size:userInfo:)).asError()
        } else {
            try! CFURLSession_easy_setopt_ptr(rawHandle, CFURLSessionOptionDEBUGDATA, nil).asError()
            try! CFURLSession_easy_setopt_ptr(rawHandle, CFURLSessionOptionDEBUGFUNCTION, nil).asError()
        }
    }
    func set(passHeadersToDataStream flag: Bool) {
        try! CFURLSession_easy_setopt_long(rawHandle, CFURLSessionOptionHEADER, flag ? 1 : 0).asError()
    }
    /// Follow any Location: header that the server sends as part of a HTTP header in a 3xx response
    func set(followLocation flag: Bool) {
        try! CFURLSession_easy_setopt_long(rawHandle, CFURLSessionOptionFOLLOWLOCATION, flag ? 1 : 0).asError()
    }
    /// Switch off the progress meter. It will also prevent the CFURLSessionOptionPROGRESSFUNCTION from getting called.
    func set(progressMeterOff flag: Bool) {
        try! CFURLSession_easy_setopt_long(rawHandle, CFURLSessionOptionNOPROGRESS, flag ? 1 : 0).asError()
    }
    /// Skip all signal handling
    /// - SeeAlso: https://curl.haxx.se/libcurl/c/CURLOPT_NOSIGNAL.html
    func set(skipAllSignalHandling flag: Bool) {
        try! CFURLSession_easy_setopt_long(rawHandle, CFURLSessionOptionNOSIGNAL, flag ? 1 : 0).asError()
    }
    /// Set error buffer for error messages
    /// - SeeAlso: https://curl.haxx.se/libcurl/c/CURLOPT_ERRORBUFFER.html
    func set(errorBuffer buffer: UnsafeMutableBufferPointer<UInt8>?) {
        try! CFURLSession_easy_setopt_ptr(rawHandle, CFURLSessionOptionERRORBUFFER, buffer?.baseAddress ?? nil).asError()
    }
    /// Request failure on HTTP response >= 400
    func set(failOnHTTPErrorCode flag: Bool) {
        try! CFURLSession_easy_setopt_long(rawHandle, CFURLSessionOptionFAILONERROR, flag ? 1 : 0).asError()
    }
    /// URL to use in the request
    /// - SeeAlso: https://curl.haxx.se/libcurl/c/CURLOPT_URL.html
    func set(url: URL) {
        url.absoluteString.withCString {
            try! CFURLSession_easy_setopt_ptr(rawHandle, CFURLSessionOptionURL, UnsafeMutablePointer(mutating: $0)).asError()
        }
    }
    /// Set allowed protocols
    ///
    /// - Note: This has security implications. Not limiting this, someone could
    /// redirect a HTTP request into one of the many other protocols that libcurl
    /// supports.
    /// - SeeAlso: https://curl.haxx.se/libcurl/c/CURLOPT_PROTOCOLS.html
    /// - SeeAlso: https://curl.haxx.se/libcurl/c/CURLOPT_REDIR_PROTOCOLS.html
    func setAllowedProtocolsToHTTPAndHTTPS() {
        let protocols = (CFURLSessionProtocolHTTP | CFURLSessionProtocolHTTPS)
        try! CFURLSession_easy_setopt_long(rawHandle, CFURLSessionOptionPROTOCOLS, protocols).asError()
        try! CFURLSession_easy_setopt_long(rawHandle, CFURLSessionOptionREDIR_PROTOCOLS, protocols).asError()
        #if os(Android)
            // See https://curl.haxx.se/docs/sslcerts.html
            // For SSL to work you need "cacert.pem" to be accessable
            // at the path pointed to by the URLSessionCAInfo env var.
            // Downloadable here: https://curl.haxx.se/ca/cacert.pem
            if let caInfo = getenv("URLSessionCAInfo")  {
                if String(cString: caInfo) == "UNSAFE_SSL_NOVERIFY" {
                    try! CFURLSession_easy_setopt_int(rawHandle, CFURLSessionOptionSSL_VERIFYPEER, 0).asError()
                }
                else {
                    try! CFURLSession_easy_setopt_ptr(rawHandle, CFURLSessionOptionCAINFO, caInfo).asError()
                }
            }
        #endif
        //TODO: Added in libcurl 7.45.0
        //TODO: Set default protocol for schemeless URLs
        //CURLOPT_DEFAULT_PROTOCOL available only in libcurl 7.45.0
    }
    
    //TODO: Proxy setting, namely CFURLSessionOptionPROXY, CFURLSessionOptionPROXYPORT,
    // CFURLSessionOptionPROXYTYPE, CFURLSessionOptionNOPROXY, CFURLSessionOptionHTTPPROXYTUNNEL, CFURLSessionOptionPROXYHEADER,
    // CFURLSessionOptionHEADEROPT, etc.
    
    /// set preferred receive buffer size
    /// - SeeAlso: https://curl.haxx.se/libcurl/c/CURLOPT_BUFFERSIZE.html
    func set(preferredReceiveBufferSize size: Int) {
        try! CFURLSession_easy_setopt_long(rawHandle, CFURLSessionOptionBUFFERSIZE, min(size, Int(CFURLSessionMaxWriteSize))).asError()
    }
    /// Set custom HTTP headers
    /// - SeeAlso: https://curl.haxx.se/libcurl/c/CURLOPT_HTTPHEADER.html
    func set(customHeaders headers: [String]) {
        let list = _CurlStringList(headers)
        try! CFURLSession_easy_setopt_ptr(rawHandle, CFURLSessionOptionHTTPHEADER, list.asUnsafeMutablePointer).asError()
        // We need to retain the list for as long as the rawHandle is in use.
        headerList = list
    }
    ///TODO: Wait for pipelining/multiplexing. Unavailable on Ubuntu 14.0
{'fraction_non_alphanumeric': 0.10880515817046141, 'fraction_numerical': 0.005037275841225065, 'mean_word_length': 2.881329423264907, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 17, 'https://': 24, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1681354', 'n_tokens_mistral': 5891, 'n_tokens_neox': 5712, 'n_words': 1695}