#define AL_LOG_SECTION "cache_http" #include #include "../../codec/codec.h" #include "../backings/memory.h" //#include "../backings/file.h" #include "../threaded_waits.h" #include "../wait.h" #include "http.h" static str USER_AGENT = al_str_c("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Safari/537.36"); static bool handler_http_can_seek(struct cch_handler *handler) { (void)handler; return false; } static void http_callback(void *userdata, u8 op, u8 *buf, void *opaque) { struct cch_handler_http *http = (struct cch_handler_http *)userdata; switch (op) { case NNWT_HTTP_WRITE: { size_t n = *(size_t *)opaque; // If we got data before a Content-Length, assume the size is unknown. if (http->backing->get_size_if_known(http->backing) < 0) { http->backing->set_size(http->backing, 0, false); } http->backing->write(http->backing, buf, http->pointer, &n); http->pointer += n; *(size_t *)opaque = n; cch_threaded_waits_evaluate(&http->waits, http->backing); break; } case NNWT_HTTP_RESPONSE_CODE: { long response_code = *(long *)opaque; log_debug("HTTP %ld.", response_code); break; } case NNWT_HTTP_CONTENT_LENGTH: { curl_off_t length = *(curl_off_t *)opaque; log_debug("Content-Length: %"CURL_FORMAT_CURL_OFF_T".", length); if (length > 0) { // HTTP 204 No Content. finalize() with 0 size. http->backing->set_size(http->backing, length, true); } cch_threaded_waits_signal_anys(&http->waits); break; } case NNWT_HTTP_REDIRECT: { long response_code = *(long *)opaque; log_debug("Redirect %ld.", response_code); break; } case NNWT_HTTP_FINISHED: { http->backing->finalize(http->backing); if (http->backing->get_size_if_known(http->backing) == 0) { // The handle needs to consider that it could wake up from any // wait with a filesize of 0. cch_threaded_waits_disable_all(&http->waits); } else { cch_threaded_waits_evaluate(&http->waits, http->backing); } log_debug("Transfer finished."); break; } case NNWT_HTTP_ERROR: cch_threaded_waits_disable_all(&http->waits); break; case NNWT_HTTP_READ: default: al_assert_and_return(); } } static void handler_http_maybe_spawn_worker(struct cch_handler *handler, size_t index) { struct cch_handler_http *http = (struct cch_handler_http *)handler; (void)index; al_array_push(http->requests, (struct nn_http){ 0 }); struct nn_http *request = &al_array_last(http->requests); nn_http_init(request); nn_http_set_url(request, &http->url); nn_http_set_user_agent(request, &USER_AGENT); nn_http_request_stream(request, NNWT_HTTP_GET, http->loop, http_callback, http); log_debug("Spawning worker for %.*s.", al_str_x(&http->url)); } static bool handler_http_wait_for_range(struct cch_handler *handler, struct cch_wait *wait) { struct cch_handler_http *http = (struct cch_handler_http *)handler; return cch_threaded_wait_for_range(&http->waits, http->backing, wait); } static s32 handler_http_guess_format(struct cch_handler *handler) { (void)handler; return CAMU_CODEC_FALLBACK; } static void handler_http_free(struct cch_handler **handler) { struct cch_handler_http *http = (struct cch_handler_http *)*handler; cch_threaded_waits_disable_all(&http->waits); struct nn_http *request; al_array_foreach_ptr(http->requests, i, request) { nn_http_close(request); } cch_threaded_waits_close(&http->waits); al_array_free(http->requests); al_str_free(&http->url); al_str_free(&http->handler.handler); al_free(http); *handler = NULL; } struct cch_entry *cch_handler_http_create(str *url, struct nn_event_loop *loop) { struct cch_backing *backing = cch_backing_memory_create(1024 * 128); if (!backing) return NULL; struct cch_handler_http *http = al_alloc_object(struct cch_handler_http); http->backing = backing; struct cch_entry *entry = al_alloc_object(struct cch_entry); entry->backing = backing; entry->handler = (struct cch_handler *)http; entry->handler->can_seek = handler_http_can_seek; entry->handler->maybe_spawn_worker = handler_http_maybe_spawn_worker; entry->handler->wait_for_range = handler_http_wait_for_range; entry->handler->guess_format = handler_http_guess_format; entry->handler->free = handler_http_free; entry->handler->entry = entry; al_str_from(&entry->handler->handler, "codec"); http->loop = loop; al_str_clone(&http->url, url); http->pointer = 0; al_array_init(http->requests); cch_threaded_waits_init(&http->waits); return entry; }