multi-event.c (6297B)
1 /*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) 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 https://curl.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 * SPDX-License-Identifier: curl 22 * 23 ***************************************************************************/ 24 25 /* <DESC> 26 * multi_socket API using libevent 27 * </DESC> 28 */ 29 30 #include <stdio.h> 31 #include <stdlib.h> 32 #include <event2/event.h> 33 #include <curl/curl.h> 34 35 static struct event_base *base; 36 static CURLM *curl_handle; 37 static struct event *timeout; 38 39 struct curl_context { 40 struct event *event; 41 curl_socket_t sockfd; 42 }; 43 44 static void curl_perform(int fd, short event, void *arg); 45 46 static struct curl_context *create_curl_context(curl_socket_t sockfd) 47 { 48 struct curl_context *context; 49 50 context = (struct curl_context *) malloc(sizeof(*context)); 51 52 context->sockfd = sockfd; 53 54 context->event = event_new(base, sockfd, 0, curl_perform, context); 55 56 return context; 57 } 58 59 static void destroy_curl_context(struct curl_context *context) 60 { 61 event_del(context->event); 62 event_free(context->event); 63 free(context); 64 } 65 66 static void add_download(const char *url, int num) 67 { 68 char filename[50]; 69 FILE *file; 70 CURL *handle; 71 72 snprintf(filename, 50, "%d.download", num); 73 74 file = fopen(filename, "wb"); 75 if(!file) { 76 fprintf(stderr, "Error opening %s\n", filename); 77 return; 78 } 79 80 handle = curl_easy_init(); 81 curl_easy_setopt(handle, CURLOPT_WRITEDATA, file); 82 curl_easy_setopt(handle, CURLOPT_PRIVATE, file); 83 curl_easy_setopt(handle, CURLOPT_URL, url); 84 curl_multi_add_handle(curl_handle, handle); 85 fprintf(stderr, "Added download %s -> %s\n", url, filename); 86 } 87 88 static void check_multi_info(void) 89 { 90 char *done_url; 91 CURLMsg *message; 92 int pending; 93 CURL *easy_handle; 94 FILE *file; 95 96 while((message = curl_multi_info_read(curl_handle, &pending))) { 97 switch(message->msg) { 98 case CURLMSG_DONE: 99 /* Do not use message data after calling curl_multi_remove_handle() and 100 curl_easy_cleanup(). As per curl_multi_info_read() docs: 101 "WARNING: The data the returned pointer points to does not survive 102 calling curl_multi_cleanup, curl_multi_remove_handle or 103 curl_easy_cleanup." */ 104 easy_handle = message->easy_handle; 105 106 curl_easy_getinfo(easy_handle, CURLINFO_EFFECTIVE_URL, &done_url); 107 curl_easy_getinfo(easy_handle, CURLINFO_PRIVATE, &file); 108 printf("%s DONE\n", done_url); 109 110 curl_multi_remove_handle(curl_handle, easy_handle); 111 curl_easy_cleanup(easy_handle); 112 if(file) { 113 fclose(file); 114 } 115 break; 116 117 default: 118 fprintf(stderr, "CURLMSG default\n"); 119 break; 120 } 121 } 122 } 123 124 static void curl_perform(int fd, short event, void *arg) 125 { 126 int running_handles; 127 int flags = 0; 128 struct curl_context *context; 129 130 (void)fd; 131 132 if(event & EV_READ) 133 flags |= CURL_CSELECT_IN; 134 if(event & EV_WRITE) 135 flags |= CURL_CSELECT_OUT; 136 137 context = (struct curl_context *) arg; 138 139 curl_multi_socket_action(curl_handle, context->sockfd, flags, 140 &running_handles); 141 142 check_multi_info(); 143 } 144 145 static void on_timeout(evutil_socket_t fd, short events, void *arg) 146 { 147 int running_handles; 148 (void)fd; 149 (void)events; 150 (void)arg; 151 curl_multi_socket_action(curl_handle, CURL_SOCKET_TIMEOUT, 0, 152 &running_handles); 153 check_multi_info(); 154 } 155 156 static int start_timeout(CURLM *multi, long timeout_ms, void *userp) 157 { 158 (void)multi; 159 (void)userp; 160 if(timeout_ms < 0) { 161 evtimer_del(timeout); 162 } 163 else { 164 struct timeval tv; 165 if(timeout_ms == 0) 166 timeout_ms = 1; /* 0 means call socket_action asap */ 167 tv.tv_sec = timeout_ms / 1000; 168 tv.tv_usec = (timeout_ms % 1000) * 1000; 169 evtimer_del(timeout); 170 evtimer_add(timeout, &tv); 171 } 172 return 0; 173 } 174 175 static int handle_socket(CURL *easy, curl_socket_t s, int action, void *userp, 176 void *socketp) 177 { 178 struct curl_context *curl_context; 179 int events = 0; 180 181 (void)easy; 182 (void)userp; 183 184 switch(action) { 185 case CURL_POLL_IN: 186 case CURL_POLL_OUT: 187 case CURL_POLL_INOUT: 188 curl_context = socketp ? 189 (struct curl_context *) socketp : create_curl_context(s); 190 191 curl_multi_assign(curl_handle, s, (void *) curl_context); 192 193 if(action != CURL_POLL_IN) 194 events |= EV_WRITE; 195 if(action != CURL_POLL_OUT) 196 events |= EV_READ; 197 198 events |= EV_PERSIST; 199 200 event_del(curl_context->event); 201 event_assign(curl_context->event, base, curl_context->sockfd, 202 (short)events, curl_perform, curl_context); 203 event_add(curl_context->event, NULL); 204 205 break; 206 case CURL_POLL_REMOVE: 207 if(socketp) { 208 event_del(((struct curl_context*) socketp)->event); 209 destroy_curl_context((struct curl_context*) socketp); 210 curl_multi_assign(curl_handle, s, NULL); 211 } 212 break; 213 default: 214 abort(); 215 } 216 217 return 0; 218 } 219 220 int main(int argc, char **argv) 221 { 222 if(argc <= 1) 223 return 0; 224 225 if(curl_global_init(CURL_GLOBAL_ALL)) { 226 fprintf(stderr, "Could not init curl\n"); 227 return 1; 228 } 229 230 base = event_base_new(); 231 timeout = evtimer_new(base, on_timeout, NULL); 232 233 curl_handle = curl_multi_init(); 234 curl_multi_setopt(curl_handle, CURLMOPT_SOCKETFUNCTION, handle_socket); 235 curl_multi_setopt(curl_handle, CURLMOPT_TIMERFUNCTION, start_timeout); 236 237 while(argc-- > 1) { 238 add_download(argv[argc], argc); 239 } 240 241 event_base_dispatch(base); 242 243 curl_multi_cleanup(curl_handle); 244 event_free(timeout); 245 event_base_free(base); 246 247 libevent_global_shutdown(); 248 curl_global_cleanup(); 249 250 return 0; 251 }