xref: /trafficserver/proxy/InkAPIInternal.h (revision be2102e4)
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 #define TS_HTTP_MAX_USER_ARG 16 /* max number of user arguments for Transactions and Sessions */
44 
45 typedef int8_t TSMgmtByte; // Not for external use
46 
47 /* ****** Cache Structure ********* */
48 
49 // For memory corruption detection
50 enum CacheInfoMagic {
51   CACHE_INFO_MAGIC_ALIVE = 0xfeedbabe,
52   CACHE_INFO_MAGIC_DEAD  = 0xdeadbeef,
53 };
54 
55 struct CacheInfo {
56   CryptoHash cache_key;
57   CacheFragType frag_type = CACHE_FRAG_TYPE_NONE;
58   char *hostname          = nullptr;
59   int len                 = 0;
60   time_t pin_in_cache     = 0;
61   CacheInfoMagic magic    = CACHE_INFO_MAGIC_ALIVE;
62 
CacheInfoCacheInfo63   CacheInfo() {}
64 };
65 
66 class FileImpl
67 {
68   enum {
69     CLOSED = 0,
70     READ   = 1,
71     WRITE  = 2,
72   };
73 
74 public:
75   FileImpl();
76   ~FileImpl();
77 
78   int fopen(const char *filename, const char *mode);
79   void fclose();
80   ssize_t fread(void *buf, size_t length);
81   ssize_t fwrite(const void *buf, size_t length);
82   ssize_t fflush();
83   char *fgets(char *buf, size_t length);
84 
85 public:
86   int m_fd;
87   int m_mode;
88   char *m_buf;
89   size_t m_bufsize;
90   size_t m_bufpos;
91 };
92 
93 struct INKConfigImpl : public ConfigInfo {
94   void *mdata;
95   TSConfigDestroyFunc m_destroy_func;
96 
~INKConfigImplINKConfigImpl97   ~INKConfigImpl() override { m_destroy_func(mdata); }
98 };
99 
100 struct HttpAltInfo {
101   HTTPHdr m_client_req;
102   HTTPHdr m_cached_req;
103   HTTPHdr m_cached_resp;
104   float m_qvalue;
105 };
106 
107 enum APIHookScope {
108   API_HOOK_SCOPE_NONE,
109   API_HOOK_SCOPE_GLOBAL,
110   API_HOOK_SCOPE_LOCAL,
111 };
112 
113 /// A single API hook that can be invoked.
114 class APIHook
115 {
116 public:
117   INKContInternal *m_cont;
118   int invoke(int event, void *edata) const;
119   APIHook *next() const;
120   APIHook *prev() const;
121   LINK(APIHook, m_link);
122 };
123 
124 /// A collection of API hooks.
125 class APIHooks
126 {
127 public:
128   void append(INKContInternal *cont);
129   /// Get the first hook.
130   APIHook *head() const;
131   /// Remove all hooks.
132   void clear();
133   /// Check if there are no hooks.
134   bool is_empty() const;
135 
136 private:
137   Que(APIHook, m_link) m_hooks;
138 };
139 
140 inline bool
is_empty() const141 APIHooks::is_empty() const
142 {
143   return nullptr == m_hooks.head;
144 }
145 
146 /** Container for API hooks for a specific feature.
147 
148     This is an array of hook lists, each identified by a numeric identifier (id). Each array element is a list of all
149     hooks for that ID. Adding a hook means adding to the list in the corresponding array element. There is no provision
150     for removing a hook.
151 
152     @note The minimum value for a hook ID is zero. Therefore the template parameter @a N_ID should be one more than the
153     maximum hook ID so the valid ids are 0..(N-1) in the standard C array style.
154  */
155 template <typename ID, ///< Type of hook ID
156           int N        ///< Number of hooks
157           >
158 class FeatureAPIHooks
159 {
160 public:
161   FeatureAPIHooks();  ///< Constructor (empty container).
162   ~FeatureAPIHooks(); ///< Destructor.
163 
164   /// Remove all hooks.
165   void clear();
166   /// Add the hook @a cont to the end of the hooks for @a id.
167   void append(ID id, INKContInternal *cont);
168   /// Get the list of hooks for @a id.
169   APIHook *get(ID id) const;
170   /// @return @c true if @a id is a valid id, @c false otherwise.
171   static bool is_valid(ID id);
172 
173   /// Invoke the callbacks for the hook @a id.
174   void invoke(ID id, int event, void *data);
175 
176   /// Fast check for any hooks in this container.
177   ///
178   /// @return @c true if any list has at least one hook, @c false if
179   /// all lists have no hooks.
180   bool has_hooks() const;
181 
182   /// Check for existence of hooks of a specific @a id.
183   /// @return @c true if any hooks of type @a id are present.
184   bool has_hooks_for(ID id) const;
185 
186   /// Get a pointer to the set of hooks for a specific hook @id
187   APIHooks const *operator[](ID id) const;
188 
189 private:
190   bool m_hooks_p = false; ///< Flag for (not) empty container.
191   /// The array of hooks lists.
192   APIHooks m_hooks[N];
193 };
194 
FeatureAPIHooks()195 template <typename ID, int N> FeatureAPIHooks<ID, N>::FeatureAPIHooks() {}
196 
~FeatureAPIHooks()197 template <typename ID, int N> FeatureAPIHooks<ID, N>::~FeatureAPIHooks()
198 {
199   this->clear();
200 }
201 
202 template <typename ID, int N>
203 void
clear()204 FeatureAPIHooks<ID, N>::clear()
205 {
206   for (auto &h : m_hooks) {
207     h.clear();
208   }
209   m_hooks_p = false;
210 }
211 
212 template <typename ID, int N>
213 void
append(ID id,INKContInternal * cont)214 FeatureAPIHooks<ID, N>::append(ID id, INKContInternal *cont)
215 {
216   if (is_valid(id)) {
217     m_hooks_p = true;
218     m_hooks[id].append(cont);
219   }
220 }
221 
222 template <typename ID, int N>
223 APIHook *
get(ID id) const224 FeatureAPIHooks<ID, N>::get(ID id) const
225 {
226   return likely(is_valid(id)) ? m_hooks[id].head() : nullptr;
227 }
228 
operator [](ID id) const229 template <typename ID, int N> APIHooks const *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() const245 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() const271   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() const382 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