1 /** @file
2 
3   A brief file description
4 
5   @section license License
6 
7   Licensed to the Apache Software Foundation (ASF) under one
8   or more contributor license agreements.  See the NOTICE file
9   distributed with this work for additional information
10   regarding copyright ownership.  The ASF licenses this file
11   to you under the Apache License, Version 2.0 (the
12   "License"); you may not use this file except in compliance
13   with the License.  You may obtain a copy of the License at
14 
15       http://www.apache.org/licenses/LICENSE-2.0
16 
17   Unless required by applicable law or agreed to in writing, software
18   distributed under the License is distributed on an "AS IS" BASIS,
19   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20   See the License for the specific language governing permissions and
21   limitations under the License.
22  */
23 
24 /****************************************************************************
25 
26    Http1ServerSession.cc
27 
28    Description:
29 
30  ****************************************************************************/
31 #include "tscore/ink_config.h"
32 #include "tscore/BufferWriter.h"
33 #include "tscore/bwf_std_format.h"
34 #include "tscore/Allocator.h"
35 #include "Http1ServerSession.h"
36 #include "HttpSessionManager.h"
37 #include "HttpSM.h"
38 
39 static int64_t next_ss_id = static_cast<int64_t>(0);
40 ClassAllocator<Http1ServerSession> httpServerSessionAllocator("httpServerSessionAllocator");
41 
42 void
destroy()43 Http1ServerSession::destroy()
44 {
45   ink_release_assert(server_vc == nullptr);
46   ink_assert(read_buffer);
47   ink_assert(server_trans_stat == 0);
48   magic = HTTP_SS_MAGIC_DEAD;
49   if (read_buffer) {
50     free_MIOBuffer(read_buffer);
51     read_buffer = nullptr;
52   }
53 
54   mutex.clear();
55   if (TS_SERVER_SESSION_SHARING_POOL_THREAD == sharing_pool) {
56     THREAD_FREE(this, httpServerSessionAllocator, this_thread());
57   } else {
58     httpServerSessionAllocator.free(this);
59   }
60 }
61 
62 void
new_connection(NetVConnection * new_vc)63 Http1ServerSession::new_connection(NetVConnection *new_vc)
64 {
65   ink_assert(new_vc != nullptr);
66   server_vc = new_vc;
67 
68   // Used to do e.g. mutex = new_vc->thread->mutex; when per-thread pools enabled
69   mutex = new_vc->mutex;
70 
71   // Unique client session identifier.
72   con_id = ink_atomic_increment((&next_ss_id), 1);
73 
74   magic = HTTP_SS_MAGIC_ALIVE;
75   HTTP_SUM_GLOBAL_DYN_STAT(http_current_server_connections_stat, 1); // Update the true global stat
76   HTTP_INCREMENT_DYN_STAT(http_total_server_connections_stat);
77 
78   read_buffer = new_MIOBuffer(HTTP_SERVER_RESP_HDR_BUFFER_INDEX);
79 
80   buf_reader = read_buffer->alloc_reader();
81   Debug("http_ss", "[%" PRId64 "] session born, netvc %p", con_id, new_vc);
82   state = HSS_INIT;
83 
84   new_vc->set_tcp_congestion_control(SERVER_SIDE);
85 }
86 
87 void
enable_outbound_connection_tracking(OutboundConnTrack::Group * group)88 Http1ServerSession::enable_outbound_connection_tracking(OutboundConnTrack::Group *group)
89 {
90   ink_assert(nullptr == conn_track_group);
91   conn_track_group = group;
92   if (is_debug_tag_set("http_ss")) {
93     ts::LocalBufferWriter<256> w;
94     w.print("[{}] new connection, ip: {}, group ({}), count: {}\0", con_id, get_server_ip(), *group, group->_count);
95     Debug("http_ss", "%s", w.data());
96   }
97 }
98 
99 VIO *
do_io_read(Continuation * c,int64_t nbytes,MIOBuffer * buf)100 Http1ServerSession::do_io_read(Continuation *c, int64_t nbytes, MIOBuffer *buf)
101 {
102   return server_vc ? server_vc->do_io_read(c, nbytes, buf) : nullptr;
103 }
104 
105 VIO *
do_io_write(Continuation * c,int64_t nbytes,IOBufferReader * buf,bool owner)106 Http1ServerSession::do_io_write(Continuation *c, int64_t nbytes, IOBufferReader *buf, bool owner)
107 {
108   return server_vc ? server_vc->do_io_write(c, nbytes, buf, owner) : nullptr;
109 }
110 
111 void
do_io_shutdown(ShutdownHowTo_t howto)112 Http1ServerSession::do_io_shutdown(ShutdownHowTo_t howto)
113 {
114   server_vc->do_io_shutdown(howto);
115 }
116 
117 void
do_io_close(int alerrno)118 Http1ServerSession::do_io_close(int alerrno)
119 {
120   ts::LocalBufferWriter<256> w;
121   bool debug_p = is_debug_tag_set("http_ss");
122 
123   if (state == HSS_ACTIVE) {
124     HTTP_DECREMENT_DYN_STAT(http_current_server_transactions_stat);
125     this->server_trans_stat--;
126   }
127 
128   if (debug_p) {
129     w.print("[{}] session close: nevtc {:x}", con_id, server_vc);
130   }
131 
132   HTTP_SUM_GLOBAL_DYN_STAT(http_current_server_connections_stat, -1); // Make sure to work on the global stat
133   HTTP_SUM_DYN_STAT(http_transactions_per_server_con, transact_count);
134 
135   // Update upstream connection tracking data if present.
136   if (conn_track_group) {
137     if (conn_track_group->_count >= 0) {
138       auto n = (conn_track_group->_count)--;
139       if (debug_p) {
140         w.print(" conn track group ({}) count {}", conn_track_group->_key, n);
141       }
142     } else {
143       // A bit dubious, as there's no guarantee it's still negative, but even that would be interesting to know.
144       Error("[http_ss] [%" PRId64 "] number of connections should be greater than or equal to zero: %u", con_id,
145             conn_track_group->_count.load());
146     }
147   }
148   if (debug_p) {
149     Debug("http_ss", "%.*s", static_cast<int>(w.size()), w.data());
150   }
151 
152   if (server_vc) {
153     server_vc->do_io_close(alerrno);
154   }
155   server_vc = nullptr;
156 
157   if (to_parent_proxy) {
158     HTTP_DECREMENT_DYN_STAT(http_current_parent_proxy_connections_stat);
159   }
160   destroy();
161 }
162 
163 void
reenable(VIO * vio)164 Http1ServerSession::reenable(VIO *vio)
165 {
166   server_vc->reenable(vio);
167 }
168 
169 // void Http1ServerSession::release()
170 //
171 //   Releases the session for K-A reuse
172 //
173 void
release()174 Http1ServerSession::release()
175 {
176   Debug("http_ss", "Releasing session, private_session=%d, sharing_match=%d", private_session, sharing_match);
177   // Set our state to KA for stat issues
178   state = HSS_KA_SHARED;
179 
180   server_vc->control_flags.set_flags(0);
181 
182   // Private sessions are never released back to the shared pool
183   if (private_session || TS_SERVER_SESSION_SHARING_MATCH_NONE == sharing_match) {
184     this->do_io_close();
185     return;
186   }
187 
188   // Make sure the vios for the current SM are cleared
189   server_vc->do_io_read(nullptr, 0, nullptr);
190   server_vc->do_io_write(nullptr, 0, nullptr);
191 
192   HSMresult_t r = httpSessionManager.release_session(this);
193 
194   if (r == HSM_RETRY) {
195     // Session could not be put in the session manager
196     //  due to lock contention
197     // FIX:  should retry instead of closing
198     this->do_io_close();
199   } else {
200     // The session was successfully put into the session
201     //    manager and it will manage it
202     // (Note: should never get HSM_NOT_FOUND here)
203     ink_assert(r == HSM_DONE);
204   }
205 }
206 
207 NetVConnection *
get_netvc() const208 Http1ServerSession::get_netvc() const
209 {
210   return server_vc;
211 };
212 
213 void
set_netvc(NetVConnection * new_vc)214 Http1ServerSession::set_netvc(NetVConnection *new_vc)
215 {
216   server_vc = new_vc;
217 }
218 
219 // Keys for matching hostnames
220 IpEndpoint const &
get_server_ip() const221 Http1ServerSession::get_server_ip() const
222 {
223   ink_release_assert(server_vc != nullptr);
224   return server_vc->get_remote_endpoint();
225 }
226 
227 int
populate_protocol(std::string_view * result,int size) const228 Http1ServerSession::populate_protocol(std::string_view *result, int size) const
229 {
230   auto vc = this->get_netvc();
231   return vc ? vc->populate_protocol(result, size) : 0;
232 }
233 
234 const char *
protocol_contains(std::string_view tag_prefix) const235 Http1ServerSession::protocol_contains(std::string_view tag_prefix) const
236 {
237   auto vc = this->get_netvc();
238   return vc ? vc->protocol_contains(tag_prefix) : nullptr;
239 }
240