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

HttpSessionManager.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    HttpSessionManager.cc
00027 
00028    Description:
00029 
00030 
00031  ****************************************************************************/
00032 
00033 #include "HttpSessionManager.h"
00034 #include "HttpClientSession.h"
00035 #include "HttpServerSession.h"
00036 #include "HttpSM.h"
00037 #include "HttpDebugNames.h"
00038 
00039 // Initialize a thread to handle HTTP session management
00040 void
00041 initialize_thread_for_http_sessions(EThread *thread, int /* thread_index ATS_UNUSED */)
00042 {
00043   thread->server_session_pool = new ServerSessionPool;
00044 }
00045 
00046 HttpSessionManager httpSessionManager;
00047 
00048 ServerSessionPool::ServerSessionPool()
00049   : Continuation(new_ProxyMutex()), m_ip_pool(1023), m_host_pool(1023)
00050 {
00051   SET_HANDLER(&ServerSessionPool::eventHandler);
00052   m_ip_pool.setExpansionPolicy(IPHashTable::MANUAL);
00053   m_host_pool.setExpansionPolicy(HostHashTable::MANUAL);
00054 }
00055 
00056 void
00057 ServerSessionPool::purge()
00058 { 
00059   for ( IPHashTable::iterator last = m_ip_pool.end(), spot = m_ip_pool.begin() ; spot != last ; ++spot ) {
00060     spot->do_io_close();
00061   }
00062   m_ip_pool.clear();
00063   m_host_pool.clear();
00064 }
00065 
00066 bool
00067 ServerSessionPool::match(HttpServerSession* ss, sockaddr const* addr, INK_MD5 const& hostname_hash, TSServerSessionSharingMatchType match_style)
00068 {
00069   return TS_SERVER_SESSION_SHARING_MATCH_NONE != match_style && // if no matching allowed, fail immediately.
00070     // The hostname matches if we're not checking it or it (and the port!) is a match.
00071     (TS_SERVER_SESSION_SHARING_MATCH_IP == match_style ||  (ats_ip_port_cast(addr) == ats_ip_port_cast(ss->server_ip) && ss->hostname_hash == hostname_hash)) &&
00072     // The IP address matches if we're not checking it or it is a match.
00073     (TS_SERVER_SESSION_SHARING_MATCH_HOST == match_style || ats_ip_addr_port_eq(ss->server_ip, addr))
00074     ;
00075 }
00076 
00077 HttpServerSession*
00078 ServerSessionPool::acquireSession(sockaddr const* addr, INK_MD5 const& hostname_hash, TSServerSessionSharingMatchType match_style)
00079 {
00080   HttpServerSession* zret = NULL;
00081 
00082   if (TS_SERVER_SESSION_SHARING_MATCH_HOST == match_style) {
00083     // This is broken out because only in this case do we check the host hash first.
00084     HostHashTable::Location loc = m_host_pool.find(hostname_hash);
00085     in_port_t port = ats_ip_port_cast(addr);
00086     while (loc && port != ats_ip_port_cast(loc->server_ip)) ++loc; // scan for matching port.
00087     if (loc) {
00088       zret = loc;
00089       m_host_pool.remove(loc);
00090       m_ip_pool.remove(m_ip_pool.find(zret));
00091     }
00092   } else if (TS_SERVER_SESSION_SHARING_MATCH_NONE != match_style) { // matching is not disabled.
00093     IPHashTable::Location loc = m_ip_pool.find(addr);
00094     // If we're matching on the IP address we're done, this one is good enough.
00095     // Otherwise we need to scan further matches to match the host name as well.
00096     // Note we don't have to check the port because it's checked as part of the IP address key.
00097     if (TS_SERVER_SESSION_SHARING_MATCH_IP != match_style) {
00098       while (loc && loc->hostname_hash != hostname_hash)
00099         ++loc;
00100     }
00101     if (loc) {
00102       zret = loc;
00103       m_ip_pool.remove(loc);
00104       m_host_pool.remove(m_host_pool.find(zret));
00105     }
00106   }
00107   return zret;
00108 }
00109 
00110 void
00111 ServerSessionPool::releaseSession(HttpServerSession* ss)
00112 {
00113   ss->state = HSS_KA_SHARED;
00114   // Now we need to issue a read on the connection to detect
00115   //  if it closes on us.  We will get called back in the
00116   //  continuation for this bucket, ensuring we have the lock
00117   //  to remove the connection from our lists
00118   ss->do_io_read(this, INT64_MAX, ss->read_buffer);
00119 
00120   // Transfer control of the write side as well
00121   ss->do_io_write(this, 0, NULL);
00122 
00123   // we probably don't need the active timeout set, but will leave it for now
00124   ss->get_netvc()->set_inactivity_timeout(ss->get_netvc()->get_inactivity_timeout());
00125   ss->get_netvc()->set_active_timeout(ss->get_netvc()->get_active_timeout());
00126   // put it in the pools.
00127   m_ip_pool.insert(ss);
00128   m_host_pool.insert(ss);
00129 
00130   Debug("http_ss", "[%" PRId64 "] [release session] " "session placed into shared pool", ss->con_id);
00131 }
00132 
00133 //   Called from the NetProcessor to let us know that a
00134 //    connection has closed down
00135 //
00136 int
00137 ServerSessionPool::eventHandler(int event, void *data)
00138 {
00139   NetVConnection *net_vc = NULL;
00140   HttpServerSession *s = NULL;
00141 
00142   switch (event) {
00143   case VC_EVENT_READ_READY:
00144     // The server sent us data.  This is unexpected so
00145     //   close the connection
00146     /* Fall through */
00147   case VC_EVENT_EOS:
00148   case VC_EVENT_ERROR:
00149   case VC_EVENT_INACTIVITY_TIMEOUT:
00150   case VC_EVENT_ACTIVE_TIMEOUT:
00151     net_vc = static_cast<NetVConnection *>((static_cast<VIO *>(data))->vc_server);
00152     break;
00153 
00154   default:
00155     ink_release_assert(0);
00156     return 0;
00157   }
00158 
00159   sockaddr const* addr = net_vc->get_remote_addr();
00160   HttpConfigParams *http_config_params = HttpConfig::acquire();
00161   bool found = false;
00162 
00163   for ( ServerSessionPool::IPHashTable::Location lh = m_ip_pool.find(addr) ; lh ; ++lh ) {
00164     if ((s = lh)->get_netvc() == net_vc) {
00165       // if there was a timeout of some kind on a keep alive connection, and
00166       // keeping the connection alive will not keep us above the # of max connections
00167       // to the origin and we are below the min number of keep alive connections to this
00168       // origin, then reset the timeouts on our end and do not close the connection
00169       if ((event == VC_EVENT_INACTIVITY_TIMEOUT || event == VC_EVENT_ACTIVE_TIMEOUT) &&
00170           s->state == HSS_KA_SHARED &&
00171           s->enable_origin_connection_limiting) {
00172         bool connection_count_below_min = s->connection_count->getCount(s->server_ip) <= http_config_params->origin_min_keep_alive_connections;
00173 
00174         if (connection_count_below_min) {
00175           Debug("http_ss", "[%" PRId64 "] [session_bucket] session received io notice [%s], "
00176                 "reseting timeout to maintain minimum number of connections", s->con_id,
00177                 HttpDebugNames::get_event_name(event));
00178           s->get_netvc()->set_inactivity_timeout(s->get_netvc()->get_inactivity_timeout());
00179           s->get_netvc()->set_active_timeout(s->get_netvc()->get_active_timeout());
00180           found = true;
00181           break;
00182         }
00183       }
00184 
00185       // We've found our server session. Remove it from
00186       //   our lists and close it down
00187       Debug("http_ss", "[%" PRId64 "] [session_pool] session %p received io notice [%s]",
00188             s->con_id, s, HttpDebugNames::get_event_name(event));
00189       ink_assert(s->state == HSS_KA_SHARED);
00190       // Out of the pool! Now!
00191       m_ip_pool.remove(lh);
00192       m_host_pool.remove(m_host_pool.find(s));
00193       // Drop connection on this end.
00194       s->do_io_close();
00195       found = true;
00196       break;
00197     }
00198   }
00199 
00200   HttpConfig::release(http_config_params);
00201   if (!found) {
00202     // We failed to find our session.  This can only be the result
00203     //  of a programming flaw
00204     Warning("Connection leak from http keep-alive system");
00205     ink_assert(0);
00206   }
00207   return 0;
00208 }
00209 
00210 
00211 void
00212 HttpSessionManager::init()
00213 {
00214   m_g_pool = new ServerSessionPool;
00215 }
00216 
00217 // TODO: Should this really purge all keep-alive sessions?
00218 // Does this make any sense, since we always do the global pool and not the per thread?
00219 void
00220 HttpSessionManager::purge_keepalives()
00221 {
00222   EThread *ethread = this_ethread();
00223 
00224   MUTEX_TRY_LOCK(lock, m_g_pool->mutex, ethread);
00225   if (lock) {
00226     m_g_pool->purge();
00227   } // should we do something clever if we don't get the lock?
00228 }
00229 
00230 HSMresult_t
00231 HttpSessionManager::acquire_session(Continuation * /* cont ATS_UNUSED */, sockaddr const* ip,
00232                                     const char *hostname, HttpClientSession *ua_session, HttpSM *sm)
00233 {
00234   HttpServerSession *to_return = NULL;
00235   TSServerSessionSharingMatchType match_style = static_cast<TSServerSessionSharingMatchType>(sm->t_state.txn_conf->server_session_sharing_match);
00236   INK_MD5 hostname_hash;
00237 
00238   ink_code_md5((unsigned char *) hostname, strlen(hostname), (unsigned char *) &hostname_hash);
00239 
00240   // First check to see if there is a server session bound
00241   //   to the user agent session
00242   to_return = ua_session->get_server_session();
00243   if (to_return != NULL) {
00244     ua_session->attach_server_session(NULL);
00245 
00246     if (ServerSessionPool::match(to_return, ip, hostname_hash, match_style)) {
00247       Debug("http_ss", "[%" PRId64 "] [acquire session] returning attached session ", to_return->con_id);
00248       to_return->state = HSS_ACTIVE;
00249       sm->attach_server_session(to_return);
00250       return HSM_DONE;
00251     }
00252     // Release this session back to the main session pool and
00253     //   then continue looking for one from the shared pool
00254     Debug("http_ss", "[%" PRId64 "] [acquire session] " "session not a match, returning to shared pool", to_return->con_id);
00255     to_return->release();
00256     to_return = NULL;
00257   }
00258 
00259   // Now check to see if we have a connection in our shared connection pool
00260   EThread *ethread = this_ethread();
00261 
00262   if (TS_SERVER_SESSION_SHARING_POOL_THREAD == sm->t_state.txn_conf->server_session_sharing_pool) {
00263     to_return = ethread->server_session_pool->acquireSession(ip, hostname_hash, match_style);
00264   } else {
00265     MUTEX_TRY_LOCK(lock, m_g_pool->mutex, ethread);
00266     if (lock) {
00267       to_return = m_g_pool->acquireSession(ip, hostname_hash, match_style);
00268       Debug("http_ss", "[acquire session] pool search %s", to_return ? "successful" : "failed");
00269     } else {
00270       Debug("http_ss", "[acquire session] could not acquire session due to lock contention");
00271       return HSM_RETRY;
00272     }
00273   }
00274 
00275   if (to_return) {
00276     Debug("http_ss", "[%" PRId64 "] [acquire session] " "return session from shared pool", to_return->con_id);
00277     to_return->state = HSS_ACTIVE;
00278     sm->attach_server_session(to_return);
00279     return HSM_DONE;
00280   }
00281   return HSM_NOT_FOUND;
00282 }
00283 
00284 HSMresult_t
00285 HttpSessionManager::release_session(HttpServerSession *to_release)
00286 {
00287   EThread *ethread = this_ethread();
00288   ServerSessionPool* pool = TS_SERVER_SESSION_SHARING_POOL_THREAD == to_release->sharing_pool ? ethread->server_session_pool : m_g_pool;
00289   bool released_p = true;
00290   
00291   // The per thread lock looks like it should not be needed but if it's not locked the close checking I/O op will crash.
00292   MUTEX_TRY_LOCK(lock, pool->mutex, ethread);
00293   if (lock) {
00294     pool->releaseSession(to_release);
00295   } else {
00296     Debug("http_ss", "[%" PRId64 "] [release session] could not release session due to lock contention", to_release->con_id);
00297     released_p = false;
00298   }
00299 
00300   return released_p ? HSM_DONE : HSM_RETRY;
00301 }

Generated by  doxygen 1.7.1