1 #include <sys/ioctl.h>
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <sys/socket.h>
5 #include <netdb.h>
6 #include <errno.h>
7 #include <string.h>
8 
9 #include "types.h"
10 #include "socket_io.h"
11 #include "http_util.h"
12 #include "xs_util.h"
13 #include "regression_data.h"
14 
15 /* --------------------------------------------------------- */
16 
17 /* make an fd non blocking */
18 
19 static void
nonblock(int fd)20 nonblock(int fd) {
21     int i = 1;
22 #ifdef BEOS
23     setsockopt(fd, SOL_SOCKET, SO_NONBLOCK, &i, sizeof(i));
24 #else
25     ioctl(fd, FIONBIO, &i);
26 #endif
27 }
28 
29 /* --------------------------------------------------------- */
30 
31 /* start asnchronous non-blocking connection */
32 
33 static void
start_connect(struct global * registry,struct connection * c)34 start_connect(struct global * registry, struct connection * c) {
35     c->read = 0;
36     c->bread = 0;
37     c->keepalive = 0;
38     c->cbx = 0;
39     c->gotheader = 0;
40     c->fd = socket(AF_INET, SOCK_STREAM, 0);
41 
42 #ifdef AB_DEBUG
43     printf("AB_DEBUG: start of start_connect()\n");
44 #endif
45 
46     if (c->fd < 0) {
47         myerr(registry->warn_and_error, "socket error");
48         registry->failed[c->url]++;
49         close_connection(registry, c);
50         return;
51     }
52     nonblock(c->fd);
53 
54 #ifdef AB_DEBUG
55     printf("AB_DEBUG: start_connect() - stage 1\n");
56 #endif
57 
58     c->connect_time.tv_sec = 0;
59     c->connect_time.tv_usec = 0;
60     c->sent_request_time.tv_sec = 0;
61     c->sent_request_time.tv_usec = 0;
62     gettimeofday(&c->start_time, 0);
63 
64     {
65         /* get server information */
66         struct hostent *he;
67 #ifdef AB_DEBUG
68         printf("AB_DEBUG: start_connect() - stage 2, c->url: '%d'\n", c->url);
69 #endif
70         he = gethostbyname(registry->hostname[c->url]);
71 #ifdef AB_DEBUG
72         printf("AB_DEBUG: start_connect() - stage 3\n");
73 #endif
74         if (!he) {
75             char * warn = malloc(256 * sizeof(char));
76             sprintf(warn, "Bad hostname: %s, the information stored for it could be wrong!", registry->hostname[c->url]);
77             myerr(registry->warn_and_error, warn);
78             free(warn);
79             /* bad hostname, yields the resource */
80             registry->failed[c->url]++;
81             close_connection(registry, c);
82             return;
83         }
84 #ifdef AB_DEBUG
85         printf("AB_DEBUG: start_connect() - stage 4\n");
86 #endif
87         registry->server.sin_family = he->h_addrtype;
88         registry->server.sin_port = htons(registry->port[c->url]);
89         registry->server.sin_addr.s_addr = ((unsigned long *) (he->h_addr_list[0]))[0];
90     }
91 
92 #ifdef AB_DEBUG
93     printf("AB_DEBUG: start_connect() - stage 5\n");
94 #endif
95 
96     if (connect(c->fd, (struct sockaddr *) & registry->server, sizeof(registry->server)) < 0) {
97         if (errno == EINPROGRESS) {
98             FD_SET(c->fd, &registry->writebits);
99             registry->started[c->url]++;
100             return;
101         } else {
102             ab_close(c->fd);
103             /* retry the same request 10 times if it fails to connect */
104             if (registry->failed[c->url]++ > 10) {
105                 myerr(registry->warn_and_error,
106                       "Test aborted after 10 failures");
107                 /* yields the resource */
108                 close_connection(registry, c);
109                 return;
110             }
111             start_connect(registry, c);
112             return;
113         }
114     }
115 
116 #ifdef AB_DEBUG
117     printf("AB_DEBUG: start_connect() - stage 6\n");
118 #endif
119 
120     /* connected first time */
121     registry->started[c->url]++;
122     FD_SET(c->fd, &registry->writebits);
123 }
124 
125 /* --------------------------------------------------------- */
126 
127 /* read data from connection */
128 
129 static void
read_connection(struct global * registry,struct connection * c)130 read_connection(struct global * registry, struct connection * c) {
131     int r;
132 
133 #ifdef AB_DEBUG
134     printf("AB_DEBUG: start of read_connection(), postdata[%d] = %s\n", c->url, registry->postdata[c->url]);
135 #endif
136 
137     r = ab_read(c->fd, registry->buffer, sizeof(registry->buffer));
138     if (r == 0 || (r < 0 && errno != EAGAIN)) {
139         if (errno == EINPROGRESS)
140             registry->good[c->url]++;
141         close_connection(registry, c);
142         return;
143     }
144 
145 #ifdef AB_DEBUG
146     printf("AB_DEBUG: read_connection() - stage 1\n");
147 #endif
148 
149     if (r < 0 && errno == EAGAIN)
150         return;
151     c->read += r;
152     if (c->read < registry->buffersize[c->run]-1 &&
153         registry->memory[c->run] >= 3)
154         strncat(c->response, registry->buffer, r);
155 
156 #ifdef AB_DEBUG
157     printf("AB_DEBUG: read_connection() - stage 2\n");
158 #endif
159 
160     if (!c->gotheader) {
161         char *s;
162         int wslen = 4;
163         int space = CBUFFSIZE - c->cbx - 1;        /* -1 to allow for 0
164                                                  * terminator */
165         int tocopy = (space < r) ? space : r;
166 #ifndef CHARSET_EBCDIC
167         memcpy(c->cbuff + c->cbx, registry->buffer, tocopy);
168 #else                                /* CHARSET_EBCDIC */
169         ascii2ebcdic(c->cbuff + c->cbx, registry->buffer, tocopy);
170 #endif                                /* CHARSET_EBCDIC */
171         c->cbx += tocopy;
172         space -= tocopy;
173         c->cbuff[c->cbx] = 0;        /* terminate for benefit of strstr */
174         s = strstr(c->cbuff, "\r\n\r\n");
175         /*
176          * this next line is so that we talk to NCSA 1.5 which blatantly
177          * breaks the http specification
178          */
179         if (!s) {
180             s = strstr(c->cbuff, "\n\n");
181             wslen = 2;
182         }
183         if (!s) {
184             /* read rest next time */
185             if (registry->memory[c->run] >= 2)
186                 c->response_headers = "";
187             if (space)
188                 return;
189             else {
190                 /*
191                  * header is in invalid or too big - close connection
192                  */
193                 ab_close(c->fd);
194                 FD_CLR(c->fd, &registry->writebits);
195                 start_connect(registry, c);
196             }
197         } else {
198             /* have full header */
199 
200             /*
201              * XXX: this parsing isn't even remotely HTTP compliant... but in
202              * the interest of speed it doesn't totally have to be, it just
203              * needs to be extended to handle whatever servers folks want to
204              * test against. -djg
205              */
206 
207             c->gotheader = 1;
208             *s = 0;                /* terminate at end of header */
209             if (registry->memory[c->run] >= 2) {
210                 c->response_headers = malloc(CBUFFSIZE * sizeof(char));
211                 strcpy(c->response_headers, c->cbuff);
212             }
213             if (registry->keepalive[c->url] &&
214                 (strstr(c->cbuff, "Keep-Alive") ||
215                  strstr(c->cbuff, "keep-alive"))) { /* for benefit of MSIIS */
216                 char *cl;
217                 cl = strstr(c->cbuff, "Content-Length:");
218                 /* handle NCSA, which sends Content-length: */
219                 if (!cl)
220                     cl = strstr(c->cbuff, "Content-length:");
221                 if (cl) {
222                     c->keepalive = 1;
223                     c->length = atoi(cl + 16);
224                 }
225             }
226             c->bread += c->cbx - (s + wslen - c->cbuff) + r - tocopy;
227         }
228     } else {
229         /* outside header, everything we have read is entity body */
230         c->bread += r;
231     }
232 
233     /*
234      * cater for the case where we're using keepalives and doing HEAD
235      * requests
236      */
237     if (c->keepalive &&
238         ((c->bread >= c->length) || (registry->posting[c->url] < 0))) {
239         /* save current url for checking for hostname/port changes */
240         int prev = c->url;
241 
242         /* finished a keep-alive connection */
243         registry->good[c->url]++;
244         registry->finished[c->url]++;
245 
246         store_regression_data(registry, c);
247 
248         if (++registry->done >= registry->need_to_be_done)
249             return;
250 
251         if (!schedule_next_request(registry, c))
252             return;
253 
254         c->length = 0;
255         c->gotheader = 0;
256         c->cbx = 0;
257         c->read = c->bread = 0;
258         c->keepalive = 0;
259 
260         /* if new hostname/port is different from last hostname/port, or new
261            url is *not* keepalive, then we need to close connection and start
262            a new connection */
263         if (registry->keepalive[c->url] &&
264             strcmp(registry->hostname[c->url], registry->hostname[prev]) == 0
265             && registry->port[c->url] == registry->port[prev]) {
266             write_request(registry, c);
267             registry->started[c->url]++;
268             c->start_time = c->connect_time;        /* zero connect time with keep-alive */
269         } else {
270             ab_close(c->fd);
271             FD_CLR(c->fd, &registry->readbits);
272             FD_CLR(c->fd, &registry->writebits);
273             start_connect(registry, c);
274         }
275     }
276 }
277 
278 /* --------------------------------------------------------- */
279 
280 /* close down connection and save stats */
281 
282 static void
close_connection(struct global * registry,struct connection * c)283 close_connection(struct global * registry, struct connection * c) {
284 #ifdef AB_DEBUG
285     printf("AB_DEBUG: start of close_connection(), postdata[%d] = %s\n", c->url, registry->postdata[c->url]);
286 #endif
287 
288     if (registry->use_auto_cookies[c->run])
289         extract_cookies_from_response(registry, c);
290     store_regression_data(registry, c);
291     registry->finished[c->url]++;
292 
293 #ifdef AB_DEBUG
294     printf("AB_DEBUG: close_connection() - stage 1\n");
295 #endif
296 
297     ab_close(c->fd);
298     FD_CLR(c->fd, &registry->readbits);
299     FD_CLR(c->fd, &registry->writebits);
300 
301 #ifdef AB_DEBUG
302     printf("AB_DEBUG: close_connection() - stage 2\n");
303 #endif
304 
305     /* finish if last response has been received */
306     if (++registry->done >= registry->need_to_be_done)
307         return;
308 
309 #ifdef AB_DEBUG
310     printf("AB_DEBUG: close_connection() - stage 3\n");
311 #endif
312 
313     /* else continue with requests in run queues */
314     if (schedule_next_request(registry, c))
315         start_connect(registry, c);
316 }
317 
318 
319 /* --------------------------------------------------------- */
320 
321 /* write out request to a connection - assumes we can write
322    (small) request out in one go into our new socket buffer  */
323 
324 static void
write_request(struct global * registry,struct connection * c)325 write_request(struct global * registry, struct connection * c) {
326 
327 #ifndef NO_WRITEV
328     struct iovec out[2];
329     int outcnt = 1;
330 #endif
331     int bytes_sent;
332     STRLEN len;
333     SV *res;
334     char *post_body;
335 
336 #ifdef AB_DEBUG
337     printf("AB_DEBUG: write_request() - stage 1, registry->done = %d\n", registry->done);
338 #endif
339     gettimeofday(&c->before_postdata_time, 0);
340 
341     /* the url in this run has dynamicly-generated postdata */
342     if (registry->posting[c->url] == 2) {
343         res = call_perl_function__one_arg(registry->postsubs[c->url],
344                                           newSVpv(c->url > 0 ? registry->stats[c->url - 1][c->thread].response : "", 0));
345 
346         if (SvPOK(res)) {
347             post_body = SvPV(res, len);
348 #ifdef AB_DEBUG
349             printf("AB_DEBUG: write_request() - stage 1-postsub.2, postsub res %s, length %d\n", post_body, (int)len);
350 #endif
351             registry->postdata[c->url] = post_body;
352             registry->postlen[c->url] = (int) len;
353         } else {
354             registry->postdata[c->url] = "";
355             registry->postlen[c->url] = 0;
356             registry->posting[c->url] = 0; // change back to a GET request
357         }
358     }
359 
360     gettimeofday(&c->connect_time, 0); // start timer
361 
362     reset_request(registry, c); // this generates the request headers; must call the above first to determine POST content
363 
364 #ifdef AB_DEBUG
365     printf("AB_DEBUG: write_request() - stage 2, registry->done = %d\n", registry->done);
366 #endif
367 
368 #ifndef NO_WRITEV
369     out[0].iov_base = c->request;
370     out[0].iov_len = c->reqlen;
371 
372 #ifdef AB_DEBUG
373     printf("AB_DEBUG: write_request() - stage 2a.1, registry->done = %d, postdata[%d] = %s\n", registry->done, c->url, registry->postdata[c->url]);
374 #endif
375     if (registry->posting[c->url] > 0) {
376         out[1].iov_base = registry->postdata[c->url];
377         out[1].iov_len = registry->postlen[c->url];
378         outcnt = 2;
379         registry->totalposted[c->url] = (c->reqlen + registry->postlen[c->url]);
380     }
381 #ifdef AB_DEBUG
382     printf("AB_DEBUG: write_request() - stage 2a.2, registry->done = %d\n", registry->done);
383 #endif
384     bytes_sent = writev(c->fd, out, outcnt);
385 
386 #else /* NO_WRITEV */
387 
388 #ifdef AB_DEBUG
389     printf("AB_DEBUG: write_request() - stage 2b.1, registry->done = %d, postdata[%d] = %s\n", registry->done, c->url, registry->postdata[c->url]);
390 #endif
391     ab_write(c->fd, c->request, c->reqlen);
392     if (registry->posting[c->url] > 0)
393         bytes_sent = ab_write(c->fd, registry->postdata[c->url], registry->postlen[c->url]);
394 #endif /* NO_WRITEV */
395 
396 #ifdef AB_DEBUG
397     printf("AB_DEBUG: write_request() - stage 3, registry->done = %d, postdata[%d] = %s\n", registry->done, c->url, registry->postdata[c->url]);
398 #endif
399 
400     FD_SET(c->fd, &registry->readbits);
401     FD_CLR(c->fd, &registry->writebits);
402     gettimeofday(&c->sent_request_time, 0);
403 
404     if (registry->memory[c->run] >= 3)
405         c->response = calloc(1, registry->buffersize[c->run]);
406 }
407 
408 /* --------------------------------------------------------- */
409 
410 /* setup or reset request */
411 static int
reset_request(struct global * registry,struct connection * c)412 reset_request(struct global * registry, struct connection * c) {
413     int i = c->url;
414 
415     char * ctype = calloc(40, sizeof(char));
416     strcpy(ctype, "application/x-www-form-urlencoded");
417 
418 #ifdef AB_DEBUG
419     printf("AB_DEBUG: reset_request() - stage 0.1\n");
420 #endif
421     if (registry->ctypes[i]) {
422 #ifdef AB_DEBUG
423         printf("AB_DEBUG: reset_request() - stage 0.1.1\n");
424 #endif
425         free(ctype);
426 
427 #ifdef AB_DEBUG
428         printf("AB_DEBUG: reset_request() - stage 0.1.2\n");
429 #endif
430         ctype = registry->ctypes[i];
431     }
432 
433 #ifdef AB_DEBUG
434     printf("AB_DEBUG: reset_request() - stage 1\n");
435 #endif
436 
437     c->request = calloc(registry->buffersize[c->run], sizeof(char));
438     c->request_headers = calloc(registry->buffersize[c->run], sizeof(char));
439 
440     if (registry->posting[i] <= 0) {
441 #ifdef AB_DEBUG
442         printf("AB_DEBUG: reset_request() - stage 1.1 (GET)\n");
443 #endif
444         sprintf(c->request_headers, "%s %s HTTP/1.0\r\n"
445                 "User-Agent: ApacheBench-Perl/%s\r\n"
446                 "Host: %s\r\n"
447                 "Accept: */*\r\n",
448                 (registry->posting[i] == 0) ? "GET" : "HEAD",
449                 registry->path[i],
450                 registry->version,
451                 registry->hostname[i]);
452     } else {
453 #ifdef AB_DEBUG
454         printf("AB_DEBUG: reset_request() - stage 1.1 (POST)\n");
455 #endif
456         sprintf(c->request_headers, "POST %s HTTP/1.0\r\n"
457                 "User-Agent: ApacheBench-Perl/%s\r\n"
458                 "Host: %s\r\n"
459                 "Accept: */*\r\n"
460                 "Content-length: %d\r\n"
461                 "Content-type: %s\r\n",
462                 registry->path[i],
463                 registry->version,
464                 registry->hostname[i],
465                 registry->postlen[i],
466                 ctype);
467     }
468 
469 #ifdef AB_DEBUG
470     printf("AB_DEBUG: reset_request() - stage 2\n");
471 #endif
472 
473     if (registry->keepalive[i])
474         strcat(c->request_headers, "Connection: Keep-Alive\r\n");
475     if (registry->cookie[c->run]) {
476         strcat(c->request_headers, "Cookie: ");
477         strcat(c->request_headers, registry->cookie[c->run]);
478         strcat(c->request_headers, "\r\n");
479     }
480 
481 #ifdef AB_DEBUG
482     printf("AB_DEBUG: reset_request() - stage 2.1: c->run %d; c->thread %d\n", c->run, c->thread);
483 #endif
484 
485     allocate_auto_cookie_memory(registry, c);
486 
487     if (registry->use_auto_cookies[c->run] && registry->auto_cookies[c->run] != NULL && registry->auto_cookies[c->run][c->thread] != NULL) {
488 
489 #ifdef AB_DEBUG
490         printf("AB_DEBUG: reset_request() - stage 2.2a: request_headers %s\n", c->request_headers);
491         printf("AB_DEBUG: reset_request() - stage 2.2b: auto_cookies %s\n", registry->auto_cookies[c->run][c->thread]);
492 #endif
493 
494         strcat(c->request_headers, registry->auto_cookies[c->run][c->thread]);
495     }
496 
497 #ifdef AB_DEBUG
498     printf("AB_DEBUG: reset_request() - stage 2.3: c->run %d; c->thread %d\n", c->run, c->thread);
499 #endif
500 
501     if (registry->req_headers[i]) {
502         strcat(c->request_headers, registry->req_headers[i]);
503         strcat(c->request_headers, "\r\n");
504     }
505 
506     strcat(c->request_headers, "\r\n");
507 
508 #ifdef AB_DEBUG
509     printf("AB_DEBUG: reset_request() - stage 2.4: c->run %d; c->thread %d\n", c->run, c->thread);
510 #endif
511 
512     strcpy(c->request, c->request_headers);
513     c->reqlen = strlen(c->request);
514 
515 #ifdef AB_DEBUG
516     printf("AB_DEBUG: reset_request() - stage 3\n");
517 #endif
518 
519 #ifdef CHARSET_EBCDIC
520     ebcdic2ascii(c->request, c->request, c->reqlen);
521 #endif                                /* CHARSET_EBCDIC */
522 
523     return 0;
524 }
525 
526 /* --------------------------------------------------------- */
527 
528 /* setup the next request in the sequence / repetition / run to be sent
529    returns 1 if the next request is ready to be sent,
530    returns 0 if this connection is done,
531    sets the connection values: c->run, c->url, c->thread, and c->state,
532    as well as helper structures: registry->which_thread[][],
533      registry->ready_to_run_queue[], and registry->arranged[]
534 */
535 
536 static int
schedule_next_request(struct global * registry,struct connection * c)537 schedule_next_request(struct global * registry, struct connection * c) {
538 
539     if (registry->priority == RUN_PRIORITY) {
540         /* if the last url in this run has repeated enough, go to next run */
541         if (registry->started[registry->position[c->run + 1] - 1] >= registry->repeats[c->run])
542             return schedule_request_in_next_run(registry, c);
543 
544         /* possible more resources needed in this group */
545         /* started[position[c->run + 1] - 1] < repeats[c->run] */
546         if (registry->order[c->run] == DEPTH_FIRST) {
547             /* for depth_first, connect the next one and restart the
548                sequence if we're at the last url in the run */
549             if (++c->url == registry->position[c->run + 1]) {
550                 c->url = registry->position[c->run];
551                 c->thread = registry->started[c->url];
552             }
553             return 1;
554         } else { /* breadth_first */
555             if (c->url < (registry->position[c->run + 1] - 1))
556                 /* TODO: check if (registry->finished[c->url] > 0) ??? */
557                 registry->which_thread[c->url+1][registry->finished[c->url] - 1] = c->thread;
558             if (registry->started[c->url] == registry->repeats[c->run])
559                 /* go to next url in sequence if we repeated this one enough */
560                 c->url++;
561             if (c->url == registry->position[c->run]) {
562                 /* this is the first url in the sequence: set its repetition
563                    number to the initial incremental value (0, 1, 2, 3, ...) */
564                 c->thread = registry->which_thread[c->url][registry->started[c->url]];
565                 return 1;
566             }
567             /* only start another request from this run if more requests of the
568                previous url in the sequence have finished(in-order execution)*/
569             if (registry->started[c->url] < registry->finished[c->url - 1]) {
570                 c->thread = registry->started[c->url];
571                 return 1;
572             } else {
573                 return schedule_request_in_next_run(registry, c);
574             }
575         }
576 
577     } else { /* equal_opportunity */
578         /* we use a FIFO to queue up requests to be sent */
579         if (c->url < registry->position[c->run + 1]-1) {
580             /* if url is before the end of the url sequence,
581                add it to the tail of the request queue */
582             registry->ready_to_run_queue[registry->tail].url = c->url + 1;
583             registry->ready_to_run_queue[registry->tail].thread = c->thread;
584             registry->ready_to_run_queue[registry->tail++].run = c->run;
585             registry->arranged[c->url + 1]++;
586         } else if (registry->order[c->run] == DEPTH_FIRST
587                    && registry->arranged[registry->position[c->run]] < registry->repeats[c->run]) {
588             /* end of the url sequence in depth_first with more repetitions
589                necessary: start from the beginning of the url sequence */
590             registry->ready_to_run_queue[registry->tail].url = registry->position[c->run];
591             registry->ready_to_run_queue[registry->tail].thread = registry->arranged[registry->position[c->run]]++;
592             registry->ready_to_run_queue[registry->tail++].run = c->run;
593         }
594 
595         if (registry->head >= registry->tail) {
596             c->state = STATE_DONE;
597             return 0;
598         }
599         c->thread = registry->ready_to_run_queue[registry->head].thread;
600         c->url = registry->ready_to_run_queue[registry->head].url;
601         c->run = registry->ready_to_run_queue[registry->head++].run;
602         return 1;
603     }
604 }
605 
606 /* --------------------------------------------------------- */
607 
608 /* move connection to the next run, because the current run either doesn't need
609    or cannot use any more connection slots (resources)
610    returns 1 if the next request is ready to be sent,
611    returns 0 if this connection is done */
612 
613 static int
schedule_request_in_next_run(struct global * registry,struct connection * c)614 schedule_request_in_next_run(struct global * registry, struct connection * c) {
615     c->run++;
616     while (c->run < registry->number_of_runs) {
617         if (registry->started[registry->position[c->run + 1] - 1] >= registry->repeats[c->run]
618             || (registry->order[c->run] == DEPTH_FIRST
619                 && registry->started[registry->position[c->run]] > 0)) {
620             /* this run has finished all repetitions of url requests
621                or is a depth_first run which only requires one slot,
622                so doesn't need this resource anymore */
623             c->run++;
624             continue;
625         }
626         /* start at first url in the run */
627         c->url = registry->position[c->run];
628         if (registry->started[c->url] < registry->repeats[c->run]) {
629             /* for breadth_first, start one more connect to 1st url if possible
630                for depth_first, get started here */
631             c->thread = registry->which_thread[c->url][registry->started[c->url]];
632             return 1;
633         }
634         /* look at each url in the sequence until we find one which needs
635            to be repeated more */
636         while (++c->url < registry->position[c->run + 1]
637                && registry->started[c->url] >= registry->repeats[c->run]);
638         /* only start another request from this run if more requests of the
639            previous url in the sequence have finished (in-order execution) */
640         if (registry->started[c->url] < registry->finished[c->url - 1]) {
641             c->thread = registry->which_thread[c->url][registry->started[c->url]];
642             return 1;
643         } else
644             /* this run doesn't need any more resources */
645             c->run++;
646     }
647     /* no one needs any more resources */
648     c->state = STATE_DONE;
649     return 0;
650 }
651 
652