add some more files to gitignore
[openblackhole/openblackhole-enigma2.git] / lib / network / httpd.cpp
1 // #define DEBUG_HTTPD
2 #include <lib/network/httpd.h>
3
4 #include <sys/socket.h>
5 #include <lib/base/smartptr.h>
6 #include <lib/base/estring.h>
7 #include <error.h>
8 #include <errno.h>
9 #include <time.h>
10 #include <ctype.h>
11
12 #include <lib/network/http_dyn.h>
13 #include <lib/network/http_file.h>
14
15 eHTTPDataSource::eHTTPDataSource(eHTTPConnection *c): connection(c)
16 {
17 }
18
19 eHTTPDataSource::~eHTTPDataSource()
20 {
21 }
22
23 void eHTTPDataSource::haveData(void *data, int len)
24 {
25 }
26
27 int eHTTPDataSource::doWrite(int)
28 {
29         return 0;
30 }
31
32 DEFINE_REF(eHTTPError);
33
34 eHTTPError::eHTTPError(eHTTPConnection *c, int errcode): eHTTPDataSource(c), errcode(errcode)
35 {
36         std::string error="unknown error";
37         switch (errcode)
38         {
39         case 400: error="Bad Request"; break;
40         case 401: error="Unauthorized"; break;
41         case 403: error="Forbidden"; break;
42         case 404: error="Not found"; break;
43         case 405: error="Method not allowed"; break;
44         case 500: error="Internal server error"; break;
45         }
46         connection->code_descr=error;
47         connection->code=errcode;
48         
49         connection->local_header["Content-Type"]=std::string("text/html");
50 }
51
52 int eHTTPError::doWrite(int w)
53 {
54         std::string html;
55         html+="<html><head><title>Error " + getNum(connection->code) + "</title></head>"+
56                 "<body><h1>Error " + getNum(errcode) + ": " + connection->code_descr + "</h1></body></html>\n";
57         connection->writeBlock(html.c_str(), html.length());
58         return -1;
59 }
60
61 eHTTPConnection::eHTTPConnection(int socket, int issocket, eHTTPD *parent, int persistent): eSocket(socket, issocket, parent->ml), parent(parent), persistent(persistent)
62 {
63 #ifdef DEBUG_HTTPD
64         eDebug("eHTTPConnection");
65 #endif
66         CONNECT(this->readyRead_ , eHTTPConnection::readData);
67         CONNECT(this->bytesWritten_ , eHTTPConnection::bytesWritten);
68         CONNECT(this->error_ , eHTTPConnection::gotError);
69         CONNECT(this->connectionClosed_ , eHTTPConnection::destruct);
70         CONNECT(this->hangup , eHTTPConnection::gotHangup);
71
72         buffersize=128*1024;
73         localstate=stateWait;
74         remotestate=stateRequest;
75         data=0;
76 }
77
78 void eHTTPConnection::destruct()
79 {
80         eDebug("destruct, this %p!", this);
81         gotHangup();
82         delete this;
83 }
84
85 eHTTPConnection::eHTTPConnection(eMainloop *ml): eSocket(ml), parent(0), persistent(0)
86 {
87         CONNECT(this->readyRead_ , eHTTPConnection::readData);
88         CONNECT(this->bytesWritten_ , eHTTPConnection::bytesWritten);
89         CONNECT(this->error_ , eHTTPConnection::gotError);
90         CONNECT(this->connected_ , eHTTPConnection::hostConnected);     
91         CONNECT(this->connectionClosed_ , eHTTPConnection::destruct);
92
93         localstate=stateWait;
94         remotestate=stateWait;
95         
96         buffersize=64*1024;
97         data=0;
98 }
99
100 void eHTTPConnection::hostConnected()
101 {
102         processLocalState();
103 }
104
105 void eHTTPConnection::start()
106 {
107         if (localstate==stateWait)
108         {
109                 localstate=stateRequest;
110                 processLocalState();
111         }
112 }
113
114 void eHTTPConnection::gotHangup()
115 {
116         if (data && remotestate == stateData)
117                 data->haveData(0, 0);
118         data = 0;
119         transferDone(0);
120
121         localstate=stateWait;
122         remotestate=stateRequest;
123         
124         remote_header.clear();
125         local_header.clear();
126 }
127
128 eHTTPConnection *eHTTPConnection::doRequest(const char *uri, eMainloop *ml, int *error)
129 {
130         if (error)
131                 *error=0;
132
133         char *defaultproto="http";
134         std::string proto, host, path;
135         int port=80;
136         
137         int state=0; // 0 proto, 1 host, 2 port 3 path
138         
139         while (*uri)
140         {
141                 switch (state)
142                 {
143                 case 0:
144                         if (!strncmp(uri, "://", 3))
145                         {
146                                 state=1;
147                                 uri+=3;
148                         } else if ((*uri=='/') || (*uri==':'))
149                         {
150                                 host=proto;
151                                 state=1;
152                                 proto=defaultproto;
153                         } else
154                                 proto.push_back(*uri++);
155                         break;
156                 case 1:
157                         if (*uri=='/')
158                                 state=3;
159                         else if (*uri==':')
160                         {
161                                 state=2;
162                                 port=0;
163                                 uri++;
164                         } else
165                                 host.push_back(*uri++);
166                         break;
167                 case 2:
168                         if (*uri=='/')
169                                 state=3;
170                         else
171                         {
172                                 if (!isdigit(*uri))
173                                 {
174                                         port=-1;
175                                         state=3;
176                                 } else
177                                 {
178                                         port*=10;
179                                         port+=*uri++-'0';
180                                 }
181                         }
182                         break;
183                 case 3:
184                         path.push_back(*uri++);
185                 }
186         }
187         
188         if (state==0)
189         {
190                 path=proto;
191                 proto=defaultproto;
192         }
193
194 #ifdef DEBUG_HTTPD
195         eDebug("proto: '%s', host '%s', path '%s', port '%d'", proto.c_str(), host.c_str(), path.c_str(), port);
196 #endif
197
198         if (!host.size())
199         {
200                 eDebug("no host given");
201                 if (error)
202                         *error=ENOENT;
203                 return 0;
204         }
205         
206         if (strcmp(proto.c_str(), "http"))
207         {
208                 eDebug("invalid protocol (%s)", proto.c_str());
209                 if (error)
210                         *error=EINVAL;
211                 return 0;
212         }
213         
214         if (port == -1)
215         {
216                 eDebug("invalid port");
217                 if (error)
218                         *error=EINVAL;
219                 return 0;
220         }
221         
222         if (!path.size())
223                 path="/";
224
225         eHTTPConnection *c=new eHTTPConnection(ml);
226         c->request="GET";
227         c->requestpath=path.c_str();
228         c->httpversion="HTTP/1.0";
229         c->local_header["Host"]=host;
230         if ((*error=c->connectToHost(host, port))) // already deleted by error
231                 return 0;
232         return c;
233 }
234
235 void eHTTPConnection::readData()
236 {
237         processRemoteState();
238 }
239
240 void eHTTPConnection::bytesWritten(int)
241 {
242         processLocalState();
243 }
244
245 int eHTTPConnection::processLocalState()
246 {
247         switch (state())
248         {
249         case Connection:
250                 break;
251         default:
252                 return 0;
253         }
254         int done=0;
255         while (!done)
256         {
257 #ifdef DEBUG_HTTPD
258                 eDebug("processing local state %d", localstate);
259 #endif
260                 switch (localstate)
261                 {
262                 case stateWait:
263 #ifdef DEBUG_HTTPD
264                         eDebug("local wait");
265 #endif
266                         done=1;
267                         break;
268                 case stateRequest:
269                 {
270 #ifdef DEBUG_HTTPD
271                         eDebug("local request");
272 #endif
273                         std::string req=request+" "+requestpath+" "+httpversion+"\r\n";
274                         writeBlock(req.c_str(), req.length());
275                         localstate=stateHeader;
276                         remotestate=stateResponse;
277                         break;
278                 }
279                 case stateResponse:
280                 {
281 #ifdef DEBUG_HTTPD
282                         eDebug("local Response");
283 #endif
284                         writeString( (httpversion + " " + getNum(code) + " " + code_descr + "\r\n").c_str() );
285                         localstate=stateHeader;
286                         local_header["Connection"]="close";
287                         break;
288                 }
289                 case stateHeader:
290 #ifdef DEBUG_HTTPD
291                         eDebug("local header");
292 #endif
293                         for (std::map<std::string,std::string>::iterator cur=local_header.begin(); cur!=local_header.end(); ++cur)
294                         {
295                                 writeString(cur->first.c_str());
296                                 writeString(": ");
297                                 writeString(cur->second.c_str());
298                                 writeString("\r\n");
299                         }
300                         writeString("\r\n");
301                         if (request=="HEAD")
302                                 localstate=stateDone;
303                         else
304                                 localstate=stateData;
305                         break;
306                 case stateData:
307 #ifdef DEBUG_HTTPD
308                         eDebug("local data");
309 #endif
310                         if (data)
311                         {
312                                 int btw=buffersize-bytesToWrite();
313                                 if (btw>0)
314                                 {
315                                         if (data->doWrite(btw)<0)
316                                         {
317                                                 localstate=stateDone;
318                                         } else
319                                                 done=1;
320                                 } else
321                                         done=1;
322                         } else
323                                 done=1; // wait for remote response
324                         break;
325                 case stateDone:
326 #if 0
327                         // move to stateClose
328                         if (remote_header.find("Connection") != remote_header.end())
329                         {
330                                 std::string &connection=remote_header["Connection"];
331                                 if (connection == "keep-alive")
332                                         localstate=stateWait;
333                                 else
334                                         localstate=stateClose;
335                         }
336 #endif
337 #ifdef DEBUG_HTTPD
338                         eDebug("locate state done");
339 #endif
340                         if (!persistent)
341                                 localstate=stateClose;
342                         else
343                                 localstate=stateWait;
344                         break;
345                 case stateClose:
346 #ifdef DEBUG_HTTPD
347                         eDebug("closedown");
348 #endif
349                         if (persistent)
350                         {
351                                 data = 0;
352                                 localstate = stateWait;
353                         } else
354                                 close();                // bye, bye, remote
355                         return 1;
356                 }
357         }
358 #ifdef DEBUG_HTTPD
359         eDebug("end local");
360 #endif
361         return 0;
362 }
363
364 int eHTTPConnection::processRemoteState()
365 {
366         int abort=0, done=0;
367 #ifdef DEBUG_HTTPD
368         eDebug("%d bytes avail", bytesAvailable());
369 #endif
370         while (((!done) || bytesAvailable()) && !abort)
371         {
372                 switch (remotestate)
373                 {
374                 case stateWait:
375                 {
376                         int i=0;
377 #ifdef DEBUG_HTTPD
378                         eDebug("remote stateWait");
379 #endif
380                         char buffer[1024];
381                         while (bytesAvailable()) {
382                                 i=readBlock(buffer, 1024);
383                         }
384                         done=1;
385                         break;
386                 }
387                 case stateRequest:
388                 {
389 #ifdef DEBUG_HTTPD
390                         eDebug("stateRequest");
391 #endif
392                         std::string line;
393                         if (!getLine(line))
394                         {
395                                 done=1;
396                                 abort=1;
397                                 break;
398                         }
399         
400                         int del[2];
401                         del[0]=line.find(" ");
402                         del[1]=line.find(" ", del[0]+1);
403                         if (del[0]==-1)
404                         {
405                                 data = 0;
406                                 eDebug("request buggy");
407                                 httpversion="HTTP/1.0";
408                                 data=new eHTTPError(this, 400);
409                                 done=0;
410                                 localstate=stateResponse;
411                                 remotestate=stateDone;
412                                 if (processLocalState())
413                                         return -1;
414                                 break;
415                         }
416                         request=line.substr(0, del[0]);
417                         requestpath=line.substr(del[0]+1, (del[1]==-1)?-1:(del[1]-del[0]-1));
418                         if (del[1]!=-1)
419                         {
420                                 is09=0;
421                                 httpversion=line.substr(del[1]+1);
422                         } else
423                                 is09=1;
424
425                         if (is09 || (httpversion.substr(0, 7) != "HTTP/1.") || httpversion.size()!=8)
426                         {
427                                 remotestate=stateData;
428                                 done=0;
429                                 httpversion="HTTP/1.0";
430                                 content_length_remaining=content_length_remaining=0;
431                                 data=new eHTTPError(this, 400); // bad request - not supporting version 0.9 yet
432                         } else
433                                 remotestate=stateHeader;
434                         break;
435                 }
436                 case stateResponse:
437                 {
438 #ifdef DEBUG_HTTPD
439                         eDebug("state response..");
440 #endif
441                         std::string line;
442                         if (!getLine(line))
443                         {
444                                 done=1;
445                                 abort=1;
446                                 break;
447                         }
448 #ifdef DEBUG_HTTPD
449                         eDebug("line: %s", line.c_str());
450 #endif
451                         int del[2];
452                         del[0]=line.find(" ");
453                         del[1]=line.find(" ", del[0]+1);
454                         if (del[0]==-1)
455                                 code=-1;
456                         else
457                         {
458                                 httpversion=line.substr(0, del[0]);
459                                 code=atoi(line.substr(del[0]+1, (del[1]==-1)?-1:(del[1]-del[0]-1)).c_str());
460                                 if (del[1] != -1)
461                                         code_descr=line.substr(del[1]+1);
462                                 else
463                                         code_descr="";
464                         }
465                         
466                         remotestate=stateHeader;
467                         break;
468                 }
469                 case stateHeader:
470                 {
471 #ifdef DEBUG_HTTPD
472                         eDebug("remote stateHeader");
473 #endif
474                         std::string line;
475                         if (!getLine(line))
476                         {
477                                 done=1;
478                                 abort=1;
479                                 break;
480                         }
481                         if (!line.length())
482                         {
483                                 content_length=0;
484                                 content_length_remaining=-1;
485                                 if (remote_header.count("Content-Length"))
486                                 {
487                                         content_length=atoi(remote_header["Content-Length"].c_str());
488                                         content_length_remaining=content_length;
489                                 }
490
491                                 if (parent)
492                                 {
493                                         for (eSmartPtrList<iHTTPPathResolver>::iterator i(parent->resolver); i != parent->resolver.end(); ++i)
494                                                 if (!(i->getDataSource(data, request, requestpath, this)))
495                                                         break;
496                                         localstate=stateResponse;               // can be overridden by dataSource
497                                 } else
498                                         data=createDataSource(this);
499
500                                 if (!data)
501                                 {
502                                         data = 0;
503                                         data = new eHTTPError(this, 404);
504                                 }
505
506                                 if (content_length ||           // if content-length is set, we have content
507                                                 remote_header.count("Content-Type") ||          // content-type - the same
508                                                 (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)
509                                         remotestate=stateData;
510                                 else
511                                 {
512                                         data->haveData(0, 0);
513                                         remotestate=stateDone;
514                                 }
515                                 if (processLocalState())
516                                         return -1;
517                         } else
518                         {
519                                 int del=line.find(":");
520                                 std::string name=line.substr(0, del), value=line.substr(del+1);
521                                 if (value[0]==' ')
522                                         value=value.substr(1);
523                                 remote_header[std::string(name)]=std::string(value);
524                         }
525                         done=1;
526                         break;
527                 }
528                 case stateData:
529                 {
530 #ifdef DEBUG_HTTPD
531                         eDebug("remote stateData");
532 #endif
533                         ASSERT(data);
534                         char buffer[16284];
535                         int len;
536                         while (bytesAvailable())
537                         {
538                                 int tr=sizeof(buffer);
539                                 if (content_length_remaining != -1)
540                                         if (tr>content_length_remaining)
541                                                 tr=content_length_remaining;
542                                 len=readBlock(buffer, tr);
543                                 data->haveData(buffer, len);
544                                 if (content_length_remaining != -1)
545                                         content_length_remaining-=len;
546                                 if (!content_length_remaining)
547                                 {
548                                         data->haveData(0, 0);
549                                         remotestate=stateDone;
550                                         break;
551                                 }
552                         }
553                         done=1;
554                         if (processLocalState())
555                                 return -1;
556                         break;
557                 }
558                 case stateDone:
559                         remotestate=stateClose;
560                         break;
561                 case stateClose:
562 //                      if (!persistent)
563                                 remotestate=stateWait;
564 //                      else
565 //                              remotestate=stateRequest;
566                         abort=1;
567                         break;
568                 default:
569                         eDebug("HTTP: invalid state %d", remotestate);
570                         done=1;
571                 }
572         }
573 #ifdef DEBUG_HTTPD
574         eDebug("end remote");
575 #endif
576         return 0;
577 }
578
579 void eHTTPConnection::writeString(const char *data)
580 {
581         writeBlock(data, strlen(data));
582 }
583
584 int eHTTPConnection::getLine(std::string &line)
585 {
586         if (!canReadLine())
587                 return 0;
588
589         line = readLine();
590         line.erase(line.length()-1);
591
592         if (line[(line.length()-1)] == '\r')
593                 line.erase(line.length()-1);
594         
595         return 1;
596 }
597
598 void eHTTPConnection::gotError(int err)
599 {
600         data = 0;
601         transferDone(err);
602         delete this;
603 }
604
605 eHTTPD::eHTTPD(int port, eMainloop *ml): eServerSocket(port, ml), ml(ml)
606 {
607         if (!ok())
608                 eDebug("[NET] httpd server FAILED on port %d", port);
609         else
610                 eDebug("[NET] httpd server started on port %d", port);
611 }
612
613 eHTTPConnection::~eHTTPConnection()
614 {
615         eDebug("HTTP connection destruct");
616         if ((!persistent) && (state()!=Idle))
617                 eWarning("~eHTTPConnection, status still %d", state());
618 }
619
620 void eHTTPD::newConnection(int socket)
621 {
622         new eHTTPConnection(socket, 1, this);
623 }