[httperf] Patch to add HTTP Basic Authentication

Durval Menezes httperf@napali.hpl.hp.com
Tue, 7 Nov 2000 22:11:00 -0200


--jho1yZJdad60DJr+
Content-Type: text/plain; charset=us-ascii

Hello,

Here it goes a patch to add HTTP Basic Authentication functionality
to httperf-0.8.

This is preliminary code, and actually it was made quite easy in
httperf-0.8 (when compared to 0.6) since it was patterned mostly 
on the new "--add-header" functionality.

It's working OK in my test setup, and has enabled me to benchmark
servers where the pages were under Basic Authentication without
disabling the authentication in the server configuration.

I'm not 100% comfortable with this code because:

1) It uses code from the old CERN HTTPD (the uuencode function),
   and even if the code itself is pretty well written and efficient,
   we had to include MIT/INRIA's copyright in it. The ideal solution
   would be to write a uuencode from scratch (but it's a PITA, and I 
   was coding it on a weekend and could'nt be bothered :-)) or pluck 
   one ready-made from some GPL'd code (like, er, GNU uuencode :-)).

2) It does not simulate *exactly* the interaction a normal user with a 
   browser has when retrieving a page protected by Basic Auth. A normal 
   user first tries to load the page, its browser gets a 404 code from 
   the server, and then prompts the user for a username/password. 
   The browser then tries again to load the page, this time including an 
   authentication header with the information passed by the user; 
   if this is sucessful, the browser renders the page and continues to 
   include this header in all future requests to the same server. 
   My code simply includes the authentication header on all the requests,
   without the first fail/ask_the_user/retry behaviour. 
   This is not important for my test setup (I'm fundamentally interested 
   in measuring the response from the server under stress conditions, 
   with tens or hundreds of thousands, of requests, so just one request 
   handled one way or the other does no difference), but it could be 
   important on some other application.

Anyway, I hope it is useful. Please enjoy it and give me feedback on
any problems/errors/sugestions.

Best Regards,
-- 
   Durval Menezes (durval@tmp.com.br, http://www.amcham.com.br/~durval)

--jho1yZJdad60DJr+
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="patch-httperf-0.8-basic_auth"

# patch-httperf-0.8-basic_auth
# 2000/11/05 Durval Menezes <durval@tmp.com.br>
#
# This patch adds HTTP Basic Authentication command-line option to httperf-0.8
#
# Apply with
#	tar xzf httperf-0.8.tar.gz
#	cd httperf-0.8
#  	patch -p1 <../patch-httperf-0.8-basic_auth
#

diff -ru httperf-0.8/gen/misc.c httperf-0.8-basic_auth/gen/misc.c
--- httperf-0.8/gen/misc.c	Wed Oct 11 21:37:05 2000
+++ httperf-0.8-basic_auth/gen/misc.c	Sun Nov  5 01:46:04 2000
@@ -28,6 +28,16 @@
 	--add-header	Adds one or more command-line specified header(s)
 			to each call request.
 
+	--basic-auth	Adds a Basic Authentication header to each call 
+			request.
+ 			
+			Note that a browser would first send the  request  
+                        without  the Authentication  Header,  get  a  401 
+                        error from the server, prompt the user for his/her
+			username  and password  and ONLY THEN send a new 
+                        request with the Authentication Header in it; this 
+                        isn't what we  do here.
+
 	--method	Sets the method to be used when performing a
 			call.  */
 
@@ -45,6 +55,9 @@
 static const char *extra;
 static size_t extra_len;
 
+static const char *basic_auth;
+static size_t basic_auth_len;
+
 static size_t method_len;
 
 /* A simple module that collects cookies from the server responses and
@@ -100,6 +113,132 @@
   return dst;
 }
 
+/* ---------------------------------------------------------------------- */
+/* the following code was extracted from the classic CERN HTTPD code in	  */
+/* w3c-httpd-3.0A/Library/Implementation/HTUU.c                           */
+
+/* As requested in the original COPYRIGH (sic) file:
+
+                                                               Copyright NOTICE
+                                    NOTICE
+                                       
+COPYRIGHT 1995 BY: MASSACHUSETTS INSTITUTE OF TECHNOLOGY (MIT), INRIA
+
+   This W3C software is being provided by the copyright holders under the
+   following license. By obtaining, using and/or copying this software, you
+   agree that you have read, understood, and will comply with the following
+   terms and conditions:
+   
+   Permission to use, copy, modify, and distribute this software and its
+   documentation for any purpose and without fee or royalty is hereby granted,
+   provided that the full text of this _NOTICE_ appears on ALLcopies of the
+   software and documentation or portions thereof, including modifications,
+   that you make.
+   
+   _THIS SOFTWARE IS PROVIDED "AS IS," AND COPYRIGHT HOLDERS MAKE NO
+   REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY OF EXAMPLE, BUT
+   NOT LIMITATION, COPYRIGHT HOLDERS MAKE NO REPRESENTATIONS OR WARRANTIES OF
+   MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE
+   SOFTWARE OR DOCUMENTATION WILL NOT INFRINGE ANY THIRD PARTY PATENTS,
+   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS. COPYRIGHT HOLDERS WILL BEAR NO
+   LIABILITY FOR ANY USE OF THIS SOFTWARE OR DOCUMENTATION._
+   
+   The name and trademarks of copyright holders may NOTbe used in advertising
+   or publicity pertaining to the software without specific, written prior
+   permission. Title to copyright in this software and any associated
+   documentation will at all times remain with copyright holders.
+   
+   
+   ___________________________________
+*/   
+
+static char six2pr[64] = {
+    'A','B','C','D','E','F','G','H','I','J','K','L','M',
+    'N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
+    'a','b','c','d','e','f','g','h','i','j','k','l','m',
+    'n','o','p','q','r','s','t','u','v','w','x','y','z',
+    '0','1','2','3','4','5','6','7','8','9','+','/'
+};
+
+/*--- function HTUU_encode -----------------------------------------------
+ *
+ *   Encode a single line of binary data to a standard format that
+ *   uses only printing ASCII characters (but takes up 33% more bytes).
+ *
+ *    Entry    bufin    points to a buffer of bytes.  If nbytes is not
+ *                      a multiple of three, then the byte just beyond
+ *                      the last byte in the buffer must be 0.
+ *             nbytes   is the number of bytes in that buffer.
+ *                      This cannot be more than 48.
+ *             bufcoded points to an output buffer.  Be sure that this
+ *                      can hold at least 1 + (4*nbytes)/3 characters.
+ *
+ *    Exit     bufcoded contains the coded line.  The first 4*nbytes/3 bytes
+ *                      contain printing ASCII characters representing
+ *                      those binary bytes. This may include one or
+ *                      two '=' characters used as padding at the end.
+ *                      The last byte is a zero byte.
+ *             Returns the number of ASCII characters in "bufcoded".
+ */
+static int HTUU_encode (const unsigned char *bufin, unsigned int nbytes, char *bufcoded)
+{
+/* ENC is the basic 1 character encoding function to make a char printing */
+#define ENC(c) six2pr[c]
+
+   register char *outptr = bufcoded;
+   unsigned int i;
+
+   for (i=0; i<nbytes; i += 3) {
+      *(outptr++) = ENC(*bufin >> 2);            /* c1 */
+      *(outptr++) = ENC(((*bufin << 4) & 060) | ((bufin[1] >> 4) & 017)); /*c2*/
+      *(outptr++) = ENC(((bufin[1] << 2) & 074) | ((bufin[2] >> 6) & 03));/*c3*/
+      *(outptr++) = ENC(bufin[2] & 077);         /* c4 */
+
+      bufin += 3;
+   }
+
+   /* If nbytes was not a multiple of 3, then we have encoded too
+    * many characters.  Adjust appropriately.
+    */
+   if(i == nbytes+1) {
+      /* There were only 2 bytes in that last group */
+      outptr[-1] = '=';
+   } else if(i == nbytes+2) {
+      /* There was only 1 byte in that last group */
+      outptr[-1] = '=';
+      outptr[-2] = '=';
+   }
+   *outptr = '\0';
+   return(outptr - bufcoded);
+}
+
+/* end of code extracted from the classic CERN HTTPD source               */
+/* ---------------------------------------------------------------------- */
+
+static const char *
+basic_auth_encode (const char *str, size_t *len)
+{
+  static char *prefix = "Authorization: basic ";
+  int maxuulen=((strlen(str) + 2) / 3 * 4) + 1; /* when uuencoding, every 3 chars generate 4 */
+  char *uu = malloc(maxuulen+1);
+  int maxdstlen=strlen(prefix)+maxuulen+3; 
+  char *dst = malloc(maxdstlen+1);
+
+  if (!uu || !dst)
+    panic ("%s: malloc() failed: %s\n", prog_name, strerror (errno));
+
+  HTUU_encode(str, strlen(str), uu);
+
+  strncat(dst, prefix, maxdstlen);
+  strncat(dst, uu, maxdstlen);
+  strncat(dst, "\r\n", maxdstlen);
+
+  free(uu);
+
+  *len = strlen(dst);
+  return dst;
+}
+
 static void
 call_created (Event_Type et, Object *obj, Any_Type reg_arg, Any_Type arg)
 {
@@ -112,6 +251,9 @@
 
   if (extra_len > 0)
     call_append_request_header (c, extra, extra_len);
+
+  if (basic_auth_len > 0)
+    call_append_request_header (c, basic_auth, basic_auth_len);
 }
 
 
@@ -122,6 +264,9 @@
 
   if (param.additional_header)
     extra = unescape (param.additional_header, &extra_len);
+
+  if (param.basic_auth_string)
+    basic_auth = basic_auth_encode (param.basic_auth_string, &basic_auth_len);
 
   if (param.method)
     method_len = strlen (param.method);
diff -ru httperf-0.8/httperf.c httperf-0.8-basic_auth/httperf.c
--- httperf-0.8/httperf.c	Tue Oct 31 18:20:00 2000
+++ httperf-0.8-basic_auth/httperf.c	Sun Nov  5 02:13:28 2000
@@ -96,6 +96,7 @@
 static struct option longopts[] =
   {
     {"add-header",   required_argument, (int *) &param.additional_header, 0},
+    {"basic-auth",   required_argument, (int *) &param.basic_auth_string, 0},
     {"burst-length", required_argument, &param.burst_len,		0},
     {"client",	     required_argument, (int *) &param.client,		0},
     {"close-with-reset", no_argument,	&param.close_with_reset,	1},
@@ -144,6 +145,7 @@
 {
   printf ("Usage: %s "
 	  "[-hdvV] [--add-header S] [--burst-length N] [--client N/N]\n"
+	  "\t[--basic-auth \"username:password\"]\n"
 	  "\t[--close-with-reset] [--debug N] [--failure-status N]\n"
 	  "\t[--help] [--hog] [--http-version S] [--max-connections N]\n"
 	  "\t[--max-piped-calls N] [--method S] [--no-host-hdr]\n"
@@ -267,6 +269,19 @@
 	    param.method = optarg;
 	  else if (flag == &param.additional_header)
 	    param.additional_header = optarg;
+	  else if (flag == &param.basic_auth_string)
+	    {
+	      char *p;
+	      if ((p=strchr(optarg,':')) == NULL ||
+		  (p-optarg) == 0		 ||
+		  (p-optarg) == strlen(optarg)-1)
+	      {
+		  fprintf(stderr, "%s: invalid string for --basic-auth '%s'\n", prog_name, optarg);
+		  exit(-1);
+	      }
+	      else
+		param.basic_auth_string = optarg;
+            }
 	  else if (flag == &param.num_calls)
 	    {
 	      errno = 0;
@@ -826,7 +841,7 @@
       gen[num_gen++] = &sess_cookie;
     }
 
-  if (param.additional_header || param.method)
+  if (param.additional_header || param.basic_auth_string || param.method)
     gen[num_gen++] = &misc;
 
   /* echo command invocation for logging purposes: */
@@ -900,6 +915,8 @@
 #endif
   if (param.additional_header)
     printf (" --add-header='%s'", param.additional_header);
+  if (param.basic_auth_string)
+    printf (" --basic-auth='%s'", param.basic_auth_string);
   if (param.method) printf (" --method=%s", param.method);
   if (param.wsesslog.num_sessions)
     {
diff -ru httperf-0.8/httperf.h httperf-0.8-basic_auth/httperf.h
--- httperf-0.8/httperf.h	Tue Oct 31 18:32:09 2000
+++ httperf-0.8-basic_auth/httperf.h	Sun Nov  5 01:29:49 2000
@@ -127,6 +127,7 @@
     const char *ssl_cipher_list; /* client's list of SSL cipher suites */
 #endif
     const char *additional_header;	/* additional request header(s) */
+    const char *basic_auth_string;	/* Basic Authentication string (username:password) */
     const char *method;	/* default call method */
     struct
       {
diff -ru httperf-0.8/httperf.man httperf-0.8-basic_auth/httperf.man
--- httperf-0.8/httperf.man	Tue Oct 31 18:17:15 2000
+++ httperf-0.8-basic_auth/httperf.man	Sun Nov  5 01:54:40 2000
@@ -6,6 +6,8 @@
 .B httperf
 .RB [ --add-header
 .IR S ]
+.RB [ --basic-auth
+.IR username : password ]
 .RB [ --burst-length
 .IR N ]
 .RB [ --client
@@ -151,6 +153,17 @@
 sequences are ``\\r'' (carriage-return), ``\\a'' (line-feed), ``\\\\''
 (backslash), and ``\\N'' where N is the code the character to be
 inserted (in octal).
+.TP
+.BI --basic-auth= "username:password"
+Specifies to include an HTTP Basic Authentication header in every
+request, with the 
+.I username 
+and 
+.I password 
+as given (separated by ``:''). Note that a browser would first send the request
+without the Authentication Header, get a 401 error from the server, prompt the
+user for his/her username and password and ONLY THEN send a new request with
+the Authentication Header in it; this isn't what we do here.
 .TP
 .BI --burst-length= N
 Specifies the length of bursts.  Each burst consists of

#Eof patch-httperf-0.8-basic_auth

--jho1yZJdad60DJr+--

--
To unsubscribe: echo unsubscribe httperf | mail majordomo@linux.hpl.hp.com