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   StatPages.cc
27 
28 
29  ****************************************************************************/
30 
31 #include "tscore/ink_config.h"
32 #include "ProxyConfig.h"
33 #include "StatPages.h"
34 #include "HdrUtils.h"
35 #include "tscore/MatcherUtils.h"
36 
37 #define MAX_STAT_PAGES 32
38 
39 // Globals
40 StatPagesManager statPagesManager;
41 
42 static struct {
43   char *module;
44   StatPagesFunc func;
45 } stat_pages[MAX_STAT_PAGES];
46 
47 static int n_stat_pages;
48 
49 void
init()50 StatPagesManager::init()
51 {
52   ink_mutex_init(&stat_pages_mutex);
53   REC_EstablishStaticConfigInt32(m_enabled, "proxy.config.http_ui_enabled");
54 }
55 
56 void
register_http(const char * module,StatPagesFunc func)57 StatPagesManager::register_http(const char *module, StatPagesFunc func)
58 {
59   ink_mutex_acquire(&stat_pages_mutex);
60   ink_release_assert(n_stat_pages < MAX_STAT_PAGES);
61 
62   stat_pages[n_stat_pages].module = static_cast<char *>(ats_malloc(strlen(module) + 3));
63   snprintf(stat_pages[n_stat_pages].module, strlen(module) + 3, "{%s}", module);
64   stat_pages[n_stat_pages++].func = func;
65   ink_mutex_release(&stat_pages_mutex);
66 }
67 
68 Action *
handle_http(Continuation * cont,HTTPHdr * header)69 StatPagesManager::handle_http(Continuation *cont, HTTPHdr *header)
70 {
71   URL *url = header->url_get();
72 
73   if (((m_enabled == 1 || m_enabled == 3) && is_cache_inspector_page(url)) ||
74       ((m_enabled == 2 || m_enabled == 3) && is_stat_page(url) && !is_cache_inspector_page(url))) {
75     int host_len;
76     char host[MAXDNAME + 1];
77     const char *h;
78     int i;
79 
80     h = url->host_get(&host_len);
81     if (host_len > MAXDNAME) {
82       host_len = MAXDNAME;
83     }
84     memcpy(host, h, host_len);
85     host[host_len] = '\0';
86     host_len       = unescapifyStr(host);
87 
88     for (i = 0; i < n_stat_pages; i++) {
89       if (strlen(host) == strlen(stat_pages[i].module) && strncmp(host, stat_pages[i].module, host_len) == 0) {
90         return stat_pages[i].func(cont, header);
91       }
92     }
93   }
94 
95   cont->handleEvent(STAT_PAGE_FAILURE, nullptr);
96   return ACTION_RESULT_DONE;
97 }
98 
99 bool
is_stat_page(URL * url)100 StatPagesManager::is_stat_page(URL *url)
101 {
102   // This gets called from the state machine, so we should optimize here and not in caller.
103   if (m_enabled <= 0) {
104     return false;
105   }
106 
107   int length;
108   const char *h = url->host_get(&length);
109   char host[MAXDNAME + 1];
110 
111   if (h == nullptr || length < 2 || length > MAXDNAME) {
112     return false;
113   }
114 
115   memcpy(host, h, length);
116   host[length] = '\0';
117   length       = unescapifyStr(host);
118 
119   if ((host[0] == '{') && (host[length - 1] == '}')) {
120     return true;
121   }
122 
123   return false;
124 }
125 
126 bool
is_cache_inspector_page(URL * url)127 StatPagesManager::is_cache_inspector_page(URL *url)
128 {
129   int length;
130   const char *h = url->host_get(&length);
131   char host[MAXDNAME + 1];
132 
133   if (h == nullptr || length < 2 || length > MAXDNAME) {
134     return false;
135   }
136 
137   memcpy(host, h, length);
138   host[length] = '\0';
139   length       = unescapifyStr(host);
140 
141   if (strncmp(host, "{cache}", length) == 0) {
142     return true;
143   } else {
144     return false;
145   }
146 }
147 
148 void
resp_clear()149 BaseStatPagesHandler::resp_clear()
150 {
151   ats_free(response);
152   response        = nullptr;
153   response_size   = 0;
154   response_length = 0;
155 }
156 
157 void
resp_add(const char * fmt,...)158 BaseStatPagesHandler::resp_add(const char *fmt, ...)
159 {
160   va_list args;
161   char buf[16384];
162   int length;
163   int size;
164 
165   va_start(args, fmt);
166   length = vsnprintf(buf, 16384, fmt, args);
167   va_end(args);
168 
169   size = response_size;
170   if (size == 0) {
171     size = 1024;
172   }
173   while ((response_length + length + 1) > size) {
174     size *= 2;
175   }
176 
177   if (size != response_size) {
178     if (!response) {
179       response = static_cast<char *>(ats_malloc(size));
180     } else {
181       response = static_cast<char *>(ats_realloc(response, size));
182     }
183     response_size = size;
184   }
185 
186   memcpy(&response[response_length], buf, length + 1);
187   response_length += length;
188 }
189 
190 void
resp_add_sep()191 BaseStatPagesHandler::resp_add_sep()
192 {
193   resp_add("<hr width=\"100%%\">\n");
194 }
195 
196 void
resp_begin(const char * title)197 BaseStatPagesHandler::resp_begin(const char *title)
198 {
199   resp_clear();
200   resp_add("<html>\n"
201            "<head><title>%s</title></head>\n"
202            "<body text=\"#000000\" bgcolor=\"#ffffff\" link=\"#0000ee\" vlink=\"#551a8b\" alink=\"#ff0000\">\n",
203            title);
204 }
205 
206 void
resp_end()207 BaseStatPagesHandler::resp_end()
208 {
209   resp_add("</body>\n"
210            "</html>\n");
211 }
212 
213 void
resp_begin_numbered()214 BaseStatPagesHandler::resp_begin_numbered()
215 {
216   resp_add("<ol>\n");
217 }
218 
219 void
resp_end_numbered()220 BaseStatPagesHandler::resp_end_numbered()
221 {
222   resp_add("</ol>\n");
223 }
224 
225 void
resp_begin_unnumbered()226 BaseStatPagesHandler::resp_begin_unnumbered()
227 {
228   resp_add("<ul>\n");
229 }
230 
231 void
resp_end_unnumbered()232 BaseStatPagesHandler::resp_end_unnumbered()
233 {
234   resp_add("</ul>\n");
235 }
236 
237 void
resp_begin_item()238 BaseStatPagesHandler::resp_begin_item()
239 {
240   resp_add("<li>\n");
241 }
242 
243 void
resp_end_item()244 BaseStatPagesHandler::resp_end_item()
245 {
246   resp_add("</li>\n");
247 }
248 
249 void
resp_begin_table(int border,int columns,int percent)250 BaseStatPagesHandler::resp_begin_table(int border, int columns, int percent)
251 {
252   resp_add("<table border=%d cols=%d width=\"%d%%\">\n", border, columns, percent);
253 }
254 
255 void
resp_end_table()256 BaseStatPagesHandler::resp_end_table()
257 {
258   resp_add("</table>\n");
259 }
260 
261 void
resp_begin_row()262 BaseStatPagesHandler::resp_begin_row()
263 {
264   resp_add("<tr>\n");
265 }
266 
267 void
resp_end_row()268 BaseStatPagesHandler::resp_end_row()
269 {
270   resp_add("</tr>\n");
271 }
272 
273 void
resp_begin_column(int percent,const char * align)274 BaseStatPagesHandler::resp_begin_column(int percent, const char *align)
275 {
276   if (percent == -1) {
277     resp_add("<td %s%s>\n", align ? "align=" : "", align ? align : "");
278   } else {
279     resp_add("<td width=\"%d%%\" %s%s>\n", percent, align ? "align=" : "", align ? align : "");
280   }
281 }
282 
283 void
resp_end_column()284 BaseStatPagesHandler::resp_end_column()
285 {
286   resp_add("</td>\n");
287 }
288