1 /* <!-- copyright */
2 /*
3 * aria2 - The high speed download utility
4 *
5 * Copyright (C) 2009 Tatsuhiro Tsujikawa
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 *
21 * In addition, as a special exception, the copyright holders give
22 * permission to link the code of portions of this program with the
23 * OpenSSL library under certain conditions as described in each
24 * individual source file, and distribute linked combinations
25 * including the two.
26 * You must obey the GNU General Public License in all respects
27 * for all of the code used other than OpenSSL. If you modify
28 * file(s) with this exception, you may extend this exception to your
29 * version of the file(s), but you are not obligated to do so. If you
30 * do not wish to do so, delete this exception statement from your
31 * version. If you delete this exception statement from all source
32 * files in the program, then also delete it here.
33 */
34 /* copyright --> */
35 #include "HttpServerBodyCommand.h"
36 #include "SocketCore.h"
37 #include "DownloadEngine.h"
38 #include "HttpServer.h"
39 #include "HttpHeader.h"
40 #include "Logger.h"
41 #include "LogFactory.h"
42 #include "RequestGroup.h"
43 #include "RequestGroupMan.h"
44 #include "RecoverableException.h"
45 #include "HttpServerResponseCommand.h"
46 #include "DelayedCommand.h"
47 #include "OptionParser.h"
48 #include "OptionHandler.h"
49 #include "wallclock.h"
50 #include "util.h"
51 #include "fmt.h"
52 #include "SocketRecvBuffer.h"
53 #include "json.h"
54 #include "DlAbortEx.h"
55 #include "message.h"
56 #include "RpcMethod.h"
57 #include "RpcMethodFactory.h"
58 #include "RpcRequest.h"
59 #include "RpcResponse.h"
60 #include "rpc_helper.h"
61 #include "JsonDiskWriter.h"
62 #include "ValueBaseJsonParser.h"
63 #ifdef ENABLE_XML_RPC
64 # include "XmlRpcRequestParserStateMachine.h"
65 # include "XmlRpcDiskWriter.h"
66 #endif // ENABLE_XML_RPC
67
68 namespace aria2 {
69
HttpServerBodyCommand(cuid_t cuid,const std::shared_ptr<HttpServer> & httpServer,DownloadEngine * e,const std::shared_ptr<SocketCore> & socket)70 HttpServerBodyCommand::HttpServerBodyCommand(
71 cuid_t cuid, const std::shared_ptr<HttpServer>& httpServer,
72 DownloadEngine* e, const std::shared_ptr<SocketCore>& socket)
73 : Command(cuid),
74 e_(e),
75 socket_(socket),
76 httpServer_(httpServer),
77 writeCheck_(false)
78 {
79 // To handle Content-Length == 0 case
80 setStatus(Command::STATUS_ONESHOT_REALTIME);
81 e_->addSocketForReadCheck(socket_, this);
82 if (!httpServer_->getSocketRecvBuffer()->bufferEmpty() ||
83 socket_->getRecvBufferedLength()) {
84 e_->setNoWait(true);
85 }
86 }
87
~HttpServerBodyCommand()88 HttpServerBodyCommand::~HttpServerBodyCommand()
89 {
90 e_->deleteSocketForReadCheck(socket_, this);
91 if (writeCheck_) {
92 e_->deleteSocketForWriteCheck(socket_, this);
93 }
94 }
95
96 namespace {
getJsonRpcContentType(bool script)97 std::string getJsonRpcContentType(bool script)
98 {
99 return script ? "text/javascript" : "application/json-rpc";
100 }
101 } // namespace
102
sendJsonRpcResponse(const rpc::RpcResponse & res,const std::string & callback)103 void HttpServerBodyCommand::sendJsonRpcResponse(const rpc::RpcResponse& res,
104 const std::string& callback)
105 {
106 bool notauthorized = rpc::not_authorized(res);
107 bool gzip = httpServer_->supportsGZip();
108 std::string responseData = rpc::toJson(res, callback, gzip);
109 if (res.code == 0) {
110 httpServer_->feedResponse(std::move(responseData),
111 getJsonRpcContentType(!callback.empty()));
112 }
113 else {
114 httpServer_->disableKeepAlive();
115 int httpCode;
116 switch (res.code) {
117 case 1:
118 // error caught while executing RpcMethod
119 httpCode = 400;
120 break;
121 case -32600:
122 httpCode = 400;
123 break;
124 case -32601:
125 httpCode = 404;
126 break;
127 default:
128 httpCode = 500;
129 };
130 httpServer_->feedResponse(httpCode, A2STR::NIL, std::move(responseData),
131 getJsonRpcContentType(!callback.empty()));
132 }
133 addHttpServerResponseCommand(notauthorized);
134 }
135
sendJsonRpcBatchResponse(const std::vector<rpc::RpcResponse> & results,const std::string & callback)136 void HttpServerBodyCommand::sendJsonRpcBatchResponse(
137 const std::vector<rpc::RpcResponse>& results, const std::string& callback)
138 {
139 bool notauthorized = rpc::any_not_authorized(results.begin(), results.end());
140 bool gzip = httpServer_->supportsGZip();
141 std::string responseData = rpc::toJsonBatch(results, callback, gzip);
142 httpServer_->feedResponse(std::move(responseData),
143 getJsonRpcContentType(!callback.empty()));
144 addHttpServerResponseCommand(notauthorized);
145 }
146
addHttpServerResponseCommand(bool delayed)147 void HttpServerBodyCommand::addHttpServerResponseCommand(bool delayed)
148 {
149 auto resp = make_unique<HttpServerResponseCommand>(getCuid(), httpServer_, e_,
150 socket_);
151 if (delayed) {
152 e_->addCommand(
153 make_unique<DelayedCommand>(getCuid(), e_, 1_s, std::move(resp), true));
154 return;
155 }
156
157 e_->addCommand(std::move(resp));
158 e_->setNoWait(true);
159 }
160
updateWriteCheck()161 void HttpServerBodyCommand::updateWriteCheck()
162 {
163 if (httpServer_->wantWrite()) {
164 if (!writeCheck_) {
165 writeCheck_ = true;
166 e_->addSocketForWriteCheck(socket_, this);
167 }
168 }
169 else if (writeCheck_) {
170 writeCheck_ = false;
171 e_->deleteSocketForWriteCheck(socket_, this);
172 }
173 }
174
execute()175 bool HttpServerBodyCommand::execute()
176 {
177 if (e_->getRequestGroupMan()->downloadFinished() || e_->isHaltRequested()) {
178 return true;
179 }
180 try {
181 if (socket_->isReadable(0) || (writeCheck_ && socket_->isWritable(0)) ||
182 socket_->getRecvBufferedLength() ||
183 !httpServer_->getSocketRecvBuffer()->bufferEmpty() ||
184 httpServer_->getContentLength() == 0) {
185 timeoutTimer_ = global::wallclock();
186
187 if (httpServer_->receiveBody()) {
188 std::string reqPath = httpServer_->getRequestPath();
189 reqPath.erase(std::find(reqPath.begin(), reqPath.end(), '#'),
190 reqPath.end());
191 std::string query(std::find(reqPath.begin(), reqPath.end(), '?'),
192 reqPath.end());
193 reqPath.erase(reqPath.size() - query.size(), query.size());
194
195 if (httpServer_->getMethod() == "OPTIONS") {
196 // Response to Preflight Request.
197 // See http://www.w3.org/TR/cors/
198 auto& header = httpServer_->getRequestHeader();
199 std::string accessControlHeaders;
200 if (!header->find(HttpHeader::ORIGIN).empty() &&
201 !header->find(HttpHeader::ACCESS_CONTROL_REQUEST_METHOD)
202 .empty() &&
203 !httpServer_->getAllowOrigin().empty()) {
204 accessControlHeaders +=
205 "Access-Control-Allow-Methods: POST, GET, OPTIONS\r\n"
206 "Access-Control-Max-Age: 1728000\r\n";
207 const std::string& accReqHeaders =
208 header->find(HttpHeader::ACCESS_CONTROL_REQUEST_HEADERS);
209 if (!accReqHeaders.empty()) {
210 // We allow all headers requested.
211 accessControlHeaders += "Access-Control-Allow-Headers: ";
212 accessControlHeaders += accReqHeaders;
213 accessControlHeaders += "\r\n";
214 }
215 }
216 httpServer_->feedResponse(200, accessControlHeaders);
217 addHttpServerResponseCommand(false);
218 return true;
219 }
220
221 // Do something for requestpath and body
222 switch (httpServer_->getRequestType()) {
223 case RPC_TYPE_XML: {
224 #ifdef ENABLE_XML_RPC
225 auto dw = static_cast<rpc::XmlRpcDiskWriter*>(httpServer_->getBody());
226 int error;
227 error = dw->finalize();
228 rpc::RpcRequest req;
229 if (error == 0) {
230 req = dw->getResult();
231 }
232 dw->reset();
233 if (error < 0) {
234 A2_LOG_INFO(fmt("CUID#%" PRId64
235 " - Failed to parse XML-RPC request",
236 getCuid()));
237 httpServer_->feedResponse(400);
238 addHttpServerResponseCommand(false);
239 return true;
240 }
241 A2_LOG_INFO(fmt("Executing RPC method %s", req.methodName.c_str()));
242 auto method = rpc::getMethod(req.methodName);
243 auto res = method->execute(std::move(req), e_);
244 bool gzip = httpServer_->supportsGZip();
245 std::string responseData = rpc::toXml(res, gzip);
246 httpServer_->feedResponse(std::move(responseData), "text/xml");
247 addHttpServerResponseCommand(false);
248 #else // !ENABLE_XML_RPC
249 httpServer_->feedResponse(404);
250 addHttpServerResponseCommand(false);
251 #endif // !ENABLE_XML_RPC
252 return true;
253 }
254 case RPC_TYPE_JSON:
255 case RPC_TYPE_JSONP: {
256 std::string callback;
257 std::unique_ptr<ValueBase> json;
258 ssize_t error = 0;
259 if (httpServer_->getRequestType() == RPC_TYPE_JSONP) {
260 json::JsonGetParam param = json::decodeGetParams(query);
261 callback = param.callback;
262 ssize_t error = 0;
263 json = json::ValueBaseJsonParser().parseFinal(
264 param.request.c_str(), param.request.size(), error);
265 }
266 else {
267 auto dw =
268 static_cast<json::JsonDiskWriter*>(httpServer_->getBody());
269 error = dw->finalize();
270 if (error == 0) {
271 json = dw->getResult();
272 }
273 dw->reset();
274 }
275 if (error < 0) {
276 A2_LOG_INFO(fmt("CUID#%" PRId64
277 " - Failed to parse JSON-RPC request",
278 getCuid()));
279 rpc::RpcResponse res(rpc::createJsonRpcErrorResponse(
280 -32700, "Parse error.", Null::g()));
281 sendJsonRpcResponse(res, callback);
282 return true;
283 }
284 Dict* jsondict = downcast<Dict>(json);
285 if (jsondict) {
286 auto res = rpc::processJsonRpcRequest(jsondict, e_);
287 sendJsonRpcResponse(res, callback);
288 }
289 else {
290 List* jsonlist = downcast<List>(json);
291 if (jsonlist) {
292 // This is batch call
293 std::vector<rpc::RpcResponse> results;
294 for (List::ValueType::const_iterator i = jsonlist->begin(),
295 eoi = jsonlist->end();
296 i != eoi; ++i) {
297 Dict* jsondict = downcast<Dict>(*i);
298 if (jsondict) {
299 auto resp = rpc::processJsonRpcRequest(jsondict, e_);
300 results.push_back(std::move(resp));
301 }
302 }
303 sendJsonRpcBatchResponse(results, callback);
304 }
305 else {
306 rpc::RpcResponse res(rpc::createJsonRpcErrorResponse(
307 -32600, "Invalid Request.", Null::g()));
308 sendJsonRpcResponse(res, callback);
309 }
310 }
311 return true;
312 }
313 default:
314 httpServer_->feedResponse(404);
315 addHttpServerResponseCommand(false);
316 return true;
317 }
318 }
319 else {
320 updateWriteCheck();
321 e_->addCommand(std::unique_ptr<Command>(this));
322 return false;
323 }
324 }
325 else {
326 if (timeoutTimer_.difference(global::wallclock()) >= 30_s) {
327 A2_LOG_INFO("HTTP request body timeout.");
328 return true;
329 }
330 else {
331 e_->addCommand(std::unique_ptr<Command>(this));
332 return false;
333 }
334 }
335 }
336 catch (RecoverableException& e) {
337 A2_LOG_INFO_EX(fmt("CUID#%" PRId64
338 " - Error occurred while reading HTTP request body",
339 getCuid()),
340 e);
341 return true;
342 }
343 }
344
345 } // namespace aria2
346