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