Saturday, March 22, 2014

Binary data for HTTP request made easy


   In many cases the data transferred over HTTP are ascii,  so they are easy to process for most performance testing platforms including JMeter.  However, occasionally we may need to send binary data to the server, as shown in a stackoverflow question.   This is where it's not quite easy any more on many testing platforms.  The following is a perl script that the author of the question wrote for one HTTP session.

 #the perl script that sends the time data
 $date_time = sprintf "%08X", time();  
 $BODY_TEMPLATE = "00${date_time}0015";  
 $body_len = (length (sprintf($BODY_TEMPLATE,0,0))) / 2;  
 # Here I set $TARGET & $HOST  
 $MSG_HEADER = "POST \/unil?URL=${TARGET} HTTP\/1.1\r\nHost: ${HOST}\r\ncontent-type:application/binary\r\ncontent-length: ${body_len}\r\n\r\n";  
 $body = pack ("H*", $BODY_TEMPLATE);  
 $message_to_send = $MSG_HEADER . $body;  
 # at this point I sent the entire message over a TCP socket I previously opened.  

    This test scenario is actually quite easy to describe:  the client needs to create a message with the time (in seconds) in the form of a 4-byte integer, with one byte (0) in the front and two bytes (0x0015) in the rear,  and send the 7-byte message as the POST body.

    The recommended solution on JMeter involves knowing Bean shell pre-prosessor and some knowledge on dealing with jmeter variable(s).  There is also a Jmeter plugin that can potentially send any raw HTTP request.  But it requires installing the plugin and non-trivial skills to be able to get the binary data into the POST body.

   On NetGend platform,  the following simple script will take care of this test scenario:

 function VUSER() {   
      http.POSTData = pack("CNn", 0,time(), 0x15);  
      action(http, "http://www.example.com/unil?URL=${TARGET}");  
 }   

    The key part of this short script is the "pack()" function.  "pack" is the word used to describe the process of converting data into binary format.  For example, the two characters "35" can be packed into one byte "5" (whose ascii code is 0x35).

     In the above Netgend script, we packed 3 fields to a binary data using the format string "CNn",

  • The first part is "C",  it will pack the first field, 0,  to a byte,  
  • The second part is "N", it will pack the second field, time(), to a 4-byte integer in the big Endian order. Here the function "time()" returns the number of seconds since Jan 1, 1970,  
  • The last part is "n", it will pack the last field, 0x15, to a 2 bytes integer in the big endian format.   
    In addition to the above 3 formats, a lot more are supported.  For example, "V" will pack a number to an 4-byte integer in the little Endian order.

   This little script is not only simple, it's also very powerful:  it can easily emulate tens of thousands of HTTP clients from one load generator.

   The "pack()" function has a sister function "unpack()" which will do just the opposite: convert a binary data to  number(s) or string(s).  "unpack" can be very useful in processing server messages  that have binary data and it apply not only to HTTP bodies but also to any TCP data in general.

   Together, "pack" and "unpack" make dealing with binary data very easy on NetGend platform.

No comments:

Post a Comment