AndroidClient.cpp 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. #include "AndroidClient.h"
  2. #ifdef USE_ANDROID_BACKEND
  3. #include <sstream>
  4. #include <type_traits>
  5. #include <dlfcn.h>
  6. std::string replace(const std::string &str, const std::string &from, const std::string &to)
  7. {
  8. std::stringstream ss;
  9. size_t oldpos = 0;
  10. while (true)
  11. {
  12. size_t pos = str.find(from, oldpos);
  13. if (pos == std::string::npos)
  14. {
  15. ss << str.substr(oldpos);
  16. break;
  17. }
  18. ss << str.substr(oldpos, pos - oldpos) << to;
  19. oldpos = pos + from.length();
  20. }
  21. return ss.str();
  22. }
  23. std::string fromJavaMUTF(const std::string &str)
  24. {
  25. static std::string null("", 1);
  26. return replace(str, "\xC0\x80", null);
  27. }
  28. jstring newStringUTF(JNIEnv *env, const std::string &str)
  29. {
  30. static std::string null("", 1);
  31. std::string newStr = replace(str, null, "\xC0\x80");
  32. jstring jstr = env->NewStringUTF(newStr.c_str());
  33. return jstr;
  34. }
  35. std::string getStringUTF(JNIEnv *env, jstring str)
  36. {
  37. static std::string null("", 1);
  38. const char *c = env->GetStringUTFChars(str, nullptr);
  39. std::string result = replace(c, "\xC0\x80", null);
  40. env->ReleaseStringUTFChars(str, c);
  41. return result;
  42. }
  43. AndroidClient::AndroidClient()
  44. : HTTPSClient()
  45. , SDL_AndroidGetJNIEnv(nullptr)
  46. {
  47. // Look for SDL_AndroidGetJNIEnv
  48. SDL_AndroidGetJNIEnv = (decltype(SDL_AndroidGetJNIEnv)) dlsym(RTLD_DEFAULT, "SDL_AndroidGetJNIEnv");
  49. }
  50. bool AndroidClient::valid() const
  51. {
  52. if (SDL_AndroidGetJNIEnv)
  53. {
  54. JNIEnv *env = SDL_AndroidGetJNIEnv();
  55. if (env)
  56. {
  57. jclass httpsClass = env->FindClass("org/love2d/luahttps/LuaHTTPS");
  58. env->DeleteLocalRef(httpsClass);
  59. return httpsClass != nullptr;
  60. }
  61. }
  62. return false;
  63. }
  64. HTTPSClient::Reply AndroidClient::request(const HTTPSClient::Request &req)
  65. {
  66. JNIEnv *env = SDL_AndroidGetJNIEnv();
  67. jclass httpsClass = env->FindClass("org/love2d/luahttps/LuaHTTPS");
  68. if (httpsClass == nullptr)
  69. {
  70. env->ExceptionClear();
  71. throw std::runtime_error("Could not find class 'org.love2d.luahttps.LuaHTTPS'");
  72. }
  73. jmethodID constructor = env->GetMethodID(httpsClass, "<init>", "()V");
  74. jmethodID setURL = env->GetMethodID(httpsClass, "setUrl", "(Ljava/lang/String;)V");
  75. jmethodID request = env->GetMethodID(httpsClass, "request", "()Z");
  76. jmethodID getInterleavedHeaders = env->GetMethodID(httpsClass, "getInterleavedHeaders", "()[Ljava/lang/String;");
  77. jmethodID getResponse = env->GetMethodID(httpsClass, "getResponse", "()[B");
  78. jmethodID getResponseCode = env->GetMethodID(httpsClass, "getResponseCode", "()I");
  79. jobject httpsObject = env->NewObject(httpsClass, constructor);
  80. // Set URL
  81. jstring url = env->NewStringUTF(req.url.c_str());
  82. env->CallVoidMethod(httpsObject, setURL, url);
  83. env->DeleteLocalRef(url);
  84. // Set post data
  85. if (req.method == Request::POST)
  86. {
  87. jmethodID setPostData = env->GetMethodID(httpsClass, "setPostData", "([B)V");
  88. jbyteArray byteArray = env->NewByteArray((jsize) req.postdata.length());
  89. jbyte *byteArrayData = env->GetByteArrayElements(byteArray, nullptr);
  90. memcpy(byteArrayData, req.postdata.data(), req.postdata.length());
  91. env->ReleaseByteArrayElements(byteArray, byteArrayData, 0);
  92. env->CallVoidMethod(httpsObject, setPostData, byteArray);
  93. env->DeleteLocalRef(byteArray);
  94. }
  95. // Set headers
  96. if (!req.headers.empty())
  97. {
  98. jmethodID addHeader = env->GetMethodID(httpsClass, "addHeader", "(Ljava/lang/String;Ljava/lang/String;)V");
  99. for (auto &header : req.headers)
  100. {
  101. jstring headerKey = newStringUTF(env, header.first);
  102. jstring headerValue = newStringUTF(env, header.second);
  103. env->CallVoidMethod(httpsObject, addHeader, headerKey, headerValue);
  104. env->DeleteLocalRef(headerKey);
  105. env->DeleteLocalRef(headerValue);
  106. }
  107. }
  108. // Do request
  109. HTTPSClient::Reply response;
  110. jboolean status = env->CallBooleanMethod(httpsObject, request);
  111. // Get response
  112. response.responseCode = env->CallIntMethod(httpsObject, getResponseCode);
  113. if (status)
  114. {
  115. // Get headers
  116. jobjectArray interleavedHeaders = (jobjectArray) env->CallObjectMethod(httpsObject, getInterleavedHeaders);
  117. int len = env->GetArrayLength(interleavedHeaders);
  118. for (int i = 0; i < len; i += 2)
  119. {
  120. jstring key = (jstring) env->GetObjectArrayElement(interleavedHeaders, i);
  121. jstring value = (jstring) env->GetObjectArrayElement(interleavedHeaders, i + 1);
  122. response.headers[getStringUTF(env, key)] = getStringUTF(env, value);
  123. env->DeleteLocalRef(key);
  124. env->DeleteLocalRef(value);
  125. }
  126. env->DeleteLocalRef(interleavedHeaders);
  127. // Get response data
  128. jbyteArray responseData = (jbyteArray) env->CallObjectMethod(httpsObject, getResponse);
  129. if (responseData)
  130. {
  131. int len = env->GetArrayLength(responseData);
  132. jbyte *responseByte = env->GetByteArrayElements(responseData, nullptr);
  133. response.body = std::string((char *) responseByte, len);
  134. env->DeleteLocalRef(responseData);
  135. }
  136. }
  137. return response;
  138. }
  139. #endif