1 /** @file
2
3 Internal SDK stuff
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 #pragma once
25
26 #include "P_EventSystem.h"
27 #include "URL.h"
28 #include "P_Net.h"
29 #include "HTTP.h"
30 #include "tscore/List.h"
31 #include "ProxyConfig.h"
32 #include "P_Cache.h"
33 #include "I_Tasks.h"
34 #include "Plugin.h"
35
36 #include "ts/InkAPIPrivateIOCore.h"
37 #include "ts/experimental.h"
38
39 #include <typeinfo>
40
41 /* Some defines that might be candidates for configurable settings later.
42 */
43 typedef int8_t TSMgmtByte; // Not for external use
44
45 /* ****** Cache Structure ********* */
46
47 // For memory corruption detection
48 enum CacheInfoMagic {
49 CACHE_INFO_MAGIC_ALIVE = 0xfeedbabe,
50 CACHE_INFO_MAGIC_DEAD = 0xdeadbeef,
51 };
52
53 struct CacheInfo {
54 CryptoHash cache_key;
55 CacheFragType frag_type = CACHE_FRAG_TYPE_NONE;
56 char *hostname = nullptr;
57 int len = 0;
58 time_t pin_in_cache = 0;
59 CacheInfoMagic magic = CACHE_INFO_MAGIC_ALIVE;
60
CacheInfoCacheInfo61 CacheInfo() {}
62 };
63
64 class FileImpl
65 {
66 enum {
67 CLOSED = 0,
68 READ = 1,
69 WRITE = 2,
70 };
71
72 public:
73 FileImpl();
74 ~FileImpl();
75
76 int fopen(const char *filename, const char *mode);
77 void fclose();
78 ssize_t fread(void *buf, size_t length);
79 ssize_t fwrite(const void *buf, size_t length);
80 ssize_t fflush();
81 char *fgets(char *buf, size_t length);
82
83 public:
84 int m_fd;
85 int m_mode;
86 char *m_buf;
87 size_t m_bufsize;
88 size_t m_bufpos;
89 };
90
91 struct INKConfigImpl : public ConfigInfo {
92 void *mdata;
93 TSConfigDestroyFunc m_destroy_func;
94
~INKConfigImplINKConfigImpl95 ~INKConfigImpl() override { m_destroy_func(mdata); }
96 };
97
98 struct HttpAltInfo {
99 HTTPHdr m_client_req;
100 HTTPHdr m_cached_req;
101 HTTPHdr m_cached_resp;
102 float m_qvalue;
103 };
104
105 enum APIHookScope {
106 API_HOOK_SCOPE_NONE,
107 API_HOOK_SCOPE_GLOBAL,
108 API_HOOK_SCOPE_LOCAL,
109 };
110
111 /// A single API hook that can be invoked.
112 class APIHook
113 {
114 public:
115 INKContInternal *m_cont;
116 int invoke(int event, void *edata) const;
117 APIHook *next() const;
118 APIHook *prev() const;
119 LINK(APIHook, m_link);
120 };
121
122 /// A collection of API hooks.
123 class APIHooks
124 {
125 public:
126 void append(INKContInternal *cont);
127 /// Get the first hook.
128 APIHook *head() const;
129 /// Remove all hooks.
130 void clear();
131 /// Check if there are no hooks.
132 bool is_empty() const;
133
134 private:
135 Que(APIHook, m_link) m_hooks;
136 };
137
138 inline bool
is_empty()139 APIHooks::is_empty() const
140 {
141 return nullptr == m_hooks.head;
142 }
143
144 /** Container for API hooks for a specific feature.
145
146 This is an array of hook lists, each identified by a numeric identifier (id). Each array element is a list of all
147 hooks for that ID. Adding a hook means adding to the list in the corresponding array element. There is no provision
148 for removing a hook.
149
150 @note The minimum value for a hook ID is zero. Therefore the template parameter @a N_ID should be one more than the
151 maximum hook ID so the valid ids are 0..(N-1) in the standard C array style.
152 */
153 template <typename ID, ///< Type of hook ID
154 int N ///< Number of hooks
155 >
156 class FeatureAPIHooks
157 {
158 public:
159 FeatureAPIHooks(); ///< Constructor (empty container).
160 ~FeatureAPIHooks(); ///< Destructor.
161
162 /// Remove all hooks.
163 void clear();
164 /// Add the hook @a cont to the end of the hooks for @a id.
165 void append(ID id, INKContInternal *cont);
166 /// Get the list of hooks for @a id.
167 APIHook *get(ID id) const;
168 /// @return @c true if @a id is a valid id, @c false otherwise.
169 static bool is_valid(ID id);
170
171 /// Invoke the callbacks for the hook @a id.
172 void invoke(ID id, int event, void *data);
173
174 /// Fast check for any hooks in this container.
175 ///
176 /// @return @c true if any list has at least one hook, @c false if
177 /// all lists have no hooks.
178 bool has_hooks() const;
179
180 /// Check for existence of hooks of a specific @a id.
181 /// @return @c true if any hooks of type @a id are present.
182 bool has_hooks_for(ID id) const;
183
184 /// Get a pointer to the set of hooks for a specific hook @id
185 APIHooks const *operator[](ID id) const;
186
187 private:
188 bool m_hooks_p = false; ///< Flag for (not) empty container.
189 /// The array of hooks lists.
190 APIHooks m_hooks[N];
191 };
192
FeatureAPIHooks()193 template <typename ID, int N> FeatureAPIHooks<ID, N>::FeatureAPIHooks() {}
194
~FeatureAPIHooks()195 template <typename ID, int N> FeatureAPIHooks<ID, N>::~FeatureAPIHooks()
196 {
197 this->clear();
198 }
199
200 template <typename ID, int N>
201 void
clear()202 FeatureAPIHooks<ID, N>::clear()
203 {
204 for (auto &h : m_hooks) {
205 h.clear();
206 }
207 m_hooks_p = false;
208 }
209
210 template <typename ID, int N>
211 void
append(ID id,INKContInternal * cont)212 FeatureAPIHooks<ID, N>::append(ID id, INKContInternal *cont)
213 {
214 if (is_valid(id)) {
215 m_hooks_p = true;
216 m_hooks[id].append(cont);
217 }
218 }
219
220 template <typename ID, int N>
221 APIHook *
get(ID id)222 FeatureAPIHooks<ID, N>::get(ID id) const
223 {
224 return likely(is_valid(id)) ? m_hooks[id].head() : nullptr;
225 }
226
227 template <typename ID, int N>
228 APIHooks const *
229 FeatureAPIHooks<ID, N>::operator[](ID id) const
230 {
231 return likely(is_valid(id)) ? &(m_hooks[id]) : nullptr;
232 }
233
234 template <typename ID, int N>
235 void
invoke(ID id,int event,void * data)236 FeatureAPIHooks<ID, N>::invoke(ID id, int event, void *data)
237 {
238 if (likely(is_valid(id))) {
239 m_hooks[id].invoke(event, data);
240 }
241 }
242
243 template <typename ID, int N>
244 bool
has_hooks()245 FeatureAPIHooks<ID, N>::has_hooks() const
246 {
247 return m_hooks_p;
248 }
249
250 template <typename ID, int N>
251 bool
is_valid(ID id)252 FeatureAPIHooks<ID, N>::is_valid(ID id)
253 {
254 return 0 <= id && id < N;
255 }
256
257 class HttpAPIHooks : public FeatureAPIHooks<TSHttpHookID, TS_HTTP_LAST_HOOK>
258 {
259 };
260
261 class TSSslHookInternalID
262 {
263 public:
TSSslHookInternalID(TSHttpHookID id)264 explicit constexpr TSSslHookInternalID(TSHttpHookID id) : _id(id - TS_SSL_FIRST_HOOK) {}
265
266 constexpr operator int() const { return _id; }
267
268 static const int NUM = TS_SSL_LAST_HOOK - TS_SSL_FIRST_HOOK + 1;
269
270 constexpr bool
is_in_bounds()271 is_in_bounds() const
272 {
273 return (_id >= 0) && (_id < NUM);
274 }
275
276 private:
277 const int _id;
278 };
279
280 class SslAPIHooks : public FeatureAPIHooks<TSSslHookInternalID, TSSslHookInternalID::NUM>
281 {
282 };
283
284 class LifecycleAPIHooks : public FeatureAPIHooks<TSLifecycleHookID, TS_LIFECYCLE_LAST_HOOK>
285 {
286 };
287
288 class ConfigUpdateCallback : public Continuation
289 {
290 public:
ConfigUpdateCallback(INKContInternal * contp)291 explicit ConfigUpdateCallback(INKContInternal *contp) : Continuation(contp->mutex.get()), m_cont(contp)
292 {
293 SET_HANDLER(&ConfigUpdateCallback::event_handler);
294 }
295
296 int
event_handler(int,void *)297 event_handler(int, void *)
298 {
299 if (m_cont->mutex) {
300 MUTEX_TRY_LOCK(trylock, m_cont->mutex, this_ethread());
301 if (!trylock.is_locked()) {
302 eventProcessor.schedule_in(this, HRTIME_MSECONDS(10), ET_TASK);
303 } else {
304 m_cont->handleEvent(TS_EVENT_MGMT_UPDATE, nullptr);
305 delete this;
306 }
307 } else {
308 m_cont->handleEvent(TS_EVENT_MGMT_UPDATE, nullptr);
309 delete this;
310 }
311
312 return 0;
313 }
314
315 private:
316 INKContInternal *m_cont;
317 };
318
319 class ConfigUpdateCbTable
320 {
321 public:
322 ConfigUpdateCbTable();
323 ~ConfigUpdateCbTable();
324
325 void insert(INKContInternal *contp, const char *name);
326 void invoke(const char *name);
327 void invoke(INKContInternal *contp);
328
329 private:
330 std::unordered_map<std::string, INKContInternal *> cb_table;
331 };
332
333 class HttpHookState
334 {
335 public:
336 /// Scope tags for interacting with a live instance.
337 enum ScopeTag { GLOBAL, SSN, TXN };
338
339 /// Default Constructor
340 HttpHookState();
341
342 /// Initialize the hook state to track up to 3 sources of hooks.
343 /// The argument order to this method is used to break priority ties (callbacks from earlier args are invoked earlier)
344 /// The order in terms of @a ScopeTag is GLOBAL, SESSION, TRANSACTION.
345 void init(TSHttpHookID id, HttpAPIHooks const *global, HttpAPIHooks const *ssn = nullptr, HttpAPIHooks const *txn = nullptr);
346
347 /// Select a hook for invocation and advance the state to the next valid hook
348 /// @return nullptr if no current hook.
349 APIHook const *getNext();
350
351 /// Get the hook ID
352 TSHttpHookID id() const;
353
354 /// Temporary function to return true. Later will be used to decide if a plugin is enabled for the hooks
355 bool is_enabled();
356
357 protected:
358 /// Track the state of one scope of hooks.
359 struct Scope {
360 APIHook const *_c; ///< Current hook (candidate for invocation).
361 APIHook const *_p; ///< Previous hook (already invoked).
362 APIHooks const *_hooks; ///< Reference to the real hook list
363
364 /// Initialize the scope.
365 void init(HttpAPIHooks const *scope, TSHttpHookID id);
366 /// Clear the scope.
367 void clear();
368 /// Return the current candidate.
369 APIHook const *candidate();
370 /// Advance state to the next hook.
371 void operator++();
372 };
373
374 private:
375 TSHttpHookID _id;
376 Scope _global; ///< Chain from global hooks.
377 Scope _ssn; ///< Chain from session hooks.
378 Scope _txn; ///< Chain from transaction hooks.
379 };
380
381 inline TSHttpHookID
id()382 HttpHookState::id() const
383 {
384 return _id;
385 }
386
387 void api_init();
388
389 extern HttpAPIHooks *http_global_hooks;
390 extern LifecycleAPIHooks *lifecycle_hooks;
391 extern SslAPIHooks *ssl_hooks;
392 extern ConfigUpdateCbTable *global_config_cbs;
393