int main() { int64 r; if (!io_fd(0)) return 111; if (!io_fd(1)) return 111; for (;;) { if (flageof && writepos >= readpos) return 0; if (!flageof && readpos < sizeof buf) io_wantread(0); if (writepos < readpos) io_wantwrite(1); io_wait(); if (!flageof && readpos < sizeof buf) io_dontwantread(0); if (writepos < readpos) io_dontwantwrite(1); if (!flageof && readpos < sizeof buf) { r = io_tryread(0,buf + readpos,sizeof buf - readpos); if (r <= -2) return 111; /* read error other than EAGAIN */ if (r == 0) flageof = 1; if (r > 0) readpos += r; } if (writepos < readpos) { r = io_trywrite(1,buf + writepos,readpos - writepos); if (r <= -2) return 111; /* write error other than EAGAIN */ if (r > 0) { writepos += r; if (writepos == readpos) readpos = writepos = 0; /* if writepos is big, might want to left-shift buffer here */ } } } }
int64 io_trywritetimeout(int64 d,const char* buf,int64 len) { int64 r=io_trywrite(d,buf,len); if (r==-1) { tai6464 x; io_entry* e=array_get(&io_fds,sizeof(io_entry),d); taia_now(&x); if (!taia_less(&x,&e->timeout)) { errno=ETIMEDOUT; r=-2; } } return r; }
/* Can write new sync data to the stream */ static void handle_write( int64 peersocket ) { proxy_peer *peer = io_getcookie( peersocket ); if( !peer ) { /* Can't happen ;) */ io_close( peersocket ); return; } switch( peer->state & FLAG_MASK ) { case FLAG_DISCONNECTED: default: /* Should not happen */ io_close( peersocket ); break; case FLAG_CONNECTING: /* Ensure that the connection is established and handle connection error */ if( peer->state & FLAG_OUTGOING && !socket_connected( peersocket ) ) { fprintf( stderr, "failed\n" ); reset_info_block( peer ); io_close( peersocket ); break; } if( io_trywrite( peersocket, (void*)&g_tracker_id, sizeof( g_tracker_id ) ) == sizeof( g_tracker_id ) ) { PROXYPEER_SETWAITTRACKERID( peer->state ); io_dontwantwrite( peersocket ); io_wantread( peersocket ); } else { fprintf( stderr, "Handshake incomplete, closing socket\n" ); io_close( peersocket ); reset_info_block( peer ); } break; case FLAG_CONNECTED: switch( iob_send( peersocket, &peer->outdata ) ) { case 0: /* all data sent */ io_dontwantwrite( peersocket ); break; case -3: /* an error occured */ io_close( peersocket ); reset_info_block( peer ); break; default: /* Normal operation or eagain */ break; } break; } return; }
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; }
int main(int argc, char* argv[]) { int s = socket_tcp6(); uint32 scope_id; char ip[16]; uint16 port; char hisip[16]; uint16 hisport; uint32 hisscope_id; static char seed[128]; static stralloc fqdn; static stralloc out; if(argc != 4) { usage: buffer_putsflush(buffer_2, "usage: proxy myip myport hisip hisport\n" "\n" "e.g.: proxy 0 119 news.fu-berlin.de 119\n"); return 0; } if(argv[1][scan_ip6if(argv[1], ip, &scope_id)]) { if(str_equal(argv[1], "0")) { byte_zero(ip, 16); scope_id = 0; } else goto usage; } if(argv[2][scan_ushort(argv[2], &port)]) goto usage; if(argv[3][scan_ip6if(argv[3], hisip, &hisscope_id)]) { dns_random_init(seed); if(!stralloc_copys(&fqdn, argv[3])) goto nomem; if(dns_ip4(&out, &fqdn) == -1) { buffer_puts(buffer_2, "unable to find IP address for "); buffer_puts(buffer_2, argv[3]); buffer_puts(buffer_2, ": "); buffer_puterror(buffer_2); buffer_putnlflush(buffer_2); return 111; } } else if(!stralloc_catb(&out, hisip, 16)) { nomem: buffer_putsflush(buffer_2, "out of memory\n"); return 111; } if(argv[4][scan_ushort(argv[4], &hisport)]) goto usage; if(socket_bind6_reuse(s, ip, port, scope_id) == -1) { buffer_puts(buffer_2, "socket_bind6_reuse: "); buffer_puterror(buffer_2); buffer_putnlflush(buffer_2); return 111; } if(socket_listen(s, 16) == -1) { buffer_puts(buffer_2, "socket_listen: "); buffer_puterror(buffer_2); buffer_putnlflush(buffer_2); return 111; } if(!io_fd(s)) { buffer_puts(buffer_2, "io_fd: "); buffer_puterror(buffer_2); buffer_putnlflush(buffer_2); return 111; } io_wantread(s); for(;;) { int64 i; io_wait(); while((i = io_canread()) != -1) { if(i == s) { /* the read event is on the server socket */ /* that means it's an incoming connection */ int n; while((n = socket_accept6(s, ip, &port, &scope_id)) != -1) { int x = socket_tcp6(); if(x == -1) { buffer_puts(buffer_2, "socket_tcp6 failed: "); fail: buffer_puterror(buffer_2); buffer_putnlflush(buffer_2); io_close(n); } else { struct state* s = malloc(sizeof(struct state)); if(!s) goto closefail; s->a = n; s->b = x; s->connected = 0; s->done = s->todo = 0; s->dir = UNDECIDED; io_nonblock(x); socket_connect6(x, out.s, hisport, hisscope_id); if(!io_fd(x) || !io_fd(n)) { buffer_puts(buffer_2, "io_fd failed: "); closefail: free(s); io_close(x); goto fail; } io_setcookie(x, s); io_setcookie(n, s); io_wantwrite(x); } } if(errno != EAGAIN) { buffer_puts(buffer_2, "socket_accept6 failed: "); buffer_puterror(buffer_2); buffer_putnlflush(buffer_2); } } else { /* read event on an established connection */ struct state* s = io_getcookie(i); int l = io_tryread(i, s->buf, sizeof(s->buf)); if(l == -1) { buffer_puts(buffer_2, "io_tryread("); buffer_putulong(buffer_2, i); buffer_puts(buffer_2, "): "); buffer_puterror(buffer_2); buffer_putnlflush(buffer_2); io_close(s->a); io_close(s->b); } else if(l == 0) { buffer_puts(buffer_2, "eof on fd #"); buffer_putulong(buffer_2, i); buffer_putnlflush(buffer_2); io_close(i); } else { int r; switch(r = io_trywrite(i, s->buf, l)) { case -1: buffer_puts(buffer_2, "io_tryread("); buffer_putulong(buffer_2, i); buffer_puts(buffer_2, "): "); buffer_puterror(buffer_2); buffer_putnlflush(buffer_2); io_close(i); break; case 0: buffer_puts(buffer_2, "write eof on fd #"); buffer_putulong(buffer_2, i); buffer_putnlflush(buffer_2); io_close(i); default: if(r != l) { buffer_puts(buffer_2, "short write on fd #"); buffer_putulong(buffer_2, i); buffer_puts(buffer_2, ": wrote "); buffer_putulong(buffer_2, r); buffer_puts(buffer_2, ", wanted to write "); buffer_putulong(buffer_2, l); buffer_putsflush(buffer_2, ").\n"); } } } } } } return 0; }
/* New sync data on the stream */ static void handle_read( int64 peersocket ) { int i; int64 datalen; uint32_t tracker_id; proxy_peer *peer = io_getcookie( peersocket ); if( !peer ) { /* Can't happen ;) */ io_close( peersocket ); return; } switch( peer->state & FLAG_MASK ) { case FLAG_DISCONNECTED: io_close( peersocket ); break; /* Shouldnt happen */ case FLAG_CONNECTING: case FLAG_WAITTRACKERID: /* We want at least the first four bytes to come at once, to avoid keeping extra states (for now) This also catches 0 bytes reads == EOF and negative values, denoting connection errors */ if( io_tryread( peersocket, (void*)&tracker_id, sizeof( tracker_id ) ) != sizeof( tracker_id ) ) goto close_socket; /* See, if we already have a connection to that peer */ for( i=0; i<MAX_PEERS; ++i ) if( ( g_connections[i].state & FLAG_MASK ) == FLAG_CONNECTED && g_connections[i].tracker_id == tracker_id ) { fprintf( stderr, "Peer already connected. Closing connection.\n" ); goto close_socket; } /* Also no need for soliloquy */ if( tracker_id == g_tracker_id ) goto close_socket; /* The new connection is good, send our tracker_id on incoming connections */ if( peer->state == FLAG_CONNECTING ) if( io_trywrite( peersocket, (void*)&g_tracker_id, sizeof( g_tracker_id ) ) != sizeof( g_tracker_id ) ) goto close_socket; peer->tracker_id = tracker_id; PROXYPEER_SETCONNECTED( peer->state ); if( peer->state & FLAG_OUTGOING ) fprintf( stderr, "succeeded.\n" ); else fprintf( stderr, "Incoming connection successful.\n" ); break; close_socket: fprintf( stderr, "Handshake incomplete, closing socket\n" ); io_close( peersocket ); reset_info_block( peer ); break; case FLAG_CONNECTED: /* Here we acutally expect data from peer indata_length should be less than 20+256*7 bytes, for incomplete torrent entries */ datalen = io_tryread( peersocket, (void*)(peer->indata + peer->indata_length), sizeof( peer->indata ) - peer->indata_length ); if( !datalen || datalen < -1 ) { fprintf( stderr, "Connection closed by remote peer.\n" ); io_close( peersocket ); reset_info_block( peer ); } else if( datalen > 0 ) { peer->indata_length += datalen; process_indata( peer ); } break; } }