~ubuntu-branches/ubuntu/lucid/curl/lucid-201101212007

« back to all changes in this revision

Viewing changes to docs/examples/hiperfifo.c

  • Committer: Bazaar Package Importer
  • Author(s): Matthias Klose
  • Date: 2007-05-16 15:16:54 UTC
  • mto: (3.1.1 lenny) (1.2.1 upstream)
  • mto: This revision was merged to the branch mainline in revision 12.
  • Revision ID: james.westby@ubuntu.com-20070516151654-x9nkigtr2j0i8d0v
Tags: upstream-7.16.2
ImportĀ upstreamĀ versionĀ 7.16.2

Show diffs side-by-side

added added

removed removed

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