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   HttpBodyFactory.h
27 
28   This implements a user-customizable response message generation system.
29 
30   The concept is simple.  Error/response messages are classified into
31   several types, each given a name, such as "request/header_error".
32 
33   The HttpBodyFactory can build a message body for each response type.
34   The user can create custom message body text for each type (stored
35   in a text file directory), containing templates with space-holders for
36   variables which are inline-substituted with current values.  The resulting
37   body is dynamically allocated and returned.
38 
39   The major data types implemented in this file are:
40 
41     HttpBodyFactory       The main data structure which is the machine
42                           that maintains configuration information, reads
43                           user error message template files, and performs
44                           the substitution to generate the message bodies.
45 
46     HttpBodySet           The data structure representing a set of
47                           templates, including the templates and metadata.
48 
49     HttpBodyTemplate      The template loaded from the directory to be
50                           instantiated with variables, producing a body.
51 
52 
53  ****************************************************************************/
54 
55 #pragma once
56 
57 #include <strings.h>
58 #include <sys/types.h>
59 #include "tscore/ink_platform.h"
60 #include "HTTP.h"
61 #include "HttpConfig.h"
62 #include "HttpCompat.h"
63 #include "HttpTransact.h"
64 #include "tscore/ink_sprintf.h"
65 
66 #include <memory>
67 #include <unordered_map>
68 
69 #define HTTP_BODY_TEMPLATE_MAGIC 0xB0DFAC00
70 #define HTTP_BODY_SET_MAGIC 0xB0DFAC55
71 #define HTTP_BODY_FACTORY_MAGIC 0xB0DFACFF
72 
73 ////////////////////////////////////////////////////////////////////////
74 //
75 //      class HttpBodyTemplate
76 //
77 //      An HttpBodyTemplate object represents a template with HTML
78 //      text, and unexpanded log fields.  The object also has methods
79 //      to dump out the contents of the template, and to instantiate
80 //      the template into a buffer given a context.
81 //
82 ////////////////////////////////////////////////////////////////////////
83 
84 class HttpBodyTemplate
85 {
86 public:
87   HttpBodyTemplate();
88   ~HttpBodyTemplate();
89 
90   void reset();
91   int load_from_file(char *dir, char *file);
92   bool
is_sane()93   is_sane()
94   {
95     return (magic == HTTP_BODY_TEMPLATE_MAGIC);
96   }
97   char *build_instantiated_buffer(HttpTransact::State *context, int64_t *length_return);
98 
99   unsigned int magic;
100   int64_t byte_count;
101   char *template_buffer;
102   char *template_pathname;
103 };
104 
105 ////////////////////////////////////////////////////////////////////////
106 //
107 //      class HttpBodySetRawData
108 //
109 //      Raw data members of HttpBodySet
110 //
111 ////////////////////////////////////////////////////////////////////////
112 
113 struct HttpBodySetRawData {
114   using TemplateTable = std::unordered_map<std::string, HttpBodyTemplate *>;
115   unsigned int magic  = 0;
116   char *set_name;
117   char *content_language;
118   char *content_charset;
119   std::unique_ptr<TemplateTable> table_of_pages;
120 };
121 
122 ////////////////////////////////////////////////////////////////////////
123 //
124 //      class HttpBodySet
125 //
126 //      An HttpBodySet object represents a set of body factory
127 //      templates.  It includes operators to get the hash table of
128 //      templates, and the associated metadata for the set.
129 //
130 //      The raw data members come from HttpBodySetRawData above
131 //
132 ////////////////////////////////////////////////////////////////////////
133 
134 class HttpBodySet : public HttpBodySetRawData
135 {
136 public:
137   HttpBodySet();
138   ~HttpBodySet();
139 
140   int init(char *set_name, char *dir);
141   bool
is_sane()142   is_sane()
143   {
144     return (magic == HTTP_BODY_SET_MAGIC);
145   }
146 
147   HttpBodyTemplate *get_template_by_name(const char *name);
148   void set_template_by_name(const char *name, HttpBodyTemplate *t);
149 };
150 
151 ////////////////////////////////////////////////////////////////////////
152 //
153 //      class HttpBodyFactory
154 //
155 //      An HttpBodyFactory object is the main object which keeps track
156 //      of all the response body templates, and which provides the
157 //      methods to create response bodies.
158 //
159 //      Once an HttpBodyFactory object is initialized, and the template
160 //      data has been loaded, the HttpBodyFactory object allows the
161 //      caller to make error message bodies w/fabricate_with_old_api
162 //
163 ////////////////////////////////////////////////////////////////////////
164 
165 class HttpBodyFactory
166 {
167 public:
168   using BodySetTable = std::unordered_map<std::string, HttpBodySetRawData *>;
169   HttpBodyFactory();
170   ~HttpBodyFactory();
171 
172   ///////////////////////
173   // primary user APIs //
174   ///////////////////////
175   char *fabricate_with_old_api(const char *type, HttpTransact::State *context, int64_t max_buffer_length,
176                                int64_t *resulting_buffer_length, char *content_language_out_buf, size_t content_language_buf_size,
177                                char *content_type_out_buf, size_t content_type_buf_size, int format_size, const char *format);
178 
179   char *
getFormat(int64_t max_buffer_length,int64_t * resulting_buffer_length,const char * format,...)180   getFormat(int64_t max_buffer_length, int64_t *resulting_buffer_length, const char *format, ...)
181   {
182     char *msg = nullptr;
183     if (format) {
184       va_list ap;
185 
186       va_start(ap, format);
187 
188       // The length from ink_bvsprintf includes the trailing NUL, so adjust the final
189       // length accordingly. Note that ink_bvsprintf() copies the va_list, so we only
190       // have to set it up once.
191       int l = ink_bvsprintf(nullptr, format, ap);
192 
193       if (l <= max_buffer_length) {
194         msg                      = (char *)ats_malloc(l);
195         *resulting_buffer_length = ink_bvsprintf(msg, format, ap) - 1;
196       }
197       va_end(ap);
198     }
199     return msg;
200   }
201 
202   void dump_template_tables(FILE *fp = stderr);
203   void reconfigure();
204   static const char *determine_set_by_language(std::unique_ptr<BodySetTable> &table_of_sets, StrList *acpt_language_list,
205                                                StrList *acpt_charset_list, float *Q_best_ptr, int *La_best_ptr, int *Lc_best_ptr,
206                                                int *I_best_ptr);
207 
208 private:
209   char *fabricate(StrList *acpt_language_list, StrList *acpt_charset_list, const char *type, HttpTransact::State *context,
210                   int64_t *resulting_buffer_length, const char **content_language_return, const char **content_charset_return,
211                   const char **set_return = nullptr);
212 
213   const char *determine_set_by_language(StrList *acpt_language_list, StrList *acpt_charset_list);
214   const char *determine_set_by_host(HttpTransact::State *context);
215   HttpBodyTemplate *find_template(const char *set, const char *type, HttpBodySet **body_set_return);
216   bool is_response_suppressed(HttpTransact::State *context);
217   bool
is_sane()218   is_sane()
219   {
220     return (magic == HTTP_BODY_FACTORY_MAGIC);
221   }
222   void
sanity_check()223   sanity_check()
224   {
225     ink_assert(is_sane());
226   }
227 
228 private:
229   ////////////////////////////
230   // initialization methods //
231   ////////////////////////////
232   void nuke_template_tables();
233   std::unique_ptr<BodySetTable> load_sets_from_directory(char *set_dir);
234   HttpBodySet *load_body_set_from_directory(char *set_name, char *tmpl_dir);
235 
236   /////////////////////////////////////////////////
237   // internal data structure concurrency control //
238   /////////////////////////////////////////////////
239   void
lock()240   lock()
241   {
242     ink_mutex_acquire(&mutex);
243   }
244   void
unlock()245   unlock()
246   {
247     ink_mutex_release(&mutex);
248   }
249 
250   /////////////////////////////////////
251   // manager configuration variables //
252   /////////////////////////////////////
253   int enable_customizations     = 0;    // 0:no custom,1:custom,2:language-targeted
254   bool enable_logging           = true; // the user wants body factory logging
255   int response_suppression_mode = 0;    // when to suppress responses
256 
257   ////////////////////
258   // internal state //
259   ////////////////////
260   unsigned int magic = HTTP_BODY_FACTORY_MAGIC; // magic for sanity checks/debugging
261   ink_mutex mutex;                              // prevents reconfig/read races
262   bool callbacks_established = false;           // all config variables present
263   std::unique_ptr<BodySetTable> table_of_sets;  // sets of template hash tables
264 };
265