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 #pragma once
25 
26 #include <string>
27 #include <map>
28 #include "lib/ComponentBase.h"
29 #include "lib/StringHash.h"
30 #include "lib/DocNode.h"
31 #include "EsiParser.h"
32 #include "HttpDataFetcher.h"
33 #include "Variables.h"
34 #include "Expression.h"
35 #include "SpecialIncludeHandler.h"
36 #include "HandlerManager.h"
37 
38 class EsiProcessor : private EsiLib::ComponentBase
39 {
40 public:
41   enum UsePackedNodeResult {
42     PROCESS_IN_PROGRESS,
43     UNPACK_FAILURE,
44     PROCESS_SUCCESS,
45     PROCESS_FAILURE,
46   };
47 
48   EsiProcessor(const char *debug_tag, const char *parser_debug_tag, const char *expression_debug_tag,
49                EsiLib::ComponentBase::Debug debug_func, EsiLib::ComponentBase::Error error_func, HttpDataFetcher &fetcher,
50                EsiLib::Variables &variables, const EsiLib::HandlerManager &handler_mgr);
51 
52   /** Initializes the processor with the context of the request to be processed */
53   bool start();
54 
55   /** Adds data to be parsed */
56   bool addParseData(const char *data, int data_len = -1);
57 
58   /** convenient alternative to method above */
59   bool
addParseData(const std::string & data)60   addParseData(const std::string &data)
61   {
62     return addParseData(data.data(), data.size());
63   }
64 
65   /** Tells processor to wrap-up parsing; a final or the only piece of
66    * data can be optionally provided */
67   bool completeParse(const char *data = nullptr, int data_len = -1);
68 
69   /** convenient alternative to method above */
70   bool
completeParse(const std::string & data)71   completeParse(const std::string &data)
72   {
73     return completeParse(data.data(), data.size());
74   }
75 
76   enum ReturnCode {
77     FAILURE,
78     SUCCESS,
79     NEED_MORE_DATA,
80   };
81 
82   /** Processes the currently parsed ESI document and returns processed
83    * data in supplied out-parameters. Should be called when fetcher has
84    * finished pulling in all data.
85    *
86    * try/attempt/except construct can generate new fetch requests
87    * during processing. Only in such cases is NEED_MORE_DATA returned;
88    * else FAILURE/SUCCESS is returned. */
89   ReturnCode process(const char *&data, int &data_len);
90 
91   /** Process the ESI document and flush processed data as much as
92    * possible. Can be called when fetcher hasn't finished pulling
93    * in all data. */
94   ReturnCode flush(std::string &data, int &overall_len);
95 
96   /** returns packed version of document currently being processed */
97   void
packNodeList(std::string & buffer,bool retain_buffer_data)98   packNodeList(std::string &buffer, bool retain_buffer_data)
99   {
100     return _node_list.pack(buffer, retain_buffer_data);
101   }
102 
103   /** Unpacks previously parsed and packed ESI node list from given
104    * buffer and preps for process(); Unpacked document will point to
105    * data in argument (i.e., caller space) */
106   UsePackedNodeResult usePackedNodeList(const char *data, int data_len);
107 
108   /** convenient alternative to method above */
109   inline UsePackedNodeResult
usePackedNodeList(const std::string & data)110   usePackedNodeList(const std::string &data)
111   {
112     return usePackedNodeList(data.data(), data.size());
113   }
114 
115   /** Clears state from current request */
116   void stop();
117 
118   virtual ~EsiProcessor();
119 
120 private:
121   enum EXEC_STATE {
122     STOPPED,
123     PARSING,
124     WAITING_TO_PROCESS,
125     PROCESSED,
126     ERRORED,
127   };
128   EXEC_STATE _curr_state;
129 
130   std::string _output_data;
131 
132   EsiParser _parser;
133   EsiLib::DocNodeList _node_list;
134   int _n_prescanned_nodes;
135   int _n_processed_nodes;
136   int _n_processed_try_nodes;
137   int _overall_len;
138 
139   HttpDataFetcher &_fetcher;
140   EsiLib::StringHash _include_urls;
141 
142   bool _usePackedNodeList;
143 
144   bool _processEsiNode(const EsiLib::DocNodeList::iterator &iter);
145   bool _handleParseComplete();
146   bool _getIncludeData(const EsiLib::DocNode &node, const char **content_ptr = nullptr, int *content_len_ptr = nullptr);
147   DataStatus _getIncludeStatus(const EsiLib::DocNode &node);
148   bool _handleVars(const char *str, int str_len);
149   bool _handleChoose(EsiLib::DocNodeList::iterator &curr_node);
150   bool _handleTry(EsiLib::DocNodeList::iterator &curr_node);
151   bool _handleHtmlComment(const EsiLib::DocNodeList::iterator &curr_node);
152   bool _preprocess(EsiLib::DocNodeList &node_list, int &n_prescanned_nodes);
153   inline bool _isWhitespace(const char *data, int data_len);
154   void _addFooterData();
155 
156   EsiLib::Variables &_esi_vars;
157   EsiLib::Expression _expression;
158 
159   struct TryBlock {
160     EsiLib::DocNodeList &attempt_nodes;
161     EsiLib::DocNodeList &except_nodes;
162     EsiLib::DocNodeList::iterator pos;
TryBlockTryBlock163     TryBlock(EsiLib::DocNodeList &att, EsiLib::DocNodeList &exc, EsiLib::DocNodeList::iterator p)
164       : attempt_nodes(att), except_nodes(exc), pos(p){};
165   };
166   typedef std::list<TryBlock> TryBlockList;
167   TryBlockList _try_blocks;
168   int _n_try_blocks_processed;
169 
170   const EsiLib::HandlerManager &_handler_manager;
171 
172   static const char *INCLUDE_DATA_ID_ATTR;
173 
174   typedef std::map<std::string, EsiLib::SpecialIncludeHandler *> IncludeHandlerMap;
175   IncludeHandlerMap _include_handlers;
176 
177   void
error()178   error()
179   {
180     stop();
181     _curr_state = ERRORED;
182   }
183 };
184