1 /*
2 * (C) Radim Kolar 1997-2004
3 * This is free software, see GNU Public License version 2 for
4 * details.
5 *
6 * Simple forking WWW Server benchmark:
7 *
8 * Usage:
9 * webbench --help
10 *
11 * Return codes:
12 * 0 - sucess
13 * 1 - benchmark failed (server is not on-line)
14 * 2 - bad param
15 * 3 - internal error, fork failed
16 *
17 */
18 #include "socket.c"
19 #include <unistd.h>
20 #include <sys/param.h>
21 #include <rpc/types.h>
22 #include <getopt.h>
23 #include <strings.h>
24 #include <time.h>
25 #include <signal.h>
26
27 /* values */
28 volatile int timerexpired=0;
29 int speed=0;
30 int failed=0;
31 int bytes=0;
32 /* globals */
33 int http10=1; /* 0 - http/0.9, 1 - http/1.0, 2 - http/1.1 */
34 /* Allow: GET, HEAD, OPTIONS, TRACE */
35 #define METHOD_GET 0
36 #define METHOD_HEAD 1
37 #define METHOD_OPTIONS 2
38 #define METHOD_TRACE 3
39 #define PROGRAM_VERSION "1.5"
40 int method=METHOD_GET;
41 int clients=1;
42 int force=0;
43 int force_reload=0;
44 int proxyport=80;
45 char *proxyhost=NULL;
46 int benchtime=30;
47 /* internal */
48 int mypipe[2];
49 char host[MAXHOSTNAMELEN];
50 #define REQUEST_SIZE 2048
51 char request[REQUEST_SIZE];
52
53 static const struct option long_options[]=
54 {
55 {"force",no_argument,&force,1},
56 {"reload",no_argument,&force_reload,1},
57 {"time",required_argument,NULL,'t'},
58 {"help",no_argument,NULL,'?'},
59 {"http09",no_argument,NULL,'9'},
60 {"http10",no_argument,NULL,'1'},
61 {"http11",no_argument,NULL,'2'},
62 {"get",no_argument,&method,METHOD_GET},
63 {"head",no_argument,&method,METHOD_HEAD},
64 {"options",no_argument,&method,METHOD_OPTIONS},
65 {"trace",no_argument,&method,METHOD_TRACE},
66 {"version",no_argument,NULL,'V'},
67 {"proxy",required_argument,NULL,'p'},
68 {"clients",required_argument,NULL,'c'},
69 {NULL,0,NULL,0}
70 };
71
72 /* prototypes */
73 static void benchcore(const char* host,const int port, const char *request);
74 static int bench(void);
75 static void build_request(const char *url);
76
alarm_handler(int signal)77 static void alarm_handler(int signal)
78 {
79 timerexpired=1;
80 }
81
usage(void)82 static void usage(void)
83 {
84 fprintf(stderr,
85 "webbench [option]... URL\n"
86 " -f|--force Don't wait for reply from server.\n"
87 " -r|--reload Send reload request - Pragma: no-cache.\n"
88 " -t|--time <sec> Run benchmark for <sec> seconds. Default 30.\n"
89 " -p|--proxy <server:port> Use proxy server for request.\n"
90 " -c|--clients <n> Run <n> HTTP clients at once. Default one.\n"
91 " -9|--http09 Use HTTP/0.9 style requests.\n"
92 " -1|--http10 Use HTTP/1.0 protocol.\n"
93 " -2|--http11 Use HTTP/1.1 protocol.\n"
94 " --get Use GET request method.\n"
95 " --head Use HEAD request method.\n"
96 " --options Use OPTIONS request method.\n"
97 " --trace Use TRACE request method.\n"
98 " -?|-h|--help This information.\n"
99 " -V|--version Display program version.\n"
100 );
101 };
main(int argc,char * argv[])102 int main(int argc, char *argv[])
103 {
104 int opt=0;
105 int options_index=0;
106 char *tmp=NULL;
107
108 if(argc==1)
109 {
110 usage();
111 return 2;
112 }
113
114 while((opt=getopt_long(argc,argv,"912Vfrt:p:c:?h",long_options,&options_index))!=EOF )
115 {
116 switch(opt)
117 {
118 case 0 : break;
119 case 'f': force=1;break;
120 case 'r': force_reload=1;break;
121 case '9': http10=0;break;
122 case '1': http10=1;break;
123 case '2': http10=2;break;
124 case 'V': printf(PROGRAM_VERSION"\n");exit(0);
125 case 't': benchtime=atoi(optarg);break;
126 case 'p':
127 /* proxy server parsing server:port */
128 tmp=strrchr(optarg,':');
129 proxyhost=optarg;
130 if(tmp==NULL)
131 {
132 break;
133 }
134 if(tmp==optarg)
135 {
136 fprintf(stderr,"Error in option --proxy %s: Missing hostname.\n",optarg);
137 return 2;
138 }
139 if(tmp==optarg+strlen(optarg)-1)
140 {
141 fprintf(stderr,"Error in option --proxy %s Port number is missing.\n",optarg);
142 return 2;
143 }
144 *tmp='\0';
145 proxyport=atoi(tmp+1);break;
146 case ':':
147 case 'h':
148 case '?': usage();return 2;break;
149 case 'c': clients=atoi(optarg);break;
150 }
151 }
152
153 if(optind==argc) {
154 fprintf(stderr,"webbench: Missing URL!\n");
155 usage();
156 return 2;
157 }
158
159 if(clients==0) clients=1;
160 if(benchtime==0) benchtime=60;
161 /* Copyright */
162 fprintf(stderr,"Webbench - Simple Web Benchmark "PROGRAM_VERSION"\n"
163 "Copyright (c) Radim Kolar 1997-2004, GPL Open Source Software.\n"
164 );
165 build_request(argv[optind]);
166 /* print bench info */
167 printf("\nBenchmarking: ");
168 switch(method)
169 {
170 case METHOD_GET:
171 default:
172 printf("GET");break;
173 case METHOD_OPTIONS:
174 printf("OPTIONS");break;
175 case METHOD_HEAD:
176 printf("HEAD");break;
177 case METHOD_TRACE:
178 printf("TRACE");break;
179 }
180 printf(" %s",argv[optind]);
181 switch(http10)
182 {
183 case 0: printf(" (using HTTP/0.9)");break;
184 case 2: printf(" (using HTTP/1.1)");break;
185 }
186 printf("\n");
187 if(clients==1) printf("1 client");
188 else
189 printf("%d clients",clients);
190
191 printf(", running %d sec", benchtime);
192 if(force) printf(", early socket close");
193 if(proxyhost!=NULL) printf(", via proxy server %s:%d",proxyhost,proxyport);
194 if(force_reload) printf(", forcing reload");
195 printf(".\n");
196 return bench();
197 }
198
build_request(const char * url)199 void build_request(const char *url)
200 {
201 char tmp[10];
202 int i;
203
204 bzero(host,MAXHOSTNAMELEN);
205 bzero(request,REQUEST_SIZE);
206
207 if(force_reload && proxyhost!=NULL && http10<1) http10=1;
208 if(method==METHOD_HEAD && http10<1) http10=1;
209 if(method==METHOD_OPTIONS && http10<2) http10=2;
210 if(method==METHOD_TRACE && http10<2) http10=2;
211
212 switch(method)
213 {
214 default:
215 case METHOD_GET: strcpy(request,"GET");break;
216 case METHOD_HEAD: strcpy(request,"HEAD");break;
217 case METHOD_OPTIONS: strcpy(request,"OPTIONS");break;
218 case METHOD_TRACE: strcpy(request,"TRACE");break;
219 }
220
221 strcat(request," ");
222
223 if(NULL==strstr(url,"://"))
224 {
225 fprintf(stderr, "\n%s: is not a valid URL.\n",url);
226 exit(2);
227 }
228 if(strlen(url)>1500)
229 {
230 fprintf(stderr,"URL is too long.\n");
231 exit(2);
232 }
233 if(proxyhost==NULL)
234 if (0!=strncasecmp("http://",url,7))
235 { fprintf(stderr,"\nOnly HTTP protocol is directly supported, set --proxy for others.\n");
236 exit(2);
237 }
238 /* protocol/host delimiter */
239 i=strstr(url,"://")-url+3;
240 /* printf("%d\n",i); */
241
242 if(strchr(url+i,'/')==NULL) {
243 fprintf(stderr,"\nInvalid URL syntax - hostname don't ends with '/'.\n");
244 exit(2);
245 }
246 if(proxyhost==NULL)
247 {
248 /* get port from hostname */
249 if(index(url+i,':')!=NULL &&
250 index(url+i,':')<index(url+i,'/'))
251 {
252 strncpy(host,url+i,strchr(url+i,':')-url-i);
253 bzero(tmp,10);
254 strncpy(tmp,index(url+i,':')+1,strchr(url+i,'/')-index(url+i,':')-1);
255 /* printf("tmp=%s\n",tmp); */
256 proxyport=atoi(tmp);
257 if(proxyport==0) proxyport=80;
258 } else
259 {
260 strncpy(host,url+i,strcspn(url+i,"/"));
261 }
262 // printf("Host=%s\n",host);
263 strcat(request+strlen(request),url+i+strcspn(url+i,"/"));
264 } else
265 {
266 // printf("ProxyHost=%s\nProxyPort=%d\n",proxyhost,proxyport);
267 strcat(request,url);
268 }
269 if(http10==1)
270 strcat(request," HTTP/1.0");
271 else if (http10==2)
272 strcat(request," HTTP/1.1");
273 strcat(request,"\r\n");
274 if(http10>0)
275 strcat(request,"User-Agent: WebBench "PROGRAM_VERSION"\r\n");
276 if(proxyhost==NULL && http10>0)
277 {
278 strcat(request,"Host: ");
279 strcat(request,host);
280 strcat(request,"\r\n");
281 }
282 if(force_reload && proxyhost!=NULL)
283 {
284 strcat(request,"Pragma: no-cache\r\n");
285 }
286 if(http10>1)
287 strcat(request,"Connection: close\r\n");
288 /* add empty line at end */
289 if(http10>0) strcat(request,"\r\n");
290 // printf("Req=%s\n",request);
291 }
292
293 /* vraci system rc error kod */
bench(void)294 static int bench(void)
295 {
296 int i,j,k;
297 pid_t pid=0;
298 FILE *f;
299
300 /* check avaibility of target server */
301 i=Socket(proxyhost==NULL?host:proxyhost,proxyport);
302 if(i<0) {
303 fprintf(stderr,"\nConnect to server failed. Aborting benchmark.\n");
304 return 1;
305 }
306 close(i);
307 /* create pipe */
308 if(pipe(mypipe))
309 {
310 perror("pipe failed.");
311 return 3;
312 }
313
314 /* not needed, since we have alarm() in childrens */
315 /* wait 4 next system clock tick */
316 /*
317 cas=time(NULL);
318 while(time(NULL)==cas)
319 sched_yield();
320 */
321
322 /* fork childs */
323 for(i=0;i<clients;i++)
324 {
325 pid=fork();
326 if(pid <= (pid_t) 0)
327 {
328 /* child process or error*/
329 sleep(1); /* make childs faster */
330 break;
331 }
332 }
333
334 if( pid< (pid_t) 0)
335 {
336 fprintf(stderr,"problems forking worker no. %d\n",i);
337 perror("fork failed.");
338 return 3;
339 }
340
341 if(pid== (pid_t) 0)
342 {
343 /* I am a child */
344 if(proxyhost==NULL)
345 benchcore(host,proxyport,request);
346 else
347 benchcore(proxyhost,proxyport,request);
348
349 /* write results to pipe */
350 f=fdopen(mypipe[1],"w");
351 if(f==NULL)
352 {
353 perror("open pipe for writing failed.");
354 return 3;
355 }
356 /* fprintf(stderr,"Child - %d %d\n",speed,failed); */
357 fprintf(f,"%d %d %d\n",speed,failed,bytes);
358 fclose(f);
359 return 0;
360 } else
361 {
362 f=fdopen(mypipe[0],"r");
363 if(f==NULL)
364 {
365 perror("open pipe for reading failed.");
366 return 3;
367 }
368 setvbuf(f,NULL,_IONBF,0);
369 speed=0;
370 failed=0;
371 bytes=0;
372
373 while(1)
374 {
375 pid=fscanf(f,"%d %d %d",&i,&j,&k);
376 if(pid<2)
377 {
378 fprintf(stderr,"Some of our childrens died.\n");
379 break;
380 }
381 speed+=i;
382 failed+=j;
383 bytes+=k;
384 /* fprintf(stderr,"*Knock* %d %d read=%d\n",speed,failed,pid); */
385 if(--clients==0) break;
386 }
387 fclose(f);
388
389 printf("\nSpeed=%d pages/min, %d bytes/sec.\nRequests: %d susceed, %d failed.\n",
390 (int)((speed+failed)/(benchtime/60.0f)),
391 (int)(bytes/(float)benchtime),
392 speed,
393 failed);
394 }
395 return i;
396 }
397
benchcore(const char * host,const int port,const char * req)398 void benchcore(const char *host,const int port,const char *req)
399 {
400 int rlen;
401 char buf[1500];
402 int s,i;
403 struct sigaction sa;
404
405 /* setup alarm signal handler */
406 sa.sa_handler=alarm_handler;
407 sa.sa_flags=0;
408 if(sigaction(SIGALRM,&sa,NULL))
409 exit(3);
410 alarm(benchtime);
411
412 rlen=strlen(req);
413 nexttry:while(1)
414 {
415 if(timerexpired)
416 {
417 if(failed>0)
418 {
419 /* fprintf(stderr,"Correcting failed by signal\n"); */
420 failed--;
421 }
422 return;
423 }
424 s=Socket(host,port);
425 if(s<0) { failed++;continue;}
426 if(rlen!=write(s,req,rlen)) {failed++;close(s);continue;}
427 if(http10==0)
428 if(shutdown(s,1)) { failed++;close(s);continue;}
429 if(force==0)
430 {
431 /* read all available data from socket */
432 while(1)
433 {
434 if(timerexpired) break;
435 i=read(s,buf,1500);
436 /* fprintf(stderr,"%d\n",i); */
437 if(i<0)
438 {
439 failed++;
440 close(s);
441 goto nexttry;
442 }
443 else
444 if(i==0) break;
445 else
446 bytes+=i;
447 }
448 }
449 if(close(s)) {failed++;continue;}
450 speed++;
451 }
452 }
453