Created
June 6, 2011 09:33
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* 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 *) ¶m.client, 0}, | |
{"close-with-reset", no_argument, ¶m.close_with_reset, 1}, | |
{"debug", required_argument, 0, 'd'}, | |
+ {"embedded-http-headers", no_argument, ¶m.use_embedded_http_headers, 1}, | |
{"failure-status", required_argument, ¶m.failure_status, 0}, | |
{"help", no_argument, 0, 'h'}, | |
{"hog", no_argument, ¶m.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