diff options
| author | Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com> | 2009-01-25 09:58:40 +0000 |
|---|---|---|
| committer | Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com> | 2009-01-25 09:58:40 +0000 |
| commit | 0742e3921f185613f98c2b31cd03182d8aa1d6f5 (patch) | |
| tree | d658bfc69cb3cec3cf4cab9fb4ee89062bed9e24 /src | |
| parent | 9505df51efaf83dd90818cb1c29487f286632f14 (diff) | |
2009-01-25 Tatsuhiro Tsujikawa <t-tujikawa@users.sourceforge.net>
Added experimental built-in HTTP server. Currently, when a
client accesses to the server, it responds with the current
download progress. By default, it is disabled. To enable the
server, give --enable-http-server option. To change the default
port number for the server to listen to, use
--http-server-listen-port option.
The response HTML is very simple and refreshes it self each 1
second. Because of this refresh, you see flicker in normal web
browser such as Firefox. I recommend to use console-based
browser such as elinks, w3m. To connect to the server, run
'elinks http://localhost:6800/' while running aria2. Please
replace port number '6800'(which is default) with your
preference.
* src/DownloadEngineFactory.cc
* src/HttpHeader.cc
* src/HttpHeader.h
* src/HttpHeaderProcessor.cc
* src/HttpHeaderProcessor.h
* src/HttpListenCommand.cc
* src/HttpListenCommand.h
* src/HttpServer.cc
* src/HttpServer.h
* src/HttpServerCommand.cc
* src/HttpServerCommand.h
* src/HttpServerResponseCommand.cc
* src/HttpServerResponseCommand.h
* src/Makefile.am
* src/OptionHandlerFactory.cc
* src/Util.cc
* src/Util.h
* src/help_tags.h
* src/option_processing.cc
* src/prefs.cc
* src/prefs.h
* src/usage_text.h
* test/HttpHeaderProcessorTest.cc
* test/UtilTest.cc
Diffstat (limited to 'src')
| -rw-r--r-- | src/DownloadEngineFactory.cc | 10 | ||||
| -rw-r--r-- | src/HttpHeader.cc | 20 | ||||
| -rw-r--r-- | src/HttpHeader.h | 18 | ||||
| -rw-r--r-- | src/HttpHeaderProcessor.cc | 25 | ||||
| -rw-r--r-- | src/HttpHeaderProcessor.h | 4 | ||||
| -rw-r--r-- | src/HttpListenCommand.cc | 94 | ||||
| -rw-r--r-- | src/HttpListenCommand.h | 62 | ||||
| -rw-r--r-- | src/HttpServer.cc | 96 | ||||
| -rw-r--r-- | src/HttpServer.h | 74 | ||||
| -rw-r--r-- | src/HttpServerCommand.cc | 247 | ||||
| -rw-r--r-- | src/HttpServerCommand.h | 65 | ||||
| -rw-r--r-- | src/HttpServerResponseCommand.cc | 80 | ||||
| -rw-r--r-- | src/HttpServerResponseCommand.h | 67 | ||||
| -rw-r--r-- | src/Makefile.am | 6 | ||||
| -rw-r--r-- | src/Makefile.in | 28 | ||||
| -rw-r--r-- | src/OptionHandlerFactory.cc | 20 | ||||
| -rw-r--r-- | src/Util.cc | 22 | ||||
| -rw-r--r-- | src/Util.h | 2 | ||||
| -rw-r--r-- | src/help_tags.h | 1 | ||||
| -rw-r--r-- | src/option_processing.cc | 9 | ||||
| -rw-r--r-- | src/prefs.cc | 4 | ||||
| -rw-r--r-- | src/prefs.h | 4 | ||||
| -rw-r--r-- | src/usage_text.h | 12 |
23 files changed, 958 insertions, 12 deletions
diff --git a/src/DownloadEngineFactory.cc b/src/DownloadEngineFactory.cc index 04c15fc3..4a97da44 100644 --- a/src/DownloadEngineFactory.cc +++ b/src/DownloadEngineFactory.cc @@ -63,6 +63,7 @@ #include "SelectEventPoll.h" #include "DlAbortEx.h" #include "FileAllocationEntry.h" +#include "HttpListenCommand.h" namespace aria2 { @@ -138,6 +139,15 @@ DownloadEngineFactory::newDownloadEngine(Option* op, stopSec)); } } + if(op->getAsBool(PREF_ENABLE_HTTP_SERVER)) { + HttpListenCommand* httpListenCommand = + new HttpListenCommand(e->newCUID(), e.get()); + if(httpListenCommand->bindPort(op->getAsInt(PREF_HTTP_SERVER_LISTEN_PORT))){ + e->addRoutineCommand(httpListenCommand); + } else { + delete httpListenCommand; + } + } return e; } diff --git a/src/HttpHeader.cc b/src/HttpHeader.cc index d7557e87..23ab0b27 100644 --- a/src/HttpHeader.cc +++ b/src/HttpHeader.cc @@ -193,6 +193,26 @@ void HttpHeader::setVersion(const std::string& version) _version = version; } +const std::string& HttpHeader::getMethod() const +{ + return _method; +} + +void HttpHeader::setMethod(const std::string& method) +{ + _method = method; +} + +const std::string& HttpHeader::getRequestPath() const +{ + return _requestPath; +} + +void HttpHeader::setRequestPath(const std::string& requestPath) +{ + _requestPath = requestPath; +} + void HttpHeader::fill(std::istream& in) { std::string line; diff --git a/src/HttpHeader.h b/src/HttpHeader.h index 7e5c43a4..96bb14d5 100644 --- a/src/HttpHeader.h +++ b/src/HttpHeader.h @@ -36,12 +36,14 @@ #define _D_HTTP_HEADER_H_ #include "common.h" -#include "SharedHandle.h" + #include <map> #include <deque> #include <string> #include <iosfwd> +#include "SharedHandle.h" + namespace aria2 { class Range; @@ -56,6 +58,12 @@ private: // HTTP version, e.g. HTTP/1.1 std::string _version; + + // HTTP Method, e.g. GET, POST, etc + std::string _method; + + // Request Path + std::string _requestPath; public: HttpHeader() {} ~HttpHeader() {} @@ -77,6 +85,14 @@ public: void setVersion(const std::string& version); + const std::string& getMethod() const; + + void setMethod(const std::string& method); + + const std::string& getRequestPath() const; + + void setRequestPath(const std::string& requestPath); + void fill(std::istream& in); // Clears table. _responseStatus and _version are unchanged. diff --git a/src/HttpHeaderProcessor.cc b/src/HttpHeaderProcessor.cc index 9b2ef0e0..e5d2d46b 100644 --- a/src/HttpHeaderProcessor.cc +++ b/src/HttpHeaderProcessor.cc @@ -111,6 +111,31 @@ SharedHandle<HttpHeader> HttpHeaderProcessor::getHttpResponseHeader() return httpHeader; } +SharedHandle<HttpHeader> HttpHeaderProcessor::getHttpRequestHeader() +{ + // The minimum case of the first line is: + // GET / HTTP/1.x + // At least 14bytes before \r\n or \n. + std::string::size_type delimpos = std::string::npos; + if(((delimpos = _buf.find("\r\n")) == std::string::npos && + (delimpos = _buf.find("\n")) == std::string::npos) || + delimpos < 14) { + throw DlRetryEx(EX_NO_STATUS_HEADER); + } + std::deque<std::string> firstLine; + Util::slice(firstLine, _buf.substr(0, delimpos), ' ', true); + if(firstLine.size() != 3) { + throw DlAbortEx("Malformed HTTP request header."); + } + SharedHandle<HttpHeader> httpHeader(new HttpHeader()); + httpHeader->setMethod(firstLine[0]); + httpHeader->setRequestPath(firstLine[1]); + httpHeader->setVersion(firstLine[2]); + std::istringstream strm(_buf.substr(14)); + httpHeader->fill(strm); + return httpHeader; +} + std::string HttpHeaderProcessor::getHeaderString() const { std::string::size_type delimpos = std::string::npos; diff --git a/src/HttpHeaderProcessor.h b/src/HttpHeaderProcessor.h index 1e0a703a..d8e8fddc 100644 --- a/src/HttpHeaderProcessor.h +++ b/src/HttpHeaderProcessor.h @@ -74,7 +74,9 @@ public: * Processes the recieved header as a http response header and returns * HttpHeader object. */ - SharedHandle<HttpHeader> getHttpResponseHeader(); + SharedHandle<HttpHeader> getHttpResponseHeader(); + + SharedHandle<HttpHeader> getHttpRequestHeader(); std::string getHeaderString() const; diff --git a/src/HttpListenCommand.cc b/src/HttpListenCommand.cc new file mode 100644 index 00000000..d16f6ce5 --- /dev/null +++ b/src/HttpListenCommand.cc @@ -0,0 +1,94 @@ +/* <!-- copyright */ +/* + * aria2 - The high speed download utility + * + * Copyright (C) 2009 Tatsuhiro Tsujikawa + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, and distribute linked combinations + * including the two. + * You must obey the GNU General Public License in all respects + * for all of the code used other than OpenSSL. If you modify + * file(s) with this exception, you may extend this exception to your + * version of the file(s), but you are not obligated to do so. If you + * do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source + * files in the program, then also delete it here. + */ +/* copyright --> */ +#include "HttpListenCommand.h" +#include "DownloadEngine.h" +#include "RecoverableException.h" +#include "message.h" +#include "Logger.h" +#include "SocketCore.h" +#include "HttpServerCommand.h" +#include "CUIDCounter.h" +#include "RequestGroupMan.h" + +namespace aria2 { + +HttpListenCommand::HttpListenCommand(int32_t cuid, DownloadEngine* e): + Command(cuid),_e(e) {} + +HttpListenCommand::~HttpListenCommand() {} + +bool HttpListenCommand::execute() +{ + if(_e->_requestGroupMan->downloadFinished() || _e->isHaltRequested()) { + return true; + } + try { + if(_serverSocket->isReadable(0)) { + SharedHandle<SocketCore> socket(_serverSocket->acceptConnection()); + HttpServerCommand* c = + new HttpServerCommand(_e->newCUID(), _e, socket); + c->setStatus(Command::STATUS_ONESHOT_REALTIME); + _e->setNoWait(true); + _e->commands.push_back(c); + } + } catch(RecoverableException& e) { + logger->debug(MSG_ACCEPT_FAILURE, _e, cuid); + } + _e->commands.push_back(this); + return false; +} + +bool HttpListenCommand::bindPort(uint16_t port) +{ + if(!_serverSocket.isNull()) { + _e->deleteSocketForReadCheck(_serverSocket, this); + } + _serverSocket.reset(new SocketCore()); + logger->info("CUID#%d - Setting up HttpListenCommand", cuid); + try { + _serverSocket->bind(port); + _serverSocket->beginListen(); + _serverSocket->setNonBlockingMode(); + logger->info(MSG_LISTENING_PORT, cuid, port); + _e->addSocketForReadCheck(_serverSocket, this); + return true; + } catch(RecoverableException& e) { + logger->error(MSG_BIND_FAILURE, e, cuid, port); + _serverSocket->closeConnection(); + } + return false; +} + +} // namespace aria2 diff --git a/src/HttpListenCommand.h b/src/HttpListenCommand.h new file mode 100644 index 00000000..fd165292 --- /dev/null +++ b/src/HttpListenCommand.h @@ -0,0 +1,62 @@ +/* <!-- copyright */ +/* + * aria2 - The high speed download utility + * + * Copyright (C) 2009 Tatsuhiro Tsujikawa + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, and distribute linked combinations + * including the two. + * You must obey the GNU General Public License in all respects + * for all of the code used other than OpenSSL. If you modify + * file(s) with this exception, you may extend this exception to your + * version of the file(s), but you are not obligated to do so. If you + * do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source + * files in the program, then also delete it here. + */ +/* copyright --> */ +#ifndef _D_HTTP_LISTEN_COMMAND_H_ +#define _D_HTTP_LISTEN_COMMAND_H_ + +#include "Command.h" +#include "SharedHandle.h" + +namespace aria2 { + +class DownloadEngine; +class SocketCore; + +class HttpListenCommand : public Command { +private: + DownloadEngine* _e; + SharedHandle<SocketCore> _serverSocket; +public: + HttpListenCommand(int32_t cuid, DownloadEngine* e); + + virtual ~HttpListenCommand(); + + virtual bool execute(); + + bool bindPort(uint16_t port); +}; + +} // namespace aria2 + +#endif // _D_HTTP_LISTEN_COMMAND_H_ diff --git a/src/HttpServer.cc b/src/HttpServer.cc new file mode 100644 index 00000000..14cf6192 --- /dev/null +++ b/src/HttpServer.cc @@ -0,0 +1,96 @@ +/* <!-- copyright */ +/* + * aria2 - The high speed download utility + * + * Copyright (C) 2009 Tatsuhiro Tsujikawa + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, and distribute linked combinations + * including the two. + * You must obey the GNU General Public License in all respects + * for all of the code used other than OpenSSL. If you modify + * file(s) with this exception, you may extend this exception to your + * version of the file(s), but you are not obligated to do so. If you + * do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source + * files in the program, then also delete it here. + */ +/* copyright --> */ +#include "HttpServer.h" +#include "HttpHeader.h" +#include "SocketCore.h" +#include "HttpHeaderProcessor.h" +#include "DlAbortEx.h" +#include "message.h" +#include "Util.h" + +namespace aria2 { + +HttpServer::HttpServer(const SharedHandle<SocketCore>& socket, + DownloadEngine* e): + _socket(socket), + _socketBuffer(socket), + _e(e), + _headerProcessor(new HttpHeaderProcessor()) +{} + +HttpServer::~HttpServer() {} + +SharedHandle<HttpHeader> HttpServer::receiveRequest() +{ + size_t size = 512; + unsigned char buf[size]; + _socket->peekData(buf, size); + if(size == 0 && !(_socket->wantRead() || _socket->wantWrite())) { + throw DlAbortEx(EX_EOF_FROM_PEER); + } + _headerProcessor->update(buf, size); + if(!_headerProcessor->eoh()) { + _socket->readData(buf, size); + return SharedHandle<HttpHeader>(); + } + size_t putbackDataLength = _headerProcessor->getPutBackDataLength(); + size -= putbackDataLength; + _socket->readData(buf, size); + + return _headerProcessor->getHttpRequestHeader(); +} + +void HttpServer::feedResponse(const std::string& text) +{ + std::string header = "HTTP/1.0 200 OK\r\n" + "Content-Type: text/html\r\n" + "Content-Length: "+Util::uitos(text.size())+"\r\n" + "Connection: close\r\n" + "\r\n"; + _socketBuffer.feedSendBuffer(header); + _socketBuffer.feedSendBuffer(text); +} + +ssize_t HttpServer::sendResponse() +{ + return _socketBuffer.send(); +} + +bool HttpServer::sendBufferIsEmpty() const +{ + return _socketBuffer.sendBufferIsEmpty(); +} + +} // namespace aria2 diff --git a/src/HttpServer.h b/src/HttpServer.h new file mode 100644 index 00000000..9b755f18 --- /dev/null +++ b/src/HttpServer.h @@ -0,0 +1,74 @@ +/* <!-- copyright */ +/* + * aria2 - The high speed download utility + * + * Copyright (C) 2009 Tatsuhiro Tsujikawa + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, and distribute linked combinations + * including the two. + * You must obey the GNU General Public License in all respects + * for all of the code used other than OpenSSL. If you modify + * file(s) with this exception, you may extend this exception to your + * version of the file(s), but you are not obligated to do so. If you + * do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source + * files in the program, then also delete it here. + */ +/* copyright --> */ +#ifndef _D_HTTP_SERVER_H_ +#define _D_HTTP_SERVER_H_ + +#include "common.h" + +#include <string> + +#include "SharedHandle.h" +#include "SocketBuffer.h" + +namespace aria2 { + +class SocketCore; +class HttpHeader; +class HttpHeaderProcessor; +class DownloadEngine; + +class HttpServer { +private: + SharedHandle<SocketCore> _socket; + SocketBuffer _socketBuffer; + DownloadEngine* _e; + SharedHandle<HttpHeaderProcessor> _headerProcessor; +public: + HttpServer(const SharedHandle<SocketCore>& socket, DownloadEngine* e); + + ~HttpServer(); + + SharedHandle<HttpHeader> receiveRequest(); + + void feedResponse(const std::string& text); + + ssize_t sendResponse(); + + bool sendBufferIsEmpty() const; +}; + +} // namespace aria2 + +#endif // _D_HTTP_SERVER_H_ diff --git a/src/HttpServerCommand.cc b/src/HttpServerCommand.cc new file mode 100644 index 00000000..3670605b --- /dev/null +++ b/src/HttpServerCommand.cc @@ -0,0 +1,247 @@ +/* <!-- copyright */ +/* + * aria2 - The high speed download utility + * + * Copyright (C) 2009 Tatsuhiro Tsujikawa + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, and distribute linked combinations + * including the two. + * You must obey the GNU General Public License in all respects + * for all of the code used other than OpenSSL. If you modify + * file(s) with this exception, you may extend this exception to your + * version of the file(s), but you are not obligated to do so. If you + * do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source + * files in the program, then also delete it here. + */ +/* copyright --> */ +#include "HttpServerCommand.h" + +#include <sstream> +#include <algorithm> +#include <iostream> +#include <iomanip> + +#include "SocketCore.h" +#include "DownloadEngine.h" +#include "HttpServer.h" +#include "HttpHeader.h" +#include "Logger.h" +#include "RequestGroup.h" +#include "RequestGroupMan.h" +#include "BtContext.h" +#include "Util.h" +#include "HttpServerResponseCommand.h" +#include "CheckIntegrityEntry.h" +#include "FileAllocationEntry.h" + +namespace aria2 { + +HttpServerCommand::HttpServerCommand(int32_t cuid, DownloadEngine* e, + const SharedHandle<SocketCore>& socket): + Command(cuid), + _e(e), + _socket(socket), + _httpServer(new HttpServer(socket, e)) +{ + _e->addSocketForReadCheck(_socket, this); +} + +HttpServerCommand::~HttpServerCommand() +{ + _e->deleteSocketForReadCheck(_socket, this); +} + +class PrintSummaryHtml +{ +private: + std::ostream& _o; + +public: + PrintSummaryHtml(std::ostream& o):_o(o) {} + + void operator()(const SharedHandle<RequestGroup>& rg) + { + _o << "<div id=\"gid" << rg->getGID() << "\">" + << "[#" << rg->getGID() << "]" + << " FILE:" << "<strong>" + << Util::htmlEscape(rg->getFilePath()) << "</strong>"; +#ifdef ENABLE_BITTORRENT + SharedHandle<BtContext> btContext = + dynamic_pointer_cast<BtContext>(rg->getDownloadContext()); + if(!btContext.isNull()) { + _o << "<br />" << " Info Hash:" << btContext->getInfoHashAsString(); + } +#endif // ENABLE_BITTORRENT + _o << "<br />"; + + TransferStat stat = rg->calculateStat(); + unsigned int eta = 0; + if(rg->getTotalLength() > 0 && stat.getDownloadSpeed() > 0) { + eta = + (rg->getTotalLength()-rg->getCompletedLength())/stat.getDownloadSpeed(); + } +#ifdef ENABLE_BITTORRENT + if(!btContext.isNull() && rg->downloadFinished()) { + _o << "SEEDING" << "(" << "ratio:" + << std::fixed << std::setprecision(1) + << ((stat.getAllTimeUploadLength()*10)/rg->getCompletedLength())/10.0 + << ") "; + } +#endif // ENABLE_BITTORRENT + _o << Util::abbrevSize(rg->getCompletedLength()) + << "B" + << "/" + << Util::abbrevSize(rg->getTotalLength()) + << "B"; + if(rg->getTotalLength() > 0) { + _o << "(" + << 100*rg->getCompletedLength()/rg->getTotalLength() + << "%)"; + } + _o << " " + << "CN:" + << rg->getNumConnection(); + if(!rg->downloadFinished()) { + _o << " " + << "SPD:" + << std::fixed << std::setprecision(2) + << stat.getDownloadSpeed()/1024.0 << "KiB/s"; + } + if(stat.getSessionUploadLength() > 0) { + _o << " " + << "UP:" + << std::fixed << std::setprecision(2) + << stat.getUploadSpeed()/1024.0 << "KiB/s" + << "(" << Util::abbrevSize(stat.getAllTimeUploadLength()) << "B)"; + } + if(eta > 0) { + _o << " " + << "ETA:" + << Util::htmlEscape(Util::secfmt(eta)); + } + _o << "</div>" + << "<hr />"; + } +}; + +static std::string createResponse(DownloadEngine* e) +{ + std::ostringstream strm; + const std::deque<SharedHandle<RequestGroup> > groups = + e->_requestGroupMan->getRequestGroups(); + std::for_each(groups.begin(), groups.end(), PrintSummaryHtml(strm)); + + { + SharedHandle<FileAllocationEntry> entry = + e->_fileAllocationMan->getPickedEntry(); + if(!entry.isNull()) { + strm << "<div id=\"filealloc\">" + << "[FileAlloc:" + << "#" << entry->getRequestGroup()->getGID() << " " + << Util::abbrevSize(entry->getCurrentLength()) + << "B" + << "/" + << Util::abbrevSize(entry->getTotalLength()) + << "B" + << "("; + if(entry->getTotalLength() > 0) { + strm << 100*entry->getCurrentLength()/entry->getTotalLength(); + } else { + strm << "--"; + } + strm << "%)" + << "]"; + if(e->_fileAllocationMan->hasNext()) { + strm << "(" + << e->_fileAllocationMan->countEntryInQueue() + << "waiting...)"; + } + strm << "</div><hr />"; + } + } +#ifdef ENABLE_MESSAGE_DIGEST + { + SharedHandle<CheckIntegrityEntry> entry = + e->_checkIntegrityMan->getPickedEntry(); + if(!entry.isNull()) { + strm << "<div id=\"hashcheck\">" + << "[HashCheck:" + << "#" << entry->getRequestGroup()->getGID() << " " + << Util::abbrevSize(entry->getCurrentLength()) + << "B" + << "/" + << Util::abbrevSize(entry->getTotalLength()) + << "B" + << "(" + << 100*entry->getCurrentLength()/entry->getTotalLength() + << "%)" + << "]"; + if(e->_checkIntegrityMan->hasNext()) { + strm << "(" + << e->_checkIntegrityMan->countEntryInQueue() + << "waiting...)"; + } + strm << "</div><hr />"; + } + } +#endif // ENABLE_MESSAGE_DIGEST + std::string body = + "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\"" + " \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">" + "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">" + "<head>" + "<meta http-equiv=\"refresh\" content=\"1\" />" + "<title>aria2</title>" + "</head>" + "<body><h1>aria2 - Download Progress</h1>"+strm.str()+"</body>" + "</html>"; + return body; +} + +bool HttpServerCommand::execute() +{ + if(_socket->isReadable(0)) { + _timeout.reset(); + SharedHandle<HttpHeader> header = _httpServer->receiveRequest(); + if(header.isNull()) { + _e->commands.push_back(this); + return false; + } else { + _httpServer->feedResponse(createResponse(_e)); + Command* command = new HttpServerResponseCommand(cuid, _httpServer, _e, + _socket); + command->setStatus(Command::STATUS_ONESHOT_REALTIME); + _e->commands.push_back(command); + _e->setNoWait(true); + return true; + } + } else { + if(_timeout.elapsed(30)) { + logger->info("HTTP request timeout."); + return true; + } else { + _e->commands.push_back(this); + return false; + } + } +} + +} // namespace aria2 diff --git a/src/HttpServerCommand.h b/src/HttpServerCommand.h new file mode 100644 index 00000000..cfb42168 --- /dev/null +++ b/src/HttpServerCommand.h @@ -0,0 +1,65 @@ +/* <!-- copyright */ +/* + * aria2 - The high speed download utility + * + * Copyright (C) 2009 Tatsuhiro Tsujikawa + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, and distribute linked combinations + * including the two. + * You must obey the GNU General Public License in all respects + * for all of the code used other than OpenSSL. If you modify + * file(s) with this exception, you may extend this exception to your + * version of the file(s), but you are not obligated to do so. If you + * do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source + * files in the program, then also delete it here. + */ +/* copyright --> */ +#ifndef _D_HTTP_SERVER_COMMAND_H_ +#define _D_HTTP_SERVER_COMMAND_H_ + +#include "Command.h" +#include "SharedHandle.h" +#include "TimeA2.h" + +namespace aria2 { + +class DownloadEngine; +class SocketCore; +class HttpServer; + +class HttpServerCommand : public Command { +private: + DownloadEngine* _e; + SharedHandle<SocketCore> _socket; + SharedHandle<HttpServer> _httpServer; + Time _timeout; +public: + HttpServerCommand(int32_t cuid, DownloadEngine* e, + const SharedHandle<SocketCore>& socket); + + virtual ~HttpServerCommand(); + + virtual bool execute(); +}; + +} // namespace aria2 + +#endif // _D_HTTP_SERVER_COMMAND_H_ diff --git a/src/HttpServerResponseCommand.cc b/src/HttpServerResponseCommand.cc new file mode 100644 index 00000000..0578c7a8 --- /dev/null +++ b/src/HttpServerResponseCommand.cc @@ -0,0 +1,80 @@ +/* <!-- copyright */ +/* + * aria2 - The high speed download utility + * + * Copyright (C) 2009 Tatsuhiro Tsujikawa + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, and distribute linked combinations + * including the two. + * You must obey the GNU General Public License in all respects + * for all of the code used other than OpenSSL. If you modify + * file(s) with this exception, you may extend this exception to your + * version of the file(s), but you are not obligated to do so. If you + * do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source + * files in the program, then also delete it here. + */ +/* copyright --> */ +#include "HttpServerResponseCommand.h" +#include "SocketCore.h" +#include "DownloadEngine.h" +#include "HttpServer.h" +#include "Logger.h" + +namespace aria2 { + +HttpServerResponseCommand::HttpServerResponseCommand +(int32_t cuid, + const SharedHandle<HttpServer>& httpServer, + DownloadEngine* e, + const SharedHandle<SocketCore>& socket): + Command(cuid), + _e(e), + _socket(socket), + _httpServer(httpServer) +{ + + _e->addSocketForWriteCheck(_socket, this); +} + +HttpServerResponseCommand::~HttpServerResponseCommand() +{ + _e->deleteSocketForWriteCheck(_socket, this); +} + +bool HttpServerResponseCommand::execute() +{ + _httpServer->sendResponse(); + if(_httpServer->sendBufferIsEmpty()) { + logger->info("CUID#%d - HttpServer: all response transmitted.", cuid); + return true; + } else { + if(_timeout.elapsed(10)) { + logger->info("CUID#%d - HttpServer: Timeout while trasmitting response.", + cuid); + return true; + } else { + _e->commands.push_back(this); + return true; + } + } +} + +} // namespace aria2 diff --git a/src/HttpServerResponseCommand.h b/src/HttpServerResponseCommand.h new file mode 100644 index 00000000..52523cf2 --- /dev/null +++ b/src/HttpServerResponseCommand.h @@ -0,0 +1,67 @@ +/* <!-- copyright */ +/* + * aria2 - The high speed download utility + * + * Copyright (C) 2009 Tatsuhiro Tsujikawa + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, and distribute linked combinations + * including the two. + * You must obey the GNU General Public License in all respects + * for all of the code used other than OpenSSL. If you modify + * file(s) with this exception, you may extend this exception to your + * version of the file(s), but you are not obligated to do so. If you + * do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source + * files in the program, then also delete it here. + */ +/* copyright --> */ +#ifndef _D_HTTP_SERVER_RESPONSE_COMMAND_H_ +#define _D_HTTP_SERVER_RESPONSE_COMMAND_H_ + +#include "Command.h" +#include "SharedHandle.h" +#include "TimeA2.h" + +namespace aria2 { + +class DownloadEngine; +class SocketCore; +class HttpServer; + +class HttpServerResponseCommand : public Command { +private: + DownloadEngine* _e; + SharedHandle<SocketCore> _socket; + SharedHandle<HttpServer> _httpServer; + Time _timeout; +public: + HttpServerResponseCommand(int32_t cuid, + const SharedHandle<HttpServer>& httpServer, + DownloadEngine* e, + const SharedHandle<SocketCore>& socket); + + virtual ~HttpServerResponseCommand(); + + virtual bool execute(); +}; + +} // namespace aria2 + +#endif // _D_HTTP_SERVER_RESPONSE_COMMAND_H_ diff --git a/src/Makefile.am b/src/Makefile.am index 616009a8..152a08bb 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -200,7 +200,11 @@ SRCS = Socket.h\ EventPoll.h\ SelectEventPoll.cc SelectEventPoll.h\ SequentialPicker.h\ - SequentialDispatcherCommand.h + SequentialDispatcherCommand.h\ + HttpListenCommand.cc HttpListenCommand.h\ + HttpServerCommand.cc HttpServerCommand.h\ + HttpServerResponseCommand.cc HttpServerResponseCommand.h\ + HttpServer.cc HttpServer.h if HAVE_EPOLL SRCS += EpollEventPoll.cc EpollEventPoll.h diff --git a/src/Makefile.in b/src/Makefile.in index 8c1ecc15..01adb93c 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -414,6 +414,9 @@ am__libaria2c_a_SOURCES_DIST = Socket.h SocketCore.cc SocketCore.h \ OptionHandlerException.h bencode.cc bencode.h URIResult.cc \ URIResult.h EventPoll.h SelectEventPoll.cc SelectEventPoll.h \ SequentialPicker.h SequentialDispatcherCommand.h \ + HttpListenCommand.cc HttpListenCommand.h HttpServerCommand.cc \ + HttpServerCommand.h HttpServerResponseCommand.cc \ + HttpServerResponseCommand.h HttpServer.cc HttpServer.h \ EpollEventPoll.cc EpollEventPoll.h TLSContext.h \ LibgnutlsTLSContext.cc LibgnutlsTLSContext.h \ LibsslTLSContext.cc LibsslTLSContext.h GZipDecoder.cc \ @@ -810,14 +813,16 @@ am__objects_22 = SocketCore.$(OBJEXT) Command.$(OBJEXT) \ NsCookieParser.$(OBJEXT) CookieStorage.$(OBJEXT) \ SocketBuffer.$(OBJEXT) OptionHandlerException.$(OBJEXT) \ bencode.$(OBJEXT) URIResult.$(OBJEXT) \ - SelectEventPoll.$(OBJEXT) $(am__objects_1) $(am__objects_2) \ - $(am__objects_3) $(am__objects_4) $(am__objects_5) \ - $(am__objects_6) $(am__objects_7) $(am__objects_8) \ - $(am__objects_9) $(am__objects_10) $(am__objects_11) \ - $(am__objects_12) $(am__objects_13) $(am__objects_14) \ - $(am__objects_15) $(am__objects_16) $(am__objects_17) \ - $(am__objects_18) $(am__objects_19) $(am__objects_20) \ - $(am__objects_21) + SelectEventPoll.$(OBJEXT) HttpListenCommand.$(OBJEXT) \ + HttpServerCommand.$(OBJEXT) \ + HttpServerResponseCommand.$(OBJEXT) HttpServer.$(OBJEXT) \ + $(am__objects_1) $(am__objects_2) $(am__objects_3) \ + $(am__objects_4) $(am__objects_5) $(am__objects_6) \ + $(am__objects_7) $(am__objects_8) $(am__objects_9) \ + $(am__objects_10) $(am__objects_11) $(am__objects_12) \ + $(am__objects_13) $(am__objects_14) $(am__objects_15) \ + $(am__objects_16) $(am__objects_17) $(am__objects_18) \ + $(am__objects_19) $(am__objects_20) $(am__objects_21) am_libaria2c_a_OBJECTS = $(am__objects_22) libaria2c_a_OBJECTS = $(am_libaria2c_a_OBJECTS) am__installdirs = "$(DESTDIR)$(bindir)" @@ -1143,6 +1148,9 @@ SRCS = Socket.h SocketCore.cc SocketCore.h BinaryStream.h Command.cc \ OptionHandlerException.h bencode.cc bencode.h URIResult.cc \ URIResult.h EventPoll.h SelectEventPoll.cc SelectEventPoll.h \ SequentialPicker.h SequentialDispatcherCommand.h \ + HttpListenCommand.cc HttpListenCommand.h HttpServerCommand.cc \ + HttpServerCommand.h HttpServerResponseCommand.cc \ + HttpServerResponseCommand.h HttpServer.cc HttpServer.h \ $(am__append_1) $(am__append_2) $(am__append_3) \ $(am__append_4) $(am__append_5) $(am__append_6) \ $(am__append_7) $(am__append_8) $(am__append_9) \ @@ -1406,12 +1414,16 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/HttpHeader.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/HttpHeaderProcessor.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/HttpInitiateConnectionCommand.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/HttpListenCommand.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/HttpProxyRequestCommand.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/HttpProxyResponseCommand.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/HttpRequest.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/HttpRequestCommand.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/HttpResponse.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/HttpResponseCommand.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/HttpServer.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/HttpServerCommand.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/HttpServerResponseCommand.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/HttpSkipResponseCommand.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/InOrderURISelector.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/InitialMetalinkParserState.Po@am__quote@ diff --git a/src/OptionHandlerFactory.cc b/src/OptionHandlerFactory.cc index f662b5b7..4db13b09 100644 --- a/src/OptionHandlerFactory.cc +++ b/src/OptionHandlerFactory.cc @@ -151,6 +151,14 @@ OptionHandlers OptionHandlerFactory::createOptionHandlers() handlers.push_back(op); } { + SharedHandle<OptionHandler> op(new BooleanOptionHandler + (PREF_ENABLE_HTTP_SERVER, + TEXT_ENABLE_HTTP_SERVER, + V_FALSE)); + op->addTag(TAG_EXPERIMENTAL); + handlers.push_back(op); + } + { std::string params[] = { #ifdef HAVE_EPOLL V_EPOLL, @@ -188,6 +196,15 @@ OptionHandlers OptionHandlerFactory::createOptionHandlers() handlers.push_back(op); } { + SharedHandle<OptionHandler> op(new NumberOptionHandler + (PREF_HTTP_SERVER_LISTEN_PORT, + TEXT_HTTP_SERVER_LISTEN_PORT, + "6800", + 1024, UINT16_MAX)); + op->addTag(TAG_EXPERIMENTAL); + handlers.push_back(op); + } + { SharedHandle<OptionHandler> op(new DefaultOptionHandler (PREF_INPUT_FILE, TEXT_INPUT_FILE, @@ -1013,7 +1030,7 @@ OptionHandlers OptionHandlerFactory::createOptionHandlers() ("help", TEXT_HELP, TAG_BASIC, - StringFormat("%s,%s,%s,%s,%s,%s,%s,%s,all", + StringFormat("%s,%s,%s,%s,%s,%s,%s,%s,%s,all", TAG_BASIC, TAG_ADVANCED, TAG_HTTP, @@ -1021,6 +1038,7 @@ OptionHandlers OptionHandlerFactory::createOptionHandlers() TAG_FTP, TAG_METALINK, TAG_BITTORRENT, + TAG_EXPERIMENTAL, TAG_HELP).str())); op->addTag(TAG_BASIC); op->addTag(TAG_HELP); diff --git a/src/Util.cc b/src/Util.cc index ebe5c4fa..0eb59873 100644 --- a/src/Util.cc +++ b/src/Util.cc @@ -912,4 +912,26 @@ Util::getNumericNameInfo(const struct sockaddr* sockaddr, socklen_t len) return std::pair<std::string, uint16_t>(host, atoi(service)); // TODO } +std::string Util::htmlEscape(const std::string& src) +{ + std::string dest; + for(std::string::const_iterator i = src.begin(); i != src.end(); ++i) { + char ch = *i; + if(ch == '<') { + dest += "<"; + } else if(ch == '>') { + dest += ">"; + } else if(ch == '&') { + dest += "&"; + } else if(ch == '\'') { + dest += "'"; + } else if(ch == '"') { + dest += """; + } else { + dest += ch; + } + } + return dest; +} + } // namespace aria2 @@ -277,6 +277,8 @@ public: static std::pair<std::string, uint16_t> getNumericNameInfo(const struct sockaddr* sockaddr, socklen_t len); + + static std::string htmlEscape(const std::string& src); }; } // namespace aria2 diff --git a/src/help_tags.h b/src/help_tags.h index 88993f86..11cbd195 100644 --- a/src/help_tags.h +++ b/src/help_tags.h @@ -42,6 +42,7 @@ #define TAG_FTP "ftp" #define TAG_METALINK "metalink" #define TAG_BITTORRENT "bittorrent" +#define TAG_EXPERIMENTAL "experimental" #define TAG_HELP "help" #endif // _D_HELP_TAGS_H_ diff --git a/src/option_processing.cc b/src/option_processing.cc index f3011968..0a810749 100644 --- a/src/option_processing.cc +++ b/src/option_processing.cc @@ -192,6 +192,8 @@ Option* option_processing(int argc, char* const argv[]) { PREF_NO_PROXY.c_str(), required_argument, &lopt, 235 }, { PREF_USE_HEAD.c_str(), optional_argument, &lopt, 236 }, { PREF_EVENT_POLL.c_str(), required_argument, &lopt, 237 }, + { PREF_HTTP_SERVER_LISTEN_PORT.c_str(), required_argument, &lopt, 238 }, + { PREF_ENABLE_HTTP_SERVER.c_str(), optional_argument, &lopt, 239 }, #if defined ENABLE_BITTORRENT || defined ENABLE_METALINK { PREF_SHOW_FILES.c_str(), no_argument, NULL, 'S' }, { PREF_SELECT_FILE.c_str(), required_argument, &lopt, 21 }, @@ -487,6 +489,13 @@ Option* option_processing(int argc, char* const argv[]) case 237: cmdstream << PREF_EVENT_POLL << "=" << optarg << "\n"; break; + case 238: + cmdstream << PREF_HTTP_SERVER_LISTEN_PORT << "=" << optarg << "\n"; + break; + case 239: + cmdstream << PREF_ENABLE_HTTP_SERVER << "=" << toBoolArg(optarg) + << "\n"; + break; } break; } diff --git a/src/prefs.cc b/src/prefs.cc index 710b3385..19e9a8ca 100644 --- a/src/prefs.cc +++ b/src/prefs.cc @@ -150,6 +150,10 @@ const std::string PREF_MAX_FILE_NOT_FOUND("max-file-not-found"); const std::string PREF_EVENT_POLL("event-poll"); const std::string V_EPOLL("epoll"); const std::string V_SELECT("select"); +// value: 1*digit +const std::string PREF_HTTP_SERVER_LISTEN_PORT("http-server-listen-port"); +// value: true | false +const std::string PREF_ENABLE_HTTP_SERVER("enable-http-server"); /** * FTP related preferences diff --git a/src/prefs.h b/src/prefs.h index c4ae3688..6eb7089e 100644 --- a/src/prefs.h +++ b/src/prefs.h @@ -154,6 +154,10 @@ extern const std::string PREF_MAX_FILE_NOT_FOUND; extern const std::string PREF_EVENT_POLL; extern const std::string V_EPOLL; extern const std::string V_SELECT; +// value: 1*digit +extern const std::string PREF_HTTP_SERVER_LISTEN_PORT; +// value: true | false +extern const std::string PREF_ENABLE_HTTP_SERVER; /** * FTP related preferences diff --git a/src/usage_text.h b/src/usage_text.h index 58d6b864..544d6a3d 100644 --- a/src/usage_text.h +++ b/src/usage_text.h @@ -462,3 +462,15 @@ _(" --use-head[=true|false] Use HEAD method for the first request to the HT " server.") #define TEXT_EVENT_POLL \ _(" --event-poll=POLL Specify the method for polling events.") +#define TEXT_HTTP_SERVER_LISTEN_PORT \ +_(" --http-server-listen-port=PORT Specify a port number for the built-in HTTP\n"\ + " Server to listen to.") +// Excluded from translation candidiates because it is subject to change. +#define TEXT_ENABLE_HTTP_SERVER \ + " --enable-http-server[=true|false] Enable the built-in HTTP server. Currently,\n"\ + " this is the experimental feature and it just\n"\ + " provides the current download progress. Use your\n"\ + " web browser(recommend to use console-based one,\n"\ + " such as elinks, w3m) to connect the server and see\n"\ + " what's what." + |
