/* */ #include "HttpServerBodyCommand.h" #include "SocketCore.h" #include "DownloadEngine.h" #include "HttpServer.h" #include "HttpHeader.h" #include "Logger.h" #include "LogFactory.h" #include "RequestGroup.h" #include "RequestGroupMan.h" #include "RecoverableException.h" #include "HttpServerResponseCommand.h" #include "OptionParser.h" #include "OptionHandler.h" #include "wallclock.h" #include "util.h" #include "fmt.h" #include "SocketRecvBuffer.h" #include "json.h" #include "DlAbortEx.h" #include "message.h" #include "RpcMethod.h" #include "RpcMethodFactory.h" #include "RpcRequest.h" #include "RpcResponse.h" #ifdef ENABLE_XML_RPC # include "XmlRpcRequestProcessor.h" # include "XmlRpcRequestParserStateMachine.h" #endif // ENABLE_XML_RPC namespace aria2 { HttpServerBodyCommand::HttpServerBodyCommand (cuid_t cuid, const SharedHandle& httpServer, DownloadEngine* e, const SharedHandle& socket) : Command(cuid), e_(e), socket_(socket), httpServer_(httpServer) { // To handle Content-Length == 0 case setStatus(Command::STATUS_ONESHOT_REALTIME); e_->addSocketForReadCheck(socket_, this); if(!httpServer_->getSocketRecvBuffer()->bufferEmpty()) { e_->setNoWait(true); } } HttpServerBodyCommand::~HttpServerBodyCommand() { e_->deleteSocketForReadCheck(socket_, this); } namespace { rpc::RpcResponse createJsonRpcErrorResponse (int code, const std::string& msg, const SharedHandle& id) { SharedHandle params = Dict::g(); params->put("code", Integer::g(code)); params->put("message", msg); rpc::RpcResponse res(code, params, id); return res; } } // namespace namespace { std::string getJsonRpcContentType(bool script) { return script ? "text/javascript" : "application/json-rpc"; } } // namespace void HttpServerBodyCommand::sendJsonRpcResponse (const rpc::RpcResponse& res, const std::string& callback) { bool gzip = httpServer_->supportsGZip(); std::string responseData = res.toJson(callback, gzip); if(res.code == 0) { httpServer_->feedResponse(responseData, getJsonRpcContentType(!callback.empty())); } else { httpServer_->disableKeepAlive(); std::string httpCode; switch(res.code) { case -32600: httpCode = "400 Bad Request"; break; case -32601: httpCode = "404 Not Found"; break; default: httpCode = "500 Internal Server Error"; }; httpServer_->feedResponse(httpCode, A2STR::NIL, responseData, getJsonRpcContentType(!callback.empty())); } addHttpServerResponseCommand(); } void HttpServerBodyCommand::sendJsonRpcBatchResponse (const std::vector& results, const std::string& callback) { bool gzip = httpServer_->supportsGZip(); std::string responseData = rpc::toJsonBatch(results, callback, gzip); httpServer_->feedResponse(responseData, getJsonRpcContentType(!callback.empty())); addHttpServerResponseCommand(); } void HttpServerBodyCommand::addHttpServerResponseCommand() { Command* command = new HttpServerResponseCommand(getCuid(), httpServer_, e_, socket_); e_->addCommand(command); e_->setNoWait(true); } rpc::RpcResponse HttpServerBodyCommand::processJsonRpcRequest(const Dict* jsondict) { SharedHandle id = jsondict->get("id"); if(!id) { return createJsonRpcErrorResponse(-32600, "Invalid Request.", Null::g()); } const String* methodName = asString(jsondict->get("method")); if(!methodName) { return createJsonRpcErrorResponse(-32600, "Invalid Request.", id); } SharedHandle params; const SharedHandle& tempParams = jsondict->get("params"); if(asList(tempParams)) { params = static_pointer_cast(tempParams); } else if(!tempParams) { params = List::g(); } else { // TODO No support for Named params return createJsonRpcErrorResponse(-32602, "Invalid params.", id); } rpc::RpcRequest req(methodName->s(), params, id); req.jsonRpc = true; SharedHandle method; try { method = rpc::RpcMethodFactory::create(req.methodName); } catch(RecoverableException& e) { A2_LOG_INFO_EX(EX_EXCEPTION_CAUGHT, e); return createJsonRpcErrorResponse(-32601, "Method not found.", id); } A2_LOG_INFO(fmt("Executing RPC method %s", req.methodName.c_str())); rpc::RpcResponse res = method->execute(req, e_); return res; } bool HttpServerBodyCommand::execute() { if(e_->getRequestGroupMan()->downloadFinished() || e_->isHaltRequested()) { return true; } try { if(socket_->isReadable(0) || !httpServer_->getSocketRecvBuffer()->bufferEmpty() || httpServer_->getContentLength() == 0) { timeoutTimer_ = global::wallclock; if(httpServer_->receiveBody()) { std::string reqPath = httpServer_->getRequestPath(); reqPath.erase(std::find(reqPath.begin(), reqPath.end(), '#'), reqPath.end()); std::string query(std::find(reqPath.begin(), reqPath.end(), '?'), reqPath.end()); reqPath.erase(reqPath.size()-query.size(), query.size()); // Do something for requestpath and body if(reqPath == "/rpc") { #ifdef ENABLE_XML_RPC rpc::RpcRequest req = rpc::XmlRpcRequestProcessor().parseMemory(httpServer_->getBody()); SharedHandle method = rpc::RpcMethodFactory::create(req.methodName); A2_LOG_INFO(fmt("Executing RPC method %s", req.methodName.c_str())); rpc::RpcResponse res = method->execute(req, e_); bool gzip = httpServer_->supportsGZip(); std::string responseData = res.toXml(gzip); httpServer_->feedResponse(responseData, "text/xml"); addHttpServerResponseCommand(); #endif // ENABLE_XML_RPC return true; } else if(reqPath == "/jsonrpc") { std::string callback; SharedHandle json; try { if(httpServer_->getMethod() == "GET") { json::JsonGetParam param = json::decodeGetParams(query); callback = param.callback; json = json::decode(param.request); } else { json = json::decode(httpServer_->getBody()); } } catch(RecoverableException& e) { A2_LOG_INFO_EX (fmt("CUID#%lld - Failed to parse JSON-RPC request", getCuid()), e); rpc::RpcResponse res (createJsonRpcErrorResponse(-32700, "Parse error.", Null::g())); sendJsonRpcResponse(res, callback); return true; } const Dict* jsondict = asDict(json); if(jsondict) { rpc::RpcResponse res = processJsonRpcRequest(jsondict); sendJsonRpcResponse(res, callback); } else { const List* jsonlist = asList(json); if(jsonlist) { // This is batch call std::vector results; for(List::ValueType::const_iterator i = jsonlist->begin(), eoi = jsonlist->end(); i != eoi; ++i) { const Dict* jsondict = asDict(*i); if(jsondict) { rpc::RpcResponse r = processJsonRpcRequest(jsondict); results.push_back(r); } } sendJsonRpcBatchResponse(results, callback); } else { rpc::RpcResponse res (createJsonRpcErrorResponse (-32600, "Invalid Request.", Null::g())); sendJsonRpcResponse(res, callback); } } return true; } else { return true; } } else { e_->addCommand(this); return false; } } else { if(timeoutTimer_.difference(global::wallclock) >= 30) { A2_LOG_INFO("HTTP request body timeout."); return true; } else { e_->addCommand(this); return false; } } } catch(RecoverableException& e) { A2_LOG_INFO_EX (fmt("CUID#%lld - Error occurred while reading HTTP request body", getCuid()), e); return true; } } } // namespace aria2