"Fossies" - the Fresh Open Source Software Archive

Member "httperf-0.9.0/src/gen/session.c" (7 Apr 2007, 11792 Bytes) of package /linux/www/old/httperf-0.9.0.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "session.c" see the Fossies "Dox" file reference documentation.

    1 /*
    2     httperf -- a tool for measuring web server performance
    3     Copyright 2000-2007 Hewlett-Packard Company and Contributors listed in
    4     AUTHORS file. Originally contributed by David Mosberger-Tang
    5 
    6     This file is part of httperf, a web server performance measurment
    7     tool.
    8 
    9     This program is free software; you can redistribute it and/or
   10     modify it under the terms of the GNU General Public License as
   11     published by the Free Software Foundation; either version 2 of the
   12     License, or (at your option) any later version.
   13     
   14     In addition, as a special exception, the copyright holders give
   15     permission to link the code of this work with the OpenSSL project's
   16     "OpenSSL" library (or with modified versions of it that use the same
   17     license as the "OpenSSL" library), and distribute linked combinations
   18     including the two.  You must obey the GNU General Public License in
   19     all respects for all of the code used other than "OpenSSL".  If you
   20     modify this file, you may extend this exception to your version of the
   21     file, but you are not obligated to do so.  If you do not wish to do
   22     so, delete this exception statement from your version.
   23 
   24     This program is distributed in the hope that it will be useful,
   25     but WITHOUT ANY WARRANTY; without even the implied warranty of
   26     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   27     General Public License for more details.
   28 
   29     You should have received a copy of the GNU General Public License
   30     along with this program; if not, write to the Free Software
   31     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  
   32     02110-1301, USA
   33 */
   34 
   35 /* A session consists of a number of calls.  Workload generators such
   36    as wsess and wsesslog determine when and how to issue calls.  This
   37    module is responsible for the actual mechanics of issuing the
   38    calls.  This includes creating and managing connections as
   39    necessary.  Connection management can be controlled by command-line
   40    options --max-piped-calls=Np and --max-connections=Nc.  This module
   41    creates up to Nc concurrent connections to dispatch calls.  On each
   42    connection, up to Np pipelined requests can be issued.  When no
   43    more calls can be issued, this module waits until some of the
   44    pending calls complete.
   45 
   46    Note that HTTP/1.1 allows a server to close a connection pretty
   47    much any time it feels like.  This means that a session may fail
   48    (be closed) while there pipelined calls are pending.  In such a
   49    case, this module takes care of creating a new connection and
   50    re-issuing the calls that were pending on the failed connection.
   51 
   52    A session is considered to fail if:
   53 
   54    (a) any operation exceeds the timeout parameters, or
   55 
   56    (b) a connection closes on us before we received at least one
   57        reply, or
   58 
   59    (c) param.failure_status is non-zero and the reply status of a call
   60        matches this failure status.
   61    */
   62 
   63 #include <assert.h>
   64 #include <stdio.h>
   65 #include <stdlib.h>
   66 #include <string.h>
   67 
   68 #include <httperf.h>
   69 #include <call.h>
   70 #include <conn.h>
   71 #include <core.h>
   72 #include <event.h>
   73 #include <sess.h>
   74 #include <session.h>
   75 
   76 #define MAX_CONN         4  /* max # of connections per session */
   77 #define MAX_PIPED       32  /* max # of calls that can be piped */
   78 
   79 #define SESS_PRIVATE_DATA(c)                        \
   80   ((Sess_Private_Data *) ((char *)(c) + sess_private_data_offset))
   81 
   82 #define CONN_PRIVATE_DATA(c)                        \
   83   ((Conn_Private_Data *) ((char *)(c) + conn_private_data_offset))
   84 
   85 #define CALL_PRIVATE_DATA(c)                        \
   86   ((Call_Private_Data *) ((char *)(c) + call_private_data_offset))
   87 
   88 typedef struct Sess_Private_Data
   89   {
   90     struct Conn_Info
   91       {
   92     Conn *conn;     /* connection or NULL */
   93     u_int is_connected : 1; /* is connection ready for use? */
   94     u_int is_successful : 1; /* got at least one reply on this conn? */
   95 
   96     /* Ring-buffer of pending calls: */
   97     u_int num_pending;  /* # of calls pending */
   98     u_int num_sent;     /* # of calls sent so far */
   99     u_int rd;       /* first pending call */
  100     u_int wr;       /* where to insert next call */
  101     Call *call[MAX_PIPED];
  102       }
  103     conn_info[MAX_CONN];
  104   }
  105 Sess_Private_Data;
  106 
  107 typedef struct Conn_Private_Data
  108   {
  109     Sess *sess;
  110     struct Conn_Info *ci;   /* pointer to relevant conn-info */
  111   }
  112 Conn_Private_Data;
  113 
  114 typedef struct Call_Private_Data
  115   {
  116     Sess *sess;
  117   }
  118 Call_Private_Data;
  119 
  120 static size_t sess_private_data_offset = -1;
  121 static size_t conn_private_data_offset = -1;
  122 static size_t call_private_data_offset = -1;
  123 static size_t max_qlen;
  124 
  125 static void
  126 create_conn (Sess *sess, struct Conn_Info *ci)
  127 {
  128   Conn_Private_Data *cpriv;
  129 
  130   /* No connection yet (or anymore).  Create a new connection.  Note
  131      that CI->CONN is NOT reference-counted.  This is again to avoid
  132      introducing recursive dependencies (see also comment regarding
  133      member CONN in call.h). */
  134   ci->conn = conn_new ();
  135   if (!ci->conn)
  136     {
  137       sess_failure (sess);
  138       return;
  139     }
  140   cpriv = CONN_PRIVATE_DATA (ci->conn);
  141   cpriv->sess = sess;
  142   cpriv->ci = ci;
  143 
  144   ci->is_connected = 0;
  145   ci->is_successful = 0;
  146   ci->num_sent = 0;     /* (re-)send all pending calls */
  147 
  148 #ifdef HAVE_SSL
  149   if (param.ssl_reuse && ci->conn->ssl && sess->ssl)
  150     {
  151       if (DBG > 0)
  152     fprintf (stderr, "create_conn: reusing SSL session %p\n",
  153          (void *) sess->ssl);
  154       SSL_copy_session_id (ci->conn->ssl, sess->ssl);
  155     }
  156 #endif
  157 
  158   if (core_connect (ci->conn) < 0)
  159     sess_failure (sess);
  160 }
  161 
  162 static void
  163 send_calls (Sess *sess, struct Conn_Info *ci)
  164 {
  165   u_int rd;
  166   int i;
  167 
  168   if (!ci->conn)
  169     {
  170       create_conn (sess, ci);
  171       return;
  172     }
  173 
  174   if (!ci->is_connected)
  175     /* wait until connection is connected (or has failed)  */
  176     return;
  177 
  178   rd = (ci->rd + ci->num_sent) % MAX_PIPED;
  179 
  180   for (i = ci->num_sent; i < ci->num_pending; ++i)
  181     {
  182       core_send (ci->conn, ci->call[rd]);
  183       ++ci->num_sent;
  184       rd = (rd + 1) % MAX_PIPED;
  185     }
  186 }
  187 
  188 static void
  189 sess_destroyed (Event_Type et, Object *obj, Any_Type regarg, Any_Type callarg)
  190 {
  191   Sess_Private_Data *priv;
  192   struct Conn_Info *ci;
  193   Sess *sess;
  194   int i, j, rd;
  195 
  196   assert (et == EV_SESS_DESTROYED && object_is_sess (obj));
  197   sess = (Sess *) obj;
  198   priv = SESS_PRIVATE_DATA (sess);
  199 
  200   for (i = 0; i < param.max_conns; ++i)
  201     {
  202       ci = priv->conn_info + i;
  203 
  204       if (ci->conn)
  205     core_close (ci->conn);
  206 
  207       rd = ci->rd;
  208       for (j = 0; j < ci->num_pending; ++j)
  209     {
  210       call_dec_ref (ci->call[rd]);
  211       rd = (rd + 1) % MAX_PIPED;
  212     }
  213     }
  214 }
  215 
  216 static void
  217 conn_connected (Event_Type et, Object *obj, Any_Type regarg, Any_Type callarg)
  218 {
  219   Conn_Private_Data *cpriv;
  220   struct Conn_Info *ci;
  221   Sess *sess;
  222   Conn *conn;
  223 
  224   assert (et == EV_CONN_CONNECTED && object_is_conn (obj));
  225 
  226   conn = (Conn *) obj;
  227   cpriv = CONN_PRIVATE_DATA (conn);
  228   sess = cpriv->sess;
  229   ci = cpriv->ci;
  230 
  231   ci->is_connected = 1;
  232 
  233 #ifdef HAVE_SSL
  234   if (param.ssl_reuse && !sess->ssl && ci->conn->ssl)
  235     {
  236       sess->ssl = SSL_dup (ci->conn->ssl);
  237       if (DBG > 0)
  238     fprintf (stderr, "create_conn: cached SSL session %p as %p\n",
  239          (void *) ci->conn->ssl, (void *) sess->ssl);
  240     }
  241 #endif /* HAVE_SSL */
  242 
  243   send_calls (sess, ci);
  244 }
  245 
  246 static void
  247 conn_failed (Event_Type et, Object *obj, Any_Type regarg, Any_Type callarg)
  248 {
  249   Conn_Private_Data *cpriv;
  250   struct Conn_Info *ci;
  251   Conn *conn;
  252   Sess *sess;
  253 
  254   assert (et == EV_CONN_FAILED && object_is_conn (obj));
  255   conn = (Conn *) obj;
  256   cpriv = CONN_PRIVATE_DATA (conn);
  257   sess = cpriv->sess;
  258   ci = cpriv->ci;
  259 
  260   if (ci->is_successful || param.retry_on_failure)
  261     /* try to create a new connection so we can issue the remaining
  262        calls. */
  263     create_conn (sess, ci);
  264   else
  265     /* The connection failed before we got even one reply, so declare
  266        the session as dead... */
  267     sess_failure (cpriv->sess);
  268 }
  269 
  270 static void
  271 conn_timeout (Event_Type et, Object *obj, Any_Type regarg, Any_Type callarg)
  272 {
  273   Conn_Private_Data *cpriv;
  274   Conn *conn;
  275 
  276   /* doh, this session is dead now... */
  277 
  278   assert (et == EV_CONN_TIMEOUT && object_is_conn (obj));
  279   conn = (Conn *) obj;
  280   cpriv = CONN_PRIVATE_DATA (conn);
  281 
  282   sess_failure (cpriv->sess);
  283 }
  284 
  285 static void
  286 call_done (Event_Type et, Object *obj, Any_Type regarg, Any_Type callarg)
  287 {
  288   Conn_Private_Data *cpriv;
  289   struct Conn_Info *ci;
  290   Sess *sess;
  291   Conn *conn;
  292   Call *call;
  293 
  294   assert (et == EV_CALL_RECV_STOP && object_is_call (obj));
  295   call = (Call *) obj;
  296   conn = call->conn;
  297   cpriv = CONN_PRIVATE_DATA (conn);
  298   sess = cpriv->sess;
  299   ci = cpriv->ci;
  300 
  301   ci->is_successful = 1;    /* conn has received at least one reply */
  302 
  303   /* remove the call from the conn_info structure */
  304   assert (ci->call[ci->rd] == call && ci->num_pending > 0 && ci->num_sent > 0);
  305   ci->call[ci->rd] = 0;
  306   ci->rd = (ci->rd + 1) % MAX_PIPED;
  307   --ci->num_pending;
  308   --ci->num_sent;
  309 
  310   /* if the reply status matches the failure status, the session has
  311      failed */
  312   if (param.failure_status && call->reply.status == param.failure_status)
  313     {
  314       if (param.retry_on_failure)
  315     session_issue_call (sess, call);
  316       else
  317     sess_failure (sess);
  318     }
  319 
  320   call_dec_ref (call);
  321 
  322   if (param.http_version < 0x10001)
  323     {
  324       /* Rather than waiting for the connection to close on us, we
  325      close it pro-actively (this is what a pre-1.1 browser would
  326      do.  */
  327       core_close (ci->conn);
  328       ci->conn = 0;
  329     }
  330 }
  331 
  332 void
  333 session_init (void)
  334 {
  335   Any_Type arg;
  336 
  337   if (!param.max_conns)
  338     param.max_conns = MAX_CONN;
  339 
  340   if (!param.max_piped)
  341     {
  342       if (param.http_version >= 0x10001)
  343     param.max_piped = MAX_PIPED;
  344       else
  345     /* no pipelining before HTTP/1.1... */
  346     param.max_piped = 1;
  347     }
  348 
  349   if (param.max_conns > MAX_CONN)
  350     {
  351       fprintf (stderr, "%s.session_init: --max-conns must be <= %u\n",
  352            prog_name, MAX_CONN);
  353       exit (1);
  354     }
  355   if (param.max_piped > MAX_PIPED)
  356     {
  357       fprintf (stderr, "%s.session_init: --max-piped-calls must be <= %u\n",
  358            prog_name, MAX_PIPED);
  359       exit (1);
  360     }
  361 
  362   max_qlen = param.max_conns * param.max_piped;
  363 
  364   sess_private_data_offset = object_expand (OBJ_SESS,
  365                         sizeof (Sess_Private_Data));
  366   conn_private_data_offset = object_expand (OBJ_CONN,
  367                         sizeof (Conn_Private_Data));
  368   call_private_data_offset = object_expand (OBJ_CALL,
  369                         sizeof (Call_Private_Data));
  370 
  371   arg.l = 0;
  372   event_register_handler (EV_SESS_DESTROYED, sess_destroyed, arg);
  373 
  374   event_register_handler (EV_CONN_CONNECTED, conn_connected, arg);
  375   event_register_handler (EV_CONN_FAILED, conn_failed, arg);
  376   event_register_handler (EV_CONN_TIMEOUT, conn_timeout, arg);
  377 
  378   event_register_handler (EV_CALL_RECV_STOP, call_done, arg);
  379 }
  380 
  381 size_t
  382 session_max_qlen (Sess *sess)
  383 {
  384   return max_qlen;
  385 }
  386 
  387 size_t
  388 session_current_qlen (Sess *sess)
  389 {
  390   Sess_Private_Data *priv;
  391   size_t num_pending = 0;
  392   int i;
  393 
  394   priv = SESS_PRIVATE_DATA (sess);
  395 
  396   for (i = 0; i < param.max_conns; ++i)
  397     num_pending += priv->conn_info[i].num_pending;
  398 
  399   return num_pending;
  400 }
  401 
  402 int
  403 session_issue_call (Sess *sess, Call *call)
  404 {
  405   Call_Private_Data *cpriv;
  406   Sess_Private_Data *priv;
  407   struct Conn_Info *ci;
  408   int i;
  409 
  410   priv = SESS_PRIVATE_DATA (sess);
  411 
  412   cpriv = CALL_PRIVATE_DATA (call);
  413   cpriv->sess = sess;
  414 
  415   for (i = 0; i < param.max_conns; ++i)
  416     {
  417       ci = priv->conn_info + i;
  418       if (ci->num_pending < param.max_piped)
  419     {
  420       ++ci->num_pending;
  421       ci->call[ci->wr] = call;
  422       call_inc_ref (call);
  423       ci->wr = (ci->wr + 1) % MAX_PIPED;
  424       send_calls (sess, ci);
  425       return 0;
  426     }
  427     }
  428   fprintf (stderr, "%s.session_issue_call: too many calls pending!\n"
  429        "\tIncrease --max-connections and/or --max-piped-calls.\n",
  430        prog_name);
  431   exit (1);
  432 }
  433 
  434 Sess *
  435 session_get_sess_from_conn (Conn *conn)
  436 {
  437   assert (object_is_conn (conn));
  438   return CONN_PRIVATE_DATA (conn)->sess;
  439 }
  440 
  441 Sess *
  442 session_get_sess_from_call (Call *call)
  443 {
  444   assert (object_is_call (call));
  445   return CALL_PRIVATE_DATA (call)->sess;
  446 }