blob: ebba6b243da772bbd85dd95f81a1433c4a3e7ca0 [file]
/** @file
A brief file description
@section license License
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/****************************************************************************
HttpSessionManager.h
Description:
****************************************************************************/
#pragma once
#include "iocore/eventsystem/EventSystem.h"
#include "proxy/PoolableSession.h"
#include "swoc/IntrusiveHashMap.h"
#include <atomic>
#include <vector>
class ProxyTransaction;
class HttpSM;
void initialize_thread_for_http_sessions(EThread *thread, int thread_index);
enum class HSMresult_t {
DONE,
RETRY,
NOT_FOUND,
};
/** A pool of server sessions.
This is a continuation so that it can get callbacks from the server sessions.
This is used to track remote closes on the sessions so they can be cleaned up.
@internal Cleanup is the real reason we will always need an IP address mapping for the
sessions. The I/O callback will have only the NetVC and thence the remote IP address for the
closed session and we need to be able find it based on that.
*/
class ServerSessionPool : public Continuation
{
public:
/// Default constructor.
/// Constructs an empty pool.
/// @param thread The thread that owns this pool, or @c nullptr for the shared pool.
explicit ServerSessionPool(EThread *thread = nullptr);
/// Handle events from server sessions.
int eventHandler(int event, void *data);
static bool validate_host_sni(HttpSM *sm, NetVConnection *netvc);
static bool validate_sni(HttpSM *sm, NetVConnection *netvc);
static bool validate_cert(HttpSM *sm, NetVConnection *netvc);
void removeSession(PoolableSession *ssn);
void addSession(PoolableSession *ssn);
int
count() const
{
return m_ip_pool.count();
}
private:
using IPTable = swoc::IntrusiveHashMap<PoolableSession::IPLinkage>;
using FQDNTable = swoc::IntrusiveHashMap<PoolableSession::FQDNLinkage>;
public:
/** Check if a session matches address and host name.
*/
static bool match(PoolableSession *ss, sockaddr const *addr, CryptoHash const &host_hash,
TSServerSessionSharingMatchMask match_style);
/** Search the pool for a server session compatible with the given address, hostname, and state machine.
*
* The selection criteria are controlled by @p match_style. At least one of
* @c TS_SERVER_SESSION_SHARING_MATCH_MASK_IP or @c TS_SERVER_SESSION_SHARING_MATCH_MASK_HOSTONLY
* must be set for a match to be possible; if neither is set, @c HSMresult_t::NOT_FOUND is
* returned unconditionally.
*
* Regardless of @p match_style, a session that supports multiplexed streams is selected only if
* its origin certificate is valid for the host that @p sm would verify on a new connection.
*
* When a compatible session is found and does not support multiplexed streams, it is removed from
* the pool and ownership transfers to the caller. A session that supports multiplexed streams is
* not removed and remains available for subsequent acquisitions.
*
* @param[in] addr Remote address and port to match.
* @param[in] hostname_hash Hash of the target hostname, computed the same way as
* @c PoolableSession::hostname_hash.
* @param[in] match_style Bitmask of @c TSServerSessionSharingMatchMask values specifying which
* attributes must agree between the candidate session and the request.
* @param[in] sm The requesting HTTP state machine. Must be non-null unless no SNI,
* HOSTSNISYNC, or CERT bit is set in @p match_style and no session
* supporting multiplexed streams has been added to this pool; when
* non-null, @c t_state.txn_conf,
* @c t_state.scheme, and @c t_state.hdr_info.server_request must be
* initialized.
* @param[out] server_session Set to the matched session when @c HSMresult_t::DONE is returned;
* @c nullptr otherwise.
*
* @return @c HSMresult_t::DONE if a matching session was found;
* @c HSMresult_t::NOT_FOUND otherwise.
*
* @pre Called on an @c EThread.
*
* @post If the returned session was removed from the pool, @c http_rsb.pooled_server_connections
* is decremented, and the caller must redirect the session's I/O away from this pool before
* this pool can receive another event for it.
*
* @par Thread Safety
* Not thread-safe.
*/
HSMresult_t acquireSession(sockaddr const *addr, CryptoHash const &hostname_hash, TSServerSessionSharingMatchMask match_style,
HttpSM *sm, PoolableSession *&server_session);
/** Release a session to the pool.
@return @c true if the session was pooled; @c false if the session could not be pooled
(the caller is responsible for closing it in that case).
*/
bool releaseSession(PoolableSession *ss);
/** Ask the thread that owns this pool to close its sessions in the shared pool.
This can be called from any thread.
*/
void requestPurge();
/** Remove the sessions whose connections belong to @a thread.
The caller must hold the pool lock and is responsible for closing the sessions.
*/
void removeSessionsOf(EThread *thread, std::vector<PoolableSession *> &sessions);
/// Close sessions whose connections belong to the current thread.
static void closeSessions(std::vector<PoolableSession *> const &sessions);
/** Close a session from an event on the current thread.
This is for closing a session while a shared pool is locked. The session must already be out
of the pool and this must be the pool of the thread that owns the session.
*/
void deferClose(PoolableSession *ssn);
// Pools of server sessions.
// Note that each server session is stored in both pools.
IPTable m_ip_pool;
FQDNTable m_fqdn_pool;
private:
void closeDeferred();
/// The thread that owns this pool, @c nullptr for the shared pool.
EThread *m_thread = nullptr;
/// Sessions waiting to be closed by this pool's thread.
std::vector<PoolableSession *> m_deferred_close;
/// Set while this pool's thread has been asked to purge and has not yet done so.
std::atomic<bool> m_purge_requested{false};
};
class HttpSessionManager
{
public:
HttpSessionManager() {}
~HttpSessionManager() {}
HSMresult_t acquire_session(HttpSM *sm, sockaddr const *addr, const char *hostname, ProxyTransaction *ua_txn);
HSMresult_t release_session(PoolableSession *to_release);
/** Close the keep-alive sessions in the shared pool.
Each thread closes its own sessions. The current thread's are closed before this returns if
the shared pool lock is free, the others when their thread next runs.
*/
void purge_keepalives();
/** Close the shared pool sessions whose connections belong to the current thread.
@return @c false if the shared pool was locked by another thread and nothing was done.
*/
bool purge_thread_keepalives();
void init();
int main_handler(int event, void *data);
void
set_pool_type(int pool_type)
{
m_pool_type = static_cast<TSServerSessionSharingPoolType>(pool_type);
}
TSServerSessionSharingPoolType
get_pool_type() const
{
return m_pool_type;
}
private:
/// Global pool, used if not per thread pools.
/// @internal We delay creating this because the session manager is created during global statistics init.
ServerSessionPool *m_g_pool = nullptr;
HSMresult_t _acquire_session(sockaddr const *ip, CryptoHash const &hostname_hash, HttpSM *sm,
TSServerSessionSharingMatchMask match_style, TSServerSessionSharingPoolType pool_type);
TSServerSessionSharingPoolType m_pool_type = TS_SERVER_SESSION_SHARING_POOL_THREAD;
};
extern HttpSessionManager httpSessionManager;