ab()
  initialize();
  test();

test()
  foreach (concurrent request slot) {
    start a new connection; start_connect()
  }
  while (there are more requests to be sent) {
    foreach (concurrent request slot) {
      if (this slot needs no more resources) continue with next slot;
      if (an exception occurred)
        -> mark as failed and start a new connection; start_connect()
      else if (there is data available to read on this slot)
        -> read the data; read_connection()
      else if (this slot is ready to write to)
        -> send a new request; write_request()
    }
  }

start_connect()
  if (a socket error occurred)
    -> close connection and return; close_connection()
  open a socket connection;
  if (socket connection succeeded)
    -> return;
  else if (this is the 10th failure)
    -> close_connection(); return;
  else
    -> try again; start_connect()

close_connection()
  save request-response statistics; store_regression_data()
  close the socket connection;
  schedule and start the next request; schedule_next_request(), start_connect()

read_connection()
  read a chunk of data from a socket;
  if (all data has been read OR an error occurred)
    -> close_connection(); return;
  if (there is no data available to read at the moment: read returned EAGAIN)
    -> return;
  if (we have not received the full http header)
    if (this chunk of data contains end of http header: "\r\n\r\n" or "\n\n")
      -> check response header for Keep-Alive and mark accordingly;
    else
      -> return;
  if (response indicated keepalive and we've received entire response)
    -> store_regression_data();
    -> schedule_next_request();
    if (next scheduled request is keepalive and the hostname/port are same)
      -> write_request();
    else
      -> close the connection and start a fresh one: start_connect();

write_request()
  reset_request();
  write full http request to socket;

reset_request()
  build http request according to GET/HEAD/POST settings;
