Skip to content

Instantly share code, notes, and snippets.

@mrloop
Created June 6, 2011 09:33
Show Gist options
  • Save mrloop/1009997 to your computer and use it in GitHub Desktop.
Save mrloop/1009997 to your computer and use it in GitHub Desktop.
Copy of patch for custom headers in httperf 0.9.0 including setting custom user agent
/* from http://code.google.com/p/httperf/issues/detail?id=1 */
Index: configure.ac
===================================================================
--- configure.ac (revision 132)
+++ configure.ac (working copy)
@@ -83,6 +83,8 @@
AC_FUNC_VPRINTF
AC_CHECK_FUNCS([getopt_long])
+AC_CHECK_FUNCS([strnstr])
+
# Turn on Debug if necessary
AC_ARG_ENABLE(debug,
[ --enable-debug enable debug support])
Index: src/httperf.c
===================================================================
--- src/httperf.c (revision 132)
+++ src/httperf.c (working copy)
@@ -114,6 +114,7 @@
{"client", required_argument, (int *) &param.client, 0},
{"close-with-reset", no_argument, &param.close_with_reset, 1},
{"debug", required_argument, 0, 'd'},
+ {"embedded-http-headers", no_argument, &param.use_embedded_http_headers, 1},
{"failure-status", required_argument, &param.failure_status, 0},
{"help", no_argument, 0, 'h'},
{"hog", no_argument, &param.hog, 1},
@@ -176,7 +177,8 @@
"\t[--wlog y|n,file] [--wsess N,N,X] [--wsesslog N,X,file]\n"
"\t[--wset N,X]\n"
"\t[--period [v]T1,D1[,T2,D2]\n"
- "\t[--use-timer-cache]\n", prog_name);
+ "\t[--use-timer-cache]\n"
+ "\t[--embedded-http-headers]\n", prog_name);
}
void
@@ -1063,6 +1065,8 @@
#endif
if (param.additional_header)
printf(" --add-header='%s'", param.additional_header);
+ if (param.use_embedded_http_headers)
+ printf(" --embedded-http-headers");
if (param.method)
printf(" --method=%s", param.method);
if (param.use_timer_cache)
Index: src/call.h
===================================================================
--- src/call.h (revision 132)
+++ src/call.h (working copy)
@@ -37,7 +37,7 @@
#include <sys/uio.h>
/* Max. # of additional request header lines we allow: */
-#define MAX_EXTRA_HEADERS 4
+#define MAX_EXTRA_HEADERS 10
typedef enum IOV_Element
{
Index: src/httperf.h
===================================================================
--- src/httperf.h (revision 132)
+++ src/httperf.h (working copy)
@@ -122,6 +122,7 @@
int use_timer_cache;
const char *additional_header; /* additional request header(s) */
const char *method; /* default call method */
+ int use_embedded_http_headers; /* use (or not) embedded http headers (at request level) */
struct
{
u_int id;
Index: src/gen/uri_wlog.c
===================================================================
--- src/gen/uri_wlog.c (revision 132)
+++ src/gen/uri_wlog.c (working copy)
@@ -56,6 +56,13 @@
This way, we don't need any parsing of the string when generating
the URI.
+ If, however, the --embedded-http-headers option is given, then the
+ is as follows:
+
+ http-headers^AURI1\0http-headers^AYURI2\0...URIn\0
+
+ Where, the requests are separated by the \0, and the http headers for each request are given before the first control-A character. The headers are parsed exactly as they are when passed by --add-header. However, each request is given it's own set of http headers. (That is, the headers are not additive.)
+
You can choose to loop on te list of URIs by using the following
command line option to httperf:
@@ -89,11 +96,65 @@
static char *fbase, *fend, *fcurrent;
+/* borrowed from src/gen/misc.c */
+static const char *
+unescape_local (const char *str, int *len)
+{
+ char *dp, *dst = strdup (str);
+ const char *cp;
+ int ch;
+
+ if (!dst)
+ panic ("%s: strdup() failed: %s\n", prog_name, strerror (errno));
+
+ for (cp = str, dp = dst; (ch = *cp++); )
+ {
+ if (ch == '\\')
+ {
+ ch = *cp++;
+ switch (ch)
+ {
+ case '\\': /* \\ -> \ */
+ break;
+
+ case 'a': /* \a -> LF */
+ ch = 10;
+ break;
+
+ case 'r': /* \r -> CR */
+ ch = 13;
+ break;
+
+ case 'n': /* \n -> CR/LF */
+ *dp++ = 13;
+ ch = 10;
+ break;
+
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ ch = strtol (cp - 1, (char **) &cp, 8);
+ break;
+
+ default:
+ fprintf (stderr, "%s: ignoring unknown escape sequence "
+ "`\\%c' in embedded http headers\n", prog_name, ch);
+ break;
+ }
+ }
+ *dp++ = ch;
+ }
+ *len = dp - dst;
+ return dst;
+}
+
+
static void
set_uri (Event_Type et, Call * c)
{
int len, did_wrap = 0;
- const char *uri;
+ const char *uri, *extra_request_headers, *extra_request_headers_escaped;
+ char *ptr, *erh_2;
+ int extra_request_headers_len, extra_request_headers_len2, len_to_advance;
assert (et == EV_CALL_NEW && object_is_call (c));
@@ -116,8 +177,67 @@
}
uri = fcurrent;
len = strlen (fcurrent);
+ len_to_advance = len;
+
+ /* look for global option for embedded HTTP headers.
+ if set, look for control-A separator. If found, parse from fcurrent to ctrl-A as headers, and set ctrl-A+1 as uri. Then set len to len -= strlen(headers)
+
+ be sure that the extra (which is the headers itself) is unescaped
+ -- unescape calls strdup, so it's safe to use the returned string
+
+ */
+ if (param.use_embedded_http_headers)
+ {
+ extra_request_headers = '\0';
+ extra_request_headers = memchr(uri, '\a', len);
+ if (extra_request_headers != '\0')
+ {
+ /* control-A character found ... swap variables so they make sense */
+ ptr = (char *)uri;
+ uri = extra_request_headers;
+ uri++;
+ extra_request_headers = ptr;
+ /*
+ now, uri points to the start of the URL to request
+ and extra_request_headers points to the start of the http
+ Loop thru the headers, find the \a, and replace it with \0.
+ */
+
+
+ extra_request_headers_len = 0;
+ erh_2 = '\0';
+ ptr = (char *)extra_request_headers;
+ while (*ptr)
+ {
+ if (*ptr == '\a')
+ {
+ erh_2 = (char *)malloc(extra_request_headers_len+1);
+ memset(erh_2, '\0', extra_request_headers_len+1);
+ memcpy(erh_2, extra_request_headers, extra_request_headers_len);
+ break;
+ }
+ ptr++;
+ extra_request_headers_len++;
+ }
+
+ extra_request_headers_escaped =
+ unescape_local(erh_2, &extra_request_headers_len2);
+ if (erh_2)
+ free(erh_2);
+ if (DBG > 5)
+ fprintf(stderr, "Generated http headers [%.*s] uri [%s]\n",
+ extra_request_headers_len2, extra_request_headers_escaped, uri);
+
+ call_append_request_header (c,
+ extra_request_headers_escaped, extra_request_headers_len2);
+ /* and reduce the length of the URI by the header length */
+ len -= extra_request_headers_len + 1;
+
+ }
+ }
+
call_set_uri (c, uri, len);
- fcurrent += len + 1;
+ fcurrent += len_to_advance + 1;
}
while (len == 0);
Index: src/core.c
===================================================================
--- src/core.c (revision 132)
+++ src/core.c (working copy)
@@ -91,6 +91,17 @@
static char http11req_nohost[] =
" HTTP/1.1\r\nUser-Agent: httperf/" VERSION;
+/* repeated for custom user-agent */
+static char http10req_nouseragent[] =
+ " HTTP/1.0\r\nConnection: keep-alive\r\nHost: ";
+static char http11req_nouseragent[] =
+ " HTTP/1.1\r\nHost: ";
+
+static char http10req_nohost_nouseragent[] =
+ " HTTP/1.0\r\nConnection: keep-alive";
+static char http11req_nohost_nouseragent[] =
+ " HTTP/1.1";
+
#ifndef SOL_TCP
# define SOL_TCP 6 /* probably ought to do getprotlbyname () */
#endif
@@ -135,6 +146,30 @@
}
#endif
+#ifndef HAVE_STRNSTR
+char *strnstr(const char *haystack, const char *needle, size_t haystacklen)
+{
+ char *p;
+ size_t plen;
+ size_t len = strlen(needle);
+
+ if (*needle == '\0') /* everything matches empty string */
+ return (char*) haystack;
+
+ plen = haystacklen;
+ for (p = (char*) haystack; p != NULL; p = memchr(p + 1, *needle, plen-1)) {
+ plen = haystacklen - (p - haystack);
+
+ if (plen < len) return NULL;
+
+ if (strncmp(p, needle, len) == 0)
+ return (p);
+ }
+ return NULL;
+}
+#endif /* HAVE_STRNSTR */
+
+
struct hash_entry {
const char *hostname;
int port;
@@ -969,7 +1004,9 @@
core_send(Conn * conn, Call * call)
{
Any_Type arg;
+ int have_custom_user_agent;
+ have_custom_user_agent = 0;
arg.l = 0;
event_signal(EV_CALL_ISSUE, (Object *) call, arg);
@@ -986,6 +1023,25 @@
call->req.iov[IE_HOST].iov_len = conn->fqdname_len;
}
+ /* if we're using embedded http headers, look to see if User-Agent was specified */
+ if (param.use_embedded_http_headers)
+ {
+ char *base_copy, *hdr;
+ int base_len, start_idx, end_idx, i, hdr_len;
+ end_idx = IE_CONTENT;
+ for (i = IE_METHOD; i < end_idx; ++i)
+ {
+ hdr = call->req.iov[i].iov_base;
+ hdr_len = call->req.iov[i].iov_len;
+
+ if ((hdr_len) && (strnstr(hdr, "User-Agent: ", hdr_len) != NULL))
+ {
+ have_custom_user_agent = 1;
+ break;
+ }
+ }
+ }
+
/*
* NOTE: the protocol version indicates what the _client_ can
* understand. If we send HTTP/1.1, it doesn't mean that the server
@@ -993,29 +1049,71 @@
* leaves it up to the server whether it wants to reply with a 1.0 or
* 1.1 reply.
*/
+ if (DBG > 5)
+ {
+ fprintf(stderr, "Custom user-agent is [%d]\n", have_custom_user_agent);
+ }
switch (call->req.version) {
case 0x10000:
if (param.no_host_hdr) {
- call->req.iov[IE_PROTL].iov_base =
- (caddr_t) http10req_nohost;
- call->req.iov[IE_PROTL].iov_len =
- sizeof(http10req_nohost) - 1;
+ /* do we need the non-user-agent string? */
+ if (have_custom_user_agent == 1)
+ {
+ call->req.iov[IE_PROTL].iov_base =
+ (caddr_t) http10req_nohost_nouseragent;
+ call->req.iov[IE_PROTL].iov_len =
+ sizeof(http10req_nohost_nouseragent) - 1;
+ }
+ else
+ {
+ call->req.iov[IE_PROTL].iov_base =
+ (caddr_t) http10req_nohost;
+ call->req.iov[IE_PROTL].iov_len =
+ sizeof(http10req_nohost) - 1;
+ }
} else {
- call->req.iov[IE_PROTL].iov_base = (caddr_t) http10req;
- call->req.iov[IE_PROTL].iov_len =
- sizeof(http10req) - 1;
+ if (have_custom_user_agent == 1)
+ {
+ call->req.iov[IE_PROTL].iov_base = (caddr_t) http10req_nouseragent;
+ call->req.iov[IE_PROTL].iov_len =
+ sizeof(http10req_nouseragent) - 1;
+ }
+ else
+ {
+ call->req.iov[IE_PROTL].iov_base = (caddr_t) http10req;
+ call->req.iov[IE_PROTL].iov_len =
+ sizeof(http10req) - 1;
+ }
}
break;
case 0x10001:
if (param.no_host_hdr) {
- call->req.iov[IE_PROTL].iov_base = http11req_nohost;
- call->req.iov[IE_PROTL].iov_len =
- sizeof(http11req_nohost) - 1;
+ if (have_custom_user_agent == 1)
+ {
+ call->req.iov[IE_PROTL].iov_base = http11req_nohost_nouseragent;
+ call->req.iov[IE_PROTL].iov_len =
+ sizeof(http11req_nohost_nouseragent) - 1;
+ }
+ else
+ {
+ call->req.iov[IE_PROTL].iov_base = http11req_nohost;
+ call->req.iov[IE_PROTL].iov_len =
+ sizeof(http11req_nohost) - 1;
+ }
} else {
- call->req.iov[IE_PROTL].iov_base = http11req;
- call->req.iov[IE_PROTL].iov_len =
- sizeof(http11req) - 1;
+ if (have_custom_user_agent == 1)
+ {
+ call->req.iov[IE_PROTL].iov_base = http11req_nouseragent;
+ call->req.iov[IE_PROTL].iov_len =
+ sizeof(http11req_nouseragent) - 1;
+ }
+ else
+ {
+ call->req.iov[IE_PROTL].iov_base = http11req;
+ call->req.iov[IE_PROTL].iov_len =
+ sizeof(http11req) - 1;
+ }
}
break;
Index: man/httperf.1
===================================================================
--- man/httperf.1 (revision 132)
+++ man/httperf.1 (working copy)
@@ -13,6 +13,7 @@
.RB [ \-\-close\-with\-reset ]
.RB [ \-d | \-\-debug
.I R N ]
+.RB [ \-\-embedded\-http\-headers ]
.RB [ \-\-failure\-status
.I R N ]
.RB [ \-h | \-\-help ]
@@ -196,6 +197,19 @@
absolutely necessary and even then it should not be used unless its
implications are fully understood.
.TP
+.BI \-\-embedded\-http\-headers
+Allows
+.B \-\-wlog
+files to contain embedded http headers that change from request to
+request. The file format of the
+.B \-\-wlog
+file is as follows: the requests are separated by an ASCII NUL
+character. The HTTP headers to use are separated from the URI by
+a single ASCII ctrl-A character (ASCII 7). Multiple HTTP headers may
+be added, following the same conventions used by the
+.B \-\-add\-header
+parameter.
+.TP
.BI \-d= N
.TP
.BI \-\-debug= N
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment