aboutsummaryrefslogtreecommitdiff
path: root/lib/network/httpd.cpp
diff options
context:
space:
mode:
authorFelix Domke <tmbinc@elitedvb.net>2003-10-17 15:35:43 +0000
committerFelix Domke <tmbinc@elitedvb.net>2003-10-17 15:35:43 +0000
commitfc2f5b2cd655f1391f2abda1b39e37cdec98a951 (patch)
tree312efcea86a319de407a7c314fb981fb1c71019a /lib/network/httpd.cpp
downloadenigma2-fc2f5b2cd655f1391f2abda1b39e37cdec98a951.tar.gz
enigma2-fc2f5b2cd655f1391f2abda1b39e37cdec98a951.zip
Initial revision
Diffstat (limited to 'lib/network/httpd.cpp')
-rw-r--r--lib/network/httpd.cpp631
1 files changed, 631 insertions, 0 deletions
diff --git a/lib/network/httpd.cpp b/lib/network/httpd.cpp
new file mode 100644
index 00000000..b8ed1a9d
--- /dev/null
+++ b/lib/network/httpd.cpp
@@ -0,0 +1,631 @@
+// #define DEBUG_HTTPD
+#include <lib/network/httpd.h>
+
+#include <sys/socket.h>
+#include <error.h>
+#include <errno.h>
+#include <time.h>
+#include <ctype.h>
+
+eHTTPDataSource::eHTTPDataSource(eHTTPConnection *c): connection(c)
+{
+}
+
+eHTTPDataSource::~eHTTPDataSource()
+{
+}
+
+void eHTTPDataSource::haveData(void *data, int len)
+{
+}
+
+int eHTTPDataSource::doWrite(int)
+{
+ return 0;
+}
+
+eHTTPError::eHTTPError(eHTTPConnection *c, int errcode): eHTTPDataSource(c), errcode(errcode)
+{
+ eString error="unknown error";
+ switch (errcode)
+ {
+ case 400: error="Bad Request"; break;
+ case 401: error="Unauthorized"; break;
+ case 403: error="Forbidden"; break;
+ case 404: error="Not found"; break;
+ case 405: error="Method not allowed"; break;
+ case 500: error="Internal server error"; break;
+ }
+ connection->code_descr=error;
+ connection->code=errcode;
+
+ connection->local_header["Content-Type"]=std::string("text/html");
+}
+
+int eHTTPError::doWrite(int w)
+{
+ eString html;
+ html+="<html><head><title>Error "+eString().setNum(connection->code)+"</title></head>"+
+ "<body><h1>Error "+eString().setNum(errcode)+": "+connection->code_descr+"</h1></body></html>\n";
+ connection->writeBlock(html.c_str(), html.length());
+ return -1;
+}
+
+eHTTPConnection::eHTTPConnection(int socket, int issocket, eHTTPD *parent, int persistent): eSocket(socket, issocket, parent->ml), parent(parent), persistent(persistent)
+{
+#ifdef DEBUG_HTTPD
+ eDebug("eHTTPConnection");
+#endif
+ CONNECT(this->readyRead_ , eHTTPConnection::readData);
+ CONNECT(this->bytesWritten_ , eHTTPConnection::bytesWritten);
+ CONNECT(this->error_ , eHTTPConnection::gotError);
+ CONNECT(this->connectionClosed_ , eHTTPConnection::destruct);
+ CONNECT(this->hangup , eHTTPConnection::gotHangup);
+
+ buffersize=128*1024;
+ localstate=stateWait;
+ remotestate=stateRequest;
+ data=0;
+}
+
+void eHTTPConnection::destruct()
+{
+ gotHangup();
+ delete this;
+}
+
+eHTTPConnection::eHTTPConnection(eMainloop *ml): eSocket(ml), parent(0), persistent(0)
+{
+ CONNECT(this->readyRead_ , eHTTPConnection::readData);
+ CONNECT(this->bytesWritten_ , eHTTPConnection::bytesWritten);
+ CONNECT(this->error_ , eHTTPConnection::gotError);
+ CONNECT(this->connected_ , eHTTPConnection::hostConnected);
+ CONNECT(this->connectionClosed_ , eHTTPConnection::destruct);
+
+ localstate=stateWait;
+ remotestate=stateWait;
+
+ buffersize=64*1024;
+ data=0;
+}
+
+void eHTTPConnection::hostConnected()
+{
+ processLocalState();
+}
+
+void eHTTPConnection::start()
+{
+ if (localstate==stateWait)
+ {
+ localstate=stateRequest;
+ processLocalState();
+ }
+}
+
+void eHTTPConnection::gotHangup()
+{
+ if (data && remotestate == stateData)
+ data->haveData(0, 0);
+ if (data)
+ {
+ delete data;
+ data=0;
+ }
+ transferDone(0);
+
+ localstate=stateWait;
+ remotestate=stateRequest;
+
+ remote_header.clear();
+ local_header.clear();
+}
+
+eHTTPConnection *eHTTPConnection::doRequest(const char *uri, eMainloop *ml, int *error)
+{
+ if (error)
+ *error=0;
+
+ char *defaultproto="http";
+ std::string proto, host, path;
+ int port=80;
+
+ int state=0; // 0 proto, 1 host, 2 port 3 path
+
+ while (*uri)
+ {
+ switch (state)
+ {
+ case 0:
+ if (!strncmp(uri, "://", 3))
+ {
+ state=1;
+ uri+=3;
+ } else if ((*uri=='/') || (*uri==':'))
+ {
+ host=proto;
+ state=1;
+ proto=defaultproto;
+ } else
+ proto.push_back(*uri++);
+ break;
+ case 1:
+ if (*uri=='/')
+ state=3;
+ else if (*uri==':')
+ {
+ state=2;
+ port=0;
+ uri++;
+ } else
+ host.push_back(*uri++);
+ break;
+ case 2:
+ if (*uri=='/')
+ state=3;
+ else
+ {
+ if (!isdigit(*uri))
+ {
+ port=-1;
+ state=3;
+ } else
+ {
+ port*=10;
+ port+=*uri++-'0';
+ }
+ }
+ break;
+ case 3:
+ path.push_back(*uri++);
+ }
+ }
+
+ if (state==0)
+ {
+ path=proto;
+ proto=defaultproto;
+ }
+
+#ifdef DEBUG_HTTPD
+ eDebug("proto: '%s', host '%s', path '%s', port '%d'", proto.c_str(), host.c_str(), path.c_str(), port);
+#endif
+
+ if (!host.size())
+ {
+ eDebug("no host given");
+ if (error)
+ *error=ENOENT;
+ return 0;
+ }
+
+ if (strcmp(proto.c_str(), "http"))
+ {
+ eDebug("invalid protocol (%s)", proto.c_str());
+ if (error)
+ *error=EINVAL;
+ return 0;
+ }
+
+ if (port == -1)
+ {
+ eDebug("invalid port");
+ if (error)
+ *error=EINVAL;
+ return 0;
+ }
+
+ if (!path.size())
+ path="/";
+
+ eHTTPConnection *c=new eHTTPConnection(ml);
+ c->request="GET";
+ c->requestpath=path.c_str();
+ c->httpversion="HTTP/1.0";
+ c->local_header["Host"]=host;
+ if ((*error=c->connectToHost(host, port))) // already deleted by error
+ return 0;
+ return c;
+}
+
+void eHTTPConnection::readData()
+{
+ processRemoteState();
+}
+
+void eHTTPConnection::bytesWritten(int)
+{
+ processLocalState();
+}
+
+int eHTTPConnection::processLocalState()
+{
+ switch (state())
+ {
+ case Connection:
+ break;
+ default:
+ return 0;
+ }
+ int done=0;
+ while (!done)
+ {
+#ifdef DEBUG_HTTPD
+ eDebug("processing local state %d", localstate);
+#endif
+ switch (localstate)
+ {
+ case stateWait:
+#ifdef DEBUG_HTTPD
+ eDebug("local wait");
+#endif
+ done=1;
+ break;
+ case stateRequest:
+ {
+#ifdef DEBUG_HTTPD
+ eDebug("local request");
+#endif
+ eString req=request+" "+requestpath+" "+httpversion+"\r\n";
+ writeBlock(req.c_str(), req.length());
+ localstate=stateHeader;
+ remotestate=stateResponse;
+ break;
+ }
+ case stateResponse:
+ {
+#ifdef DEBUG_HTTPD
+ eDebug("local Response");
+#endif
+ writeString( (httpversion + " " + eString().setNum(code)+" " + code_descr + "\r\n").c_str() );
+ localstate=stateHeader;
+ local_header["Connection"]="close";
+ break;
+ }
+ case stateHeader:
+#ifdef DEBUG_HTTPD
+ eDebug("local header");
+#endif
+ for (std::map<std::string,std::string>::iterator cur=local_header.begin(); cur!=local_header.end(); ++cur)
+ {
+ writeString(cur->first.c_str());
+ writeString(": ");
+ writeString(cur->second.c_str());
+ writeString("\r\n");
+ }
+ writeString("\r\n");
+ if (request=="HEAD")
+ localstate=stateDone;
+ else
+ localstate=stateData;
+ break;
+ case stateData:
+#ifdef DEBUG_HTTPD
+ eDebug("local data");
+#endif
+ if (data)
+ {
+ int btw=buffersize-bytesToWrite();
+ if (btw>0)
+ {
+ if (data->doWrite(btw)<0)
+ {
+ localstate=stateDone;
+ } else
+ done=1;
+ } else
+ done=1;
+ } else
+ done=1; // wait for remote response
+ break;
+ case stateDone:
+#if 0
+ // move to stateClose
+ if (remote_header.find("Connection") != remote_header.end())
+ {
+ eString &connection=remote_header["Connection"];
+ if (connection == "keep-alive")
+ localstate=stateWait;
+ else
+ localstate=stateClose;
+ }
+#endif
+#ifdef DEBUG_HTTPD
+ eDebug("locate state done");
+#endif
+ if (!persistent)
+ localstate=stateClose;
+ else
+ localstate=stateWait;
+ break;
+ case stateClose:
+#ifdef DEBUG_HTTPD
+ eDebug("closedown");
+#endif
+ if (persistent)
+ {
+ if (data)
+ delete data;
+ data=0;
+ localstate=stateWait;
+ } else
+ close(); // bye, bye, remote
+ return 1;
+ }
+ }
+#ifdef DEBUG_HTTPD
+ eDebug("end local");
+#endif
+ return 0;
+}
+
+int eHTTPConnection::processRemoteState()
+{
+ int abort=0, done=0;
+#ifdef DEBUG_HTTPD
+ eDebug("%d bytes avail", bytesAvailable());
+#endif
+ while (((!done) || bytesAvailable()) && !abort)
+ {
+ switch (remotestate)
+ {
+ case stateWait:
+ {
+ int i=0;
+#ifdef DEBUG_HTTPD
+ eDebug("remote stateWait");
+#endif
+ char buffer[1024];
+ while (bytesAvailable()) {
+ i=readBlock(buffer, 1024);
+ }
+ done=1;
+ break;
+ }
+ case stateRequest:
+ {
+#ifdef DEBUG_HTTPD
+ eDebug("stateRequest");
+#endif
+ eString line;
+ if (!getLine(line))
+ {
+ done=1;
+ abort=1;
+ break;
+ }
+
+ int del[2];
+ del[0]=line.find(" ");
+ del[1]=line.find(" ", del[0]+1);
+ if (del[0]==-1)
+ {
+ if (data)
+ delete data;
+ eDebug("request buggy");
+ httpversion="HTTP/1.0";
+ data=new eHTTPError(this, 400);
+ done=0;
+ localstate=stateResponse;
+ remotestate=stateDone;
+ if (processLocalState())
+ return -1;
+ break;
+ }
+ request=line.left(del[0]);
+ requestpath=line.mid(del[0]+1, (del[1]==-1)?-1:(del[1]-del[0]-1));
+ if (del[1]!=-1)
+ {
+ is09=0;
+ httpversion=line.mid(del[1]+1);
+ } else
+ is09=1;
+
+ if (is09 || (httpversion.left(7) != "HTTP/1.") || httpversion.size()!=8)
+ {
+ remotestate=stateData;
+ done=0;
+ httpversion="HTTP/1.0";
+ content_length_remaining=content_length_remaining=0;
+ data=new eHTTPError(this, 400); // bad request - not supporting version 0.9 yet
+ } else
+ remotestate=stateHeader;
+ break;
+ }
+ case stateResponse:
+ {
+#ifdef DEBUG_HTTPD
+ eDebug("state response..");
+#endif
+ eString line;
+ if (!getLine(line))
+ {
+ done=1;
+ abort=1;
+ break;
+ }
+#ifdef DEBUG_HTTPD
+ eDebug("line: %s", line.c_str());
+#endif
+ int del[2];
+ del[0]=line.find(" ");
+ del[1]=line.find(" ", del[0]+1);
+ if (del[0]==-1)
+ code=-1;
+ else
+ {
+ httpversion=line.left(del[0]);
+ code=atoi(line.mid(del[0]+1, (del[1]==-1)?-1:(del[1]-del[0]-1)).c_str());
+ if (del[1] != -1)
+ code_descr=line.mid(del[1]+1);
+ else
+ code_descr="";
+ }
+
+ remotestate=stateHeader;
+ break;
+ }
+ case stateHeader:
+ {
+#ifdef DEBUG_HTTPD
+ eDebug("remote stateHeader");
+#endif
+ eString line;
+ if (!getLine(line))
+ {
+ done=1;
+ abort=1;
+ break;
+ }
+ if (!line.length())
+ {
+ content_length=0;
+ content_length_remaining=-1;
+ if (remote_header.count("Content-Length"))
+ {
+ content_length=atoi(remote_header["Content-Length"].c_str());
+ content_length_remaining=content_length;
+ }
+
+ if (parent)
+ {
+ for (ePtrList<eHTTPPathResolver>::iterator i(parent->resolver); i != parent->resolver.end(); ++i)
+ {
+ if ((data=i->getDataSource(request, requestpath, this)))
+ break;
+ }
+ localstate=stateResponse; // can be overridden by dataSource
+ } else
+ data=createDataSource(this);
+
+ if (!data)
+ {
+ if (data)
+ delete data;
+ data=new eHTTPError(this, 404);
+ }
+
+ if (content_length || // if content-length is set, we have content
+ remote_header.count("Content-Type") || // content-type - the same
+ (localstate != stateResponse)) // however, if we are NOT in response-state, so we are NOT server, there's ALWAYS more data to come. (exception: http/1.1 persistent)
+ remotestate=stateData;
+ else
+ {
+ data->haveData(0, 0);
+ remotestate=stateDone;
+ }
+ if (processLocalState())
+ return -1;
+ } else
+ {
+ int del=line.find(":");
+ eString name=line.left(del), value=line.mid(del+1);
+ if (value[0]==' ')
+ value=value.mid(1);
+ remote_header[std::string(name)]=std::string(value);
+ }
+ done=1;
+ break;
+ }
+ case stateData:
+ {
+#ifdef DEBUG_HTTPD
+ eDebug("remote stateData");
+#endif
+ ASSERT(data);
+ char buffer[16284];
+ int len;
+ while (bytesAvailable())
+ {
+ int tr=sizeof(buffer);
+ if (content_length_remaining != -1)
+ if (tr>content_length_remaining)
+ tr=content_length_remaining;
+ len=readBlock(buffer, tr);
+ data->haveData(buffer, len);
+ if (content_length_remaining != -1)
+ content_length_remaining-=len;
+ if (!content_length_remaining)
+ {
+ data->haveData(0, 0);
+ remotestate=stateDone;
+ break;
+ }
+ }
+ done=1;
+ if (processLocalState())
+ return -1;
+ break;
+ }
+ case stateDone:
+ remotestate=stateClose;
+ break;
+ case stateClose:
+// if (!persistent)
+ remotestate=stateWait;
+// else
+// remotestate=stateRequest;
+ abort=1;
+ break;
+ default:
+ eDebug("HTTP: invalid state %d", remotestate);
+ done=1;
+ }
+ }
+#ifdef DEBUG_HTTPD
+ eDebug("end remote");
+#endif
+ return 0;
+}
+
+void eHTTPConnection::writeString(const char *data)
+{
+ writeBlock(data, strlen(data));
+}
+
+int eHTTPConnection::getLine(eString &line)
+{
+ if (!canReadLine())
+ return 0;
+
+ line = readLine();
+ line.erase(line.length()-1);
+
+ if (line[(line.length()-1)] == '\r')
+ line.erase(line.length()-1);
+
+ return 1;
+}
+
+void eHTTPConnection::gotError(int err)
+{
+ if (data)
+ {
+ delete data;
+ data=0;
+ }
+ transferDone(err);
+ delete this;
+}
+
+eHTTPD::eHTTPD(int port, eMainloop *ml): eServerSocket(port, ml), ml(ml)
+{
+ if (!ok())
+ eDebug("[NET] httpd server FAILED on port %d", port);
+ else
+ eDebug("[NET] httpd server started on port %d", port);
+ resolver.setAutoDelete(true);
+}
+
+eHTTPConnection::~eHTTPConnection()
+{
+ if ((!persistent) && (state()!=Idle))
+ eWarning("~eHTTPConnection, status still %d", state());
+ if (data)
+ delete data;
+}
+
+void eHTTPD::newConnection(int socket)
+{
+ new eHTTPConnection(socket, 1, this);
+}