1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
|
#define AL_LOG_SECTION "cache_http"
#include <al/log.h>
#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;
}
|