<commit_before>#include "HTTPClientAndroidImpl.h"
#include "components/Exceptions.h"
#include "utils/AndroidUtils.h"
#include "utils/JNIUniqueGlobalRef.h"
#include "utils/Log.h"

#include <chrono>
#include <limits>
#include <regex>

#include <boost/lexical_cast.hpp>

namespace carto {
    
    struct HTTPClient::AndroidImpl::URLClass {
        JNIUniqueGlobalRef<jclass> clazz;
        jmethodID constructor;
        jmethodID openConnection;
        
        explicit URLClass(JNIEnv* jenv) {
            clazz = JNIUniqueGlobalRef<jclass>(jenv->NewGlobalRef(jenv->FindClass("java/net/URL")));
            constructor = jenv->GetMethodID(clazz, "<init>", "(Ljava/lang/String;)V");
            openConnection = jenv->GetMethodID(clazz, "openConnection", "()Ljava/net/URLConnection;");
        }
    };
    
    struct HTTPClient::AndroidImpl::HttpURLConnectionClass {
        JNIUniqueGlobalRef<jclass> clazz;
        jmethodID setRequestMethod;
        jmethodID setDoInput;
        jmethodID setDoOutput;
        jmethodID setUseCaches;
        jmethodID setAllowUserInteraction;
        jmethodID setInstanceFollowRedirects;
        jmethodID setRequestProperty;
        jmethodID setConnectTimeout;
        jmethodID setReadTimeout;
        jmethodID connect;
        jmethodID disconnect;
        jmethodID getResponseCode;
        jmethodID getHeaderFieldKey;
        jmethodID getHeaderField;
        jmethodID getInputStream;
        jmethodID getOutputStream;
        jmethodID getErrorStream;
        
        explicit HttpURLConnectionClass(JNIEnv* jenv) {
            clazz = JNIUniqueGlobalRef<jclass>(jenv->NewGlobalRef(jenv->FindClass("java/net/HttpURLConnection")));
            setRequestMethod = jenv->GetMethodID(clazz, "setRequestMethod", "(Ljava/lang/String;)V");
            setDoInput = jenv->GetMethodID(clazz, "setDoInput", "(Z)V");
            setDoOutput = jenv->GetMethodID(clazz, "setDoOutput", "(Z)V");
            setUseCaches = jenv->GetMethodID(clazz, "setUseCaches", "(Z)V");
            setAllowUserInteraction = jenv->GetMethodID(clazz, "setAllowUserInteraction", "(Z)V");
            setInstanceFollowRedirects = jenv->GetMethodID(clazz, "setInstanceFollowRedirects", "(Z)V");
            setRequestProperty = jenv->GetMethodID(clazz, "setRequestProperty", "(Ljava/lang/String;Ljava/lang/String;)V");
            setConnectTimeout = jenv->GetMethodID(clazz, "setConnectTimeout", "(I)V");
            setReadTimeout = jenv->GetMethodID(clazz, "setReadTimeout", "(I)V");
            connect = jenv->GetMethodID(clazz, "connect", "()V");
            disconnect = jenv->GetMethodID(clazz, "disconnect", "()V");
            getResponseCode = jenv->GetMethodID(clazz, "getResponseCode", "()I");
            getHeaderFieldKey = jenv->GetMethodID(clazz, "getHeaderFieldKey", "(I)Ljava/lang/String;");
            getHeaderField = jenv->GetMethodID(clazz, "getHeaderField", "(I)Ljava/lang/String;");
            getInputStream = jenv->GetMethodID(clazz, "getInputStream", "()Ljava/io/InputStream;");
            getOutputStream = jenv->GetMethodID(clazz, "getOutputStream", "()Ljava/io/OutputStream;");
            getErrorStream = jenv->GetMethodID(clazz, "getErrorStream", "()Ljava/io/InputStream;");
        }
    };
    
    struct HTTPClient::AndroidImpl::InputStreamClass {
        JNIUniqueGlobalRef<jclass> clazz;
        jmethodID read;
        jmethodID close;

        explicit InputStreamClass(JNIEnv* jenv) {
            clazz = JNIUniqueGlobalRef<jclass>(jenv->NewGlobalRef(jenv->FindClass("java/io/InputStream")));
            read = jenv->GetMethodID(clazz, "read", "([B)I");
            close = jenv->GetMethodID(clazz, "close", "()V");
        }
    };
    
    struct HTTPClient::AndroidImpl::OutputStreamClass {
        JNIUniqueGlobalRef<jclass> clazz;
        jmethodID write;
        jmethodID close;

        explicit OutputStreamClass(JNIEnv* jenv) {
            clazz = JNIUniqueGlobalRef<jclass>(jenv->NewGlobalRef(jenv->FindClass("java/io/OutputStream")));
            write = jenv->GetMethodID(clazz, "write", "([B)V");
            close = jenv->GetMethodID(clazz, "close", "()V");
        }
    };
    
    HTTPClient::AndroidImpl::AndroidImpl(bool log) :
        _log(log),
        _timeout(-1)
    {
    }

    void HTTPClient::AndroidImpl::setTimeout(int milliseconds) {
        _timeout = milliseconds;
    }
    
    bool HTTPClient::AndroidImpl::makeRequest(const HTTPClient::Request& request, HeadersFunc headersFn, DataFunc dataFn) const {
        JNIEnv* jenv = AndroidUtils::GetCurrentThreadJNIEnv();
        AndroidUtils::JNILocalFrame jframe(jenv, 32, "HTTPClient::AndroidImpl::HTTPClientAndroidImpl");
        if (!jframe.isValid()) {
            Log::Error("HTTPClient::AndroidImpl::makeRequest: JNILocalFrame not valid");
            throw std::runtime_error("JNILocalFrame not valid");
        }
        
        {
            std::lock_guard<std::mutex> lock(_Mutex);
            if (!_URLClass) {
                _URLClass = std::unique_ptr<URLClass>(new URLClass(jenv));
            }
            if (!_HttpURLConnectionClass) {
                _HttpURLConnectionClass = std::unique_ptr<HttpURLConnectionClass>(new HttpURLConnectionClass(jenv));
            }
            if (!_InputStreamClass) {
                _InputStreamClass = std::unique_ptr<InputStreamClass>(new InputStreamClass(jenv));
            }
            if (!_OutputStreamClass) {
                _OutputStreamClass = std::unique_ptr<OutputStreamClass>(new OutputStreamClass(jenv));
            }
        }

        // Create URL
        jobject url = jenv->NewObject(_URLClass->clazz, _URLClass->constructor, jenv->NewStringUTF(request.url.c_str()));
        if (jenv->ExceptionCheck()) {
            jenv->ExceptionClear();
            throw NetworkException("Invalid URL", request.url);
        }

        // Open HTTP connection
        jobject conn = jenv->CallObjectMethod(url, _URLClass->openConnection);
        if (jenv->ExceptionCheck()) {
            jenv->ExceptionClear();
            throw NetworkException("Unable to open connection", request.url);
        }
        
        // Configure connection parameters
        jenv->CallVoidMethod(conn, _HttpURLConnectionClass->setRequestMethod, jenv->NewStringUTF(request.method.c_str())));
        jenv->CallVoidMethod(conn, _HttpURLConnectionClass->setDoInput, (jboolean)true);
        jenv->CallVoidMethod(conn, _HttpURLConnectionClass->setDoOutput, (jboolean)!request.contentType.empty());
        jenv->CallVoidMethod(conn, _HttpURLConnectionClass->setUseCaches, (jboolean)false);
        jenv->CallVoidMethod(conn, _HttpURLConnectionClass->setAllowUserInteraction, (jboolean)false);
        jenv->CallVoidMethod(conn, _HttpURLConnectionClass->setInstanceFollowRedirects, (jboolean)true);
        if (_timeout > 0) {
            jenv->CallVoidMethod(conn, _HttpURLConnectionClass->setConnectTimeout, _timeout);
            jenv->CallVoidMethod(conn, _HttpURLConnectionClass->setReadTimeout, _timeout);
        }
        
        // Set request headers
        for (auto it = request.headers.begin(); it != request.headers.end(); it++) {
            jstring key = jenv->NewStringUTF(it->first.c_str());
            jstring value = jenv->NewStringUTF(it->second.c_str());
            jenv->CallVoidMethod(conn, _HttpURLConnectionClass->setRequestProperty, key, value);
        }

        // If Content-Type is set, write request body to output stream<commit_msg>fix compile error<commit_after>#include "HTTPClientAndroidImpl.h"
#include "components/Exceptions.h"
#include "utils/AndroidUtils.h"
#include "utils/JNIUniqueGlobalRef.h"
#include "utils/Log.h"

#include <chrono>
#include <limits>
#include <regex>

#include <boost/lexical_cast.hpp>

namespace carto {
    
    struct HTTPClient::AndroidImpl::URLClass {
        JNIUniqueGlobalRef<jclass> clazz;
        jmethodID constructor;
        jmethodID openConnection;
        
        explicit URLClass(JNIEnv* jenv) {
            clazz = JNIUniqueGlobalRef<jclass>(jenv->NewGlobalRef(jenv->FindClass("java/net/URL")));
            constructor = jenv->GetMethodID(clazz, "<init>", "(Ljava/lang/String;)V");
            openConnection = jenv->GetMethodID(clazz, "openConnection", "()Ljava/net/URLConnection;");
        }
    };
    
    struct HTTPClient::AndroidImpl::HttpURLConnectionClass {
        JNIUniqueGlobalRef<jclass> clazz;
        jmethodID setRequestMethod;
        jmethodID setDoInput;
        jmethodID setDoOutput;
        jmethodID setUseCaches;
        jmethodID setAllowUserInteraction;
        jmethodID setInstanceFollowRedirects;
        jmethodID setRequestProperty;
        jmethodID setConnectTimeout;
        jmethodID setReadTimeout;
        jmethodID connect;
        jmethodID disconnect;
        jmethodID getResponseCode;
        jmethodID getHeaderFieldKey;
        jmethodID getHeaderField;
        jmethodID getInputStream;
        jmethodID getOutputStream;
        jmethodID getErrorStream;
        
        explicit HttpURLConnectionClass(JNIEnv* jenv) {
            clazz = JNIUniqueGlobalRef<jclass>(jenv->NewGlobalRef(jenv->FindClass("java/net/HttpURLConnection")));
            setRequestMethod = jenv->GetMethodID(clazz, "setRequestMethod", "(Ljava/lang/String;)V");
            setDoInput = jenv->GetMethodID(clazz, "setDoInput", "(Z)V");
            setDoOutput = jenv->GetMethodID(clazz, "setDoOutput", "(Z)V");
            setUseCaches = jenv->GetMethodID(clazz, "setUseCaches", "(Z)V");
            setAllowUserInteraction = jenv->GetMethodID(clazz, "setAllowUserInteraction", "(Z)V");
            setInstanceFollowRedirects = jenv->GetMethodID(clazz, "setInstanceFollowRedirects", "(Z)V");
            setRequestProperty = jenv->GetMethodID(clazz, "setRequestProperty", "(Ljava/lang/String;Ljava/lang/String;)V");
            setConnectTimeout = jenv->GetMethodID(clazz, "setConnectTimeout", "(I)V");
            setReadTimeout = jenv->GetMethodID(clazz, "setReadTimeout", "(I)V");
            connect = jenv->GetMethodID(clazz, "connect", "()V");
            disconnect = jenv->GetMethodID(clazz, "disconnect", "()V");
            getResponseCode = jenv->GetMethodID(clazz, "getResponseCode", "()I");
            getHeaderFieldKey = jenv->GetMethodID(clazz, "getHeaderFieldKey", "(I)Ljava/lang/String;");
            getHeaderField = jenv->GetMethodID(clazz, "getHeaderField", "(I)Ljava/lang/String;");
            getInputStream = jenv->GetMethodID(clazz, "getInputStream", "()Ljava/io/InputStream;");
            getOutputStream = jenv->GetMethodID(clazz, "getOutputStream", "()Ljava/io/OutputStream;");
            getErrorStream = jenv->GetMethodID(clazz, "getErrorStream", "()Ljava/io/InputStream;");
        }
    };
    
    struct HTTPClient::AndroidImpl::InputStreamClass {
        JNIUniqueGlobalRef<jclass> clazz;
        jmethodID read;
        jmethodID close;

        explicit InputStreamClass(JNIEnv* jenv) {
            clazz = JNIUniqueGlobalRef<jclass>(jenv->NewGlobalRef(jenv->FindClass("java/io/InputStream")));
            read = jenv->GetMethodID(clazz, "read", "([B)I");
            close = jenv->GetMethodID(clazz, "close", "()V");
        }
    };
    
    struct HTTPClient::AndroidImpl::OutputStreamClass {
        JNIUniqueGlobalRef<jclass> clazz;
        jmethodID write;
        jmethodID close;

        explicit OutputStreamClass(JNIEnv* jenv) {
            clazz = JNIUniqueGlobalRef<jclass>(jenv->NewGlobalRef(jenv->FindClass("java/io/OutputStream")));
            write = jenv->GetMethodID(clazz, "write", "([B)V");
            close = jenv->GetMethodID(clazz, "close", "()V");
        }
    };
    
    HTTPClient::AndroidImpl::AndroidImpl(bool log) :
        _log(log),
        _timeout(-1)
    {
    }

    void HTTPClient::AndroidImpl::setTimeout(int milliseconds) {
        _timeout = milliseconds;
    }
    
    bool HTTPClient::AndroidImpl::makeRequest(const HTTPClient::Request& request, HeadersFunc headersFn, DataFunc dataFn) const {
        JNIEnv* jenv = AndroidUtils::GetCurrentThreadJNIEnv();
        AndroidUtils::JNILocalFrame jframe(jenv, 32, "HTTPClient::AndroidImpl::HTTPClientAndroidImpl");
        if (!jframe.isValid()) {
            Log::Error("HTTPClient::AndroidImpl::makeRequest: JNILocalFrame not valid");
            throw std::runtime_error("JNILocalFrame not valid");
        }
        
        {
            std::lock_guard<std::mutex> lock(_Mutex);
            if (!_URLClass) {
                _URLClass = std::unique_ptr<URLClass>(new URLClass(jenv));
            }
            if (!_HttpURLConnectionClass) {
                _HttpURLConnectionClass = std::unique_ptr<HttpURLConnectionClass>(new HttpURLConnectionClass(jenv));
            }
            if (!_InputStreamClass) {
                _InputStreamClass = std::unique_ptr<InputStreamClass>(new InputStreamClass(jenv));
            }
            if (!_OutputStreamClass) {
                _OutputStreamClass = std::unique_ptr<OutputStreamClass>(new OutputStreamClass(jenv));
            }
        }

        // Create URL
        jobject url = jenv->NewObject(_URLClass->clazz, _URLClass->constructor, jenv->NewStringUTF(request.url.c_str()));
        if (jenv->ExceptionCheck()) {
            jenv->ExceptionClear();
            throw NetworkException("Invalid URL", request.url);
        }

        // Open HTTP connection
        jobject conn = jenv->CallObjectMethod(url, _URLClass->openConnection);
        if (jenv->ExceptionCheck()) {
            jenv->ExceptionClear();
            throw NetworkException("Unable to open connection", request.url);
        }
        
        // Configure connection parameters
        jenv->CallVoidMethod(conn, _HttpURLConnectionClass->setRequestMethod, jenv->NewStringUTF(request.method.c_str()));
        jenv->CallVoidMethod(conn, _HttpURLConnectionClass->setDoInput, (jboolean)true);
        jenv->CallVoidMethod(conn, _HttpURLConnectionClass->setDoOutput, (jboolean)!request.contentType.empty());
        jenv->CallVoidMethod(conn, _HttpURLConnectionClass->setUseCaches, (jboolean)false);
        jenv->CallVoidMethod(conn, _HttpURLConnectionClass->setAllowUserInteraction, (jboolean)false);
        jenv->CallVoidMethod(conn, _HttpURLConnectionClass->setInstanceFollowRedirects, (jboolean)true);
        if (_timeout > 0) {
            jenv->CallVoidMethod(conn, _HttpURLConnectionClass->setConnectTimeout, _timeout);
            jenv->CallVoidMethod(conn, _HttpURLConnectionClass->setReadTimeout, _timeout);
        }
        
        // Set request headers
        for (auto it = request.headers.begin(); it != request.headers.end(); it++) {
            jstring key = jenv->NewStringUTF(it->first.c_str());
            jstring value = jenv->NewStringUTF(it->second.c_str());
            jenv->CallVoidMethod(conn, _HttpURLConnectionClass->setRequestProperty, key, value);
        }

        // If Content-Type is set, write request body to output stream
{'fraction_non_alphanumeric': 0.1258446511005553, 'fraction_numerical': 0.000535224459757811, 'mean_word_length': 3.045466847090663, 'pattern_counts': {'":': 0, '<': 39, '<?xml version=': 0, '>': 169, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, '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': '3741561', 'n_tokens_mistral': 4340, 'n_tokens_neox': 4343, 'n_words': 683}