naett.c (49705B)
1 // Inlined amalgam.h: // 2 #include "naett.h" 3 // Inlined naett_core.c: // 4 // Inlined naett_internal.h: // 5 #ifndef NAETT_INTERNAL_H 6 #define NAETT_INTERNAL_H 7 8 #ifdef _MSC_VER 9 #define strcasecmp _stricmp 10 #undef strdup 11 #define strdup _strdup 12 #endif 13 14 #ifdef _WIN32 15 #define WIN32_LEAN_AND_MEAN 16 #define NOMINMAX 17 #include <windows.h> 18 #include <winhttp.h> 19 #ifndef min 20 #define min(x,y) ((x) < (y) ? (x) : (y)) 21 #endif 22 #define __WINDOWS__ 1 23 #endif 24 25 #if __linux__ && !__ANDROID__ 26 #define __LINUX__ 1 27 #include <curl/curl.h> 28 #endif 29 30 #if __ANDROID__ 31 #include <jni.h> 32 #include <pthread.h> 33 #endif 34 35 #ifdef __APPLE__ 36 #include "TargetConditionals.h" 37 #include <objc/objc.h> 38 #if TARGET_OS_IPHONE 39 #define __IOS__ 1 40 #else 41 #define __MACOS__ 1 42 #endif 43 #endif 44 45 #define naettAlloc(TYPE, VAR) TYPE* VAR = (TYPE*)calloc(1, sizeof(TYPE)) 46 47 typedef struct KVLink { 48 const char* key; 49 const char* value; 50 struct KVLink* next; 51 } KVLink; 52 53 typedef struct Buffer { 54 void* data; 55 int size; 56 int capacity; 57 int position; 58 } Buffer; 59 60 typedef struct { 61 const char* method; 62 const char* userAgent; 63 int timeoutMS; 64 naettReadFunc bodyReader; 65 void* bodyReaderData; 66 naettWriteFunc bodyWriter; 67 void* bodyWriterData; 68 KVLink* headers; 69 Buffer body; 70 } RequestOptions; 71 72 typedef struct { 73 RequestOptions options; 74 const char* url; 75 #if __APPLE__ 76 id urlRequest; 77 #endif 78 #if __ANDROID__ 79 jobject urlObject; 80 #endif 81 #if __WINDOWS__ 82 HINTERNET session; 83 HINTERNET connection; 84 HINTERNET request; 85 LPWSTR host; 86 LPWSTR resource; 87 #endif 88 } InternalRequest; 89 90 typedef struct { 91 InternalRequest* request; 92 int code; 93 int complete; 94 KVLink* headers; 95 Buffer body; 96 int contentLength; // 0 if headers not read, -1 if Content-Length missing. 97 int totalBytesRead; 98 #if __APPLE__ 99 id session; 100 #endif 101 #if __ANDROID__ 102 pthread_t workerThread; 103 int closeRequested; 104 #endif 105 #if __LINUX__ 106 struct curl_slist* headerList; 107 #endif 108 #if __WINDOWS__ 109 char buffer[10240]; 110 size_t bytesLeft; 111 #endif 112 } InternalResponse; 113 114 void naettPlatformInit(naettInitData initData); 115 int naettPlatformInitRequest(InternalRequest* req); 116 void naettPlatformMakeRequest(InternalResponse* res); 117 void naettPlatformFreeRequest(InternalRequest* req); 118 void naettPlatformCloseResponse(InternalResponse* res); 119 120 #endif // NAETT_INTERNAL_H 121 // End of inlined naett_internal.h // 122 123 #include <stdarg.h> 124 #include <stdlib.h> 125 #include <stddef.h> 126 #include <string.h> 127 #include <assert.h> 128 129 typedef struct InternalParam* InternalParamPtr; 130 typedef void (*ParamSetter)(InternalParamPtr param, InternalRequest* req); 131 132 typedef struct InternalParam { 133 ParamSetter setter; 134 int offset; 135 union { 136 int integer; 137 const char* string; 138 struct { 139 const char* key; 140 const char* value; 141 } kv; 142 void* ptr; 143 void (*func)(void); 144 }; 145 } InternalParam; 146 147 typedef struct InternalOption { 148 #define maxParams 2 149 int numParams; 150 InternalParam params[maxParams]; 151 } InternalOption; 152 153 static void stringSetter(InternalParamPtr param, InternalRequest* req) { 154 char* stringCopy = strdup(param->string); 155 char* opaque = (char*)&req->options; 156 char** stringField = (char**)(opaque + param->offset); 157 if (*stringField) { 158 free(*stringField); 159 } 160 *stringField = stringCopy; 161 } 162 163 static void intSetter(InternalParamPtr param, InternalRequest* req) { 164 char* opaque = (char*)&req->options; 165 int* intField = (int*)(opaque + param->offset); 166 *intField = param->integer; 167 } 168 169 static void ptrSetter(InternalParamPtr param, InternalRequest* req) { 170 char* opaque = (char*)&req->options; 171 void** ptrField = (void**)(opaque + param->offset); 172 *ptrField = param->ptr; 173 } 174 175 static void kvSetter(InternalParamPtr param, InternalRequest* req) { 176 char* opaque = (char*)&req->options; 177 KVLink** kvField = (KVLink**)(opaque + param->offset); 178 179 naettAlloc(KVLink, newNode); 180 newNode->key = strdup(param->kv.key); 181 newNode->value = strdup(param->kv.value); 182 newNode->next = *kvField; 183 184 *kvField = newNode; 185 } 186 187 static int defaultBodyReader(void* dest, int bufferSize, void* userData) { 188 Buffer* buffer = (Buffer*) userData; 189 190 if (dest == NULL) { 191 return buffer->size; 192 } 193 194 int bytesToRead = buffer->size - buffer->position; 195 if (bytesToRead > bufferSize) { 196 bytesToRead = bufferSize; 197 } 198 199 const char* source = ((const char*)buffer->data) + buffer->position; 200 memcpy(dest, source, bytesToRead); 201 buffer->position += bytesToRead; 202 return bytesToRead; 203 } 204 205 static int defaultBodyWriter(const void* source, int bytes, void* userData) { 206 Buffer* buffer = (Buffer*) userData; 207 int newCapacity = buffer->capacity; 208 if (newCapacity == 0) { 209 newCapacity = bytes; 210 } 211 while (newCapacity - buffer->size < bytes) { 212 newCapacity *= 2; 213 } 214 if (newCapacity != buffer->capacity) { 215 buffer->data = realloc(buffer->data, newCapacity); 216 buffer->capacity = newCapacity; 217 } 218 char* dest = ((char*)buffer->data) + buffer->size; 219 memcpy(dest, source, bytes); 220 buffer->size += bytes; 221 return bytes; 222 } 223 224 static int initialized = 0; 225 226 static void initRequest(InternalRequest* req, const char* url) { 227 assert(initialized); 228 req->options.method = strdup("GET"); 229 req->options.timeoutMS = 5000; 230 req->url = strdup(url); 231 } 232 233 static void applyOptionParams(InternalRequest* req, InternalOption* option) { 234 for (int j = 0; j < option->numParams; j++) { 235 InternalParam* param = option->params + j; 236 param->setter(param, req); 237 } 238 } 239 240 // Public API 241 242 void naettInit(naettInitData initData) { 243 assert(!initialized); 244 naettPlatformInit(initData); 245 initialized = 1; 246 } 247 248 naettOption* naettMethod(const char* method) { 249 naettAlloc(InternalOption, option); 250 option->numParams = 1; 251 InternalParam* param = &option->params[0]; 252 253 param->string = method; 254 param->offset = offsetof(RequestOptions, method); 255 param->setter = stringSetter; 256 257 return (naettOption*)option; 258 } 259 260 naettOption* naettHeader(const char* name, const char* value) { 261 naettAlloc(InternalOption, option); 262 option->numParams = 1; 263 InternalParam* param = &option->params[0]; 264 265 param->kv.key = name; 266 param->kv.value = value; 267 param->offset = offsetof(RequestOptions, headers); 268 param->setter = kvSetter; 269 270 return (naettOption*)option; 271 } 272 273 naettOption* naettUserAgent(const char* userAgent) { 274 naettAlloc(InternalOption, option); 275 option->numParams = 1; 276 InternalParam* param = &option->params[0]; 277 278 param->string = userAgent; 279 param->offset = offsetof(RequestOptions, userAgent); 280 param->setter = stringSetter; 281 282 return (naettOption*)option; 283 } 284 285 naettOption* naettTimeout(int timeoutMS) { 286 naettAlloc(InternalOption, option); 287 option->numParams = 1; 288 InternalParam* param = &option->params[0]; 289 290 param->integer = timeoutMS; 291 param->offset = offsetof(RequestOptions, timeoutMS); 292 param->setter = intSetter; 293 294 return (naettOption*)option; 295 } 296 297 naettOption* naettBody(const char* body, int size) { 298 naettAlloc(InternalOption, option); 299 option->numParams = 2; 300 301 InternalParam* bodyParam = &option->params[0]; 302 InternalParam* sizeParam = &option->params[1]; 303 304 bodyParam->ptr = (void*)body; 305 bodyParam->offset = offsetof(RequestOptions, body) + offsetof(Buffer, data); 306 bodyParam->setter = ptrSetter; 307 308 sizeParam->integer = size; 309 sizeParam->offset = offsetof(RequestOptions, body) + offsetof(Buffer, size); 310 sizeParam->setter = intSetter; 311 312 return (naettOption*)option; 313 } 314 315 naettOption* naettBodyReader(naettReadFunc reader, void* userData) { 316 naettAlloc(InternalOption, option); 317 option->numParams = 2; 318 319 InternalParam* readerParam = &option->params[0]; 320 InternalParam* dataParam = &option->params[1]; 321 322 readerParam->func = (void (*)(void)) reader; 323 readerParam->offset = offsetof(RequestOptions, bodyReader); 324 readerParam->setter = ptrSetter; 325 326 dataParam->ptr = userData; 327 dataParam->offset = offsetof(RequestOptions, bodyReaderData); 328 dataParam->setter = ptrSetter; 329 330 return (naettOption*)option; 331 } 332 333 naettOption* naettBodyWriter(naettWriteFunc writer, void* userData) { 334 naettAlloc(InternalOption, option); 335 option->numParams = 2; 336 337 InternalParam* writerParam = &option->params[0]; 338 InternalParam* dataParam = &option->params[1]; 339 340 writerParam->func = (void(*)(void)) writer; 341 writerParam->offset = offsetof(RequestOptions, bodyWriter); 342 writerParam->setter = ptrSetter; 343 344 dataParam->ptr = userData; 345 dataParam->offset = offsetof(RequestOptions, bodyWriterData); 346 dataParam->setter = ptrSetter; 347 348 return (naettOption*)option; 349 } 350 351 void setupDefaultRW(InternalRequest* req) { 352 if (req->options.bodyReader == NULL) { 353 req->options.bodyReader = defaultBodyReader; 354 req->options.bodyReaderData = (void*) &req->options.body; 355 } 356 if (req->options.bodyReader == defaultBodyReader) { 357 req->options.body.position = 0; 358 } 359 if (req->options.bodyWriter == NULL) { 360 req->options.bodyWriter = defaultBodyWriter; 361 } 362 } 363 364 naettReq* naettRequest_va(const char* url, int numArgs, ...) { 365 assert(url != NULL); 366 367 va_list args; 368 InternalOption* option; 369 naettAlloc(InternalRequest, req); 370 initRequest(req, url); 371 372 va_start(args, numArgs); 373 for (int i = 0; i < numArgs; i++) { 374 option = va_arg(args, InternalOption*); 375 applyOptionParams(req, option); 376 free(option); 377 } 378 va_end(args); 379 380 setupDefaultRW(req); 381 382 if (naettPlatformInitRequest(req)) { 383 return (naettReq*)req; 384 } 385 386 naettFree((naettReq*) req); 387 return NULL; 388 } 389 390 naettReq* naettRequestWithOptions(const char* url, int numOptions, const naettOption** options) { 391 assert(url != NULL); 392 assert(numOptions == 0 || options != NULL); 393 394 naettAlloc(InternalRequest, req); 395 initRequest(req, url); 396 397 for (int i = 0; i < numOptions; i++) { 398 InternalOption* option = (InternalOption*)options[i]; 399 applyOptionParams(req, option); 400 free(option); 401 } 402 403 setupDefaultRW(req); 404 405 if (naettPlatformInitRequest(req)) { 406 return (naettReq*)req; 407 } 408 409 naettFree((naettReq*) req); 410 return NULL; 411 } 412 413 naettRes* naettMake(naettReq* request) { 414 assert(initialized); 415 assert(request != NULL); 416 417 InternalRequest* req = (InternalRequest*)request; 418 naettAlloc(InternalResponse, res); 419 res->request = req; 420 421 if (req->options.bodyWriter == defaultBodyWriter) { 422 req->options.bodyWriterData = (void*) &res->body; 423 } 424 425 naettPlatformMakeRequest(res); 426 return (naettRes*) res; 427 } 428 429 const void* naettGetBody(naettRes* response, int* size) { 430 assert(response != NULL); 431 assert(size != NULL); 432 433 InternalResponse* res = (InternalResponse*)response; 434 *size = res->body.size; 435 return res->body.data; 436 } 437 438 int naettGetTotalBytesRead(naettRes* response, int* totalSize) { 439 assert(response != NULL); 440 assert(totalSize != NULL); 441 442 InternalResponse* res = (InternalResponse*)response; 443 *totalSize = res->contentLength; 444 return res->totalBytesRead; 445 } 446 447 const char* naettGetHeader(naettRes* response, const char* name) { 448 assert(response != NULL); 449 assert(name != NULL); 450 451 InternalResponse* res = (InternalResponse*)response; 452 KVLink* node = res->headers; 453 while (node) { 454 if (strcasecmp(name, node->key) == 0) { 455 return node->value; 456 } 457 node = node->next; 458 } 459 return NULL; 460 } 461 462 void naettListHeaders(naettRes* response, naettHeaderLister lister, void* userData) { 463 assert(response != NULL); 464 assert(lister != NULL); 465 466 InternalResponse* res = (InternalResponse*)response; 467 KVLink* node = res->headers; 468 while (node) { 469 if (!lister(node->key, node->value, userData)) { 470 return; 471 } 472 node = node->next; 473 } 474 } 475 476 naettReq* naettGetRequest(naettRes* response) { 477 assert(response != NULL); 478 InternalResponse* res = (InternalResponse*)response; 479 return (naettReq*) res->request; 480 } 481 482 int naettComplete(const naettRes* response) { 483 assert(response != NULL); 484 InternalResponse* res = (InternalResponse*)response; 485 return res->complete; 486 } 487 488 int naettGetStatus(const naettRes* response) { 489 assert(response != NULL); 490 InternalResponse* res = (InternalResponse*)response; 491 return res->code; 492 } 493 494 static void freeKVList(KVLink* node) { 495 while (node != NULL) { 496 free((void*) node->key); 497 free((void*) node->value); 498 KVLink* next = node->next; 499 free(node); 500 node = next; 501 } 502 } 503 504 void naettFree(naettReq* request) { 505 assert(request != NULL); 506 507 InternalRequest* req = (InternalRequest*)request; 508 naettPlatformFreeRequest(req); 509 KVLink* node = req->options.headers; 510 freeKVList(node); 511 free((void*)req->options.method); 512 free((void*)req->url); 513 free(request); 514 } 515 516 void naettClose(naettRes* response) { 517 assert(response != NULL); 518 519 InternalResponse* res = (InternalResponse*)response; 520 res->request = NULL; 521 naettPlatformCloseResponse(res); 522 KVLink* node = res->headers; 523 freeKVList(node); 524 free(res->body.data); 525 free(response); 526 } 527 // End of inlined naett_core.c // 528 529 // Inlined naett_osx.c: // 530 //#include "naett_internal.h" 531 532 #ifdef __APPLE__ 533 534 // Inlined naett_objc.h: // 535 #ifndef NAETT_OBJC_H 536 #define NAETT_OBJC_H 537 538 #if defined(__IOS__) || defined (__MACOS__) 539 #include <assert.h> 540 #include <math.h> 541 542 #include <objc/NSObjCRuntime.h> 543 #include <objc/message.h> 544 #include <objc/objc.h> 545 #include <objc/runtime.h> 546 547 #if defined(__OBJC__) && __has_feature(objc_arc) 548 #error "ARC is not supported" 549 #endif 550 551 // ABI is a bit different between platforms 552 #ifdef __arm64__ 553 #define abi_objc_msgSend_stret objc_msgSend 554 #else 555 #define abi_objc_msgSend_stret objc_msgSend_stret 556 #endif 557 #ifdef __i386__ 558 #define abi_objc_msgSend_fpret objc_msgSend_fpret 559 #else 560 #define abi_objc_msgSend_fpret objc_msgSend 561 #endif 562 563 #define objc_msgSendSuper_t(RET, ...) ((RET(*)(struct objc_super*, SEL, ##__VA_ARGS__))objc_msgSendSuper) 564 #define objc_msgSend_t(RET, ...) ((RET(*)(id, SEL, ##__VA_ARGS__))objc_msgSend) 565 #define objc_msgSend_stret_t(RET, ...) ((RET(*)(id, SEL, ##__VA_ARGS__))abi_objc_msgSend_stret) 566 #define objc_msgSend_id objc_msgSend_t(id) 567 #define objc_msgSend_void objc_msgSend_t(void) 568 #define objc_msgSend_void_id objc_msgSend_t(void, id) 569 #define objc_msgSend_void_bool objc_msgSend_t(void, bool) 570 571 #define sel(NAME) sel_registerName(NAME) 572 #define class(NAME) ((id)objc_getClass(NAME)) 573 #define makeClass(NAME, SUPER) \ 574 objc_allocateClassPair((Class)objc_getClass(SUPER), NAME, 0) 575 576 // Check here to get the signature right: 577 // https://nshipster.com/type-encodings/ 578 // https://ko9.org/posts/encode-types/ 579 #define addMethod(CLASS, NAME, IMPL, SIGNATURE) \ 580 if (!class_addMethod(CLASS, sel(NAME), (IMP) (IMPL), (SIGNATURE))) assert(false) 581 582 #define addIvar(CLASS, NAME, SIZE, SIGNATURE) \ 583 if (!class_addIvar(CLASS, NAME, SIZE, rint(log2(SIZE)), SIGNATURE)) assert(false) 584 585 #define objc_alloc(CLASS) objc_msgSend_id(class(CLASS), sel("alloc")) 586 #define autorelease(OBJ) objc_msgSend_void(OBJ, sel("autorelease")) 587 #define retain(OBJ) objc_msgSend_void(OBJ, sel("retain")) 588 #define release(OBJ) objc_msgSend_void(OBJ, sel("release")) 589 590 #if __LP64__ || NS_BUILD_32_LIKE_64 591 #define NSIntegerEncoding "q" 592 #define NSUIntegerEncoding "L" 593 #else 594 #define NSIntegerEncoding "i" 595 #define NSUIntegerEncoding "I" 596 #endif 597 598 #endif // defined(__IOS__) || defined (__MACOS__) 599 #endif // NAETT_OBJC_H 600 // End of inlined naett_objc.h // 601 602 #include <stdlib.h> 603 #include <string.h> 604 #include <stdio.h> 605 606 #ifdef DEBUG 607 static void _showPools(const char* context) { 608 fprintf(stderr, "NSAutoreleasePool@%s:\n", context); 609 objc_msgSend_void(class("NSAutoreleasePool"), sel("showPools")); 610 } 611 #define showPools(x) _showPools((x)) 612 #else 613 #define showPools(x) 614 #endif 615 616 static id pool(void) { 617 return objc_msgSend_id(objc_alloc("NSAutoreleasePool"), sel("init")); 618 } 619 620 static id sessionConfiguration = nil; 621 622 void naettPlatformInit(naettInitData initData) { 623 id NSThread = class("NSThread"); 624 SEL isMultiThreaded = sel("isMultiThreaded"); 625 626 if (!objc_msgSend_t(bool)(NSThread, isMultiThreaded)) { 627 // Make a dummy call from a new thread to kick Cocoa into multi-threaded mode 628 objc_msgSend_t(void, SEL, id, id)( 629 NSThread, sel("detachNewThreadSelector:toTarget:withObject:"), isMultiThreaded, NSThread, nil); 630 } 631 632 sessionConfiguration = objc_msgSend_id(class("NSURLSessionConfiguration"), sel("ephemeralSessionConfiguration")); 633 retain(sessionConfiguration); 634 } 635 636 id NSString(const char* string) { 637 return objc_msgSend_t(id, const char*)(class("NSString"), sel("stringWithUTF8String:"), string); 638 } 639 640 int naettPlatformInitRequest(InternalRequest* req) { 641 id p = pool(); 642 643 id urlString = NSString(req->url); 644 id url = objc_msgSend_t(id, id)(class("NSURL"), sel("URLWithString:"), urlString); 645 646 id request = objc_msgSend_t(id, id)(class("NSMutableURLRequest"), sel("requestWithURL:"), url); 647 648 objc_msgSend_t(void, double)(request, sel("setTimeoutInterval:"), (double)(req->options.timeoutMS) / 1000.0); 649 id methodString = NSString(req->options.method); 650 objc_msgSend_t(void, id)(request, sel("setHTTPMethod:"), methodString); 651 652 { 653 id name = NSString("User-Agent"); 654 id value = NSString(req->options.userAgent ? req->options.userAgent : NAETT_UA); 655 objc_msgSend_t(void, id, id)(request, sel("setValue:forHTTPHeaderField:"), value, name); 656 } 657 658 KVLink* header = req->options.headers; 659 while (header != NULL) { 660 id name = NSString(header->key); 661 id value = NSString(header->value); 662 objc_msgSend_t(void, id, id)(request, sel("setValue:forHTTPHeaderField:"), value, name); 663 header = header->next; 664 } 665 666 char byteBuffer[10240]; 667 int bytesRead = 0; 668 669 if (req->options.bodyReader != NULL) { 670 id bodyData = 671 objc_msgSend_t(id, NSUInteger)(class("NSMutableData"), sel("dataWithCapacity:"), sizeof(byteBuffer)); 672 673 int totalBytesRead = 0; 674 do { 675 bytesRead = req->options.bodyReader(byteBuffer, sizeof(byteBuffer), req->options.bodyReaderData); 676 totalBytesRead += bytesRead; 677 objc_msgSend_t(void, const void*, NSUInteger)(bodyData, sel("appendBytes:length:"), byteBuffer, bytesRead); 678 } while (bytesRead > 0); 679 680 if (totalBytesRead > 0) { 681 objc_msgSend_t(void, id)(request, sel("setHTTPBody:"), bodyData); 682 } 683 } 684 685 retain(request); 686 req->urlRequest = request; 687 688 release(p); 689 return 1; 690 } 691 692 void didReceiveData(id self, SEL _sel, id session, id dataTask, id data) { 693 InternalResponse* res = NULL; 694 id p = pool(); 695 696 object_getInstanceVariable(self, "response", (void**)&res); 697 698 if (res->headers == NULL) { 699 id response = objc_msgSend_t(id)(dataTask, sel("response")); 700 res->code = objc_msgSend_t(NSInteger)(response, sel("statusCode")); 701 id allHeaders = objc_msgSend_t(id)(response, sel("allHeaderFields")); 702 703 NSUInteger headerCount = objc_msgSend_t(NSUInteger)(allHeaders, sel("count")); 704 id headerNames[headerCount]; 705 id headerValues[headerCount]; 706 707 objc_msgSend_t(NSInteger, id*, id*, NSUInteger)( 708 allHeaders, sel("getObjects:andKeys:count:"), headerValues, headerNames, headerCount); 709 KVLink* firstHeader = NULL; 710 for (int i = 0; i < headerCount; i++) { 711 naettAlloc(KVLink, node); 712 node->key = strdup(objc_msgSend_t(const char*)(headerNames[i], sel("UTF8String"))); 713 node->value = strdup(objc_msgSend_t(const char*)(headerValues[i], sel("UTF8String"))); 714 node->next = firstHeader; 715 firstHeader = node; 716 } 717 res->headers = firstHeader; 718 719 const char* contentLength = naettGetHeader((naettRes*)res, "Content-Length"); 720 if (!contentLength || sscanf(contentLength, "%d", &res->contentLength) != 1) { 721 res->contentLength = -1; 722 } 723 } 724 725 const void* bytes = objc_msgSend_t(const void*)(data, sel("bytes")); 726 NSUInteger length = objc_msgSend_t(NSUInteger)(data, sel("length")); 727 728 res->request->options.bodyWriter(bytes, length, res->request->options.bodyWriterData); 729 res->totalBytesRead += (int)length; 730 731 release(p); 732 } 733 734 static void didComplete(id self, SEL _sel, id session, id dataTask, id error) { 735 InternalResponse* res = NULL; 736 object_getInstanceVariable(self, "response", (void**)&res); 737 if (res != NULL) { 738 if (error != nil) { 739 res->code = naettConnectionError; 740 } 741 res->complete = 1; 742 } 743 } 744 745 static id createDelegate(void) { 746 static Class TaskDelegateClass = nil; 747 748 if (!TaskDelegateClass) { 749 TaskDelegateClass = objc_allocateClassPair((Class)objc_getClass("NSObject"), "naettTaskDelegate", 0); 750 class_addProtocol(TaskDelegateClass, (Protocol* _Nonnull)objc_getProtocol("NSURLSessionDataDelegate")); 751 752 addMethod(TaskDelegateClass, "URLSession:dataTask:didReceiveData:", didReceiveData, "v@:@@@"); 753 addMethod(TaskDelegateClass, "URLSession:task:didCompleteWithError:", didComplete, "v@:@@@"); 754 addIvar(TaskDelegateClass, "response", sizeof(void*), "^v"); 755 } 756 757 id delegate = objc_msgSend_id((id)TaskDelegateClass, sel("alloc")); 758 delegate = objc_msgSend_id(delegate, sel("init")); 759 760 return delegate; 761 } 762 763 void naettPlatformMakeRequest(InternalResponse* res) { 764 InternalRequest* req = res->request; 765 id p = pool(); 766 767 id delegate = createDelegate(); 768 // delegate will be retained by session below 769 autorelease(delegate); 770 object_setInstanceVariable(delegate, "response", (void*)res); 771 772 id session = objc_msgSend_t(id, id, id, id)(class("NSURLSession"), 773 sel("sessionWithConfiguration:delegate:delegateQueue:"), 774 sessionConfiguration, 775 delegate, 776 nil); 777 778 res->session = session; 779 780 id task = objc_msgSend_t(id, id)(session, sel("dataTaskWithRequest:"), req->urlRequest); 781 objc_msgSend_void(task, sel("resume")); 782 783 release(p); 784 } 785 786 void naettPlatformFreeRequest(InternalRequest* req) { 787 release(req->urlRequest); 788 req->urlRequest = nil; 789 } 790 791 void naettPlatformCloseResponse(InternalResponse* res) { 792 objc_msgSend_void(res->session, sel("invalidateAndCancel")); 793 res->session = nil; 794 } 795 796 #endif // __APPLE__ 797 // End of inlined naett_osx.c // 798 799 // Inlined naett_linux.c: // 800 //#include "naett_internal.h" 801 802 #if __linux__ && !__ANDROID__ 803 804 #include <curl/curl.h> 805 #include <assert.h> 806 #include <pthread.h> 807 #include <unistd.h> 808 #include <fcntl.h> 809 #include <string.h> 810 811 static pthread_t workerThread; 812 static int handleReadFD = 0; 813 static int handleWriteFD = 0; 814 815 static void panic(const char* message) { 816 fprintf(stderr, "%s\n", message); 817 exit(1); 818 } 819 820 static void* curlWorker(void* data) { 821 CURLM* mc = (CURLM*)data; 822 int activeHandles = 0; 823 int messagesLeft = 1; 824 825 struct curl_waitfd readFd = { handleReadFD, CURL_WAIT_POLLIN }; 826 827 union { 828 CURL* handle; 829 char buf[sizeof(CURL*)]; 830 } newHandle; 831 832 int newHandlePos = 0; 833 834 while (1) { 835 int status = curl_multi_perform(mc, &activeHandles); 836 if (status != CURLM_OK) { 837 panic("CURL processing failure"); 838 } 839 840 struct CURLMsg* message = curl_multi_info_read(mc, &messagesLeft); 841 if (message && message->msg == CURLMSG_DONE) { 842 CURL* handle = message->easy_handle; 843 InternalResponse* res = NULL; 844 curl_easy_getinfo(handle, CURLINFO_PRIVATE, (char**)&res); 845 curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &res->code); 846 res->complete = 1; 847 curl_easy_cleanup(handle); 848 } 849 850 851 int readyFDs = 0; 852 curl_multi_wait(mc, &readFd, 1, 1, &readyFDs); 853 854 if (readyFDs == 0 && activeHandles == 0 && messagesLeft == 0) { 855 usleep(100 * 1000); 856 } 857 858 int bytesRead = read(handleReadFD, newHandle.buf, sizeof(newHandle.buf) - newHandlePos); 859 if (bytesRead > 0) { 860 newHandlePos += bytesRead; 861 } 862 if (newHandlePos == sizeof(newHandle.buf)) { 863 curl_multi_add_handle(mc, newHandle.handle); 864 newHandlePos = 0; 865 } 866 } 867 868 return NULL; 869 } 870 871 void naettPlatformInit(naettInitData initData) { 872 curl_global_init(CURL_GLOBAL_ALL); 873 CURLM* mc = curl_multi_init(); 874 int fds[2]; 875 if (pipe(fds) != 0) { 876 panic("Failed to open pipe"); 877 } 878 handleReadFD = fds[0]; 879 handleWriteFD = fds[1]; 880 881 int flags = fcntl(handleReadFD, F_GETFL, 0); 882 fcntl(handleReadFD, F_SETFL, flags | O_NONBLOCK); 883 884 pthread_attr_t attr; 885 pthread_attr_init(&attr); 886 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); 887 pthread_create(&workerThread, &attr, curlWorker, mc); 888 } 889 890 int naettPlatformInitRequest(InternalRequest* req) { 891 return 1; 892 } 893 894 static size_t readCallback(char* buffer, size_t size, size_t numItems, void* userData) { 895 InternalResponse* res = (InternalResponse*)userData; 896 InternalRequest* req = res->request; 897 return req->options.bodyReader(buffer, size * numItems, req->options.bodyReaderData); 898 } 899 900 static size_t writeCallback(char* ptr, size_t size, size_t numItems, void* userData) { 901 InternalResponse* res = (InternalResponse*)userData; 902 InternalRequest* req = res->request; 903 size_t bytesWritten = req->options.bodyWriter(ptr, size * numItems, req->options.bodyWriterData); 904 res->totalBytesRead += bytesWritten; 905 return bytesWritten; 906 } 907 908 #define METHOD(A, B, C) (((A) << 16) | ((B) << 8) | (C)) 909 910 static void setupMethod(CURL* curl, const char* method) { 911 if (strlen(method) < 3) { 912 return; 913 } 914 915 int methodCode = (method[0] << 16) | (method[1] << 8) | method[2]; 916 917 switch (methodCode) { 918 case METHOD('G', 'E', 'T'): 919 case METHOD('C', 'O', 'N'): 920 case METHOD('O', 'P', 'T'): 921 curl_easy_setopt(curl, CURLOPT_HTTPGET, 1); 922 break; 923 case METHOD('P', 'O', 'S'): 924 case METHOD('P', 'A', 'T'): 925 case METHOD('D', 'E', 'L'): 926 curl_easy_setopt(curl, CURLOPT_POST, 1); 927 break; 928 case METHOD('P', 'U', 'T'): 929 curl_easy_setopt(curl, CURLOPT_UPLOAD, 1); 930 break; 931 case METHOD('H', 'E', 'A'): 932 case METHOD('T', 'R', 'A'): 933 curl_easy_setopt(curl, CURLOPT_NOBODY, 1); 934 break; 935 } 936 937 curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, method); 938 } 939 940 static size_t headerCallback(char* buffer, size_t size, size_t nitems, void* userData) { 941 InternalResponse* res = (InternalResponse*) userData; 942 size_t headerSize = size * nitems; 943 944 char* headerName = strndup(buffer, headerSize); 945 char* split = strchr(headerName, ':'); 946 if (split) { 947 *split = 0; 948 split++; 949 while (*split == ' ') { 950 split++; 951 } 952 char* headerValue = strdup(split); 953 954 char* cr = strchr(headerValue, 13); 955 if (cr) { 956 *cr = 0; 957 } 958 959 char* lf = strchr(headerValue, 10); 960 if (lf) { 961 *lf = 0; 962 } 963 964 naettAlloc(KVLink, node); 965 node->next = res->headers; 966 node->key = headerName; 967 node->value = headerValue; 968 res->headers = node; 969 } 970 971 return headerSize; 972 } 973 974 void naettPlatformMakeRequest(InternalResponse* res) { 975 InternalRequest* req = res->request; 976 977 CURL* c = curl_easy_init(); 978 curl_easy_setopt(c, CURLOPT_URL, req->url); 979 curl_easy_setopt(c, CURLOPT_CONNECTTIMEOUT_MS, req->options.timeoutMS); 980 981 curl_easy_setopt(c, CURLOPT_READFUNCTION, readCallback); 982 curl_easy_setopt(c, CURLOPT_READDATA, res); 983 984 curl_easy_setopt(c, CURLOPT_WRITEFUNCTION, writeCallback); 985 curl_easy_setopt(c, CURLOPT_WRITEDATA, res); 986 987 curl_easy_setopt(c, CURLOPT_HEADERFUNCTION, headerCallback); 988 curl_easy_setopt(c, CURLOPT_HEADERDATA, res); 989 990 curl_easy_setopt(c, CURLOPT_FOLLOWLOCATION, 1); 991 992 int bodySize = res->request->options.bodyReader(NULL, 0, res->request->options.bodyReaderData); 993 curl_easy_setopt(c, CURLOPT_POSTFIELDSIZE, bodySize); 994 995 setupMethod(c, req->options.method); 996 997 struct curl_slist* headerList = NULL; 998 char uaBuf[512]; 999 snprintf(uaBuf, sizeof(uaBuf), "User-Agent: %s", req->options.userAgent ? req->options.userAgent : NAETT_UA); 1000 headerList = curl_slist_append(headerList, uaBuf); 1001 1002 KVLink* header = req->options.headers; 1003 size_t bufferSize = 0; 1004 char* buffer = NULL; 1005 while (header) { 1006 size_t headerLength = strlen(header->key) + strlen(header->value) + 1 + 1; // colon + null 1007 if (headerLength > bufferSize) { 1008 bufferSize = headerLength; 1009 buffer = (char*)realloc(buffer, bufferSize); 1010 } 1011 snprintf(buffer, bufferSize, "%s:%s", header->key, header->value); 1012 headerList = curl_slist_append(headerList, buffer); 1013 header = header->next; 1014 } 1015 curl_easy_setopt(c, CURLOPT_HTTPHEADER, headerList); 1016 free(buffer); 1017 res->headerList = headerList; 1018 1019 curl_easy_setopt(c, CURLOPT_PRIVATE, res); 1020 1021 write(handleWriteFD, &c, sizeof(c)); 1022 } 1023 1024 void naettPlatformFreeRequest(InternalRequest* req) { 1025 } 1026 1027 void naettPlatformCloseResponse(InternalResponse* res) { 1028 curl_slist_free_all(res->headerList); 1029 } 1030 1031 #endif 1032 // End of inlined naett_linux.c // 1033 1034 // Inlined naett_win.c: // 1035 //#include "naett_internal.h" 1036 1037 #ifdef __WINDOWS__ 1038 1039 #include <stdlib.h> 1040 #include <stdio.h> 1041 #include <string.h> 1042 #include <winhttp.h> 1043 #include <assert.h> 1044 #include <tchar.h> 1045 1046 void naettPlatformInit(naettInitData initData) { 1047 } 1048 1049 static char* winToUTF8(LPWSTR source) { 1050 int length = WideCharToMultiByte(CP_UTF8, 0, source, -1, NULL, 0, NULL, NULL); 1051 char* chars = (char*)malloc(length); 1052 int result = WideCharToMultiByte(CP_UTF8, 0, source, -1, chars, length, NULL, NULL); 1053 if (!result) { 1054 free(chars); 1055 return NULL; 1056 } 1057 return chars; 1058 } 1059 1060 static LPWSTR winFromUTF8(const char* source) { 1061 int length = MultiByteToWideChar(CP_UTF8, 0, source, -1, NULL, 0); 1062 LPWSTR chars = (LPWSTR)malloc(length * sizeof(WCHAR)); 1063 int result = MultiByteToWideChar(CP_UTF8, 0, source, -1, chars, length); 1064 if (!result) { 1065 free(chars); 1066 return NULL; 1067 } 1068 return chars; 1069 } 1070 1071 #define ASPRINTF(result, fmt, ...) \ 1072 { \ 1073 size_t len = snprintf(NULL, 0, fmt, __VA_ARGS__); \ 1074 *(result) = (char*)malloc(len + 1); \ 1075 snprintf(*(result), len + 1, fmt, __VA_ARGS__); \ 1076 } 1077 1078 static LPWSTR wcsndup(LPCWSTR str, size_t len) { 1079 LPWSTR result = calloc(1, sizeof(WCHAR) * (len + 1)); 1080 wcsncpy(result, str, len); 1081 return result; 1082 } 1083 1084 static LPCWSTR packHeaders(InternalRequest* req) { 1085 char* packed = strdup(""); 1086 1087 KVLink* node = req->options.headers; 1088 while (node != NULL) { 1089 char* update; 1090 ASPRINTF(&update, "%s%s:%s%s", packed, node->key, node->value, node->next ? "\r\n" : ""); 1091 free(packed); 1092 packed = update; 1093 node = node->next; 1094 } 1095 1096 LPCWSTR winHeaders = winFromUTF8(packed); 1097 free(packed); 1098 return winHeaders; 1099 } 1100 1101 static void unpackHeaders(InternalResponse* res, LPWSTR packed) { 1102 size_t len = 0; 1103 KVLink* firstHeader = NULL; 1104 while ((len = wcslen(packed)) != 0) { 1105 char* header = winToUTF8(packed); 1106 char* split = strchr(header, ':'); 1107 if (split) { 1108 *split = 0; 1109 split++; 1110 while (*split == ' ') { 1111 split++; 1112 } 1113 naettAlloc(KVLink, node); 1114 node->key = strdup(header); 1115 node->value = strdup(split); 1116 node->next = firstHeader; 1117 firstHeader = node; 1118 } 1119 free(header); 1120 packed += len + 1; 1121 } 1122 res->headers = firstHeader; 1123 } 1124 1125 static void CALLBACK 1126 callback(HINTERNET request, DWORD_PTR context, DWORD status, LPVOID statusInformation, DWORD statusInfoLength) { 1127 InternalResponse* res = (InternalResponse*)context; 1128 1129 switch (status) { 1130 case WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE: { 1131 DWORD bufSize = 0; 1132 WinHttpQueryHeaders(request, 1133 WINHTTP_QUERY_RAW_HEADERS, 1134 WINHTTP_HEADER_NAME_BY_INDEX, 1135 NULL, 1136 &bufSize, 1137 WINHTTP_NO_HEADER_INDEX); 1138 LPWSTR buffer = (LPWSTR)malloc(bufSize); 1139 WinHttpQueryHeaders(request, 1140 WINHTTP_QUERY_RAW_HEADERS, 1141 WINHTTP_HEADER_NAME_BY_INDEX, 1142 buffer, 1143 &bufSize, 1144 WINHTTP_NO_HEADER_INDEX); 1145 unpackHeaders(res, buffer); 1146 free(buffer); 1147 1148 const char* contentLength = naettGetHeader((naettRes*)res, "Content-Length"); 1149 if (!contentLength || sscanf(contentLength, "%d", &res->contentLength) != 1) { 1150 res->contentLength = -1; 1151 } 1152 1153 DWORD statusCode = 0; 1154 DWORD statusCodeSize = sizeof(statusCode); 1155 1156 WinHttpQueryHeaders(request, 1157 WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER, 1158 WINHTTP_HEADER_NAME_BY_INDEX, 1159 &statusCode, 1160 &statusCodeSize, 1161 WINHTTP_NO_HEADER_INDEX); 1162 res->code = statusCode; 1163 1164 if (!WinHttpQueryDataAvailable(request, NULL)) { 1165 res->code = naettProtocolError; 1166 res->complete = 1; 1167 } 1168 } break; 1169 1170 case WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE: { 1171 DWORD* available = (DWORD*)statusInformation; 1172 res->bytesLeft = *available; 1173 if (res->bytesLeft == 0) { 1174 res->complete = 1; 1175 break; 1176 } 1177 1178 size_t bytesToRead = min(res->bytesLeft, sizeof(res->buffer)); 1179 if (!WinHttpReadData(request, res->buffer, (DWORD)bytesToRead, NULL)) { 1180 res->code = naettReadError; 1181 res->complete = 1; 1182 } 1183 } break; 1184 1185 case WINHTTP_CALLBACK_STATUS_READ_COMPLETE: { 1186 size_t bytesRead = statusInfoLength; 1187 1188 InternalRequest* req = res->request; 1189 if (req->options.bodyWriter(res->buffer, (int)bytesRead, req->options.bodyWriterData) != bytesRead) { 1190 res->code = naettReadError; 1191 res->complete = 1; 1192 } 1193 res->totalBytesRead += (int)bytesRead; 1194 res->bytesLeft -= bytesRead; 1195 if (res->bytesLeft > 0) { 1196 size_t bytesToRead = min(res->bytesLeft, sizeof(res->buffer)); 1197 if (!WinHttpReadData(request, res->buffer, (DWORD)bytesToRead, NULL)) { 1198 res->code = naettReadError; 1199 res->complete = 1; 1200 } 1201 } else { 1202 if (!WinHttpQueryDataAvailable(request, NULL)) { 1203 res->code = naettProtocolError; 1204 res->complete = 1; 1205 } 1206 } 1207 } break; 1208 1209 case WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE: 1210 case WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE: { 1211 int bytesRead = res->request->options.bodyReader( 1212 res->buffer, sizeof(res->buffer), res->request->options.bodyReaderData); 1213 if (bytesRead) { 1214 WinHttpWriteData(request, res->buffer, bytesRead, NULL); 1215 } else { 1216 if (!WinHttpReceiveResponse(request, NULL)) { 1217 res->code = naettReadError; 1218 res->complete = 1; 1219 } 1220 } 1221 } break; 1222 1223 // 1224 case WINHTTP_CALLBACK_STATUS_REQUEST_ERROR: { 1225 WINHTTP_ASYNC_RESULT* result = (WINHTTP_ASYNC_RESULT*)statusInformation; 1226 switch (result->dwResult) { 1227 case API_RECEIVE_RESPONSE: 1228 case API_QUERY_DATA_AVAILABLE: 1229 case API_READ_DATA: 1230 res->code = naettReadError; 1231 break; 1232 case API_WRITE_DATA: 1233 res->code = naettWriteError; 1234 break; 1235 case API_SEND_REQUEST: 1236 res->code = naettConnectionError; 1237 break; 1238 default: 1239 res->code = naettGenericError; 1240 } 1241 1242 res->complete = 1; 1243 } break; 1244 } 1245 } 1246 1247 int naettPlatformInitRequest(InternalRequest* req) { 1248 LPWSTR url = winFromUTF8(req->url); 1249 1250 URL_COMPONENTS components; 1251 ZeroMemory(&components, sizeof(components)); 1252 components.dwStructSize = sizeof(components); 1253 components.dwSchemeLength = (DWORD)-1; 1254 components.dwHostNameLength = (DWORD)-1; 1255 components.dwUrlPathLength = (DWORD)-1; 1256 components.dwExtraInfoLength = (DWORD)-1; 1257 BOOL cracked = WinHttpCrackUrl(url, 0, 0, &components); 1258 1259 if (!cracked) { 1260 free(url); 1261 return 0; 1262 } 1263 1264 req->host = wcsndup(components.lpszHostName, components.dwHostNameLength); 1265 req->resource = wcsndup(components.lpszUrlPath, components.dwUrlPathLength + components.dwExtraInfoLength); 1266 free(url); 1267 1268 LPWSTR uaBuf = winFromUTF8(req->options.userAgent ? req->options.userAgent : NAETT_UA); 1269 req->session = WinHttpOpen(uaBuf, 1270 WINHTTP_ACCESS_TYPE_NO_PROXY, 1271 WINHTTP_NO_PROXY_NAME, 1272 WINHTTP_NO_PROXY_BYPASS, 1273 WINHTTP_FLAG_ASYNC); 1274 free(uaBuf); 1275 1276 if (!req->session) { 1277 return 0; 1278 } 1279 1280 WinHttpSetStatusCallback(req->session, callback, WINHTTP_CALLBACK_FLAG_ALL_COMPLETIONS, 0); 1281 1282 // Set the connect timeout. Leave the other three timeouts at their default values. 1283 WinHttpSetTimeouts(req->session, 0, req->options.timeoutMS, 30000, 30000); 1284 1285 req->connection = WinHttpConnect(req->session, req->host, components.nPort, 0); 1286 if (!req->connection) { 1287 naettPlatformFreeRequest(req); 1288 return 0; 1289 } 1290 1291 LPWSTR verb = winFromUTF8(req->options.method); 1292 req->request = WinHttpOpenRequest(req->connection, 1293 verb, 1294 req->resource, 1295 NULL, 1296 WINHTTP_NO_REFERER, 1297 WINHTTP_DEFAULT_ACCEPT_TYPES, 1298 components.nScheme == INTERNET_SCHEME_HTTPS ? WINHTTP_FLAG_SECURE : 0); 1299 free(verb); 1300 if (!req->request) { 1301 naettPlatformFreeRequest(req); 1302 return 0; 1303 } 1304 1305 LPCWSTR headers = packHeaders(req); 1306 if (headers[0] != 0) { 1307 if (!WinHttpAddRequestHeaders( 1308 req->request, headers, -1, WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE)) { 1309 naettPlatformFreeRequest(req); 1310 free((LPWSTR)headers); 1311 return 0; 1312 } 1313 } 1314 free((LPWSTR)headers); 1315 1316 return 1; 1317 } 1318 1319 void naettPlatformMakeRequest(InternalResponse* res) { 1320 InternalRequest* req = res->request; 1321 1322 LPCWSTR extraHeaders = WINHTTP_NO_ADDITIONAL_HEADERS; 1323 WCHAR contentLengthHeader[64]; 1324 1325 int contentLength = req->options.bodyReader(NULL, 0, req->options.bodyReaderData); 1326 if (contentLength > 0) { 1327 swprintf(contentLengthHeader, 64, L"Content-Length: %d", contentLength); 1328 extraHeaders = contentLengthHeader; 1329 } 1330 1331 if (!WinHttpSendRequest(req->request, extraHeaders, -1, NULL, 0, 0, (DWORD_PTR)res)) { 1332 res->code = naettConnectionError; 1333 res->complete = 1; 1334 } 1335 } 1336 1337 void naettPlatformFreeRequest(InternalRequest* req) { 1338 assert(req != NULL); 1339 1340 if (req->request != NULL) { 1341 WinHttpCloseHandle(req->request); 1342 req->request = NULL; 1343 } 1344 if (req->connection != NULL) { 1345 WinHttpCloseHandle(req->connection); 1346 req->connection = NULL; 1347 } 1348 if (req->session != NULL) { 1349 WinHttpCloseHandle(req->session); 1350 req->session = NULL; 1351 } 1352 if (req->host != NULL) { 1353 free(req->host); 1354 req->host = NULL; 1355 } 1356 if (req->resource != NULL) { 1357 free(req->resource); 1358 req->resource = NULL; 1359 } 1360 } 1361 1362 void naettPlatformCloseResponse(InternalResponse* res) { 1363 } 1364 1365 #endif // __WINDOWS__ 1366 // End of inlined naett_win.c // 1367 1368 // Inlined naett_android.c: // 1369 //#include "naett_internal.h" 1370 1371 #ifdef __ANDROID__ 1372 1373 #ifdef __cplusplus 1374 #error "Cannot build in C++ mode" 1375 #endif 1376 1377 #include <stdlib.h> 1378 #include <string.h> 1379 #include <jni.h> 1380 #include <android/log.h> 1381 #include <pthread.h> 1382 #include <stdarg.h> 1383 1384 #ifndef NDEBUG 1385 #define LOGD(...) ((void)__android_log_print(ANDROID_LOG_DEBUG, "naett", __VA_ARGS__)) 1386 #else 1387 #define LOGD(...) ((void)0) 1388 #endif 1389 #define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "naett", __VA_ARGS__)) 1390 #define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, "naett", __VA_ARGS__)) 1391 1392 static JavaVM* globalVM = NULL; 1393 1394 static JavaVM* getVM() { 1395 if (globalVM == NULL) { 1396 LOGE("Panic: No VM configured, exiting."); 1397 exit(42); 1398 } 1399 return globalVM; 1400 } 1401 1402 static JNIEnv* getEnv() { 1403 JavaVM* vm = getVM(); 1404 JNIEnv* env; 1405 (*vm)->AttachCurrentThread(vm, &env, NULL); 1406 return env; 1407 } 1408 1409 static int catch (JNIEnv* env) { 1410 int thrown = (*env)->ExceptionCheck(env); 1411 if (thrown) { 1412 (*env)->ExceptionDescribe(env); 1413 } 1414 return thrown; 1415 } 1416 1417 static jmethodID getMethod(JNIEnv* env, jobject instance, const char* method, const char* sig) { 1418 jclass clazz = (*env)->GetObjectClass(env, instance); 1419 jmethodID id = (*env)->GetMethodID(env, clazz, method, sig); 1420 (*env)->DeleteLocalRef(env, clazz); 1421 return id; 1422 } 1423 1424 static jobject call(JNIEnv* env, jobject instance, const char* method, const char* sig, ...) { 1425 jmethodID methodID = getMethod(env, instance, method, sig); 1426 va_list args; 1427 va_start(args, sig); 1428 jobject result = (*env)->CallObjectMethodV(env, instance, methodID, args); 1429 va_end(args); 1430 return result; 1431 } 1432 1433 static void voidCall(JNIEnv* env, jobject instance, const char* method, const char* sig, ...) { 1434 jmethodID methodID = getMethod(env, instance, method, sig); 1435 va_list args; 1436 va_start(args, sig); 1437 (*env)->CallVoidMethodV(env, instance, methodID, args); 1438 va_end(args); 1439 } 1440 1441 static jint intCall(JNIEnv* env, jobject instance, const char* method, const char* sig, ...) { 1442 jmethodID methodID = getMethod(env, instance, method, sig); 1443 va_list args; 1444 va_start(args, sig); 1445 jint result = (*env)->CallIntMethodV(env, instance, methodID, args); 1446 va_end(args); 1447 return result; 1448 } 1449 1450 void naettPlatformInit(naettInitData initData) { 1451 globalVM = initData; 1452 } 1453 1454 int naettPlatformInitRequest(InternalRequest* req) { 1455 JNIEnv* env = getEnv(); 1456 (*env)->PushLocalFrame(env, 10); 1457 jclass URL = (*env)->FindClass(env, "java/net/URL"); 1458 jmethodID newURL = (*env)->GetMethodID(env, URL, "<init>", "(Ljava/lang/String;)V"); 1459 jstring urlString = (*env)->NewStringUTF(env, req->url); 1460 jobject url = (*env)->NewObject(env, URL, newURL, urlString); 1461 if (catch (env)) { 1462 (*env)->PopLocalFrame(env, NULL); 1463 return 0; 1464 } 1465 req->urlObject = (*env)->NewGlobalRef(env, url); 1466 (*env)->PopLocalFrame(env, NULL); 1467 return 1; 1468 } 1469 1470 static void* processRequest(void* data) { 1471 const int bufSize = 10240; 1472 char byteBuffer[bufSize]; 1473 InternalResponse* res = (InternalResponse*)data; 1474 InternalRequest* req = res->request; 1475 1476 JNIEnv* env = getEnv(); 1477 (*env)->PushLocalFrame(env, 100); 1478 1479 jobject connection = call(env, req->urlObject, "openConnection", "()Ljava/net/URLConnection;"); 1480 if (catch (env)) { 1481 res->code = naettConnectionError; 1482 goto finally; 1483 } 1484 1485 { 1486 jstring name = (*env)->NewStringUTF(env, "User-Agent"); 1487 jstring value = (*env)->NewStringUTF(env, req->options.userAgent ? req->options.userAgent : NAETT_UA); 1488 voidCall(env, connection, "addRequestProperty", "(Ljava/lang/String;Ljava/lang/String;)V", name, value); 1489 (*env)->DeleteLocalRef(env, name); 1490 (*env)->DeleteLocalRef(env, value); 1491 } 1492 1493 KVLink* header = req->options.headers; 1494 while (header != NULL) { 1495 jstring name = (*env)->NewStringUTF(env, header->key); 1496 jstring value = (*env)->NewStringUTF(env, header->value); 1497 voidCall(env, connection, "addRequestProperty", "(Ljava/lang/String;Ljava/lang/String;)V", name, value); 1498 (*env)->DeleteLocalRef(env, name); 1499 (*env)->DeleteLocalRef(env, value); 1500 header = header->next; 1501 } 1502 1503 jobject outputStream = NULL; 1504 if (strcmp(req->options.method, "POST") == 0 || strcmp(req->options.method, "PUT") == 0 || 1505 strcmp(req->options.method, "PATCH") == 0 || strcmp(req->options.method, "DELETE") == 0) { 1506 voidCall(env, connection, "setDoOutput", "(Z)V", 1); 1507 outputStream = call(env, connection, "getOutputStream", "()Ljava/io/OutputStream;"); 1508 } 1509 jobject methodString = (*env)->NewStringUTF(env, req->options.method); 1510 voidCall(env, connection, "setRequestMethod", "(Ljava/lang/String;)V", methodString); 1511 voidCall(env, connection, "setConnectTimeout", "(I)V", req->options.timeoutMS); 1512 voidCall(env, connection, "setInstanceFollowRedirects", "(Z)V", 1); 1513 1514 voidCall(env, connection, "connect", "()V"); 1515 if (catch (env)) { 1516 res->code = naettConnectionError; 1517 goto finally; 1518 } 1519 1520 jbyteArray buffer = (*env)->NewByteArray(env, bufSize); 1521 1522 if (outputStream != NULL) { 1523 int bytesRead = 0; 1524 if (req->options.bodyReader != NULL) 1525 do { 1526 bytesRead = req->options.bodyReader(byteBuffer, bufSize, req->options.bodyReaderData); 1527 if (bytesRead > 0) { 1528 (*env)->SetByteArrayRegion(env, buffer, 0, bytesRead, (const jbyte*) byteBuffer); 1529 voidCall(env, outputStream, "write", "([BII)V", buffer, 0, bytesRead); 1530 } else { 1531 break; 1532 } 1533 } while (!res->closeRequested); 1534 voidCall(env, outputStream, "close", "()V"); 1535 } 1536 1537 jobject headerMap = call(env, connection, "getHeaderFields", "()Ljava/util/Map;"); 1538 if (catch (env)) { 1539 res->code = naettProtocolError; 1540 goto finally; 1541 } 1542 1543 jobject headerSet = call(env, headerMap, "keySet", "()Ljava/util/Set;"); 1544 jarray headers = call(env, headerSet, "toArray", "()[Ljava/lang/Object;"); 1545 jsize headerCount = (*env)->GetArrayLength(env, headers); 1546 1547 KVLink *firstHeader = NULL; 1548 for (int i = 0; i < headerCount; i++) { 1549 jstring name = (*env)->GetObjectArrayElement(env, headers, i); 1550 if (name == NULL) { 1551 continue; 1552 } 1553 const char* nameString = (*env)->GetStringUTFChars(env, name, NULL); 1554 1555 jobject values = call(env, headerMap, "get", "(Ljava/lang/Object;)Ljava/lang/Object;", name); 1556 jstring value = call(env, values, "get", "(I)Ljava/lang/Object;", 0); 1557 const char* valueString = (*env)->GetStringUTFChars(env, value, NULL); 1558 1559 naettAlloc(KVLink, node); 1560 node->key = strdup(nameString); 1561 node->value = strdup(valueString); 1562 node->next = firstHeader; 1563 firstHeader = node; 1564 1565 (*env)->ReleaseStringUTFChars(env, name, nameString); 1566 (*env)->ReleaseStringUTFChars(env, value, valueString); 1567 1568 (*env)->DeleteLocalRef(env, name); 1569 (*env)->DeleteLocalRef(env, value); 1570 (*env)->DeleteLocalRef(env, values); 1571 } 1572 res->headers = firstHeader; 1573 1574 const char *contentLength = naettGetHeader((naettRes *)res, "Content-Length"); 1575 if (!contentLength || sscanf(contentLength, "%d", &res->contentLength) != 1) { 1576 res->contentLength = -1; 1577 } 1578 1579 int statusCode = intCall(env, connection, "getResponseCode", "()I"); 1580 1581 jobject inputStream = NULL; 1582 1583 if (statusCode >= 400) { 1584 inputStream = call(env, connection, "getErrorStream", "()Ljava/io/InputStream;"); 1585 } else { 1586 inputStream = call(env, connection, "getInputStream", "()Ljava/io/InputStream;"); 1587 } 1588 if (catch (env)) { 1589 res->code = naettProtocolError; 1590 goto finally; 1591 } 1592 1593 jint bytesRead = 0; 1594 do { 1595 bytesRead = intCall(env, inputStream, "read", "([B)I", buffer); 1596 if (catch (env)) { 1597 res->code = naettReadError; 1598 goto finally; 1599 } 1600 if (bytesRead < 0) { 1601 break; 1602 } else if (bytesRead > 0) { 1603 (*env)->GetByteArrayRegion(env, buffer, 0, bytesRead, (jbyte*) byteBuffer); 1604 req->options.bodyWriter(byteBuffer, bytesRead, req->options.bodyWriterData); 1605 res->totalBytesRead += bytesRead; 1606 } 1607 } while (!res->closeRequested); 1608 1609 voidCall(env, inputStream, "close", "()V"); 1610 1611 res->code = statusCode; 1612 1613 finally: 1614 res->complete = 1; 1615 (*env)->PopLocalFrame(env, NULL); 1616 JavaVM* vm = getVM(); 1617 (*env)->ExceptionClear(env); 1618 (*vm)->DetachCurrentThread(vm); 1619 return NULL; 1620 } 1621 1622 static void startWorkerThread(InternalResponse* res) { 1623 pthread_attr_t attr; 1624 pthread_attr_init(&attr); 1625 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); 1626 pthread_create(&res->workerThread, &attr, processRequest, res); 1627 pthread_setname_np(res->workerThread, "naett worker thread"); 1628 } 1629 1630 void naettPlatformMakeRequest(InternalResponse* res) { 1631 startWorkerThread(res); 1632 } 1633 1634 void naettPlatformFreeRequest(InternalRequest* req) { 1635 JNIEnv* env = getEnv(); 1636 (*env)->DeleteGlobalRef(env, req->urlObject); 1637 } 1638 1639 void naettPlatformCloseResponse(InternalResponse* res) { 1640 res->closeRequested = 1; 1641 if (res->workerThread != 0) { 1642 int joinResult = pthread_join(res->workerThread, NULL); 1643 if (joinResult != 0) { 1644 LOGE("Failed to join: %s", strerror(joinResult)); 1645 } 1646 } 1647 } 1648 1649 #endif // __ANDROID__ 1650 // End of inlined naett_android.c // 1651 1652 // End of inlined amalgam.h //