]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
CONTRIB: tcploop: support sending plain strings
authorWilly Tarreau <w@1wt.eu>
Sat, 12 Nov 2016 17:25:45 +0000 (18:25 +0100)
committerWilly Tarreau <w@1wt.eu>
Sat, 12 Nov 2016 17:39:32 +0000 (18:39 +0100)
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.

contrib/tcploop/tcploop.c

index 5dfc3aee751dd523fd92baa8bf4733507d659d65..8b9a0a0a7070906ff7e85fce6a84f3540b429f0c 100644 (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);