main.c revision 1.1 1 1.1 christos #include <assert.h>
2 1.1 christos #include <stdio.h>
3 1.1 christos #include <stdlib.h>
4 1.1 christos #include <uv.h>
5 1.1 christos #include <curl/curl.h>
6 1.1 christos
7 1.1 christos uv_loop_t *loop;
8 1.1 christos CURLM *curl_handle;
9 1.1 christos uv_timer_t timeout;
10 1.1 christos
11 1.1 christos typedef struct curl_context_s {
12 1.1 christos uv_poll_t poll_handle;
13 1.1 christos curl_socket_t sockfd;
14 1.1 christos } curl_context_t;
15 1.1 christos
16 1.1 christos curl_context_t *create_curl_context(curl_socket_t sockfd) {
17 1.1 christos curl_context_t *context;
18 1.1 christos
19 1.1 christos context = (curl_context_t*) malloc(sizeof *context);
20 1.1 christos
21 1.1 christos context->sockfd = sockfd;
22 1.1 christos
23 1.1 christos int r = uv_poll_init_socket(loop, &context->poll_handle, sockfd);
24 1.1 christos assert(r == 0);
25 1.1 christos context->poll_handle.data = context;
26 1.1 christos
27 1.1 christos return context;
28 1.1 christos }
29 1.1 christos
30 1.1 christos void curl_close_cb(uv_handle_t *handle) {
31 1.1 christos curl_context_t *context = (curl_context_t*) handle->data;
32 1.1 christos free(context);
33 1.1 christos }
34 1.1 christos
35 1.1 christos void destroy_curl_context(curl_context_t *context) {
36 1.1 christos uv_close((uv_handle_t*) &context->poll_handle, curl_close_cb);
37 1.1 christos }
38 1.1 christos
39 1.1 christos
40 1.1 christos void add_download(const char *url, int num) {
41 1.1 christos char filename[50];
42 1.1 christos sprintf(filename, "%d.download", num);
43 1.1 christos FILE *file;
44 1.1 christos
45 1.1 christos file = fopen(filename, "w");
46 1.1 christos if (file == NULL) {
47 1.1 christos fprintf(stderr, "Error opening %s\n", filename);
48 1.1 christos return;
49 1.1 christos }
50 1.1 christos
51 1.1 christos CURL *handle = curl_easy_init();
52 1.1 christos curl_easy_setopt(handle, CURLOPT_WRITEDATA, file);
53 1.1 christos curl_easy_setopt(handle, CURLOPT_URL, url);
54 1.1 christos curl_multi_add_handle(curl_handle, handle);
55 1.1 christos fprintf(stderr, "Added download %s -> %s\n", url, filename);
56 1.1 christos }
57 1.1 christos
58 1.1 christos void check_multi_info(void) {
59 1.1 christos char *done_url;
60 1.1 christos CURLMsg *message;
61 1.1 christos int pending;
62 1.1 christos
63 1.1 christos while ((message = curl_multi_info_read(curl_handle, &pending))) {
64 1.1 christos switch (message->msg) {
65 1.1 christos case CURLMSG_DONE:
66 1.1 christos curl_easy_getinfo(message->easy_handle, CURLINFO_EFFECTIVE_URL,
67 1.1 christos &done_url);
68 1.1 christos printf("%s DONE\n", done_url);
69 1.1 christos
70 1.1 christos curl_multi_remove_handle(curl_handle, message->easy_handle);
71 1.1 christos curl_easy_cleanup(message->easy_handle);
72 1.1 christos break;
73 1.1 christos
74 1.1 christos default:
75 1.1 christos fprintf(stderr, "CURLMSG default\n");
76 1.1 christos abort();
77 1.1 christos }
78 1.1 christos }
79 1.1 christos }
80 1.1 christos
81 1.1 christos void curl_perform(uv_poll_t *req, int status, int events) {
82 1.1 christos uv_timer_stop(&timeout);
83 1.1 christos int running_handles;
84 1.1 christos int flags = 0;
85 1.1 christos if (status < 0) flags = CURL_CSELECT_ERR;
86 1.1 christos if (!status && events & UV_READABLE) flags |= CURL_CSELECT_IN;
87 1.1 christos if (!status && events & UV_WRITABLE) flags |= CURL_CSELECT_OUT;
88 1.1 christos
89 1.1 christos curl_context_t *context;
90 1.1 christos
91 1.1 christos context = (curl_context_t*)req;
92 1.1 christos
93 1.1 christos curl_multi_socket_action(curl_handle, context->sockfd, flags, &running_handles);
94 1.1 christos check_multi_info();
95 1.1 christos }
96 1.1 christos
97 1.1 christos void on_timeout(uv_timer_t *req) {
98 1.1 christos int running_handles;
99 1.1 christos curl_multi_socket_action(curl_handle, CURL_SOCKET_TIMEOUT, 0, &running_handles);
100 1.1 christos check_multi_info();
101 1.1 christos }
102 1.1 christos
103 1.1 christos void start_timeout(CURLM *multi, long timeout_ms, void *userp) {
104 1.1 christos if (timeout_ms <= 0)
105 1.1 christos timeout_ms = 1; /* 0 means directly call socket_action, but we'll do it in a bit */
106 1.1 christos uv_timer_start(&timeout, on_timeout, timeout_ms, 0);
107 1.1 christos }
108 1.1 christos
109 1.1 christos int handle_socket(CURL *easy, curl_socket_t s, int action, void *userp, void *socketp) {
110 1.1 christos curl_context_t *curl_context;
111 1.1 christos if (action == CURL_POLL_IN || action == CURL_POLL_OUT) {
112 1.1 christos if (socketp) {
113 1.1 christos curl_context = (curl_context_t*) socketp;
114 1.1 christos }
115 1.1 christos else {
116 1.1 christos curl_context = create_curl_context(s);
117 1.1 christos curl_multi_assign(curl_handle, s, (void *) curl_context);
118 1.1 christos }
119 1.1 christos }
120 1.1 christos
121 1.1 christos switch (action) {
122 1.1 christos case CURL_POLL_IN:
123 1.1 christos uv_poll_start(&curl_context->poll_handle, UV_READABLE, curl_perform);
124 1.1 christos break;
125 1.1 christos case CURL_POLL_OUT:
126 1.1 christos uv_poll_start(&curl_context->poll_handle, UV_WRITABLE, curl_perform);
127 1.1 christos break;
128 1.1 christos case CURL_POLL_REMOVE:
129 1.1 christos if (socketp) {
130 1.1 christos uv_poll_stop(&((curl_context_t*)socketp)->poll_handle);
131 1.1 christos destroy_curl_context((curl_context_t*) socketp);
132 1.1 christos curl_multi_assign(curl_handle, s, NULL);
133 1.1 christos }
134 1.1 christos break;
135 1.1 christos default:
136 1.1 christos abort();
137 1.1 christos }
138 1.1 christos
139 1.1 christos return 0;
140 1.1 christos }
141 1.1 christos
142 1.1 christos int main(int argc, char **argv) {
143 1.1 christos loop = uv_default_loop();
144 1.1 christos
145 1.1 christos if (argc <= 1)
146 1.1 christos return 0;
147 1.1 christos
148 1.1 christos if (curl_global_init(CURL_GLOBAL_ALL)) {
149 1.1 christos fprintf(stderr, "Could not init cURL\n");
150 1.1 christos return 1;
151 1.1 christos }
152 1.1 christos
153 1.1 christos uv_timer_init(loop, &timeout);
154 1.1 christos
155 1.1 christos curl_handle = curl_multi_init();
156 1.1 christos curl_multi_setopt(curl_handle, CURLMOPT_SOCKETFUNCTION, handle_socket);
157 1.1 christos curl_multi_setopt(curl_handle, CURLMOPT_TIMERFUNCTION, start_timeout);
158 1.1 christos
159 1.1 christos while (argc-- > 1) {
160 1.1 christos add_download(argv[argc], argc);
161 1.1 christos }
162 1.1 christos
163 1.1 christos uv_run(loop, UV_RUN_DEFAULT);
164 1.1 christos curl_multi_cleanup(curl_handle);
165 1.1 christos return 0;
166 1.1 christos }
167