• Main Page
  • Related Pages
  • Namespaces
  • Data Structures
  • Files
  • File List
  • Globals

HttpClientSession.cc

Go to the documentation of this file.
00001 /** @file
00002 
00003   A brief file description
00004 
00005   @section license License
00006 
00007   Licensed to the Apache Software Foundation (ASF) under one
00008   or more contributor license agreements.  See the NOTICE file
00009   distributed with this work for additional information
00010   regarding copyright ownership.  The ASF licenses this file
00011   to you under the Apache License, Version 2.0 (the
00012   "License"); you may not use this file except in compliance
00013   with the License.  You may obtain a copy of the License at
00014 
00015       http://www.apache.org/licenses/LICENSE-2.0
00016 
00017   Unless required by applicable law or agreed to in writing, software
00018   distributed under the License is distributed on an "AS IS" BASIS,
00019   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00020   See the License for the specific language governing permissions and
00021   limitations under the License.
00022  */
00023 
00024 /****************************************************************************
00025 
00026    HttpClientSession.cc
00027 
00028    Description:
00029 
00030 
00031  ****************************************************************************/
00032 
00033 #include "ink_config.h"
00034 #include "Allocator.h"
00035 #include "HttpClientSession.h"
00036 #include "HttpSM.h"
00037 #include "HttpDebugNames.h"
00038 #include "HttpServerSession.h"
00039 #include "Plugin.h"
00040 
00041 #define DebugHttpSsn(fmt, ...) DebugSsn(this, "http_cs", fmt, __VA_ARGS__)
00042 
00043 #define STATE_ENTER(state_name, event, vio) do { \
00044     /*ink_assert (magic == HTTP_SM_MAGIC_ALIVE);  REMEMBER (event, NULL, reentrancy_count); */ \
00045   DebugHttpSsn("[%" PRId64 "] [%s, %s]", con_id, #state_name, HttpDebugNames::get_event_name(event)); \
00046 } while(0)
00047 
00048 enum
00049 {
00050   HTTP_CS_MAGIC_ALIVE = 0x0123F00D,
00051   HTTP_CS_MAGIC_DEAD = 0xDEADF00D
00052 };
00053 
00054 // We have debugging list that we can use to find stuck
00055 //  client sessions
00056 DLL<HttpClientSession> debug_cs_list;
00057 ink_mutex debug_cs_list_mutex;
00058 
00059 ClassAllocator<HttpClientSession> httpClientSessionAllocator("httpClientSessionAllocator");
00060 
00061 HttpClientSession::HttpClientSession()
00062   : con_id(0), client_vc(NULL), magic(HTTP_CS_MAGIC_DEAD),
00063     transact_count(0), tcp_init_cwnd_set(false),
00064     half_close(false), conn_decrease(false), bound_ss(NULL),
00065     read_buffer(NULL), current_reader(NULL), read_state(HCS_INIT),
00066     ka_vio(NULL), slave_ka_vio(NULL),
00067     outbound_port(0), f_outbound_transparent(false),
00068     host_res_style(HOST_RES_IPV4), acl_record(NULL),
00069     m_active(false)
00070 {
00071 }
00072 
00073 void
00074 HttpClientSession::destroy()
00075 {
00076   DebugHttpSsn("[%" PRId64 "] session destroy", con_id);
00077 
00078   ink_release_assert(client_vc == NULL);
00079   ink_release_assert(bound_ss == NULL);
00080   ink_assert(read_buffer);
00081 
00082   magic = HTTP_CS_MAGIC_DEAD;
00083   if (read_buffer) {
00084     free_MIOBuffer(read_buffer);
00085     read_buffer = NULL;
00086   }
00087 
00088 #ifdef USE_HTTP_DEBUG_LISTS
00089   ink_mutex_acquire(&debug_cs_list_mutex);
00090   debug_cs_list.remove(this, this->debug_link);
00091   ink_mutex_release(&debug_cs_list_mutex);
00092 #endif
00093 
00094   if (conn_decrease) {
00095     HTTP_DECREMENT_DYN_STAT(http_current_client_connections_stat);
00096     conn_decrease = false;
00097   }
00098 
00099   ProxyClientSession::cleanup();
00100   THREAD_FREE(this, httpClientSessionAllocator, this_thread());
00101 }
00102 
00103 void
00104 HttpClientSession::ssn_hook_append(TSHttpHookID id, INKContInternal * cont)
00105 {
00106   ProxyClientSession::ssn_hook_append(id, cont);
00107   if (current_reader) {
00108     current_reader->hooks_set = 1;
00109   }
00110 }
00111 
00112 void
00113 HttpClientSession::ssn_hook_prepend(TSHttpHookID id, INKContInternal * cont)
00114 {
00115   ProxyClientSession::ssn_hook_prepend(id, cont);
00116   if (current_reader) {
00117     current_reader->hooks_set = 1;
00118   }
00119 }
00120 
00121 void
00122 HttpClientSession::new_transaction()
00123 {
00124   ink_assert(current_reader == NULL);
00125   PluginIdentity* pi = dynamic_cast<PluginIdentity*>(client_vc);
00126 
00127   read_state = HCS_ACTIVE_READER;
00128   current_reader = HttpSM::allocate();
00129   current_reader->init();
00130   transact_count++;
00131   DebugHttpSsn("[%" PRId64 "] Starting transaction %d using sm [%" PRId64 "]", con_id, transact_count, current_reader->sm_id);
00132 
00133   current_reader->attach_client_session(this, sm_reader);
00134   if (pi) {
00135     // it's a plugin VC of some sort with identify information.
00136     // copy it to the SM.
00137     current_reader->plugin_tag = pi->getPluginTag();
00138     current_reader->plugin_id = pi->getPluginId();
00139   }
00140 }
00141 
00142 void
00143 HttpClientSession::new_connection(NetVConnection * new_vc, MIOBuffer * iobuf, IOBufferReader * reader, bool backdoor)
00144 {
00145   ink_assert(new_vc != NULL);
00146   ink_assert(client_vc == NULL);
00147   client_vc = new_vc;
00148   magic = HTTP_CS_MAGIC_ALIVE;
00149   mutex = new_vc->mutex;
00150   MUTEX_TRY_LOCK(lock, mutex, this_ethread());
00151   ink_assert(!!lock);
00152 
00153   // Disable hooks for backdoor connections.
00154   this->hooks_on = !backdoor;
00155 
00156   // Unique client session identifier.
00157   con_id = ProxyClientSession::next_connection_id();
00158 
00159   HTTP_INCREMENT_DYN_STAT(http_current_client_connections_stat);
00160   conn_decrease = true;
00161   HTTP_INCREMENT_DYN_STAT(http_total_client_connections_stat);
00162   if (static_cast<HttpProxyPort::TransportType>(new_vc->attributes) == HttpProxyPort::TRANSPORT_SSL) {
00163     HTTP_INCREMENT_DYN_STAT(https_total_client_connections_stat);
00164   }
00165 
00166   /* inbound requests stat should be incremented here, not after the
00167    * header has been read */
00168   HTTP_INCREMENT_DYN_STAT(http_total_incoming_connections_stat);
00169 
00170   // check what type of socket address we just accepted
00171   // by looking at the address family value of sockaddr_storage
00172   // and logging to stat system
00173   switch(new_vc->get_remote_addr()->sa_family) {
00174     case AF_INET:
00175       HTTP_INCREMENT_DYN_STAT(http_total_client_connections_ipv4_stat);
00176     break;
00177     case AF_INET6:
00178       HTTP_INCREMENT_DYN_STAT(http_total_client_connections_ipv6_stat);
00179     break;
00180     default:
00181       // don't do anything if the address family is not ipv4 or ipv6
00182       // (there are many other address families in <sys/socket.h>
00183       // but we don't have a need to report on all the others today)
00184     break;
00185   }
00186 
00187 #ifdef USE_HTTP_DEBUG_LISTS
00188   ink_mutex_acquire(&debug_cs_list_mutex);
00189   debug_cs_list.push(this, this->debug_link);
00190   ink_mutex_release(&debug_cs_list_mutex);
00191 #endif
00192 
00193   DebugHttpSsn("[%" PRId64 "] session born, netvc %p", con_id, new_vc);
00194 
00195   read_buffer = iobuf ? iobuf : new_MIOBuffer(HTTP_HEADER_BUFFER_SIZE_INDEX);
00196   sm_reader = reader ? reader : read_buffer->alloc_reader();
00197 
00198   // INKqa11186: Use a local pointer to the mutex as
00199   // when we return from do_api_callout, the ClientSession may
00200   // have already been deallocated.
00201   EThread *ethis = this_ethread();
00202   Ptr<ProxyMutex> lmutex = this->mutex;
00203   MUTEX_TAKE_LOCK(lmutex, ethis);
00204   do_api_callout(TS_HTTP_SSN_START_HOOK);
00205   MUTEX_UNTAKE_LOCK(lmutex, ethis);
00206   lmutex.clear();
00207 }
00208 
00209 VIO *
00210 HttpClientSession::do_io_read(Continuation * c, int64_t nbytes, MIOBuffer * buf)
00211 {
00212   return client_vc->do_io_read(c, nbytes, buf);
00213 }
00214 
00215 VIO *
00216 HttpClientSession::do_io_write(Continuation * c, int64_t nbytes, IOBufferReader * buf, bool owner)
00217 {
00218   /* conditionally set the tcp initial congestion window
00219      before our first write. */
00220   DebugHttpSsn("tcp_init_cwnd_set %d\n", (int)tcp_init_cwnd_set);
00221   if(!tcp_init_cwnd_set) {
00222     tcp_init_cwnd_set = true;
00223     set_tcp_init_cwnd();
00224   }
00225   return client_vc->do_io_write(c, nbytes, buf, owner);
00226 }
00227 
00228 void
00229 HttpClientSession::set_tcp_init_cwnd()
00230 {
00231   int desired_tcp_init_cwnd = current_reader->t_state.txn_conf->server_tcp_init_cwnd;
00232   DebugHttpSsn("desired TCP congestion window is %d\n", desired_tcp_init_cwnd);
00233   if(desired_tcp_init_cwnd == 0) return;
00234   if(get_netvc()->set_tcp_init_cwnd(desired_tcp_init_cwnd) != 0)
00235     DebugHttpSsn("set_tcp_init_cwnd(%d) failed", desired_tcp_init_cwnd);
00236 }
00237 
00238 void
00239 HttpClientSession::do_io_shutdown(ShutdownHowTo_t howto)
00240 {
00241   client_vc->do_io_shutdown(howto);
00242 }
00243 
00244 void
00245 HttpClientSession::do_io_close(int alerrno)
00246 {
00247 
00248   if (read_state == HCS_ACTIVE_READER) {
00249     HTTP_DECREMENT_DYN_STAT(http_current_client_transactions_stat);
00250     if (m_active) {
00251       m_active = false;
00252       HTTP_DECREMENT_DYN_STAT(http_current_active_client_connections_stat);
00253     }
00254   }
00255 
00256   // Prevent double closing
00257   ink_release_assert(read_state != HCS_CLOSED);
00258 
00259   // If we have an attached server session, release
00260   //   it back to our shared pool
00261   if (bound_ss) {
00262     bound_ss->release();
00263     bound_ss = NULL;
00264     slave_ka_vio = NULL;
00265   }
00266 
00267   if (half_close) {
00268     read_state = HCS_HALF_CLOSED;
00269     SET_HANDLER(&HttpClientSession::state_wait_for_close);
00270     DebugHttpSsn("[%" PRId64 "] session half close", con_id);
00271 
00272     // We want the client to know that that we're finished
00273     //  writing.  The write shutdown accomplishes this.  Unfortuantely,
00274     //  the IO Core semantics don't stop us from getting events
00275     //  on the write side of the connection like timeouts so we
00276     //  need to zero out the write of the continuation with
00277     //  the do_io_write() call (INKqa05309)
00278     client_vc->do_io_shutdown(IO_SHUTDOWN_WRITE);
00279 
00280     ka_vio = client_vc->do_io_read(this, INT64_MAX, read_buffer);
00281     ink_assert(slave_ka_vio != ka_vio);
00282 
00283     // [bug 2610799] Drain any data read.
00284     // If the buffer is full and the client writes again, we will not receive a
00285     // READ_READY event.
00286     sm_reader->consume(sm_reader->read_avail());
00287 
00288     // Set the active timeout to the same as the inactive time so
00289     //   that this connection does not hang around forever if
00290     //   the ua hasn't closed
00291     client_vc->set_active_timeout(HRTIME_SECONDS(current_reader->t_state.txn_conf->keep_alive_no_activity_timeout_out));
00292   } else {
00293     read_state = HCS_CLOSED;
00294     client_vc->do_io_close(alerrno);
00295     DebugHttpSsn("[%" PRId64 "] session closed", con_id);
00296     client_vc = NULL;
00297     HTTP_SUM_DYN_STAT(http_transactions_per_client_con, transact_count);
00298     HTTP_DECREMENT_DYN_STAT(http_current_client_connections_stat);
00299     conn_decrease = false;
00300     do_api_callout(TS_HTTP_SSN_CLOSE_HOOK);
00301   }
00302 }
00303 
00304 int
00305 HttpClientSession::state_wait_for_close(int event, void *data)
00306 {
00307 
00308   STATE_ENTER(&HttpClientSession::state_wait_for_close, event, data);
00309 
00310   ink_assert(data == ka_vio);
00311   ink_assert(read_state == HCS_HALF_CLOSED);
00312 
00313   switch (event) {
00314   case VC_EVENT_EOS:
00315   case VC_EVENT_ERROR:
00316   case VC_EVENT_ACTIVE_TIMEOUT:
00317   case VC_EVENT_INACTIVITY_TIMEOUT:
00318     half_close = false;
00319     this->do_io_close();
00320     break;
00321   case VC_EVENT_READ_READY:
00322     // Drain any data read
00323     sm_reader->consume(sm_reader->read_avail());
00324     break;
00325   default:
00326     ink_release_assert(0);
00327     break;
00328   }
00329 
00330   return 0;
00331 }
00332 
00333 int
00334 HttpClientSession::state_slave_keep_alive(int event, void *data)
00335 {
00336 
00337   STATE_ENTER(&HttpClientSession::state_slave_keep_alive, event, data);
00338 
00339   ink_assert(data == slave_ka_vio);
00340   ink_assert(bound_ss != NULL);
00341 
00342   switch (event) {
00343   default:
00344   case VC_EVENT_READ_COMPLETE:
00345     // These events are bogus
00346     ink_assert(0);
00347     /* Fall Through */
00348   case VC_EVENT_ERROR:
00349   case VC_EVENT_READ_READY:
00350   case VC_EVENT_EOS:
00351     // The server session closed or something is amiss
00352     bound_ss->do_io_close();
00353     bound_ss = NULL;
00354     slave_ka_vio = NULL;
00355     break;
00356 
00357   case VC_EVENT_ACTIVE_TIMEOUT:
00358   case VC_EVENT_INACTIVITY_TIMEOUT:
00359     // Timeout - place the session on the shared pool
00360     bound_ss->release();
00361     bound_ss = NULL;
00362     slave_ka_vio = NULL;
00363     break;
00364   }
00365 
00366   return 0;
00367 }
00368 
00369 int
00370 HttpClientSession::state_keep_alive(int event, void *data)
00371 {
00372 
00373   // Route the event.  It is either for client vc or
00374   //  the origin server slave vc
00375   if (data && data == slave_ka_vio) {
00376     return state_slave_keep_alive(event, data);
00377   } else {
00378     ink_assert(data && data == ka_vio);
00379     ink_assert(read_state == HCS_KEEP_ALIVE);
00380   }
00381 
00382   STATE_ENTER(&HttpClientSession::state_keep_alive, event, data);
00383 
00384   switch (event) {
00385   case VC_EVENT_READ_READY:
00386     // New transaction, need to spawn of new sm to process
00387     // request
00388     new_transaction();
00389     break;
00390 
00391   case VC_EVENT_EOS:
00392     // If there is data in the buffer, start a new
00393     //  transaction, otherwise the client gave up
00394     if (sm_reader->read_avail() > 0) {
00395       new_transaction();
00396     } else {
00397       this->do_io_close();
00398     }
00399     break;
00400 
00401   case VC_EVENT_READ_COMPLETE:
00402   default:
00403     // These events are bogus
00404     ink_assert(0);
00405     // Fall through
00406   case VC_EVENT_ERROR:
00407   case VC_EVENT_ACTIVE_TIMEOUT:
00408   case VC_EVENT_INACTIVITY_TIMEOUT:
00409     // Keep-alive timed out
00410     this->do_io_close();
00411     break;
00412   }
00413 
00414   return 0;
00415 }
00416 void
00417 HttpClientSession::reenable(VIO * vio)
00418 {
00419   client_vc->reenable(vio);
00420 }
00421 
00422 void
00423 HttpClientSession::attach_server_session(HttpServerSession * ssession, bool transaction_done)
00424 {
00425   if (ssession) {
00426     ink_assert(bound_ss == NULL);
00427     ssession->state = HSS_KA_CLIENT_SLAVE;
00428     bound_ss = ssession;
00429     DebugHttpSsn("[%" PRId64 "] attaching server session [%" PRId64 "] as slave", con_id, ssession->con_id);
00430     ink_assert(ssession->get_reader()->read_avail() == 0);
00431     ink_assert(ssession->get_netvc() != client_vc);
00432 
00433     // handling potential keep-alive here
00434     if (m_active) {
00435       m_active = false;
00436       HTTP_DECREMENT_DYN_STAT(http_current_active_client_connections_stat);
00437     }
00438     // Since this our slave, issue an IO to detect a close and
00439     //  have it call the client session back.  This IO also prevent
00440     //  the server net conneciton from calling back a dead sm
00441     SET_HANDLER(&HttpClientSession::state_keep_alive);
00442     slave_ka_vio = ssession->do_io_read(this, INT64_MAX, ssession->read_buffer);
00443     ink_assert(slave_ka_vio != ka_vio);
00444 
00445     // Transfer control of the write side as well
00446     ssession->do_io_write(this, 0, NULL);
00447 
00448     if (transaction_done) {
00449       ssession->get_netvc()->
00450         set_inactivity_timeout(HRTIME_SECONDS(current_reader->t_state.txn_conf->keep_alive_no_activity_timeout_out));
00451       ssession->get_netvc()->cancel_active_timeout();
00452     } else {
00453       // we are serving from the cache - this could take a while.
00454       ssession->get_netvc()->cancel_inactivity_timeout();
00455       ssession->get_netvc()->cancel_active_timeout();
00456     }
00457   } else {
00458     ink_assert(bound_ss != NULL);
00459     bound_ss = NULL;
00460     slave_ka_vio = NULL;
00461   }
00462 }
00463 
00464 void
00465 HttpClientSession::release(IOBufferReader * r)
00466 {
00467   ink_assert(read_state == HCS_ACTIVE_READER);
00468   ink_assert(current_reader != NULL);
00469   MgmtInt ka_in = current_reader->t_state.txn_conf->keep_alive_no_activity_timeout_in;
00470 
00471   DebugHttpSsn("[%" PRId64 "] session released by sm [%" PRId64 "]", con_id, current_reader->sm_id);
00472   current_reader = NULL;
00473 
00474   // handling potential keep-alive here
00475   if (m_active) {
00476     m_active = false;
00477     HTTP_DECREMENT_DYN_STAT(http_current_active_client_connections_stat);
00478   }
00479   // Make sure that the state machine is returning
00480   //  correct buffer reader
00481   ink_assert(r == sm_reader);
00482   if (r != sm_reader) {
00483     this->do_io_close();
00484     return;
00485   }
00486 
00487   HTTP_DECREMENT_DYN_STAT(http_current_client_transactions_stat);
00488 
00489   // Check to see there is remaining data in the
00490   //  buffer.  If there is, spin up a new state
00491   //  machine to process it.  Otherwise, issue an
00492   //  IO to wait for new data
00493   if (sm_reader->read_avail() > 0) {
00494     DebugHttpSsn("[%" PRId64 "] data already in buffer, starting new transaction", con_id);
00495     new_transaction();
00496   } else {
00497     DebugHttpSsn("[%" PRId64 "] initiating io for next header", con_id);
00498     read_state = HCS_KEEP_ALIVE;
00499     SET_HANDLER(&HttpClientSession::state_keep_alive);
00500     ka_vio = this->do_io_read(this, INT64_MAX, read_buffer);
00501     ink_assert(slave_ka_vio != ka_vio);
00502     client_vc->set_inactivity_timeout(HRTIME_SECONDS(ka_in));
00503     client_vc->cancel_active_timeout();
00504   }
00505 }
00506 
00507 HttpServerSession *
00508 HttpClientSession::get_bound_ss()
00509 {
00510   return bound_ss;
00511 }

Generated by  doxygen 1.7.1