static void server_mainloop( ) { static time_t ot_last_clean_time; time_t next_timeout_check = g_now + OT_CLIENT_TIMEOUT_CHECKINTERVAL; struct iovec *iovector; int iovec_entries; for( ; ; ) { int64 i; io_wait(); while( ( i = io_canread( ) ) != -1 ) { const void *cookie = io_getcookie( i ); if( cookie == FLAG_TCP ) handle_accept( i ); else if( cookie == FLAG_UDP ) handle_udp4( i ); else handle_read( i ); } while( ( i = mutex_workqueue_popresult( &iovec_entries, &iovector ) ) != -1 ) http_sendiovecdata( i, iovec_entries, iovector ); while( ( i = io_canwrite( ) ) != -1 ) handle_write( i ); if( g_now > next_timeout_check ) { while( ( i = io_timeouted() ) != -1 ) handle_dead( i ); next_timeout_check = g_now + OT_CLIENT_TIMEOUT_CHECKINTERVAL; } /* See if we need to move our pools */ if( NOW != ot_last_clean_time ) { ot_last_clean_time = NOW; clean_all_torrents(); } /* Enforce setting the clock */ signal_handler( SIGALRM ); } }
int main(int argc,char* argv[]) { char server[1024]; int* fds; int* avail; int* keepleft; long long* expected; unsigned long n=10000; /* requests */ unsigned long c=10; /* concurrency */ unsigned long t=0; /* time limit in seconds */ unsigned long k=0; /* keep-alive */ unsigned long K=1; /* keep-alive counter */ int report=0; unsigned long long errors=0; unsigned long long bytes=0; int v=0; unsigned long i,done; uint16 port=80; uint32 scope_id=0; stralloc ips={0}; char* request,* krequest; unsigned long rlen, krlen; tai6464 first,now,next,last; enum { SAME, REPLAY } mode; char* hostname; server[0]=0; errmsg_iam("bench"); #ifndef __MINGW32__ signal(SIGPIPE,SIG_IGN); #endif for (;;) { int i; int ch=getopt(argc,argv,"n:c:t:kvK:C:r"); if (ch==-1) break; switch (ch) { case 'r': report=1; break; case 'n': i=scan_ulong(optarg,&n); if (i==0) die(1,"could not parse -n argument \"",optarg,"\".\n"); break; case 'c': i=scan_ulong(optarg,&c); if (i==0) die(1,"could not parse -c argument \"",optarg,"\".\n"); break; case 't': i=scan_ulong(optarg,&t); if (i==0) die(1,"could not parse -t argument \"",optarg,"\".\n"); break; case 'k': k=1; break; case 'K': i=scan_ulong(optarg,&K); break; case 'v': v=1; break; case 'C': cookiefile(optarg); break; case '?': break; default: usage(); } } if (n<1 || c<1 || !argv[optind]) usage(); if (argv[optind][0]=='@') { mode=REPLAY; char* host; n=(unsigned long)-1; host=argv[optind]+1; { int tmp; tmp=str_chr(host,'/'); if (host[tmp]) { host[tmp]=0; if (!scan_ushort(host+tmp+1,&port)) usage(); } tmp=str_chr(host,'%'); if (host[tmp]) { host[tmp]=0; scope_id=socket_getifidx(host+tmp+1); if (scope_id==0) carp("warning: network interface \"",host+tmp+1,"\" not found."); } } { stralloc a={0}; stralloc_copys(&a,host); if (dns_ip6(&ips,&a)==-1) die(1,"could not find IP for \"",host,"\"!"); } hostname=host; request=krequest=0; rlen=krlen=0; } else { char* host=argv[optind]; int colon; int slash; char* c; mode=SAME; if (byte_equal(host,7,"http://")) host+=7; colon=str_chr(host,':'); slash=str_chr(host,'/'); if (host[0]=='[') { /* ipv6 IP notation */ int tmp; ++host; --colon; --slash; tmp=str_chr(host,']'); if (host[tmp]==']') host[tmp]=0; if (host[tmp+1]==':') colon=tmp+1; if (colon<tmp+1) colon=tmp+1+str_len(host+tmp+1); } if (colon<slash) { host[colon]=0; c=host+colon+1; if (c[scan_ushort(c,&port)]!='/') usage(); *c=0; } host[colon]=0; c=host+slash; *c=0; { char* tmp=alloca(str_len(host)+1); tmp[fmt_str(tmp,host)]=0; host=tmp; } *c='/'; { int tmp=str_chr(host,'%'); if (host[tmp]) { host[tmp]=0; scope_id=socket_getifidx(host+tmp+1); if (scope_id==0) carp("warning: network interface \"",host+tmp+1,"\" not found."); } } { stralloc a={0}; stralloc_copys(&a,host); if (dns_ip6(&ips,&a)==-1) die(1,"could not find IP for \"",host,"\"!"); } request=alloca(1300+str_len(host)+3*str_len(c)); krequest=alloca(1300+str_len(host)+3*str_len(c)); { int i,j; i=fmt_str(request,"GET "); i+=fmt_urlencoded(request+i,c,str_len(c)); i+=fmt_str(request+i," HTTP/1.0\r\nHost: "); i+=fmt_str(request+i,host); i+=fmt_str(request+i,":"); i+=fmt_ulong(request+i,port); i+=fmt_str(request+i,"\r\nUser-Agent: bench/1.0\r\nConnection: "); j=i; i+=fmt_str(request+i,"close\r\n\r\n"); rlen=i; request[rlen]=0; byte_copy(krequest,rlen,request); i=j+fmt_str(krequest+j,"keep-alive\r\n\r\n"); krlen=i; krequest[krlen]=0; } hostname=host; } fds=alloca(c*sizeof(*fds)); avail=alloca(c*sizeof(*avail)); expected=alloca(c*sizeof(*expected)); keepleft=alloca(c*sizeof(*keepleft)); last.sec.x=23; if (!k) K=1; for (i=0; i<c; ++i) { fds[i]=-1; avail[i]=1; keepleft[i]=K; } taia_now(&first); for (done=0; done<n; ) { if (t) { /* calculate timeout */ taia_now(&now); if (now.sec.x != last.sec.x) { byte_copy(&last,sizeof(now),&now); byte_copy(&next,sizeof(now),&now); next.sec.x += t; while ((i=io_timeouted())!=-1) { unsigned long j; char numbuf[FMT_ULONG]; numbuf[fmt_ulong(numbuf,i)]=0; carp("timeout on fd ",numbuf,"!"); j=(unsigned long)io_getcookie(i); io_close(i); avail[j]=1; fds[j]=-1; } } } /* first, fill available connections */ for (i=0; i<c; ++i) if (avail[i]==1 && fds[i]==-1) { fds[i]=make_connection(ips.s,port,scope_id,-1); if (fds[i]==-1) diesys(1,"socket/connect"); avail[i]=2; if (io_fd(fds[i])==0) diesys(1,"io_fd"); io_setcookie(fds[i],(void*)i); // io_wantread(fds[i]); io_wantwrite(fds[i]); } if (t) io_waituntil(next); else io_wait(); /* second, see if we can write on a connection */ while ((i=io_canwrite())!=-1) { int j; j=(unsigned long)io_getcookie(i); if (avail[j]==2) { if (make_connection(ips.s,port,scope_id,i)==-1) { ++errors; if (v) write(1,"!",1); io_close(i); avail[j]=1; fds[j]=-1; continue; } } { char* towrite; int writelen; if (mode==REPLAY) { static long lines; char line[1024]; char req[2048]; int len; int i; char* c; char* host; int hlen; if ((len=buffer_getline(buffer_0,line,sizeof(line)))) { ++lines; if (line[len]!='\n') die(0,"line too long: ",line); line[len]=0; c=line; if (str_start(line,"http://")) c+=7; if (c[0]=='/') { host=hostname; hlen=strlen(hostname); } else { host=c; c+=(hlen=str_chr(c,'/')); } if (!*c) c="/"; i=fmt_str(req,"GET "); i+=fmt_urlencoded(req+i,c,str_len(c)); i+=fmt_str(req+i," HTTP/1.0\r\nHost: "); byte_copy(req+i,hlen,host); i+=hlen; i+=fmt_str(req+i,":"); i+=fmt_ulong(req+i,port); if (cookies) { int j; i+=fmt_str(req+i,"\r\n"); j=nextcookie(req+i,sizeof(req)-i-100); if (j!=-1) i+=j; else i-=2; } i+=fmt_str(req+i,"\r\nUser-Agent: bench/1.0\r\nConnection: "); i+=fmt_str(req+i,keepleft[j]>1?"keep-alive\r\n\r\n":"close\r\n\r\n"); req[i]=0; towrite=req; writelen=i; } else { n=done; break; } } else { if (keepleft[j]>1) { towrite=krequest; writelen=krlen; } else { towrite=request; writelen=rlen; } if (cookies) { int i=writelen-2; int j=nextcookie(towrite+i,900); if (j!=-1) i+=j; i+=fmt_str(towrite+i,"\r\n\r\n"); writelen=i; } } if (io_trywrite(i,towrite,writelen)!=writelen) { ++errors; if (v) write(1,"-",1); io_close(i); avail[j]=1; fds[j]=-1; continue; } } io_dontwantwrite(i); io_wantread(i); expected[j]=-1; if (v) write(1,"+",1); } /* third, see if we got served */ while ((i=io_canread())!=-1) { char buf[8193]; int l,j; buf[8192]=0; j=(unsigned long)io_getcookie(i); if ((l=io_tryread(i,buf,sizeof(buf)-1))<=0) { if (l==0) { /* EOF. Mhh. */ if (expected[j]>0) { ++errors; if (v) write(1,"-",1); /* so whine a little */ } if (expected[j]==-2) ++done; io_close(i); avail[j]=1; fds[j]=-1; } else if (l==-3) { ++errors; if (v) write(1,"!",1); // carpsys("read"); } } else { bytes+=l; if (v) write(1,".",1); /* read something */ if (expected[j]==-1) { /* expecting header */ int k; /* OK, so this is a very simplistic header parser. No * buffering. At all. We expect the Content-Length header to * come in one piece. */ if (l>10 && !memcmp(buf,"HTTP/1.",7)) { if (buf[9]>='0' && buf[9]<='9') r[buf[9]-'0']++; else { write(1,buf,15); write(1,"\n",1); } } expected[j]=-2; if (!done) { for (k=0; k<l; ++k) if (str_start(buf+k,"\nServer: ")) { char* tmp=buf+(k+=9); for (; k<l; ++k) if (buf[k]=='\r') break; k=buf+k-tmp; if (k>sizeof(server)-1) k=sizeof(server)-1; byte_copy(server,k,tmp); server[k]=0; break; } } for (k=0; k<l; ++k) { if (str_start(buf+k,"\nContent-Length: ")) { k+=17; if (buf[k+scan_ulonglong(buf+k,(unsigned long long*)expected+j)] != '\r') die(1,"parse error in HTTP header!"); } else if (str_start(buf+k,"\r\n\r\n")) break; } if (expected[j]>0) { if (l-(k+4)>expected[j]) expected[j]=0; else expected[j]-=l-(k+4); } } else if (expected[j]==-2) { /* no content-length header, eat everything until EOF */ } else { if (l>expected[j]) { carp("got more than expected!"); expected[j]=0; } else expected[j]-=l; } if (expected[j]==0) { ++done; /* one down! */ avail[j]=1; // printf("fd %d: keepleft[%d]=%d\n",i,j,keepleft[j]); if (keepleft[j]>1) { --keepleft[j]; io_dontwantread(i); io_wantwrite(i); expected[j]=0; } else { keepleft[j]=K; io_close(i); fds[j]=-1; } } } } } taia_now(&now); taia_sub(&now,&now,&first); { char a[FMT_ULONG]; char b[FMT_ULONG]; char C[FMT_ULONG]; char d[FMT_ULONG]; char e[FMT_ULONG]; char f[FMT_ULONG]; char g[FMT_ULONG]; char h[FMT_ULONG]; char i[FMT_ULONG]; char j[FMT_ULONG]; unsigned long long l; a[fmt_ulong(a,now.sec.x)]=0; b[fmt_ulong0(b,(now.nano%1000000000)/100000,4)]=0; C[fmt_ulong(C,done)]=0; d[fmt_ulonglong(d,errors)]=0; e[fmt_ulonglong(e,bytes)]=0; /* let's say bytes = 10 MB, time = 1.2 sec. * then we want 10*1024*1024/1.2 == 8 MB/sec */ l = (now.sec.x * 1024) + now.nano/976562; if (l) { int i; l=bytes/l; if (report) i=fmt_ulong(f,l); else { i=fmt_humank(f,l*1024); i+=fmt_str(f+i,"iB/sec"); } f[i]=0; } else strcpy(f,"n/a"); l = (now.sec.x * 1000) + now.nano/1000000; l = l ? (done*10000) / l : 0; g[fmt_ulong(g,l/10)]=0; h[fmt_ulong(h,c)]=0; i[fmt_ulong(i,K)]=0; j[fmt_ulong(j,kaputt)]=0; if (server[0]) msg("Server: ",server); if (report) { errmsg_iam(0); msg("req\terr\tconcur\tkeep\tkbytes\tsec\ttput\tr/s\treset"); msg(C,"\t",d,"\t",h,"\t",i,"\t",e,"\t",a,".",b,"\t",f,"\t",g,"\t",j); } else { msg(C," requests, ",d," errors."); msg(e," bytes in ",a,".",b," seconds."); msg("Throughput: ",f); msg("Requests per second: ",g); msg("Connection refused/reset by peer: ",j); } { int i; for (i=0; i<9; ++i) { a[fmt_ulong(a,r[i])]=0; b[0]=i+'0'; b[1]=0; msg(b,"xx: ",a); } } } return 0; }