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