httest is a script based tool for testing and benchmarking web applications, web servers, proxy servers and web browsers. httest can emulate clients and servers in the same test script, very useful for testing proxys.
CLIENT _REQ www.google.ch 80 __GET / HTTP/1.1 __Host: www.google.ch __ _EXPECT headers "200 OK" _WAIT END
and run httest with this script with the following command:
The test script gets googles root and test if we get a 200 OK. If not the script will fail else it will terminate with a OK and exit code 0.
To list all httest script commands run
To get help for a specific script command run for example
Automatic cookie handling. But do not handle path or stuff like that, it just sends a received cookie on a given connection. If need more features do it with _MATCH or ask for new feature ;)
CLIENT ### _AUTO_COOKIE on _REQ foo.bar.com 80 __GET / HTTP/1.1 __Host: foo.bar.com __Cookie: ### AUTO __ _WAIT __GET /bla HTTP/1.1 __Host: foo.bar.com __Cookie: ### AUTO __ _WAIT END
The header Cookie: AUTO will be filled with a cookie if there is any, else this header will be skipped automatically. Safest way is to set the Cooke: AUTO for every request.
Restrict the bandwidth while httest is sending.
CLIENT _BPS 100 20 _REQ localhost 8080 __POST / HTTP/1.1 __Host: $YOUR_HOST __User-Agent: mozilla __Content-Length: AUTO __ __............................................................................. _WAIT _CLOSE _END BPS _EXIT OK END
This test will send the HTTP request with 100 byte/s to http://localhost:8080/ for about 20 seconds.
Do not send too much date in the BPS body with a low bandwidth, cause the test will delay next request until it reaches the given byte/s. If you like to send with a very high bandwidth do it also parallel with i.e CLIENT 10 and bandwith of 1000 byte/s per client
Assume there is a server with a CA loaded and does require client certificate. Let further assume there is a client pem cert and key signed by the CA loaded from the server.
The server is on your.domain:443
CLIENT _REQ your.domain SSL:443 client.cert.pem client.key.pem __GET / HTTP/1.1 __Host: your.domain __ _EXPECT . "HTTP/1.1 200 OK" _WAIT END
As in the SSL Example we open the connection on SSL:443 and we tell httest which is our cert and key. Attention must be taken with the key, there is no password support, so free your key from any password.
Many time you need to call an external script and match its output.
CLIENT _MATCH exec "Foo(.*)" BAR _EXEC echo Foobar END
In the variable $BAR "bar" ist stored in this example. This script will fail if noc Foo.* is read on the stdout.
Need newline for every line else will not appear, will be fixed with the new httest version 1.3.1
Many times you have to login and test with a valid session. Below is a very simple example how to cut session infos out of the socket stream.
CLIENT _REQ localhost 8080 __GET / HTTP/1.1 __Host: localhost:8080 __ _MATCH headers "Set-Cookie: SessionId=([^;]*);" SESSION _WAIT _REQ localhost 8080 __GET / HTTP/1.1 __Host: localhost:8080 __Cookie: SessionId=$SESSION __ _WAIT END SERVER 8080 _RES _WAIT __HTTP/1.1 200 OK __Host: localhost __Set-Cookie: SessionId=foobar path=/ __Content-Length: AUTO __ __Data _RES _WAIT __HTTP/1.1 200 OK __Host: localhost __Content-Length: AUTO __ __Data END
_MATCH do match with a regex every line (and fail if no hits) and cut the stuff in () out and store it in the given variable name.
_MATCH has two scope for performance impact "headers" and "body". Most test will only need the scope "headers".
This is self contain example and should run out of the box.
With _DBG:BP you can set a breakpoint. On break point you can
* type "cont" or "c" <enter> to continue script
* type "list" or "l" to show script around breakpoint
* type "get <variable-name>" or "g <variable-name>" to inspect the value of a variable
* type "set <variable-name>=<value>" or "s <variable-name>=<value> to overwrite or set new variable with a value.
* type "quit" or "q" to abord test
CLIENT _REQ localhost 8080 __GET /foo HTTP/1.1 __Host: localhost:8080 __ _WAIT _DBG:BP _REQ localhost 8080 __GET /foo HTTP/1.1 __Host: localhost:8080 __ _WAIT END SERVER 8080 _RES _WAIT __HTTP/1.1 200 OK __Content-Length: AUTO __ __foo _RES _WAIT __HTTP/1.1 200 OK __Content-Length: AUTO __ __foo END
CLIENT _REQ localhost 8080 __GET /your/path HTTP/1.1 __Host: localhost __User-Agent: mozilla __ _EXEC< gunzip _WAIT END
With the command _EXEC< the received stream can be piped in and the output of the executed shell command ist piped back to the _WAIT command
There are use cases where you want to print the test duration. Need ### version 1.9.0 or higher.
CLIENT _RES yourhost yourport __GET /foo HTTP/1.1 __Host: localhost __ _WAIT END BLOCK FINALLY _IF "$__STATUS" MATCH "0" _TIMER GET TIME _EXEC echo $TIME >> file END
This script measures the duration time of the test script and print this to file only if test status is "0" mean no error.
To hold everything in a single test script, you could also embedd a shell script right in the httest script.
CLIENT _MATCH exec "hello (.*)" WORLD _SH #!/bin/bash _SH echo hello world _SH END _DEBUG $WORLD END
The embedded shell script will be written in a temporary file with a random name. On _SH END the script will be called like any ohter external written script, therefore you can also use _MATCH exec.
It is very useful to call external commands in a test script.
EXEC ./your_external_program start CLIENT ... # Optional match to cut some values from the stdout of the called shell # command. I.e. the shell would return "foobar", the value stored in VAL # would be "bar" _MATCH EXEC "foo(.*)" VAL # call command _EXEC ./your_external_program stop END
There is a global and local exec command. First the external program is called with the parameter "start" and on clients end the external command is called with the parameter "stop".
_MATCH, _GREP and _EXPECT knows the body EXEC and operate on the commands output on stdout.
Attention, one could doing this task with two global EXEC commands but this will not work. Because the CLIENT body is running in the background, the last exec command would not wait for the clients end.
=## Pipe Socket Stream to External Command=
This is useful to validate for example content of a server.
CLIENT _REQ localhost 8080 __GET / HTTP/1.1 __Host: localhost __ _EXEC| cat > yourfile _WAIT END SERVER 8080 _RES _WAIT __HTTP/1.1 200 OK __Host: localhost __Content-Length: AUTO __ __Stream me to yourfile :) END
Stupid example to show how to pipe socket stream to a external command like "cat". yourfile contains after running this test script: "Stream me to yourfile :)".
The head request has as a response a Content-Length but not content.
CLIENT _REQ localhost 8080 __HEAD /your/path HTTP/1.1 __Host: $YOUR_HOST __User-Agent: mozilla __ _WAIT 0 END
To cope witht the a Content-Length header but no content in the response, we told httest to wait for the headers and zero bytes in the body.
Start on your host you want to use to generate load the following command:
htremote -r -p 8080 -e "/path/to/httest -Ssn"
httest -Ssn runs the script absolut silent. The -r option tels htremote to restart after httest has terminated, -p defines on which port this agent is listening and -e specifies the command to exectute in our case it is httest.
Your script may look like that
PERF:RAMPUP 100 60000 PERF:DISTRIBUTE your.host:8080 PERF:DISTRIBUTE your.other.host:8080 PERF:DISTRIBUTE your.last.host:8080 CLIENT 2000 _AUTO_CLOSE on _LOOP 600000 [ms] _REQ your.web.host 80 __GET /index.html HTTP/1.1 __Host: your.web.host __User-Agent: httest-2.2.5 __ _WAIT _END END
The 2000 CLIENTs will be distribute on the host the script is running and on your.host, your.other.host and your.last.host. In this case on every host there are 500 CLIENTs running. The script will wait until all host are finished with their job.
With the new PERF:RAMPUP you can define how many Clients per interval should be started. Even _LOOP can now handle time instead of count. In this example a client do run vor 10 minutes. Every minute we start a 100 clients.
Httest 2.1.6 do have a Lua module. You need Lua library installed on your System or download and build it directly from [http://www.lua.org/]. And Httest 2.1.8 do now also have the possiblity to call httest commands in a Lua block.
Enable Lua Support in httest do as follow
configure --enable-lua-module make all
If you have the Lua library build by your self do
configure --enable-lua-module --with-lua=/your/lua/src make all
We first start with simplest possible
BLOCK:LUA myTestLuaBlock print("hello world") END CLIENT myTestLuaBlock END
_CALL is optional and not needed any more.
To bring Variables from httest to a Lua block and to get results from Lua, you can use the wellknown block signature technics in httest.
BLOCK:LUA myTestLuaBlock param fooparam anyparm : myRet fooRet for i = 1,10 do print(param .." "..fooparam..";"..anyparm.." end") end return "any string", "another string" END CLIENT myTestLuaBlock "hello" world "bla bla bla" stuffFromLua moreReturnStuff _DEBUG $stuffFromLua $moreReturnStuff END
Also with httest 2.1.8 it is possible to call a httest snipplet within a Lua block
BLOCK:LUA myTestLuaBlock print(htt.version()) htt.interpret([[ _REQ localhost 8080 __GET / HTTP/1.1 __Host: localhost __ _MATCH "foo=(.*)" FOO _WAIT ]]) myFooInLua = htt.getvar("FOO"); END CLIENT myTestLuaBlock END
With httest 2.1.11 you can get transport object from the current connection, to read/write in Lua.
BLOCK:LUA MyTestLuaBlock t = htt.get_transport() print() buf = t:read(8192) print("\n--------------") print(buf.."--------------") t:write("GET / HTTP/1.1\r\n"); t:write("\r\n"); print() END CLIENT _REQ localhost 8080 __GET / HTTP/1.1 __Host: httest __ _FLUSH MyTestLuaBlock END
There is a primitiv macro mechanisme implemented to handle complicated stuff outside the test.
BLOCK foo _REQ localhost 8080 __POST $1 HTTP/1.1 __Content-Length: AUTO __ __This blocks name is $0 _WAIT END CLIENT _CALL foo /path/to/your/file END
The macro can handle parameter. It is same like in shell commands. The parameter $0 is the block name, $1 ... $x are the parameters.
Do mutual authentication.
CLIENT _REQ localhost SSL:8080 client.cert.pem client.key.pem ca.cert.pem _VERIFY_PEER __GET / HTTP/1.1 __Host: localhost __ _WAIT END SERVER 8080 _CERT server.cert.pem server.key.pem ca.cert.pem _RES _WAIT _VERIFY_PEER __HTTP/1.1 200 OK __Content-Length: AUTO __ __Hello World END
The key point is the command _VERIFY_PEER, which is integrated in the version 0.12.1 and higher.
The client do verify the server certificate and the server do request and validate the client certificate.
The certificates must all be from the same CA in this example.
It could be useful to call an external program to tranform a String, for example a base64 transformation and pipe the output back to the socket stream.
CLIENT _REQ localhost 8080 _-GET _PIPE _EXEC echo /foo/bar __ HTTP/1.1 __Host: localhost __ _EXPECT . "HTTP/1.1 200 OK" _WAIT END SERVER 8080 _RES _EXPECT . "/foo/bar" _WAIT __HTTP/1.1 200 OK __Host: localhost __Content-Length: AUTO __ __Answer END
This is somehow a stupid example you could short write the GET request with a simple
__GET /foo/bar HTTP/1.1
But it demonstrate how to pipe a external commands out put, i.e. "echo /foo/bar", into an open socket stream.
Mostly we want to stream a file output to the caller.
CLIENT _REQ localhost 8080 __GET / HTTP/1.1 __Host: localhost __ _WAIT END SERVER 8080 _RES _WAIT __HTTP/1.1 200 OK __Host: localhost __Transfer-Encoding: chunked _FLUSH _PIPE CHUNKED 30 _EXEC echo blabla bla bla bla bla bla bla bla bla bla bla _CHUNKED __ END
The EXEC echo blabla is piped into socket stream with chunks of 30 bytes.
There are many Fat Clients which do post with transfer-encoding: chunked. A simple example will demonstrat the chunked support with in httest.
CLIENT _REQ localhost 8080 __POST / HTTP/1.1 __Host: localhost __Transfer-Encoding: chunked _FLUSH __Some data _CHUNKED __More data _CHUNKED __Last data _CHUNKED _CHUNKED __ _EXPECT . "HTTP/1.1 200 OK" _WAIT END SERVER 8080 _RES _EXPECT . "Some data" _EXPECT . "More data" _EXPECT . "Last data" _WAIT __HTTP/1.1 200 OK __Host: localhost __Transfer-Encoding: chunked _FLUSH __Answer _CHUNKED _CHUNKED __ END
The command _CHUNKED to \r\n<chunk_size_in_hex>\r\n. Every command do store the data in a line cache. The _FLUSH command do send all lines in the line cache, in this case all headers. The command _CHUNKED to calculate the size in the line cache, with all lines not allready flushed.
The empty line between the headers and the body is done with the first _CHUNKED command. The Last 0 Chunkded could also be done with CHUNKED with a following newline __.
There could be a need to read line by line manually. This could be done with the following construct:
CLIENT _REQ localhost 8080 __GET /your/path HTTP/1.1 __Host: localhost __User-Agent: mozilla __ _SOCKET _EXPECT . "HTTP/1.1 200 OK" _READLINE _EXPECT . "Content-Type: text/plain _READLINE _MATCH body "Content-Length: (.*)" CONTENT_LEN _END SOCKET END
The _SOCKET ... _END SOCKET do open a buffered socket for various _READLINE and _RECV commands. Every _READLINE can handle one or more expect and match commands. In our example we first expect "HTTP/1.1 200 OK", next line must contain the Content-Type and the third line must containt the Content-Length which is then stored in the variable $CONTENT_LEN
SERVER _RES _WAIT _TIME TIME _STRFTIME $TIME "S GMT" DATE __HTTP/1.1 200 OK __Date: $DATE __ END
The Variable TIME is formated with the format string"S GMT" and stored in the variable DATE. See man page for strftime for format details.
CLIENT _URLENC "foo bar&blafasel" VAR _REQ localhost 8080 __GET /your/path?param=$VAR HTTP/1.1 __Host: localhost __User-Agent: mozilla __ _WAIT END
The string foo bar&blafasel will be stored URL encoded in the variable VAR as "foo+bar%26blafasel"
Httest 2.2.1 do integrate the power of gnoms libxml2 XPath support.
CLIENT _REQ www.google.com 80 __GET / HTTP/1.1 __Host: www.google.com __ _WAIT BUF _HTML:PARSE VAR(BUF) _HTML:XPATH /html/body/a result _DEBUG $result END