1/***************************************************************************
2 *                                  _   _ ____  _
3 *  Project                     ___| | | |  _ \| |
4 *                             / __| | | | |_) | |
5 *                            | (__| |_| |  _ <| |___
6 *                             \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
9 *
10 * This software is licensed as described in the file COPYING, which
11 * you should have received as part of this distribution. The terms
12 * are also available at http://curl.haxx.se/docs/copyright.html.
13 *
14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15 * copies of the Software, and permit persons to whom the Software is
16 * furnished to do so, under the terms of the COPYING file.
17 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
21 ***************************************************************************/
22
23/* Example application code using the multi socket interface to download
24   multiple files at once, but instead of using curl_multi_perform and
25   curl_multi_wait, which uses select(), we use libuv.
26   It supports epoll, kqueue, etc. on unixes and fast IO completion ports on
27   Windows, which means, it should be very fast on all platforms..
28
29   Written by Clemens Gruber, based on an outdated example from uvbook and
30   some tests from libuv.
31
32   Requires libuv and (of course) libcurl.
33
34   See http://nikhilm.github.com/uvbook/ for more information on libuv.
35*/
36
37#include <stdio.h>
38#include <stdlib.h>
39#include <uv.h>
40#include <curl/curl.h>
41
42uv_loop_t *loop;
43CURLM *curl_handle;
44uv_timer_t timeout;
45
46typedef struct curl_context_s {
47  uv_poll_t poll_handle;
48  curl_socket_t sockfd;
49} curl_context_t;
50
51curl_context_t* create_curl_context(curl_socket_t sockfd)
52{
53  curl_context_t *context;
54
55  context = (curl_context_t *) malloc(sizeof *context);
56
57  context->sockfd = sockfd;
58
59  uv_poll_init_socket(loop, &context->poll_handle, sockfd);
60  context->poll_handle.data = context;
61
62  return context;
63}
64
65void curl_close_cb(uv_handle_t *handle)
66{
67  curl_context_t* context = (curl_context_t*) handle->data;
68  free(context);
69}
70
71void destroy_curl_context(curl_context_t *context)
72{
73  uv_close((uv_handle_t*) &context->poll_handle, curl_close_cb);
74}
75
76
77void add_download(const char *url, int num)
78{
79  char filename[50];
80  FILE *file;
81  CURL *handle;
82
83  sprintf(filename, "%d.download", num);
84
85  file = fopen(filename, "w");
86  if (file == NULL) {
87    fprintf(stderr, "Error opening %s\n", filename);
88    return;
89  }
90
91  handle = curl_easy_init();
92  curl_easy_setopt(handle, CURLOPT_WRITEDATA, file);
93  curl_easy_setopt(handle, CURLOPT_URL, url);
94  curl_multi_add_handle(curl_handle, handle);
95  fprintf(stderr, "Added download %s -> %s\n", url, filename);
96}
97
98void curl_perform(uv_poll_t *req, int status, int events)
99{
100  int running_handles;
101  int flags = 0;
102  curl_context_t *context;
103  char *done_url;
104  CURLMsg *message;
105  int pending;
106
107  uv_timer_stop(&timeout);
108
109  if (events & UV_READABLE)
110    flags |= CURL_CSELECT_IN;
111  if (events & UV_WRITABLE)
112    flags |= CURL_CSELECT_OUT;
113
114  context = (curl_context_t*)req;
115
116  curl_multi_socket_action(curl_handle, context->sockfd, flags,
117                           &running_handles);
118
119  while ((message = curl_multi_info_read(curl_handle, &pending))) {
120    switch (message->msg) {
121    case CURLMSG_DONE:
122      curl_easy_getinfo(message->easy_handle, CURLINFO_EFFECTIVE_URL,
123                        &done_url);
124      printf("%s DONE\n", done_url);
125
126      curl_multi_remove_handle(curl_handle, message->easy_handle);
127      curl_easy_cleanup(message->easy_handle);
128
129      break;
130    default:
131      fprintf(stderr, "CURLMSG default\n");
132      abort();
133    }
134  }
135}
136
137void on_timeout(uv_timer_t *req, int status)
138{
139  int running_handles;
140  curl_multi_socket_action(curl_handle, CURL_SOCKET_TIMEOUT, 0,
141                           &running_handles);
142}
143
144void start_timeout(CURLM *multi, long timeout_ms, void *userp)
145{
146  if (timeout_ms <= 0)
147    timeout_ms = 1; /* 0 means directly call socket_action, but we'll do it in
148                       a bit */
149  uv_timer_start(&timeout, on_timeout, timeout_ms, 0);
150}
151
152int handle_socket(CURL *easy, curl_socket_t s, int action, void *userp,
153                  void *socketp)
154{
155  curl_context_t *curl_context;
156  if (action == CURL_POLL_IN || action == CURL_POLL_OUT) {
157    if (socketp) {
158      curl_context = (curl_context_t*) socketp;
159    }
160    else {
161      curl_context = create_curl_context(s);
162    }
163    curl_multi_assign(curl_handle, s, (void *) curl_context);
164  }
165
166  switch (action) {
167  case CURL_POLL_IN:
168    uv_poll_start(&curl_context->poll_handle, UV_READABLE, curl_perform);
169    break;
170  case CURL_POLL_OUT:
171    uv_poll_start(&curl_context->poll_handle, UV_WRITABLE, curl_perform);
172    break;
173  case CURL_POLL_REMOVE:
174    if (socketp) {
175      uv_poll_stop(&((curl_context_t*)socketp)->poll_handle);
176      destroy_curl_context((curl_context_t*) socketp);
177      curl_multi_assign(curl_handle, s, NULL);
178    }
179    break;
180  default:
181    abort();
182  }
183
184  return 0;
185}
186
187int main(int argc, char **argv)
188{
189  loop = uv_default_loop();
190
191  if (argc <= 1)
192    return 0;
193
194  if (curl_global_init(CURL_GLOBAL_ALL)) {
195    fprintf(stderr, "Could not init cURL\n");
196    return 1;
197  }
198
199  uv_timer_init(loop, &timeout);
200
201  curl_handle = curl_multi_init();
202  curl_multi_setopt(curl_handle, CURLMOPT_SOCKETFUNCTION, handle_socket);
203  curl_multi_setopt(curl_handle, CURLMOPT_TIMERFUNCTION, start_timeout);
204
205  while (argc-- > 1) {
206    add_download(argv[argc], argc);
207  }
208
209  uv_run(loop, UV_RUN_DEFAULT);
210  curl_multi_cleanup(curl_handle);
211  return 0;
212}
213