Monero
http_server_handlers_map2.h
Go to the documentation of this file.
1 // Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are met:
6 // * Redistributions of source code must retain the above copyright
7 // notice, this list of conditions and the following disclaimer.
8 // * Redistributions in binary form must reproduce the above copyright
9 // notice, this list of conditions and the following disclaimer in the
10 // documentation and/or other materials provided with the distribution.
11 // * Neither the name of the Andrey N. Sabelnikov nor the
12 // names of its contributors may be used to endorse or promote products
13 // derived from this software without specific prior written permission.
14 //
15 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16 // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER BE LIABLE FOR ANY
19 // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22 // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 //
26 
27 
28 #pragma once
29 #include "http_base.h"
30 #include "jsonrpc_structs.h"
33 
34 #undef MONERO_DEFAULT_LOG_CATEGORY
35 #define MONERO_DEFAULT_LOG_CATEGORY "net.http"
36 
37 
38 #define CHAIN_HTTP_TO_MAP2(context_type) bool handle_http_request(const epee::net_utils::http::http_request_info& query_info, \
39  epee::net_utils::http::http_response_info& response, \
40  context_type& m_conn_context) \
41 {\
42  MINFO("HTTP [" << m_conn_context.m_remote_address.host_str() << "] " << query_info.m_http_method_str << " " << query_info.m_URI); \
43  response.m_response_code = 200; \
44  response.m_response_comment = "Ok"; \
45  try \
46  { \
47  if(!handle_http_request_map(query_info, response, m_conn_context)) \
48  {response.m_response_code = 404;response.m_response_comment = "Not found";} \
49  } \
50  catch (const std::exception &e) \
51  { \
52  MERROR(m_conn_context << "Exception in handle_http_request_map: " << e.what()); \
53  response.m_response_code = 500; \
54  response.m_response_comment = "Internal Server Error"; \
55  } \
56  return true; \
57 }
58 
59 
60 #define BEGIN_URI_MAP2() template<class t_context> bool handle_http_request_map(const epee::net_utils::http::http_request_info& query_info, \
61  epee::net_utils::http::http_response_info& response_info, \
62  t_context& m_conn_context) { \
63  bool handled = false; \
64  if(false) return true; //just a stub to have "else if"
65 
66 #define MAP_URI2(pattern, callback) else if(std::string::npos != query_info.m_URI.find(pattern)) return callback(query_info, response_info, &m_conn_context);
67 
68 #define MAP_URI_AUTO_XML2(s_pattern, callback_f, command_type) //TODO: don't think i ever again will use xml - ambiguous and "overtagged" format
69 
70 #define MAP_URI_AUTO_JON2_IF(s_pattern, callback_f, command_type, cond) \
71  else if((query_info.m_URI == s_pattern) && (cond)) \
72  { \
73  handled = true; \
74  uint64_t ticks = epee::misc_utils::get_tick_count(); \
75  boost::value_initialized<command_type::request> req; \
76  bool parse_res = epee::serialization::load_t_from_json(static_cast<command_type::request&>(req), query_info.m_body); \
77  if (!parse_res) \
78  { \
79  MERROR("Failed to parse json: \r\n" << query_info.m_body); \
80  response_info.m_response_code = 400; \
81  response_info.m_response_comment = "Bad request"; \
82  return true; \
83  } \
84  uint64_t ticks1 = epee::misc_utils::get_tick_count(); \
85  boost::value_initialized<command_type::response> resp;\
86  MINFO(m_conn_context << "calling " << s_pattern); \
87  bool res = false; \
88  try { res = callback_f(static_cast<command_type::request&>(req), static_cast<command_type::response&>(resp), &m_conn_context); } \
89  catch (const std::exception &e) { MERROR(m_conn_context << "Failed to " << #callback_f << "(): " << e.what()); } \
90  if (!res) \
91  { \
92  response_info.m_response_code = 500; \
93  response_info.m_response_comment = "Internal Server Error"; \
94  return true; \
95  } \
96  uint64_t ticks2 = epee::misc_utils::get_tick_count(); \
97  epee::serialization::store_t_to_json(static_cast<command_type::response&>(resp), response_info.m_body); \
98  uint64_t ticks3 = epee::misc_utils::get_tick_count(); \
99  response_info.m_mime_tipe = "application/json"; \
100  response_info.m_header_info.m_content_type = " application/json"; \
101  MDEBUG( s_pattern << " processed with " << ticks1-ticks << "/"<< ticks2-ticks1 << "/" << ticks3-ticks2 << "ms"); \
102  }
103 
104 #define MAP_URI_AUTO_JON2(s_pattern, callback_f, command_type) MAP_URI_AUTO_JON2_IF(s_pattern, callback_f, command_type, true)
105 
106 #define MAP_URI_AUTO_BIN2(s_pattern, callback_f, command_type) \
107  else if(query_info.m_URI == s_pattern) \
108  { \
109  handled = true; \
110  uint64_t ticks = epee::misc_utils::get_tick_count(); \
111  boost::value_initialized<command_type::request> req; \
112  bool parse_res = epee::serialization::load_t_from_binary(static_cast<command_type::request&>(req), epee::strspan<uint8_t>(query_info.m_body)); \
113  if (!parse_res) \
114  { \
115  MERROR("Failed to parse bin body data, body size=" << query_info.m_body.size()); \
116  response_info.m_response_code = 400; \
117  response_info.m_response_comment = "Bad request"; \
118  return true; \
119  } \
120  uint64_t ticks1 = epee::misc_utils::get_tick_count(); \
121  boost::value_initialized<command_type::response> resp;\
122  MINFO(m_conn_context << "calling " << s_pattern); \
123  bool res = false; \
124  try { res = callback_f(static_cast<command_type::request&>(req), static_cast<command_type::response&>(resp), &m_conn_context); } \
125  catch (const std::exception &e) { MERROR(m_conn_context << "Failed to " << #callback_f << "()"); } \
126  if (!res) \
127  { \
128  response_info.m_response_code = 500; \
129  response_info.m_response_comment = "Internal Server Error"; \
130  return true; \
131  } \
132  uint64_t ticks2 = epee::misc_utils::get_tick_count(); \
133  epee::byte_slice buffer; \
134  epee::serialization::store_t_to_binary(static_cast<command_type::response&>(resp), buffer, 64 * 1024); \
135  uint64_t ticks3 = epee::misc_utils::get_tick_count(); \
136  response_info.m_body.assign(reinterpret_cast<const char*>(buffer.data()), buffer.size()); \
137  response_info.m_mime_tipe = " application/octet-stream"; \
138  response_info.m_header_info.m_content_type = " application/octet-stream"; \
139  MDEBUG( s_pattern << "() processed with " << ticks1-ticks << "/"<< ticks2-ticks1 << "/" << ticks3-ticks2 << "ms"); \
140  }
141 
142 #define CHAIN_URI_MAP2(callback) else {callback(query_info, response_info, m_conn_context);handled = true;}
143 
144 #define END_URI_MAP2() return handled;}
145 
146 
147 #define BEGIN_JSON_RPC_MAP(uri) else if(query_info.m_URI == uri) \
148  { \
149  uint64_t ticks = epee::misc_utils::get_tick_count(); \
150  response_info.m_mime_tipe = "application/json"; \
151  epee::serialization::portable_storage ps; \
152  if(!ps.load_from_json(query_info.m_body)) \
153  { \
154  boost::value_initialized<epee::json_rpc::error_response> rsp; \
155  static_cast<epee::json_rpc::error_response&>(rsp).jsonrpc = "2.0"; \
156  static_cast<epee::json_rpc::error_response&>(rsp).error.code = -32700; \
157  static_cast<epee::json_rpc::error_response&>(rsp).error.message = "Parse error"; \
158  epee::serialization::store_t_to_json(static_cast<epee::json_rpc::error_response&>(rsp), response_info.m_body); \
159  return true; \
160  } \
161  epee::serialization::storage_entry id_; \
162  id_ = epee::serialization::storage_entry(std::string()); \
163  ps.get_value("id", id_, nullptr); \
164  std::string callback_name; \
165  if(!ps.get_value("method", callback_name, nullptr)) \
166  { \
167  epee::json_rpc::error_response rsp; \
168  rsp.jsonrpc = "2.0"; \
169  rsp.error.code = -32600; \
170  rsp.error.message = "Invalid Request"; \
171  epee::serialization::store_t_to_json(static_cast<epee::json_rpc::error_response&>(rsp), response_info.m_body); \
172  return true; \
173  } \
174  epee::serialization::storage_entry params_; \
175  params_ = epee::serialization::storage_entry(epee::serialization::section()); \
176  if(!ps.get_value("params", params_, nullptr)) \
177  { \
178  epee::serialization::section params_section; \
179  ps.set_value("params", std::move(params_section), nullptr); \
180  } \
181  if(false) return true; //just a stub to have "else if"
182 
183 
184 #define PREPARE_OBJECTS_FROM_JSON(command_type) \
185  handled = true; \
186  response_info.m_mime_tipe = "application/json"; \
187  boost::value_initialized<epee::json_rpc::request<command_type::request> > req_; \
188  epee::json_rpc::request<command_type::request>& req = static_cast<epee::json_rpc::request<command_type::request>&>(req_);\
189  if(!req.load(ps)) \
190  { \
191  epee::json_rpc::error_response fail_resp = AUTO_VAL_INIT(fail_resp); \
192  fail_resp.jsonrpc = "2.0"; \
193  fail_resp.id = req.id; \
194  fail_resp.error.code = -32602; \
195  fail_resp.error.message = "Invalid params"; \
196  epee::serialization::store_t_to_json(static_cast<epee::json_rpc::error_response&>(fail_resp), response_info.m_body); \
197  return true; \
198  } \
199  uint64_t ticks1 = epee::misc_utils::get_tick_count(); \
200  boost::value_initialized<epee::json_rpc::response<command_type::response, epee::json_rpc::dummy_error> > resp_; \
201  epee::json_rpc::response<command_type::response, epee::json_rpc::dummy_error>& resp = static_cast<epee::json_rpc::response<command_type::response, epee::json_rpc::dummy_error> &>(resp_); \
202  resp.jsonrpc = "2.0"; \
203  resp.id = req.id;
204 
205 #define FINALIZE_OBJECTS_TO_JSON(method_name) \
206  uint64_t ticks2 = epee::misc_utils::get_tick_count(); \
207  epee::serialization::store_t_to_json(resp, response_info.m_body); \
208  uint64_t ticks3 = epee::misc_utils::get_tick_count(); \
209  response_info.m_mime_tipe = "application/json"; \
210  response_info.m_header_info.m_content_type = " application/json"; \
211  MDEBUG( query_info.m_URI << "[" << method_name << "] processed with " << ticks1-ticks << "/"<< ticks2-ticks1 << "/" << ticks3-ticks2 << "ms");
212 
213 #define MAP_JON_RPC_WE_IF(method_name, callback_f, command_type, cond) \
214  else if((callback_name == method_name) && (cond)) \
215 { \
216  PREPARE_OBJECTS_FROM_JSON(command_type) \
217  epee::json_rpc::error_response fail_resp = AUTO_VAL_INIT(fail_resp); \
218  fail_resp.jsonrpc = "2.0"; \
219  fail_resp.id = req.id; \
220  MINFO(m_conn_context << "Calling RPC method " << method_name); \
221  bool res = false; \
222  try { res = callback_f(req.params, resp.result, fail_resp.error, &m_conn_context); } \
223  catch (const std::exception &e) { MERROR(m_conn_context << "Failed to " << #callback_f << "(): " << e.what()); } \
224  if (!res) \
225  { \
226  epee::serialization::store_t_to_json(static_cast<epee::json_rpc::error_response&>(fail_resp), response_info.m_body); \
227  return true; \
228  } \
229  FINALIZE_OBJECTS_TO_JSON(method_name) \
230  return true;\
231 }
232 
233 #define MAP_JON_RPC_WE(method_name, callback_f, command_type) MAP_JON_RPC_WE_IF(method_name, callback_f, command_type, true)
234 
235 #define MAP_JON_RPC_WERI(method_name, callback_f, command_type) \
236  else if(callback_name == method_name) \
237 { \
238  PREPARE_OBJECTS_FROM_JSON(command_type) \
239  epee::json_rpc::error_response fail_resp = AUTO_VAL_INIT(fail_resp); \
240  fail_resp.jsonrpc = "2.0"; \
241  fail_resp.id = req.id; \
242  MINFO(m_conn_context << "calling RPC method " << method_name); \
243  bool res = false; \
244  try { res = callback_f(req.params, resp.result, fail_resp.error, response_info, &m_conn_context); } \
245  catch (const std::exception &e) { MERROR(m_conn_context << "Failed to " << #callback_f << "(): " << e.what()); } \
246  if (!res) \
247  { \
248  epee::serialization::store_t_to_json(static_cast<epee::json_rpc::error_response&>(fail_resp), response_info.m_body); \
249  return true; \
250  } \
251  FINALIZE_OBJECTS_TO_JSON(method_name) \
252  return true;\
253 }
254 
255 #define MAP_JON_RPC(method_name, callback_f, command_type) \
256  else if(callback_name == method_name) \
257 { \
258  PREPARE_OBJECTS_FROM_JSON(command_type) \
259  MINFO(m_conn_context << "calling RPC method " << method_name); \
260  bool res = false; \
261  try { res = callback_f(req.params, resp.result, &m_conn_context); } \
262  catch (const std::exception &e) { MERROR(m_conn_context << "Failed to " << #callback_f << "(): " << e.what()); } \
263  if (!res) \
264  { \
265  epee::json_rpc::error_response fail_resp = AUTO_VAL_INIT(fail_resp); \
266  fail_resp.jsonrpc = "2.0"; \
267  fail_resp.id = req.id; \
268  fail_resp.error.code = -32603; \
269  fail_resp.error.message = "Internal error"; \
270  epee::serialization::store_t_to_json(static_cast<epee::json_rpc::error_response&>(fail_resp), response_info.m_body); \
271  return true; \
272  } \
273  FINALIZE_OBJECTS_TO_JSON(method_name) \
274  return true;\
275 }
276 
277 #define END_JSON_RPC_MAP() \
278  epee::json_rpc::error_response rsp; \
279  rsp.id = id_; \
280  rsp.jsonrpc = "2.0"; \
281  rsp.error.code = -32601; \
282  rsp.error.message = "Method not found"; \
283  epee::serialization::store_t_to_json(static_cast<epee::json_rpc::error_response&>(rsp), response_info.m_body); \
284  return true; \
285 }
286 
287