~ubuntu-branches/ubuntu/vivid/curl/vivid

« back to all changes in this revision

Viewing changes to docs/examples/evhiperfifo.c

  • Committer: Bazaar Package Importer
  • Author(s): Andreas Schuldei
  • Date: 2009-04-02 23:35:45 UTC
  • mto: (1.2.1 upstream) (3.2.3 sid)
  • mto: This revision was merged to the branch mainline in revision 38.
  • Revision ID: james.westby@ubuntu.com-20090402233545-geixkwhe3izccjt7
Tags: upstream-7.19.4
ImportĀ upstreamĀ versionĀ 7.19.4

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*****************************************************************************
 
2
 *                                  _   _ ____  _
 
3
 *  Project                     ___| | | |  _ \| |
 
4
 *                             / __| | | | |_) | |
 
5
 *                            | (__| |_| |  _ <| |___
 
6
 *                             \___|\___/|_| \_\_____|
 
7
 *
 
8
 * $Id: evhiperfifo.c,v 1.1 2008-11-21 10:10:33 bagder Exp $
 
9
 *
 
10
 * Example application source code using the multi socket interface to
 
11
 * download many files at once.
 
12
 *
 
13
 * This example features the same basic functionality as hiperfifo.c does,
 
14
 * but this uses libev instead of libevent.
 
15
 *
 
16
 * Written by Jeff Pohlmeyer, converted to use libev by Markus Koetter
 
17
 
 
18
Requires libev and a (POSIX?) system that has mkfifo().
 
19
 
 
20
This is an adaptation of libcurl's "hipev.c" and libevent's "event-test.c"
 
21
sample programs.
 
22
 
 
23
When running, the program creates the named pipe "hiper.fifo"
 
24
 
 
25
Whenever there is input into the fifo, the program reads the input as a list
 
26
of URL's and creates some new easy handles to fetch each URL via the
 
27
curl_multi "hiper" API.
 
28
 
 
29
 
 
30
Thus, you can try a single URL:
 
31
  % echo http://www.yahoo.com > hiper.fifo
 
32
 
 
33
Or a whole bunch of them:
 
34
  % cat my-url-list > hiper.fifo
 
35
 
 
36
The fifo buffer is handled almost instantly, so you can even add more URL's
 
37
while the previous requests are still being downloaded.
 
38
 
 
39
Note:
 
40
  For the sake of simplicity, URL length is limited to 1023 char's !
 
41
 
 
42
This is purely a demo app, all retrieved data is simply discarded by the write
 
43
callback.
 
44
 
 
45
*/
 
46
 
 
47
#include <stdio.h>
 
48
#include <string.h>
 
49
#include <stdlib.h>
 
50
#include <sys/time.h>
 
51
#include <time.h>
 
52
#include <unistd.h>
 
53
#include <sys/poll.h>
 
54
#include <curl/curl.h>
 
55
#include <ev.h>
 
56
#include <fcntl.h>
 
57
#include <sys/stat.h>
 
58
#include <errno.h>
 
59
 
 
60
#define DPRINT(x...) printf(x)
 
61
 
 
62
#define MSG_OUT stdout /* Send info to stdout, change to stderr if you want */
 
63
 
 
64
 
 
65
/* Global information, common to all connections */
 
66
typedef struct _GlobalInfo
 
67
{
 
68
  struct ev_loop *loop;
 
69
  struct ev_io fifo_event;
 
70
  struct ev_timer timer_event;
 
71
  CURLM *multi;
 
72
  int prev_running;
 
73
  int still_running;
 
74
  FILE* input;
 
75
} GlobalInfo;
 
76
 
 
77
 
 
78
/* Information associated with a specific easy handle */
 
79
typedef struct _ConnInfo
 
80
{
 
81
  CURL *easy;
 
82
  char *url;
 
83
  GlobalInfo *global;
 
84
  char error[CURL_ERROR_SIZE];
 
85
} ConnInfo;
 
86
 
 
87
 
 
88
/* Information associated with a specific socket */
 
89
typedef struct _SockInfo
 
90
{
 
91
  curl_socket_t sockfd;
 
92
  CURL *easy;
 
93
  int action;
 
94
  long timeout;
 
95
  struct ev_io ev;
 
96
  int evset;
 
97
  GlobalInfo *global;
 
98
} SockInfo;
 
99
 
 
100
static void timer_cb(EV_P_ struct ev_timer *w, int revents);
 
101
 
 
102
/* Update the event timer after curl_multi library calls */
 
103
static int multi_timer_cb(CURLM *multi, long timeout_ms, GlobalInfo *g)
 
104
{
 
105
  DPRINT("%s %li\n", __PRETTY_FUNCTION__,  timeout_ms);
 
106
  ev_timer_stop(g->loop, &g->timer_event);
 
107
  if (timeout_ms > 0)
 
108
  {
 
109
    double  t = timeout_ms / 1000;
 
110
    ev_timer_init(&g->timer_event, timer_cb, t, 0.);
 
111
    ev_timer_start(g->loop, &g->timer_event);
 
112
  }else
 
113
    timer_cb(g->loop, &g->timer_event, 0);
 
114
  return 0;
 
115
}
 
116
 
 
117
/* Die if we get a bad CURLMcode somewhere */
 
118
static void mcode_or_die(const char *where, CURLMcode code)
 
119
{
 
120
  if ( CURLM_OK != code )
 
121
  {
 
122
    const char *s;
 
123
    switch ( code )
 
124
    {
 
125
    case CURLM_CALL_MULTI_PERFORM: s="CURLM_CALL_MULTI_PERFORM"; break;
 
126
    case CURLM_OK:                 s="CURLM_OK";                 break;
 
127
    case CURLM_BAD_HANDLE:         s="CURLM_BAD_HANDLE";         break;
 
128
    case CURLM_BAD_EASY_HANDLE:    s="CURLM_BAD_EASY_HANDLE";    break;
 
129
    case CURLM_OUT_OF_MEMORY:      s="CURLM_OUT_OF_MEMORY";      break;
 
130
    case CURLM_INTERNAL_ERROR:     s="CURLM_INTERNAL_ERROR";     break;
 
131
    case CURLM_UNKNOWN_OPTION:     s="CURLM_UNKNOWN_OPTION";     break;
 
132
    case CURLM_LAST:               s="CURLM_LAST";               break;
 
133
    default: s="CURLM_unknown";
 
134
      break;
 
135
    case     CURLM_BAD_SOCKET:         s="CURLM_BAD_SOCKET";
 
136
      fprintf(MSG_OUT, "ERROR: %s returns %s\n", where, s);
 
137
      /* ignore this error */
 
138
      return;
 
139
    }
 
140
    fprintf(MSG_OUT, "ERROR: %s returns %s\n", where, s);
 
141
    exit(code);
 
142
  }
 
143
}
 
144
 
 
145
 
 
146
 
 
147
/* Check for completed transfers, and remove their easy handles */
 
148
static void check_run_count(GlobalInfo *g)
 
149
{
 
150
  DPRINT("%s prev %i still %i\n", __PRETTY_FUNCTION__,
 
151
         g->prev_running, g->still_running);
 
152
  if ( g->prev_running > g->still_running )
 
153
  {
 
154
    char *eff_url=NULL;
 
155
    CURLMsg *msg;
 
156
    int msgs_left;
 
157
    ConnInfo *conn=NULL;
 
158
    CURL*easy;
 
159
    CURLcode res;
 
160
 
 
161
    fprintf(MSG_OUT, "REMAINING: %d\n", g->still_running);
 
162
    /*
 
163
      I am still uncertain whether it is safe to remove an easy
 
164
      handle from inside the curl_multi_info_read loop, so here I
 
165
      will search for completed transfers in the inner "while"
 
166
      loop, and then remove them in the outer "do-while" loop...
 
167
    */
 
168
    do
 
169
    {
 
170
      easy=NULL;
 
171
      while ( (msg = curl_multi_info_read(g->multi, &msgs_left)) )
 
172
      {
 
173
        if ( msg->msg == CURLMSG_DONE )
 
174
        {
 
175
          easy=msg->easy_handle;
 
176
          res=msg->data.result;
 
177
        }
 
178
 
 
179
        if ( easy )
 
180
        {
 
181
          curl_easy_getinfo(easy, CURLINFO_PRIVATE, &conn);
 
182
          curl_easy_getinfo(easy, CURLINFO_EFFECTIVE_URL, &eff_url);
 
183
          fprintf(MSG_OUT, "DONE: %s => (%d) %s\n", eff_url, res, conn->error);
 
184
          curl_multi_remove_handle(g->multi, easy);
 
185
          free(conn->url);
 
186
          curl_easy_cleanup(easy);
 
187
          free(conn);
 
188
        }
 
189
      }
 
190
    } while ( easy );
 
191
  }
 
192
  g->prev_running = g->still_running;
 
193
}
 
194
 
 
195
 
 
196
/* Called by libevent when we get action on a multi socket */
 
197
static void event_cb(EV_P_ struct ev_io *w, int revents)
 
198
{
 
199
  DPRINT("%s  w %p revents %i\n", __PRETTY_FUNCTION__, w, revents);
 
200
  GlobalInfo *g = (GlobalInfo*) w->data;
 
201
  CURLMcode rc;
 
202
 
 
203
  int action = (revents&EV_READ?CURL_POLL_IN:0)|
 
204
    (revents&EV_WRITE?CURL_POLL_OUT:0);
 
205
  do
 
206
  {
 
207
    rc = curl_multi_socket_action(g->multi, w->fd, action, &g->still_running);
 
208
  } while ( rc == CURLM_CALL_MULTI_PERFORM );
 
209
  mcode_or_die("event_cb: curl_multi_socket", rc);
 
210
  check_run_count(g);
 
211
  if ( g->still_running <= 0 )
 
212
  {
 
213
    fprintf(MSG_OUT, "last transfer done, kill timeout\n");
 
214
    ev_timer_stop(g->loop, &g->timer_event);
 
215
  }
 
216
}
 
217
 
 
218
/* Called by libevent when our timeout expires */
 
219
static void timer_cb(EV_P_ struct ev_timer *w, int revents)
 
220
{
 
221
  DPRINT("%s  w %p revents %i\n", __PRETTY_FUNCTION__, w, revents);
 
222
 
 
223
  GlobalInfo *g = (GlobalInfo *)w->data;
 
224
  CURLMcode rc;
 
225
 
 
226
  do
 
227
  {
 
228
    rc = curl_multi_socket_action(g->multi, CURL_SOCKET_TIMEOUT, 0, &g->still_running);
 
229
  } while ( rc == CURLM_CALL_MULTI_PERFORM );
 
230
  mcode_or_die("timer_cb: curl_multi_socket", rc);
 
231
  check_run_count(g);
 
232
}
 
233
 
 
234
/* Clean up the SockInfo structure */
 
235
static void remsock(SockInfo *f, GlobalInfo *g)
 
236
{
 
237
  printf("%s  \n", __PRETTY_FUNCTION__);
 
238
  if ( f )
 
239
  {
 
240
    if ( f->evset )
 
241
      ev_io_stop(g->loop, &f->ev);
 
242
    free(f);
 
243
  }
 
244
}
 
245
 
 
246
 
 
247
 
 
248
/* Assign information to a SockInfo structure */
 
249
static void setsock(SockInfo*f, curl_socket_t s, CURL*e, int act, GlobalInfo*g)
 
250
{
 
251
  printf("%s  \n", __PRETTY_FUNCTION__);
 
252
 
 
253
  int kind = (act&CURL_POLL_IN?EV_READ:0)|(act&CURL_POLL_OUT?EV_WRITE:0);
 
254
 
 
255
  f->sockfd = s;
 
256
  f->action = act;
 
257
  f->easy = e;
 
258
  if ( f->evset )
 
259
    ev_io_stop(g->loop, &f->ev);
 
260
  ev_io_init(&f->ev, event_cb, f->sockfd, kind);
 
261
  f->ev.data = g;
 
262
  f->evset=1;
 
263
  ev_io_start(g->loop, &f->ev);
 
264
}
 
265
 
 
266
 
 
267
 
 
268
/* Initialize a new SockInfo structure */
 
269
static void addsock(curl_socket_t s, CURL *easy, int action, GlobalInfo *g)
 
270
{
 
271
  SockInfo *fdp = calloc(sizeof(SockInfo), 1);
 
272
 
 
273
  fdp->global = g;
 
274
  setsock(fdp, s, easy, action, g);
 
275
  curl_multi_assign(g->multi, s, fdp);
 
276
}
 
277
 
 
278
/* CURLMOPT_SOCKETFUNCTION */
 
279
static int sock_cb(CURL *e, curl_socket_t s, int what, void *cbp, void *sockp)
 
280
{
 
281
  DPRINT("%s e %p s %i what %i cbp %p sockp %p\n",
 
282
         __PRETTY_FUNCTION__, e, s, what, cbp, sockp);
 
283
 
 
284
  GlobalInfo *g = (GlobalInfo*) cbp;
 
285
  SockInfo *fdp = (SockInfo*) sockp;
 
286
  const char *whatstr[]={ "none", "IN", "OUT", "INOUT", "REMOVE"};
 
287
 
 
288
  fprintf(MSG_OUT,
 
289
          "socket callback: s=%d e=%p what=%s ", s, e, whatstr[what]);
 
290
  if ( what == CURL_POLL_REMOVE )
 
291
  {
 
292
    fprintf(MSG_OUT, "\n");
 
293
    remsock(fdp, g);
 
294
  } else
 
295
  {
 
296
    if ( !fdp )
 
297
    {
 
298
      fprintf(MSG_OUT, "Adding data: %s\n", whatstr[what]);
 
299
      addsock(s, e, what, g);
 
300
    } else
 
301
    {
 
302
      fprintf(MSG_OUT,
 
303
              "Changing action from %s to %s\n",
 
304
              whatstr[fdp->action], whatstr[what]);
 
305
      setsock(fdp, s, e, what, g);
 
306
    }
 
307
  }
 
308
  return 0;
 
309
}
 
310
 
 
311
 
 
312
/* CURLOPT_WRITEFUNCTION */
 
313
static size_t write_cb(void *ptr, size_t size, size_t nmemb, void *data)
 
314
{
 
315
  size_t realsize = size * nmemb;
 
316
  ConnInfo *conn = (ConnInfo*) data;
 
317
  (void)ptr;
 
318
  (void)conn;
 
319
  return realsize;
 
320
}
 
321
 
 
322
 
 
323
/* CURLOPT_PROGRESSFUNCTION */
 
324
static int prog_cb (void *p, double dltotal, double dlnow, double ult,
 
325
                    double uln)
 
326
{
 
327
  ConnInfo *conn = (ConnInfo *)p;
 
328
  (void)ult;
 
329
  (void)uln;
 
330
 
 
331
  fprintf(MSG_OUT, "Progress: %s (%g/%g)\n", conn->url, dlnow, dltotal);
 
332
  return 0;
 
333
}
 
334
 
 
335
 
 
336
/* Create a new easy handle, and add it to the global curl_multi */
 
337
static void new_conn(char *url, GlobalInfo *g )
 
338
{
 
339
  ConnInfo *conn;
 
340
  CURLMcode rc;
 
341
 
 
342
  conn = calloc(1, sizeof(ConnInfo));
 
343
  memset(conn, 0, sizeof(ConnInfo));
 
344
  conn->error[0]='\0';
 
345
 
 
346
  conn->easy = curl_easy_init();
 
347
  if ( !conn->easy )
 
348
  {
 
349
    fprintf(MSG_OUT, "curl_easy_init() failed, exiting!\n");
 
350
    exit(2);
 
351
  }
 
352
  conn->global = g;
 
353
  conn->url = strdup(url);
 
354
  curl_easy_setopt(conn->easy, CURLOPT_URL, conn->url);
 
355
  curl_easy_setopt(conn->easy, CURLOPT_WRITEFUNCTION, write_cb);
 
356
  curl_easy_setopt(conn->easy, CURLOPT_WRITEDATA, &conn);
 
357
  curl_easy_setopt(conn->easy, CURLOPT_VERBOSE, 1L);
 
358
  curl_easy_setopt(conn->easy, CURLOPT_ERRORBUFFER, conn->error);
 
359
  curl_easy_setopt(conn->easy, CURLOPT_PRIVATE, conn);
 
360
  curl_easy_setopt(conn->easy, CURLOPT_NOPROGRESS, 0L);
 
361
  curl_easy_setopt(conn->easy, CURLOPT_PROGRESSFUNCTION, prog_cb);
 
362
  curl_easy_setopt(conn->easy, CURLOPT_PROGRESSDATA, conn);
 
363
  curl_easy_setopt(conn->easy, CURLOPT_LOW_SPEED_TIME, 3L);
 
364
  curl_easy_setopt(conn->easy, CURLOPT_LOW_SPEED_LIMIT, 10L);
 
365
 
 
366
  fprintf(MSG_OUT,
 
367
          "Adding easy %p to multi %p (%s)\n", conn->easy, g->multi, url);
 
368
  rc =curl_multi_add_handle(g->multi, conn->easy);
 
369
  mcode_or_die("new_conn: curl_multi_add_handle", rc);
 
370
 
 
371
  mcode_or_die("new_conn: curl_multi_socket_all", rc);
 
372
  check_run_count(g);
 
373
}
 
374
 
 
375
/* This gets called whenever data is received from the fifo */
 
376
static void fifo_cb(EV_P_ struct ev_io *w, int revents)
 
377
{
 
378
  char s[1024];
 
379
  long int rv=0;
 
380
  int n=0;
 
381
  GlobalInfo *g = (GlobalInfo *)w->data;
 
382
 
 
383
  do
 
384
  {
 
385
    s[0]='\0';
 
386
    rv=fscanf(g->input, "%1023s%n", s, &n);
 
387
    s[n]='\0';
 
388
    if ( n && s[0] )
 
389
    {
 
390
      new_conn(s,g);  /* if we read a URL, go get it! */
 
391
    } else break;
 
392
  } while ( rv != EOF );
 
393
}
 
394
 
 
395
/* Create a named pipe and tell libevent to monitor it */
 
396
static int init_fifo (GlobalInfo *g)
 
397
{
 
398
  struct stat st;
 
399
  static const char *fifo = "hiper.fifo";
 
400
  int sockfd;
 
401
 
 
402
  fprintf(MSG_OUT, "Creating named pipe \"%s\"\n", fifo);
 
403
  if ( lstat (fifo, &st) == 0 )
 
404
  {
 
405
    if ( (st.st_mode & S_IFMT) == S_IFREG )
 
406
    {
 
407
      errno = EEXIST;
 
408
      perror("lstat");
 
409
      exit (1);
 
410
    }
 
411
  }
 
412
  unlink(fifo);
 
413
  if ( mkfifo (fifo, 0600) == -1 )
 
414
  {
 
415
    perror("mkfifo");
 
416
    exit (1);
 
417
  }
 
418
  sockfd = open(fifo, O_RDWR | O_NONBLOCK, 0);
 
419
  if ( sockfd == -1 )
 
420
  {
 
421
    perror("open");
 
422
    exit (1);
 
423
  }
 
424
  g->input = fdopen(sockfd, "r");
 
425
 
 
426
  fprintf(MSG_OUT, "Now, pipe some URL's into > %s\n", fifo);
 
427
  ev_io_init(&g->fifo_event, fifo_cb, sockfd, EV_READ);
 
428
  ev_io_start(g->loop, &g->fifo_event);
 
429
  return(0);
 
430
}
 
431
 
 
432
int main(int argc, char **argv)
 
433
{
 
434
  GlobalInfo g;
 
435
  CURLMcode rc;
 
436
  (void)argc;
 
437
  (void)argv;
 
438
 
 
439
  memset(&g, 0, sizeof(GlobalInfo));
 
440
  g.loop = ev_default_loop(0);
 
441
 
 
442
  init_fifo(&g);
 
443
  g.multi = curl_multi_init();
 
444
 
 
445
  ev_timer_init(&g.timer_event, timer_cb, 0., 0.);
 
446
  g.timer_event.data = &g;
 
447
  g.fifo_event.data = &g;
 
448
  curl_multi_setopt(g.multi, CURLMOPT_SOCKETFUNCTION, sock_cb);
 
449
  curl_multi_setopt(g.multi, CURLMOPT_SOCKETDATA, &g);
 
450
  curl_multi_setopt(g.multi, CURLMOPT_TIMERFUNCTION, multi_timer_cb);
 
451
  curl_multi_setopt(g.multi, CURLMOPT_TIMERDATA, &g);
 
452
  do
 
453
  {
 
454
    rc = curl_multi_socket_all(g.multi, &g.still_running);
 
455
  } while ( CURLM_CALL_MULTI_PERFORM == rc );
 
456
 
 
457
  ev_loop(g.loop, 0);
 
458
  curl_multi_cleanup(g.multi);
 
459
  return 0;
 
460
}