CONTRIB: tcploop: support sending plain strings

By passing "S:<string>" instead of S<size> it's possible to send
a pre-defined string, which is convenient to write HTTP requests or
responses.

Example : produce two responses, one in keep-alive, one not for ab :

  ./tcploop 8001 L W N2 A R S:"HTTP/1.0 200 OK\r\nConnection: keep-alive\r\nContent-length: 50\r\n\r\n0123456789.123456789.123456789.123456789.123456789" R S:"HTTP/1.0 200 OK\r\nContent-length: 50\r\n\r\n0123456789.123456789.123456789.123456789.123456789"

With 20 such keep-alive responses and 10 parallel processes, ab achieves
350kreq/s, so it should be possible to get precise timings.
This commit is contained in:
Willy Tarreau 2016-11-12 18:25:45 +01:00
parent 9557bacfff
commit 59623e0aa8
1 changed files with 29 additions and 2 deletions

View File

@ -102,6 +102,7 @@ __attribute__((noreturn)) void usage(int code, const char *arg0)
" Q : disable TCP Quick-ack\n"
" R[<size>] : Read this amount of bytes. 0=infinite. unset=any amount.\n"
" S[<size>] : Send this amount of bytes. 0=infinite. unset=any amount.\n"
" S:<string> : Send this exact string. \\r, \\n, \\t, \\\\ supported.\n"
" E[<size>] : Echo this amount of bytes. 0=infinite. unset=any amount.\n"
" W[<time>] : Wait for any event on the socket, maximum <time> ms\n"
" P[<time>] : Pause for <time> ms (100 by default)\n"
@ -174,6 +175,26 @@ void dolog(const char *format, ...)
va_end(args);
}
/* convert '\n', '\t', '\r', '\\' to their respective characters */
int unescape(char *out, int size, const char *in)
{
int len;
for (len = 0; len < size && *in; in++, out++, len++) {
if (*in == '\\') {
switch (in[1]) {
case 'n' : *out = '\n'; in++; continue;
case 't' : *out = '\t'; in++; continue;
case 'r' : *out = '\r'; in++; continue;
case '\\' : *out = '\\'; in++; continue;
default : break;
}
}
*out = *in;
}
return len;
}
struct err_msg *alloc_err_msg(int size)
{
struct err_msg *err;
@ -444,14 +465,20 @@ int tcp_recv(int sock, const char *arg)
}
/* sends N bytes to the socket and returns 0 (or -1 in case of error). If not
* set, sends only one block. Sending zero means try to send forever.
* set, sends only one block. Sending zero means try to send forever. If the
* argument starts with ':' then whatever follows is interpreted as the payload
* to be sent as-is. '\r', '\n', '\t' and '\\' are detected and converted. In
* this case, blocks must be small so that send() doesn't fragment them, as
* they will be put into the trash and expected to be sent at once.
*/
int tcp_send(int sock, const char *arg)
{
int count = -1; // stop after first block
int ret;
if (arg[1]) {
if (arg[1] == ':') {
count = unescape(trash, sizeof(trash), arg + 2);
} else if (arg[1]) {
count = atoi(arg + 1);
if (count < 0) {
fprintf(stderr, "send count must be >= 0 or unset (was %d)\n", count);