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.
diff --git a/contrib/tcploop/tcploop.c b/contrib/tcploop/tcploop.c
index 5dfc3ae..8b9a0a0 100644
--- a/contrib/tcploop/tcploop.c
+++ b/contrib/tcploop/tcploop.c
@@ -102,6 +102,7 @@
" 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 @@
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 @@
}
/* 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);