http.c revision 150990
1/*-
2 * Copyright (c) 2005 Robert N. M. Watson
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 * $FreeBSD: head/tools/tools/netrate/http/http.c 150990 2005-10-06 08:41:08Z rwatson $
27 */
28
29#include <sys/types.h>
30#include <sys/socket.h>
31
32#include <netinet/in.h>
33
34#include <arpa/inet.h>
35
36#include <err.h>
37#include <pthread.h>
38#include <stdio.h>
39#include <stdlib.h>
40#include <string.h>
41#include <unistd.h>
42
43/*
44 * Simple, multi-threaded HTTP benchmark.  Fetches a single URL using the
45 * specified parameters, and after a period of execution, reports on how it
46 * worked out.
47 */
48#define	THREADS	128
49#define	SECONDS	20
50#define	BUFFER	(48*1024)
51#define	HTTP	8000
52#define	QUIET	1
53
54struct http_worker_description {
55	pthread_t	hwd_thread;
56	u_int64_t	hwd_count;
57	u_int64_t	hwd_errorcount;
58};
59
60static struct sockaddr_in		 sin;
61static char				*path;
62static struct http_worker_description	 hwd[THREADS];
63static int				 run_done;
64static pthread_barrier_t		 start_barrier;
65
66/*
67 * Given a partially processed URL, fetch it from the specified host.
68 */
69static int
70http_fetch(struct sockaddr_in *sin, char *path, int quiet)
71{
72	u_char buffer[BUFFER];
73	ssize_t len;
74	size_t sofar;
75	int sock;
76
77	sock = socket(PF_INET, SOCK_STREAM, 0);
78	if (sock < 0) {
79		if (!quiet)
80			warn("socket(PF_INET, SOCK_STREAM)");
81		return (-1);
82	}
83
84	if (connect(sock, (struct sockaddr *)sin, sizeof(*sin)) < 0) {
85		if (!quiet)
86			warn("connect");
87		close(sock);
88		return (-1);
89	}
90
91	/* Send a request. */
92	snprintf(buffer, BUFFER, "GET %s HTTP/1.0\n\n", path);
93	sofar = 0;
94	while (sofar < strlen(buffer)) {
95		len = send(sock, buffer, strlen(buffer), 0);
96		if (len < 0) {
97			if (!quiet)
98				warn("send");
99			close(sock);
100			return (-1);
101		}
102		if (len == 0) {
103			if (!quiet)
104				warnx("send: len == 0");
105		}
106		sofar += len;
107	}
108
109	/* Read until done.  Not very smart. */
110	while (1) {
111		len = recv(sock, buffer, BUFFER, 0);
112		if (len < 0) {
113			if (!quiet)
114				warn("recv");
115			close(sock);
116			return (-1);
117		}
118		if (len == 0)
119			break;
120	}
121
122	close(sock);
123	return (0);
124}
125
126static void *
127http_worker(void *arg)
128{
129	struct http_worker_description *hwdp;
130	int ret;
131
132	ret = pthread_barrier_wait(&start_barrier);
133	if (ret != 0 && ret != PTHREAD_BARRIER_SERIAL_THREAD)
134		err(-1, "pthread_barrier_wait");
135
136	hwdp = arg;
137	while (!run_done) {
138		if (http_fetch(&sin, path, QUIET) < 0) {
139			hwdp->hwd_errorcount++;
140			continue;
141		}
142		/* Don't count transfers that didn't finish in time. */
143		if (!run_done)
144			hwdp->hwd_count++;
145	}
146
147	return (NULL);
148}
149
150int
151main(int argc, char *argv[])
152{
153	u_int64_t total;
154	int i;
155
156	if (argc != 3)
157		errx(-1, "usage: http [IP] [PATH]");
158
159	bzero(&sin, sizeof(sin));
160	sin.sin_len = sizeof(sin);
161	sin.sin_family = AF_INET;
162	sin.sin_port = htons(HTTP);
163	sin.sin_addr.s_addr = inet_addr(argv[1]);
164	path = argv[2];
165
166	/*
167	 * Do one test retrieve so we can report the error from it, if any.
168	 */
169	if (http_fetch(&sin, path, 0) < 0)
170		exit(-1);
171
172	if (pthread_barrier_init(&start_barrier, NULL, THREADS) < 0)
173		err(-1, "pthread_mutex_init");
174
175	for (i = 0; i < THREADS; i++) {
176		hwd[i].hwd_count = 0;
177		if (pthread_create(&hwd[i].hwd_thread, NULL, http_worker,
178		    &hwd[i]) < 0)
179			err(-1, "pthread_create");
180	}
181	sleep(SECONDS);
182	run_done = 1;
183	for (i = 0; i < THREADS; i++) {
184		if (pthread_join(hwd[i].hwd_thread, NULL) < 0)
185			err(-1, "pthread_join");
186	}
187	total = 0;
188	for (i = 0; i < THREADS; i++)
189		total += hwd[i].hwd_count;
190	printf("%llu transfers/second\n", total / SECONDS);
191	total = 0;
192	for (i = 0; i < THREADS; i++)
193		total += hwd[i].hwd_errorcount;
194	printf("%llu errors/second\n", total / SECONDS);
195	return (0);
196}
197