Android http client
This commit is contained in:
Родитель
19370071b2
Коммит
eabab405f3
|
@ -45,10 +45,6 @@
|
|||
namespace ARIASDK_NS_BEGIN
|
||||
{
|
||||
|
||||
#ifdef ANDROID
|
||||
extern ILogManager* g_jniLogManager;
|
||||
#endif
|
||||
|
||||
bool ILogManager::DispatchEventBroadcast(DebugEvent evt)
|
||||
{
|
||||
// LOCKGUARD(ILogManagerInternal::managers_lock);
|
||||
|
@ -268,11 +264,6 @@ namespace ARIASDK_NS_BEGIN
|
|||
LOG_INFO("Started up and running");
|
||||
m_alive = true;
|
||||
|
||||
#ifdef ANDROID
|
||||
if (g_jniLogManager == nullptr) {
|
||||
g_jniLogManager = this;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -334,11 +325,7 @@ namespace ARIASDK_NS_BEGIN
|
|||
LOG_INFO("Shutdown complete in %lld ms", shutTime);
|
||||
|
||||
m_alive = false;
|
||||
#ifdef ANDROID
|
||||
if (g_jniLogManager == this) {
|
||||
g_jniLogManager = nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
status_t LogManagerImpl::Flush()
|
||||
|
|
|
@ -4,13 +4,15 @@
|
|||
#define MATSDK_DECLSPEC __declspec(dllexport)
|
||||
#endif
|
||||
|
||||
#ifndef ANDROID
|
||||
#include "http/HttpClient_CAPI.hpp"
|
||||
#endif
|
||||
#include "LogManagerProvider.hpp"
|
||||
#include "mat.h"
|
||||
#include "pal/TaskDispatcher_CAPI.hpp"
|
||||
#include "utils/Utils.hpp"
|
||||
|
||||
#include "PAL.hpp"
|
||||
#include "pal/PAL.hpp"
|
||||
|
||||
#include "CommonFields.h"
|
||||
|
||||
|
@ -128,6 +130,7 @@ evt_status_t mat_open_core(
|
|||
// Remember the original config string. Needed to avoid hash code collisions
|
||||
clients[code].ctx_data = config;
|
||||
|
||||
#ifndef ANDROID
|
||||
// Create custom HttpClient
|
||||
if (httpSendFn != nullptr && httpCancelFn != nullptr)
|
||||
{
|
||||
|
@ -142,7 +145,7 @@ evt_status_t mat_open_core(
|
|||
return EFAULT;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
// Create custom worker thread
|
||||
if (taskDispatcherQueueFn != nullptr && taskDispatcherCancelFn != nullptr && taskDispatcherJoinFn != nullptr)
|
||||
{
|
||||
|
|
|
@ -19,9 +19,13 @@
|
|||
#elif defined(MATSDK_PAL_CPP11)
|
||||
#if TARGET_OS_IPHONE || (defined(__APPLE__) && defined(APPLE_HTTP))
|
||||
#include "http/HttpClient_Apple.hpp"
|
||||
#else
|
||||
#ifdef ANDROID
|
||||
#include "http/HttpClient_Android.hpp"
|
||||
#else
|
||||
#include "http/HttpClient_Curl.hpp"
|
||||
#endif
|
||||
#endif
|
||||
#else
|
||||
#error The library cannot work without an HTTP client implementation.
|
||||
#endif
|
||||
|
@ -51,12 +55,18 @@ namespace ARIASDK_NS_BEGIN {
|
|||
LOG_TRACE("Creating HttpClient_Apple");
|
||||
return std::make_shared<HttpClient_Apple>();
|
||||
}
|
||||
#else
|
||||
#ifdef ANDROID
|
||||
std::shared_ptr<IHttpClient> HttpClientFactory::Create() {
|
||||
return HttpClient_Android::GetClientInstance();
|
||||
}
|
||||
#else
|
||||
std::shared_ptr<IHttpClient> HttpClientFactory::Create() {
|
||||
LOG_TRACE("Creating HttpClient_Curl");
|
||||
return std::make_shared<HttpClient_Curl>();
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
#else
|
||||
#error The library cannot work without an HTTP client implementation.
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,450 @@
|
|||
#include "HttpClient_Android.hpp"
|
||||
#include <algorithm>
|
||||
#include <jni.h>
|
||||
#include <cstdio>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
namespace ARIASDK_NS_BEGIN {
|
||||
|
||||
constexpr static auto Tag = "HttpClient_Android";
|
||||
|
||||
HttpClient_Android::HttpRequest::~HttpRequest() noexcept
|
||||
{
|
||||
EraseFromParent();
|
||||
if (m_java_request)
|
||||
{
|
||||
JNIEnv* env = nullptr;
|
||||
if (HttpClient_Android::s_java_vm->AttachCurrentThread(&env, nullptr) != JNI_OK)
|
||||
{
|
||||
return;
|
||||
}
|
||||
env->DeleteGlobalRef(m_java_request);
|
||||
}
|
||||
}
|
||||
|
||||
void HttpClient_Android::HttpRequest::SetMethod(std::string const& method)
|
||||
{
|
||||
m_method = method;
|
||||
}
|
||||
|
||||
void HttpClient_Android::HttpRequest::SetUrl(std::string const& url)
|
||||
{
|
||||
m_url = url;
|
||||
}
|
||||
|
||||
HttpHeaders& HttpClient_Android::HttpRequest::GetHeaders()
|
||||
{
|
||||
return m_headers;
|
||||
}
|
||||
|
||||
void HttpClient_Android::HttpRequest::SetBody(std::vector<uint8_t>& body)
|
||||
{
|
||||
m_body = body; // copy assignment for Great Copy Fun
|
||||
}
|
||||
|
||||
std::vector<uint8_t>& HttpClient_Android::HttpRequest::GetBody()
|
||||
{
|
||||
return m_body;
|
||||
}
|
||||
|
||||
void HttpClient_Android::HttpRequest::SetLatency(EventLatency latency)
|
||||
{
|
||||
// tbh, probably not ever going to do anything about this
|
||||
}
|
||||
|
||||
size_t HttpClient_Android::HttpRequest::GetSizeEstimate() const
|
||||
{
|
||||
return m_body.size(); // nope, but what the heck
|
||||
}
|
||||
|
||||
HttpClient_Android::HttpClient_Android()
|
||||
{
|
||||
m_id = 0;
|
||||
}
|
||||
|
||||
HttpClient_Android::~HttpClient_Android()
|
||||
{
|
||||
JNIEnv* env;
|
||||
s_java_vm->AttachCurrentThread(&env, nullptr);
|
||||
env->DeleteGlobalRef(m_client);
|
||||
m_client = nullptr; // well, why not?
|
||||
}
|
||||
|
||||
IHttpRequest* HttpClient_Android::CreateRequest()
|
||||
{
|
||||
HttpRequest * local_request(new HttpRequest(*this));
|
||||
std::lock_guard<std::mutex> lock(m_requestsMutex);
|
||||
m_requests.push_back(std::move(local_request));
|
||||
return local_request;
|
||||
}
|
||||
|
||||
// we do NOT own the callback object
|
||||
// we will segfault if callback is nullptr
|
||||
void HttpClient_Android::SendRequestAsync(IHttpRequest* request,
|
||||
IHttpResponseCallback* callback)
|
||||
{
|
||||
JNIEnv* env = nullptr;
|
||||
std::string const& target_id(request->GetId());
|
||||
|
||||
if (s_java_vm->AttachCurrentThread(&env, nullptr) != JNI_OK)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
HttpRequest* r = nullptr;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_requestsMutex);
|
||||
for (auto&& u : m_requests)
|
||||
{
|
||||
if (u->m_id == target_id)
|
||||
{
|
||||
if (u == request)
|
||||
{
|
||||
r = u;
|
||||
r->m_callback = callback;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!r)
|
||||
{
|
||||
return;
|
||||
}
|
||||
HttpHeaders const& headers = r->GetHeaders();
|
||||
|
||||
size_t total_size = 0;
|
||||
for (auto&& u : headers)
|
||||
{
|
||||
total_size += u.first.length() + u.second.length();
|
||||
}
|
||||
|
||||
auto header_buffer = env->NewByteArray(static_cast<jsize>(total_size));
|
||||
size_t offset = 0;
|
||||
std::vector<jint> index_buffer;
|
||||
index_buffer.reserve(2u * headers.size());
|
||||
for (auto&& u : headers)
|
||||
{
|
||||
auto key_len = u.first.length();
|
||||
index_buffer.push_back(key_len);
|
||||
if (key_len > 0)
|
||||
{
|
||||
env->SetByteArrayRegion(header_buffer, offset, key_len, reinterpret_cast<jbyte const*>(u.first.data()));
|
||||
}
|
||||
offset += key_len;
|
||||
auto value_len = u.second.length();
|
||||
index_buffer.push_back(value_len);
|
||||
if (value_len > 0)
|
||||
{
|
||||
env->SetByteArrayRegion(header_buffer, offset, value_len, reinterpret_cast<jbyte const*>(u.second.data()));
|
||||
}
|
||||
offset += value_len;
|
||||
}
|
||||
auto header_lengths = env->NewIntArray(static_cast<jsize>(index_buffer.size()));
|
||||
env->SetIntArrayRegion(header_lengths, 0, index_buffer.size(), index_buffer.data());
|
||||
|
||||
auto body = env->NewByteArray(r->m_body.size());
|
||||
env->SetByteArrayRegion(body, 0, r->m_body.size(),
|
||||
reinterpret_cast<const jbyte*>(r->m_body.data()));
|
||||
auto java_id = env->NewStringUTF(request->GetId().c_str());
|
||||
auto result = env->CallObjectMethod(m_client, m_create_id,
|
||||
env->NewStringUTF(r->m_url.c_str()),
|
||||
env->NewStringUTF(r->m_method.c_str()),
|
||||
body,
|
||||
java_id,
|
||||
header_lengths,
|
||||
header_buffer);
|
||||
HttpRequest* cancel_me = nullptr;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_requestsMutex);
|
||||
for (auto&& u : m_requests)
|
||||
{
|
||||
if (u->m_id == target_id)
|
||||
{
|
||||
u->m_callback = callback;
|
||||
u->m_java_request = env->NewGlobalRef(result);
|
||||
if (!result || u->m_cancel_request)
|
||||
{
|
||||
cancel_me = u;
|
||||
u = m_requests.back();
|
||||
m_requests.pop_back();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (cancel_me)
|
||||
{
|
||||
CallbackForCancel(env, cancel_me);
|
||||
}
|
||||
else
|
||||
{
|
||||
env->CallVoidMethod(m_client, m_execute_id, result);
|
||||
}
|
||||
}
|
||||
|
||||
void HttpClient_Android::CallbackForCancel(JNIEnv* env, HttpRequest* request)
|
||||
{
|
||||
if (request->m_java_request && env)
|
||||
{
|
||||
auto request_class = env->GetObjectClass(request->m_java_request);
|
||||
auto cancel_method = env->GetMethodID(request_class, "cancel", "(Z)Z");
|
||||
env->CallBooleanMethod(request->m_java_request, cancel_method, static_cast<jboolean>(JNI_TRUE));
|
||||
}
|
||||
if (request->m_callback)
|
||||
{
|
||||
auto failure = new HttpResponse(request->m_id);
|
||||
request->m_callback->OnHttpResponse(failure);
|
||||
}
|
||||
}
|
||||
|
||||
void HttpClient_Android::CancelRequestAsync(std::string const& id)
|
||||
{
|
||||
JNIEnv* env;
|
||||
if (s_java_vm->AttachCurrentThread(&env, nullptr) != JNI_OK)
|
||||
{
|
||||
return;
|
||||
}
|
||||
HttpRequest* cancel_me = nullptr;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_requestsMutex);
|
||||
for (auto&& u : m_requests)
|
||||
{
|
||||
if (u->m_id == id)
|
||||
{
|
||||
if (u->m_cancel_request)
|
||||
{
|
||||
return; // someone already called cancel for this request
|
||||
}
|
||||
if (u->m_callback)
|
||||
{
|
||||
cancel_me = u;
|
||||
}
|
||||
else
|
||||
{
|
||||
u->m_cancel_request = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (cancel_me)
|
||||
{
|
||||
CallbackForCancel(env, std::move(cancel_me));
|
||||
}
|
||||
}
|
||||
|
||||
void HttpClient_Android::CancelAllRequests()
|
||||
{
|
||||
JNIEnv* env;
|
||||
if (s_java_vm->AttachCurrentThread(&env, nullptr) != JNI_OK)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// We will cancel, notify, and destroy all requests, so swap
|
||||
// them into a local vector inside the mutex.
|
||||
|
||||
std::vector<HttpRequest *> local_requests;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_requestsMutex);
|
||||
local_requests.swap(m_requests);
|
||||
}
|
||||
jclass request_class = nullptr;
|
||||
jmethodID cancel_method = nullptr;
|
||||
jmethodID get_method = nullptr;
|
||||
for (const auto& u : local_requests)
|
||||
{
|
||||
if (u->m_java_request)
|
||||
{
|
||||
if (!request_class)
|
||||
{
|
||||
request_class = env->GetObjectClass(u->m_java_request);
|
||||
}
|
||||
if (!cancel_method)
|
||||
{
|
||||
cancel_method = env->GetMethodID(request_class, "cancel", "(Z)Z");
|
||||
}
|
||||
jboolean interrupt_yes = JNI_TRUE;
|
||||
env->CallBooleanMethod(u->m_java_request, cancel_method, interrupt_yes);
|
||||
if (!get_method)
|
||||
{
|
||||
get_method = env->GetMethodID(request_class, "get", "()Z");
|
||||
}
|
||||
// wait for the request to complete
|
||||
env->CallBooleanMethod(u->m_java_request, get_method);
|
||||
}
|
||||
if (u->m_callback)
|
||||
{
|
||||
auto failure = new HttpResponse(u->m_id);
|
||||
u->m_callback->OnHttpResponse(failure);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HttpClient_Android::SetClient(JNIEnv* env, jobject client_)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_requestsMutex);
|
||||
if (m_client)
|
||||
{
|
||||
env->DeleteGlobalRef(m_client);
|
||||
}
|
||||
m_client = env->NewGlobalRef(client_);
|
||||
m_client_class = env->GetObjectClass(m_client);
|
||||
m_create_id = env->GetMethodID(m_client_class, "createTask",
|
||||
"(Ljava/lang/String;Ljava/lang/String;[BLjava/lang/String;[I[B)Ljava/util/concurrent/FutureTask;");
|
||||
m_execute_id = env->GetMethodID(m_client_class, "executeTask",
|
||||
"(Ljava/util/concurrent/FutureTask;)V");
|
||||
|
||||
env->GetJavaVM(&s_java_vm);
|
||||
}
|
||||
|
||||
void HttpClient_Android::EraseRequest(HttpRequest* request)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_requestsMutex);
|
||||
for (auto&& u : m_requests)
|
||||
{
|
||||
if (u == request)
|
||||
{
|
||||
u = m_requests.back();
|
||||
m_requests.pop_back();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HttpClient_Android::HttpRequest* HttpClient_Android::GetRequest(std::string id)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_requestsMutex);
|
||||
for (auto&& u : m_requests)
|
||||
{
|
||||
if (u->m_id == id)
|
||||
{
|
||||
return u;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// since std::to_string() is unavailable (for now), old-school
|
||||
// string from number.
|
||||
|
||||
std::string HttpClient_Android::NextIdString()
|
||||
{
|
||||
auto id_binary = (m_id += 1u);
|
||||
constexpr size_t digits = 11;
|
||||
constexpr uint64_t shift = 6;
|
||||
constexpr uint64_t mask = (static_cast<uint64_t>(1) << shift) - 1u;
|
||||
char buffer[digits + 1];
|
||||
size_t i;
|
||||
for (i = 0; id_binary && i < digits; ++i)
|
||||
{
|
||||
auto r = id_binary & mask;
|
||||
buffer[i] = ' ' + r;
|
||||
id_binary >>= shift;
|
||||
}
|
||||
buffer[i] = 0;
|
||||
return std::string(buffer);
|
||||
}
|
||||
|
||||
void HttpClient_Android::CreateClientInstance(JNIEnv* env,
|
||||
jobject java_client)
|
||||
{
|
||||
auto client = std::make_shared<HttpClient_Android>();
|
||||
s_client = client;
|
||||
|
||||
client->SetClient(env, java_client);
|
||||
}
|
||||
|
||||
void HttpClient_Android::SetJavaVM(JavaVM* vm)
|
||||
{
|
||||
HttpClient_Android::s_java_vm = vm;
|
||||
}
|
||||
|
||||
void HttpClient_Android::DeleteClientInstance(JNIEnv* env)
|
||||
{
|
||||
s_client.reset();
|
||||
}
|
||||
|
||||
JavaVM* HttpClient_Android::s_java_vm = nullptr;
|
||||
|
||||
std::shared_ptr<HttpClient_Android>
|
||||
HttpClient_Android::GetClientInstance()
|
||||
{
|
||||
return std::shared_ptr<HttpClient_Android>(s_client);
|
||||
}
|
||||
|
||||
std::shared_ptr<HttpClient_Android> HttpClient_Android::s_client;
|
||||
std::mutex HttpClient_Android::s_client_mutex;
|
||||
|
||||
} ARIASDK_NS_END
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT void
|
||||
|
||||
JNICALL
|
||||
Java_com_microsoft_office_ariasdk_httpClient_createClientInstance(JNIEnv* env,
|
||||
jobject java_client)
|
||||
{
|
||||
Microsoft::Applications::Events::HttpClient_Android::CreateClientInstance(env, java_client);
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT void
|
||||
|
||||
JNICALL
|
||||
Java_com_microsoft_office_ariasdk_httpClient_deleteClientInstance(JNIEnv* env)
|
||||
{
|
||||
Microsoft::Applications::Events::HttpClient_Android::DeleteClientInstance(env);
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT void
|
||||
|
||||
JNICALL
|
||||
Java_com_microsoft_office_ariasdk_Request_nativeDispatchCallback(
|
||||
JNIEnv* env,
|
||||
jobject /* this */,
|
||||
jstring id,
|
||||
jint statusCode,
|
||||
jobjectArray headers,
|
||||
jbyteArray body)
|
||||
{
|
||||
size_t id_length = env->GetStringUTFLength(id);
|
||||
auto id_utf = env->GetStringUTFChars(id, nullptr);
|
||||
std::string id_string(id_utf, id_utf + id_length);
|
||||
env->ReleaseStringUTFChars(id, id_utf);
|
||||
using HttpClient_Android = Microsoft::Applications::Events::HttpClient_Android;
|
||||
auto client = HttpClient_Android::GetClientInstance();
|
||||
auto request = client->GetRequest(id_string);
|
||||
if (!request)
|
||||
{
|
||||
return;
|
||||
}
|
||||
auto callback = request->GetCallback();
|
||||
auto response = new Microsoft::Applications::Events::HttpClient_Android::HttpResponse(request->GetId());
|
||||
response->SetResponse(statusCode);
|
||||
|
||||
size_t n_headers = env->GetArrayLength(headers);
|
||||
for (size_t i = 0; (i + 1u) < n_headers; i += 2)
|
||||
{
|
||||
auto k = static_cast<jstring>(env->GetObjectArrayElement(headers, i));
|
||||
auto v = static_cast<jstring>(env->GetObjectArrayElement(headers, i + 1));
|
||||
const char* k_utf = env->GetStringUTFChars(k, nullptr);
|
||||
std::string key(k_utf, env->GetStringUTFLength(k));
|
||||
env->ReleaseStringUTFChars(k, k_utf);
|
||||
const char* v_utf = env->GetStringUTFChars(v, nullptr);
|
||||
std::string value(v_utf, env->GetStringUTFLength(v));
|
||||
env->ReleaseStringUTFChars(v, v_utf);
|
||||
response->AddHeader(std::move(key), std::move(value));
|
||||
}
|
||||
auto body_pointer = env->GetByteArrayElements(body, nullptr);
|
||||
response->SetBody(env->GetArrayLength(body),
|
||||
reinterpret_cast<uint8_t*>(body_pointer));
|
||||
env->ReleaseByteArrayElements(body, body_pointer, JNI_ABORT);
|
||||
// callback will own response
|
||||
callback->OnHttpResponse(response);
|
||||
}
|
|
@ -0,0 +1,193 @@
|
|||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
#include <IHttpClient.hpp>
|
||||
#include <atomic>
|
||||
#include <jni.h>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
|
||||
namespace ARIASDK_NS_BEGIN {
|
||||
|
||||
class HttpClient_Android : public IHttpClient
|
||||
{
|
||||
|
||||
public:
|
||||
class HttpResponse : public IHttpResponse {
|
||||
|
||||
public:
|
||||
HttpResponse(const std::string &id)
|
||||
: m_id(id)
|
||||
{}
|
||||
|
||||
const std::string& GetId() const override {
|
||||
return m_id;
|
||||
}
|
||||
|
||||
HttpResult GetResult() const override {
|
||||
switch (m_response) {
|
||||
case 0:
|
||||
return HttpResult_LocalFailure;
|
||||
case -1:
|
||||
return HttpResult_NetworkFailure;
|
||||
default:
|
||||
return HttpResult_OK;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int GetStatusCode() const override {
|
||||
return m_response;
|
||||
}
|
||||
|
||||
const HttpHeaders &GetHeaders() const override {
|
||||
return m_headers;
|
||||
}
|
||||
|
||||
const std::vector<uint8_t, std::allocator<uint8_t>> &GetBody() const override {
|
||||
return m_body;
|
||||
}
|
||||
|
||||
void SetResponse(int response)
|
||||
{
|
||||
m_response = response;
|
||||
}
|
||||
|
||||
void AddHeader(std::string &&key, std::string &&value)
|
||||
{
|
||||
m_headers.emplace(std::move(key), std::move(value));
|
||||
}
|
||||
|
||||
void SetBody(size_t length, const uint8_t *body)
|
||||
{
|
||||
m_body.assign(body, body+length);
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_id;
|
||||
HttpHeaders m_headers;
|
||||
std::vector<uint8_t, std::allocator<uint8_t>> m_body;
|
||||
int m_response = 0;
|
||||
};
|
||||
|
||||
public:
|
||||
class HttpRequest : public IHttpRequest
|
||||
{
|
||||
public:
|
||||
HttpRequest() = delete;
|
||||
|
||||
HttpRequest(HttpClient_Android &parent_)
|
||||
: m_parent(parent_)
|
||||
, m_id(std::move(parent_.NextIdString()))
|
||||
{}
|
||||
|
||||
~HttpRequest() noexcept;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the request ID.
|
||||
/// </summary>
|
||||
const std::string& GetId() const override
|
||||
{
|
||||
return m_id;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the request method
|
||||
/// </summary>
|
||||
/// <param name="method">A string that contains the the name of the method to set (e.g., <i>GET</i>).</param>
|
||||
void SetMethod(std::string const& method) override;
|
||||
|
||||
/// <summary>
|
||||
/// Sets the request URI.
|
||||
/// </summary>
|
||||
/// <param name="url">A string that contains the URI to set.</param>
|
||||
void SetUrl(std::string const& url) override;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the request headers.
|
||||
/// </summary>
|
||||
/// <returns>The HTTP headers in an HttpHeaders object.</returns>
|
||||
HttpHeaders& GetHeaders() override;
|
||||
|
||||
/// <summary>
|
||||
/// Sets the request body.
|
||||
/// </summary>
|
||||
/// <param name="body">A standard vector that contains the message body.</param>
|
||||
void SetBody(std::vector<uint8_t>& body) override;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the request body.
|
||||
/// </summary>
|
||||
std::vector<uint8_t>& GetBody() override;
|
||||
|
||||
/// <summary>
|
||||
/// Sets the request latency.
|
||||
/// </summary>
|
||||
/// <param name="priority">The event latency, as one of the EventLatency enumeration values.</param>
|
||||
void SetLatency(EventLatency latency) override;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the size of the request message body.
|
||||
/// </summary>
|
||||
/// <returns>The size of the request message body, in bytes.</returns>
|
||||
size_t GetSizeEstimate() const override;
|
||||
|
||||
IHttpResponseCallback* GetCallback() const
|
||||
{
|
||||
return m_callback;
|
||||
}
|
||||
|
||||
void EraseFromParent()
|
||||
{
|
||||
m_parent.EraseRequest(this);
|
||||
}
|
||||
|
||||
protected:
|
||||
HttpClient_Android & m_parent;
|
||||
HttpHeaders m_headers;
|
||||
IHttpResponseCallback* m_callback = nullptr;
|
||||
std::string m_id;
|
||||
std::string m_method;
|
||||
std::string m_url;
|
||||
std::vector<uint8_t> m_body;
|
||||
jobject m_java_request = nullptr;
|
||||
bool m_cancel_request = false;
|
||||
|
||||
friend HttpClient_Android;
|
||||
};
|
||||
|
||||
public:
|
||||
HttpClient_Android();
|
||||
~HttpClient_Android();
|
||||
IHttpRequest* CreateRequest() override;
|
||||
void SendRequestAsync(IHttpRequest* request, IHttpResponseCallback* callback) override;
|
||||
void CancelRequestAsync(std::string const& id) override;
|
||||
void CancelAllRequests() override;
|
||||
void SetClient(JNIEnv* env, jobject c);
|
||||
void EraseRequest(HttpRequest*);
|
||||
HttpRequest* GetRequest(std::string id);
|
||||
std::string NextIdString();
|
||||
|
||||
static void CreateClientInstance(JNIEnv* env,
|
||||
jobject java_client);
|
||||
static void DeleteClientInstance(JNIEnv* env);
|
||||
static std::shared_ptr<HttpClient_Android> GetClientInstance();
|
||||
void CallbackForCancel(JNIEnv* env, HttpRequest * t);
|
||||
static void SetJavaVM(JavaVM * vm);
|
||||
|
||||
protected:
|
||||
std::mutex m_requestsMutex;
|
||||
std::vector<HttpRequest *> m_requests;
|
||||
|
||||
jobject m_client = nullptr;
|
||||
jclass m_client_class = nullptr;
|
||||
jmethodID m_send_id = nullptr;
|
||||
jmethodID m_create_id = nullptr;
|
||||
jmethodID m_execute_id = nullptr;
|
||||
static JavaVM *s_java_vm;
|
||||
std::atomic<uint64_t> m_id;
|
||||
static std::shared_ptr<HttpClient_Android> s_client;
|
||||
static std::mutex s_client_mutex;
|
||||
|
||||
};
|
||||
|
||||
} ARIASDK_NS_END
|
|
@ -0,0 +1,177 @@
|
|||
package com.microsoft.office.ariasdk;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
import java.util.Vector;
|
||||
import java.util.concurrent.FutureTask;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
/**
|
||||
* Created by maharrim on 8/21/2019.
|
||||
*/
|
||||
|
||||
class Request implements Runnable {
|
||||
|
||||
Request(String url,
|
||||
String method,
|
||||
byte[] body,
|
||||
String request_id,
|
||||
int[] header_length,
|
||||
byte[] header_buffer
|
||||
) throws java.net.MalformedURLException, java.io.IOException
|
||||
{
|
||||
m_url = new URL(url);
|
||||
m_connection = (HttpURLConnection) m_url.openConnection();
|
||||
m_connection.setRequestMethod(method);
|
||||
m_body = body;
|
||||
if (body.length > 0) {
|
||||
m_connection.setFixedLengthStreamingMode(body.length);
|
||||
m_connection.setDoOutput(true);
|
||||
}
|
||||
m_request_id = request_id;
|
||||
int offset = 0;
|
||||
for (int i = 0; i + 1 < header_length.length; i += 2) {
|
||||
String k = new String(header_buffer, offset, header_length[i], UTF_8);
|
||||
offset += header_length[i];
|
||||
String v = new String(header_buffer, offset, header_length[i+1], UTF_8);
|
||||
offset += header_length[i+1];
|
||||
m_connection.setRequestProperty(k, v);
|
||||
}
|
||||
}
|
||||
|
||||
public void run()
|
||||
{
|
||||
String[] headerArray = {};
|
||||
byte[] body = {};
|
||||
int response = 0;
|
||||
try {
|
||||
if (m_body.length > 0) {
|
||||
OutputStream body_stream = m_connection.getOutputStream();
|
||||
body_stream.write(m_body);
|
||||
}
|
||||
response = m_connection.getResponseCode(); // may throw
|
||||
Map<String, List<String>> headers = m_connection.getHeaderFields();
|
||||
Vector<String> headerList = new Vector<String>();
|
||||
for (Map.Entry<String, List<String>> entry : headers.entrySet()) {
|
||||
if (entry.getKey() != null) {
|
||||
for (String v : entry.getValue()) {
|
||||
headerList.add(entry.getKey());
|
||||
headerList.add(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
headerArray = headerList.toArray(headerArray);
|
||||
BufferedInputStream in;
|
||||
if (response >= 300) {
|
||||
in = new BufferedInputStream(m_connection.getErrorStream());
|
||||
}
|
||||
else {
|
||||
in = new BufferedInputStream(m_connection.getInputStream());
|
||||
}
|
||||
byte[] buffer = new byte[1024];
|
||||
Vector<byte[]> buffers = new Vector<byte[]>();
|
||||
int size = 0;
|
||||
while (true) {
|
||||
int n = in.read(buffer, 0, 1024);
|
||||
if (n < 0) {
|
||||
break;
|
||||
}
|
||||
if (n > 0) {
|
||||
buffers.add(java.util.Arrays.copyOfRange(buffer, 0, n));
|
||||
size += n;
|
||||
}
|
||||
}
|
||||
body = new byte[size];
|
||||
int index = 0;
|
||||
for (byte[] chunk : buffers) {
|
||||
for (byte b : chunk) {
|
||||
body[index] = b;
|
||||
index += 1;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
/* pass this on as a response of 0 */
|
||||
e.getMessage();
|
||||
} finally {
|
||||
m_connection.disconnect();
|
||||
}
|
||||
dispatchCallback(
|
||||
m_request_id,
|
||||
response,
|
||||
headerArray,
|
||||
body);
|
||||
}
|
||||
|
||||
public void dispatchCallback(String id,
|
||||
int response,
|
||||
Object[] headers,
|
||||
byte[] body)
|
||||
{
|
||||
// this stub makes it easier to mock this method in
|
||||
// Java unit tests.
|
||||
nativeDispatchCallback(id, response, headers, body);
|
||||
}
|
||||
|
||||
public native void nativeDispatchCallback(String id,
|
||||
int response,
|
||||
Object[] headers,
|
||||
byte[] body);
|
||||
|
||||
private URL m_url;
|
||||
private HttpURLConnection m_connection;
|
||||
private byte[] m_body = {};
|
||||
public String m_request_id;
|
||||
}
|
||||
|
||||
public class httpClient{
|
||||
public httpClient(int n_threads)
|
||||
{
|
||||
m_executor = Executors.newFixedThreadPool(n_threads);
|
||||
createClientInstance();
|
||||
}
|
||||
|
||||
public void finalize()
|
||||
{
|
||||
deleteClientInstance();
|
||||
m_executor.shutdown();
|
||||
}
|
||||
|
||||
public native void createClientInstance();
|
||||
public native void deleteClientInstance();
|
||||
public FutureTask<Boolean> createTask(String url,
|
||||
String method,
|
||||
byte[] body,
|
||||
String request_id,
|
||||
int[] header_index,
|
||||
byte[] header_buffer)
|
||||
{
|
||||
try {
|
||||
Request r = new Request(url, method, body, request_id, header_index, header_buffer);
|
||||
FutureTask<Boolean> t = new FutureTask<Boolean>(r, true);
|
||||
m_executor.execute(t);
|
||||
return t;
|
||||
}
|
||||
catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void executeTask(FutureTask<Boolean> t)
|
||||
{
|
||||
m_executor.execute(t);
|
||||
}
|
||||
|
||||
ExecutorService m_executor;
|
||||
}
|
|
@ -8,7 +8,9 @@
|
|||
//#define HAVE_MAT_DEFAULTDATAVIEWER
|
||||
#endif
|
||||
#define HAVE_MAT_JSONHPP
|
||||
#ifndef ANDROID
|
||||
#define HAVE_MAT_ZLIB
|
||||
#endif
|
||||
#define HAVE_MAT_LOGGING
|
||||
#define HAVE_MAT_STORAGE
|
||||
#define HAVE_MAT_DEFAULT_HTTP_CLIENT
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#include "EventProperty.hpp"
|
||||
#include <EventProperty.hpp>
|
||||
|
||||
#include "utils/Utils.hpp"
|
||||
|
||||
|
@ -73,7 +73,7 @@ namespace ARIASDK_NS_BEGIN {
|
|||
// Convert to set of integer values
|
||||
unsigned long p0;
|
||||
unsigned int p1, p2, p3, p4, p5, p6, p7, p8, p9, p10;
|
||||
if (11 == sscanf_s(str,
|
||||
if (11 == sscanf_s (str,
|
||||
"%08lX-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X",
|
||||
&p0, &p1, &p2, &p3, &p4, &p5, &p6, &p7, &p8, &p9, &p10))
|
||||
{
|
||||
|
|
|
@ -109,10 +109,12 @@ namespace ARIASDK_NS_BEGIN {
|
|||
result = P_tmpdir;
|
||||
#endif
|
||||
}
|
||||
#ifdef _PATH_TMP
|
||||
if (result.empty())
|
||||
{
|
||||
result = _PATH_TMP;
|
||||
}
|
||||
#endif
|
||||
if (result.empty())
|
||||
{
|
||||
result = "/tmp";
|
||||
|
|
|
@ -29,7 +29,9 @@ namespace std
|
|||
#endif
|
||||
|
||||
#ifdef __unix__
|
||||
#ifndef ANDROID
|
||||
#include <execinfo.h>
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
|
|
Загрузка…
Ссылка в новой задаче