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