1/***************************************************************************
2 *                                  _   _ ____  _
3 *  Project                     ___| | | |  _ \| |
4 *                             / __| | | | |_) | |
5 *                            | (__| |_| |  _ <| |___
6 *                             \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 1998 - 2011, 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 * This code sets up multiple easy handles that transfer a single file from
24 * the same URL, in a serial manner after each other. Due to the connection
25 * sharing within the multi handle all transfers are performed on the same
26 * persistent connection.
27 *
28 * This source code is used for lib526, lib527 and lib532 with only #ifdefs
29 * controlling the small differences.
30 *
31 * - lib526 closes all easy handles after
32 *   they all have transfered the file over the single connection
33 * - lib527 closes each easy handle after each single transfer.
34 * - lib532 uses only a single easy handle that is removed, reset and then
35 *   re-added for each transfer
36 *
37 * Test case 526, 527 and 532 use FTP, while test 528 uses the lib526 tool but
38 * with HTTP.
39 */
40
41#include "test.h"
42
43#include <sys/types.h>
44#include <sys/stat.h>
45#include <fcntl.h>
46
47#include "testutil.h"
48#include "warnless.h"
49#include "memdebug.h"
50
51#define MAIN_LOOP_HANG_TIMEOUT     90 * 1000
52#define MULTI_PERFORM_HANG_TIMEOUT 60 * 1000
53
54#define NUM_HANDLES 4
55
56int test(char *URL)
57{
58  int res = 0;
59  CURL *curl[NUM_HANDLES];
60  int running;
61  char done=FALSE;
62  CURLM *m = NULL;
63  int current=0;
64  int i, j;
65  struct timeval ml_start;
66  struct timeval mp_start;
67  char ml_timedout = FALSE;
68  char mp_timedout = FALSE;
69
70  if (curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) {
71    fprintf(stderr, "curl_global_init() failed\n");
72    return TEST_ERR_MAJOR_BAD;
73  }
74
75  /* get NUM_HANDLES easy handles */
76  for(i=0; i < NUM_HANDLES; i++) {
77    curl[i] = curl_easy_init();
78    if(!curl[i]) {
79      fprintf(stderr, "curl_easy_init() failed "
80              "on handle #%d\n", i);
81      for (j=i-1; j >= 0; j--) {
82        curl_easy_cleanup(curl[j]);
83      }
84      curl_global_cleanup();
85      return TEST_ERR_MAJOR_BAD + i;
86    }
87    res = curl_easy_setopt(curl[i], CURLOPT_URL, URL);
88    if(res) {
89      fprintf(stderr, "curl_easy_setopt() failed "
90              "on handle #%d\n", i);
91      for (j=i; j >= 0; j--) {
92        curl_easy_cleanup(curl[j]);
93      }
94      curl_global_cleanup();
95      return TEST_ERR_MAJOR_BAD + i;
96    }
97
98    /* go verbose */
99    res = curl_easy_setopt(curl[i], CURLOPT_VERBOSE, 1L);
100    if(res) {
101      fprintf(stderr, "curl_easy_setopt() failed "
102              "on handle #%d\n", i);
103      for (j=i; j >= 0; j--) {
104        curl_easy_cleanup(curl[j]);
105      }
106      curl_global_cleanup();
107      return TEST_ERR_MAJOR_BAD + i;
108    }
109  }
110
111  if ((m = curl_multi_init()) == NULL) {
112    fprintf(stderr, "curl_multi_init() failed\n");
113    for(i=0; i < NUM_HANDLES; i++) {
114      curl_easy_cleanup(curl[i]);
115    }
116    curl_global_cleanup();
117    return TEST_ERR_MAJOR_BAD;
118  }
119
120  if ((res = (int)curl_multi_add_handle(m, curl[current])) != CURLM_OK) {
121    fprintf(stderr, "curl_multi_add_handle() failed, "
122            "with code %d\n", res);
123    curl_multi_cleanup(m);
124    for(i=0; i < NUM_HANDLES; i++) {
125      curl_easy_cleanup(curl[i]);
126    }
127    curl_global_cleanup();
128    return TEST_ERR_MAJOR_BAD;
129  }
130
131  ml_timedout = FALSE;
132  ml_start = tutil_tvnow();
133
134  fprintf(stderr, "Start at URL 0\n");
135
136  while (!done) {
137    fd_set rd, wr, exc;
138    int max_fd;
139    struct timeval interval;
140
141    interval.tv_sec = 1;
142    interval.tv_usec = 0;
143
144    if (tutil_tvdiff(tutil_tvnow(), ml_start) >
145        MAIN_LOOP_HANG_TIMEOUT) {
146      ml_timedout = TRUE;
147      break;
148    }
149    mp_timedout = FALSE;
150    mp_start = tutil_tvnow();
151
152    while (res == CURLM_CALL_MULTI_PERFORM) {
153      res = (int)curl_multi_perform(m, &running);
154      if (tutil_tvdiff(tutil_tvnow(), mp_start) >
155          MULTI_PERFORM_HANG_TIMEOUT) {
156        mp_timedout = TRUE;
157        break;
158      }
159      if (running <= 0) {
160#ifdef LIB527
161        /* NOTE: this code does not remove the handle from the multi handle
162           here, which would be the nice, sane and documented way of working.
163           This however tests that the API survives this abuse gracefully. */
164        curl_easy_cleanup(curl[current]);
165#endif
166        if(++current < NUM_HANDLES) {
167          fprintf(stderr, "Advancing to URL %d\n", current);
168#ifdef LIB532
169          /* first remove the only handle we use */
170          curl_multi_remove_handle(m, curl[0]);
171
172          /* make us re-use the same handle all the time, and try resetting
173             the handle first too */
174          curl_easy_reset(curl[0]);
175          test_setopt(curl[0], CURLOPT_URL, URL);
176          test_setopt(curl[0], CURLOPT_VERBOSE, 1L);
177
178          /* re-add it */
179          res = (int)curl_multi_add_handle(m, curl[0]);
180#else
181          res = (int)curl_multi_add_handle(m, curl[current]);
182#endif
183          if(res) {
184            fprintf(stderr, "add handle failed: %d.\n", res);
185            res = 243;
186            break;
187          }
188        }
189        else
190          done = TRUE; /* bail out */
191        break;
192      }
193    }
194    if (mp_timedout || done)
195      break;
196
197    if (res != CURLM_OK) {
198      fprintf(stderr, "not okay???\n");
199      break;
200    }
201
202    FD_ZERO(&rd);
203    FD_ZERO(&wr);
204    FD_ZERO(&exc);
205    max_fd = 0;
206
207    if (curl_multi_fdset(m, &rd, &wr, &exc, &max_fd) != CURLM_OK) {
208      fprintf(stderr, "unexpected failured of fdset.\n");
209      res = 189;
210      break;
211    }
212
213    if (select_test(max_fd+1, &rd, &wr, &exc, &interval) == -1) {
214      fprintf(stderr, "bad select??\n");
215      res = 195;
216      break;
217    }
218
219    res = CURLM_CALL_MULTI_PERFORM;
220  }
221
222  if (ml_timedout || mp_timedout) {
223    if (ml_timedout) fprintf(stderr, "ml_timedout\n");
224    if (mp_timedout) fprintf(stderr, "mp_timedout\n");
225    fprintf(stderr, "ABORTING TEST, since it seems "
226            "that it would have run forever.\n");
227    res = TEST_ERR_RUNS_FOREVER;
228  }
229
230#ifdef LIB532
231test_cleanup:
232#endif
233
234#ifndef LIB527
235  /* get NUM_HANDLES easy handles */
236  for(i=0; i < NUM_HANDLES; i++) {
237#ifdef LIB526
238    if(m)
239      curl_multi_remove_handle(m, curl[i]);
240#endif
241    curl_easy_cleanup(curl[i]);
242  }
243#endif
244  if(m)
245    curl_multi_cleanup(m);
246
247  curl_global_cleanup();
248  return res;
249}
250