diff options
| author | Felix Domke <tmbinc@elitedvb.net> | 2003-10-17 15:36:42 +0000 |
|---|---|---|
| committer | Felix Domke <tmbinc@elitedvb.net> | 2003-10-17 15:36:42 +0000 |
| commit | d63d2c3c6cbbd574dda4f8b00ebe6c661735edd5 (patch) | |
| tree | 84d0cacfd0b6c1241c236c7860f7cbd7f26901bb /lib/network/httpd.cpp | |
| download | enigma2-d63d2c3c6cbbd574dda4f8b00ebe6c661735edd5.tar.gz enigma2-d63d2c3c6cbbd574dda4f8b00ebe6c661735edd5.zip | |
import of enigma2
Diffstat (limited to 'lib/network/httpd.cpp')
| -rw-r--r-- | lib/network/httpd.cpp | 631 |
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); +} |
