1 /** @file
2 
3   Class to execute one (or more) remap plugin(s).
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 #include "RemapPlugins.h"
26 
27 ClassAllocator<RemapPlugins> pluginAllocator("RemapPluginsAlloc");
28 
29 TSRemapStatus
run_plugin(RemapPluginInst * plugin)30 RemapPlugins::run_plugin(RemapPluginInst *plugin)
31 {
32   ink_assert(_s);
33 
34   TSRemapStatus plugin_retcode;
35   TSRemapRequestInfo rri;
36   URL *map_from = _s->url_map.getFromURL();
37   URL *map_to   = _s->url_map.getToURL();
38 
39   // This is the equivalent of TSHttpTxnClientReqGet(), which every remap plugin would
40   // have to call.
41   rri.requestBufp = reinterpret_cast<TSMBuffer>(_request_header);
42   rri.requestHdrp = reinterpret_cast<TSMLoc>(_request_header->m_http);
43 
44   // Read-only URL's (TSMLoc's to the SDK)
45   rri.mapFromUrl = reinterpret_cast<TSMLoc>(map_from->m_url_impl);
46   rri.mapToUrl   = reinterpret_cast<TSMLoc>(map_to->m_url_impl);
47   rri.requestUrl = reinterpret_cast<TSMLoc>(_request_url->m_url_impl);
48 
49   rri.redirect = 0;
50 
51   // Prepare State for the future
52   if (_cur == 0) {
53     _s->os_response_plugin_inst = plugin;
54   }
55 
56   plugin_retcode = plugin->doRemap(reinterpret_cast<TSHttpTxn>(_s->state_machine), &rri);
57   // TODO: Deal with negative return codes here
58   if (plugin_retcode < 0) {
59     plugin_retcode = TSREMAP_NO_REMAP;
60   }
61 
62   // First step after plugin remap must be "redirect url" check
63   if ((TSREMAP_DID_REMAP == plugin_retcode || TSREMAP_DID_REMAP_STOP == plugin_retcode) && rri.redirect) {
64     _s->remap_redirect = _request_url->string_get(nullptr);
65   }
66 
67   return plugin_retcode;
68 }
69 
70 /**
71   This is the equivalent of the old DoRemap().
72 
73   @return 1 when you are done doing crap (otherwise, you get re-called
74     with schedule_imm and i hope you have something more to do), else
75     0 if you have something more do do (this isn't strict and we check
76     there actually *is* something to do).
77 
78 */
79 bool
run_single_remap()80 RemapPlugins::run_single_remap()
81 {
82   url_mapping *map             = _s->url_map.getMapping();
83   RemapPluginInst *plugin      = map->get_plugin_instance(_cur); // get the nth plugin in our list of plugins
84   TSRemapStatus plugin_retcode = TSREMAP_NO_REMAP;
85   bool zret                    = true; // default - last iteration.
86   Debug("url_rewrite", "running single remap rule id %d for the %d%s time", map->map_id, _cur,
87         _cur == 1 ? "st" : _cur == 2 ? "nd" : _cur == 3 ? "rd" : "th");
88 
89   if (0 == _cur) {
90     Debug("url_rewrite", "setting the remapped url by copying from mapping rule");
91     url_rewrite_remap_request(_s->url_map, _request_url, _s->hdr_info.client_request.method_get_wksidx());
92   }
93 
94   // There might not be a plugin if we are a regular non-plugin map rule. In that case, we will fall through
95   // and do the default mapping and then stop.
96   if (plugin) {
97     plugin_retcode = run_plugin(plugin);
98   }
99 
100   ++_cur;
101 
102   // If the plugin redirected, we need to end the remap chain now. Otherwise see what's next.
103   if (!_s->remap_redirect) {
104     if (TSREMAP_DID_REMAP_STOP == plugin_retcode || TSREMAP_DID_REMAP == plugin_retcode) {
105       ++_rewritten;
106     }
107 
108     if (TSREMAP_NO_REMAP_STOP == plugin_retcode || TSREMAP_DID_REMAP_STOP == plugin_retcode) {
109       Debug("url_rewrite", "breaking remap plugin chain since last plugin said we should stop after %d rewrites", _rewritten);
110     } else if (_cur >= map->plugin_instance_count()) {
111       Debug("url_rewrite", "completed all remap plugins for rule id %d, changed by %d plugins", map->map_id, _rewritten);
112     } else {
113       Debug("url_rewrite", "completed single remap, attempting another via immediate callback");
114       zret = false; // not done yet.
115     }
116   }
117   return zret;
118 }
119 
120 int
run_remap(int event,Event * e)121 RemapPlugins::run_remap(int event, Event *e)
122 {
123   Debug("url_rewrite", "Inside RemapPlugins::run_remap with cur = %d", _cur);
124 
125   ink_assert(action.continuation);
126   ink_assert(action.continuation);
127 
128   /* make sure we weren't cancelled */
129   if (action.cancelled) {
130     mutex.clear();
131     pluginAllocator.free(this); // ugly
132     return EVENT_DONE;
133   }
134 
135   switch (event) {
136   case EVENT_IMMEDIATE:
137     Debug("url_rewrite", "handling immediate event inside RemapPlugins::run_remap");
138     /**
139      * If @c run_single_remap returns @c true then we are done with this processor and we call back
140      * into the SM; otherwise, we call this function again immediately (which really isn't
141      * immediate) thru the eventProcessor, thus forcing another run of run_single_remap() which will
142      * then operate on _request_url, etc performing additional remaps (mainly another plugin run)
143      *
144      **/
145     if (run_single_remap()) {
146       action.continuation->handleEvent(EVENT_REMAP_COMPLETE, nullptr);
147       mutex.clear();
148       action.mutex.clear();
149       mutex        = nullptr;
150       action.mutex = nullptr;
151       // THREAD_FREE(this, pluginAllocator, t);
152       pluginAllocator.free(this); // ugly
153       return EVENT_DONE;
154     } else {
155       e->schedule_imm(event);
156       return EVENT_CONT;
157     }
158 
159     break;
160   default:
161     ink_assert(!"unknown event type");
162     break;
163   };
164   return EVENT_DONE;
165 }
166