diff options
Diffstat (limited to 'lib/HttpServ.cc')
-rw-r--r-- | lib/HttpServ.cc | 946 |
1 files changed, 473 insertions, 473 deletions
diff --git a/lib/HttpServ.cc b/lib/HttpServ.cc index a8e7301..a4101a7 100644 --- a/lib/HttpServ.cc +++ b/lib/HttpServ.cc @@ -1,473 +1,473 @@ -#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-#include "Socket.h"
-#include "Action.h"
-#include "HttpServ.h"
-#include "Buffer.h"
-#include "ReadJob.h"
-#include "CopyJob.h"
-#include "Task.h"
-#include "gettext.h"
-
-String endhl = "\r\n", endnl = "\n";
-
-class ProcessRequest : public Task {
- public:
- ProcessRequest(Action *, const Socket &, const String &, int);
- virtual ~ProcessRequest() {}
- virtual String GetName();
- protected:
- virtual int Do() throw (GeneralException);
- private:
- String GetMime(const String &);
- bool ParseUri(String &, String &, String &, Handle *);
- void ParseVars(Handle *, int);
- void ShowError(Handle *);
- void SendHeads(Handle *, const String &, const String & = "", time_t = -1);
- void SendRedirect(Handle *);
-
- String file, domain, t;
- Buffer b;
- Task * c, * a;
- Action * f;
- int len, localport;
- Action * p;
- Socket s;
-
- String name, host, gvars;
- Variables * Vars, * Heads;
- bool bad, hasvars, post;
-};
-
-ProcessRequest::ProcessRequest(Action * ap, const Socket & as, const String & aname, int aport) : localport(aport), p(ap), s(as), name(aname) {
- SetBurst();
-}
-
-String ProcessRequest::GetName() {
- return _("Processing HTTP request");
-}
-
-int ProcessRequest::Do() throw(GeneralException) {
- switch (current) {
- case 0:
- if (!s.IsConnected()) return TASK_DONE;
-
- c = new ReadJob(&s, &b);
- WaitFor(c);
- current = 1;
- Suspend(TASK_ON_HOLD);
-
- case 1:
- delete c;
-
- bad = false;
-
-// std::cerr << "---- Got a request from handle " << s.GetHandle() << " \n";
-
- post = ParseUri(file, domain, gvars, &b);
-
- Heads = new Variables();
- Vars = new Variables();
-
- len = -1;
- do {
- int p;
- b >> t;
-// std::cerr << "Read Request (n): " << t << std::endl;
- if ((t.strstr("Content-Length: ") == 0) || (t.strstr("Content-length: ") == 0)) {
-// std::cerr << "Saw 'Content-Lenght:', reading length from '" << t.extract(16) << "'\n";
- len = t.extract(16).to_int();
- }
- if (t.strstr("Host: ") == 0) {
- host = t.extract(6);
- }
-
- if ((p = t.strchr(':')) >= 0) {
- String s = t.extract(0, p - 1);
- s += '=';
- s += t.extract(p + 2);
- Heads->Add(s);
- }
- } while (t.strlen());
-
-// std::cerr << "---- Processing it.\n";
-
- hasvars = false;
-
- if (post) {
- // On a pas eu de ligne 'Content-Length' mais on a eu une méthode POST.
- // Cela est une erreur.
- if (len == -1) {
-// std::cerr << "Error: method POST but no Content-Length\n";
- bad = true;
- } else {
-// std::cerr << "Got a POST request. Parsing variables. (len = " << len << ")\n";
- // Les variables seront initialisées ici.
- hasvars = true;
- }
- }
-
- current = 2;
- if (hasvars && (len)) {
- c = new CopyJob(&s, &b, len);
- WaitFor(c);
- Suspend();
- } else {
- c = 0;
- }
-
- case 2:
- if (gvars != "") {
- Buffer b2;
- b2 << gvars;
- ParseVars(&b2, gvars.strlen());
- }
- if (hasvars) {
- if (c) delete c;
- ParseVars(&b, len);
- }
-
- std::cerr << " Domain = '" << domain << "' - File = '" << file << "'\n";
-
- if (!bad) {
- // Nous vérifions le domaine.
- if (domain != "") {
- bad = true;
- // Les domaines valides sont '/', '/bin' et '/image'.
- if (domain == "/image") bad = false;
- if (domain == "/bin") bad = false;
- if (domain == "/") bad = false;
- if (bad) {
- std::cerr << _("Error: bad domain.\n");
- }
- } else {
- // L'url sans domaine ni fichier est valide. (cela arrive sur certains navigateurs...)
- bad = (file != "");
- }
- }
-
- a = 0;
-
- if (bad) {
- ShowError(&b);
- } else {
- if (((domain == "") || (domain == "/")) && (file == "")) {
- // Si le navigateur a demandé l'URL '/', alors on renvoie une notification
- // de redirection.
- SendRedirect(&b);
- } else if (domain == "/bin") {
- // Le domaine 'bin' est réservé aux actions. On cherche donc l'action à effectuer.
- if ((f = p->Look4URL(file))) {
- SendHeads(&b, "text/html");
- a = f->Do(Vars, Heads, &s);
- } else {
- ShowError(&b);
- }
- } else {
- // Dans tous les autres cas de domaine, on cherche le fichier dans le répertoire datas.
- // On utilise try au cas où le fichier n'existe pas et donc que le constructeur
- // d'input renvoie une erreur.
- try {
- Handle * i = new Input(String("datas/") + file);
- SendHeads(&b, GetMime(file), String("Accept-Ranges: bytes") + endhl + "Content-Length: " + (unsigned long long int) i->GetSize() + endhl, i->GetModif());
- i->SetNonBlock();
- a = new CopyJob(i, &s);
- std::cerr << _("File found, dumping.\n");
- }
- catch (IOGeneral e) {
- ShowError(&b);
- std::cerr << _("File not found, error showed.\n");
- }
- }
- }
-
- if (a) a->Stop();
-
- delete Vars;
- delete Heads;
-// std::cerr << "---- Sending header buffer.\n";
- c = new CopyJob(&b, &s, -1, false);
- WaitFor(c);
- current = 3;
- Suspend();
-
- case 3:
- delete c;
-
- if (a) {
-// std::cerr << "---- Sending contents.\n";
- a->Restart();
- WaitFor(a);
- current = 4;
- Suspend();
- }
-
- case 4:
- if (a) delete a;
-// std::cerr << "---- End of Request.\n";
- }
- return TASK_DONE;
-}
-
-void ProcessRequest::ParseVars(Handle * s, int len) {
- String t, v;
- char conv[3], l;
- int hconv, nbvars;
- ssize_t pos = 0, next;
-
- t = "";
- for (int i = 0; i < len; i++) {
- s->read(&l, 1);
- t += l;
- }
-// std::cerr << "Post variables line: '" << t << "'\n";
-
-
- // Les variables sont sous la forme 'var1=val1&var2=val2&val3=var3'. Donc le nombre d'occurences
- // du caractère '=' indique le nombre de variables.
- nbvars = t.strchrcnt('=');
-
- for (int i = 0; i < nbvars; i++) {
- // Les variables sont sous la forme 'var1=val1&var2=val2&val3=var3'. Donc on cherche le caractère
- // & dans la chaine POST.
- next = t.strchr('&', pos);
- if (next < 0) next = t.strlen();
- v = "";
- while (pos != next) {
- switch (t[pos]) {
- // Le navigateur encode les caractères spéciaux à l'aide du format %XX où XX indique
- // la valeur hexadécimale du caractère. Nous encodons surtout les caractères
- // ' ', '=', '%', et '/' avec cette technique.
- case '%':
- pos++;
- conv[0] = t[pos++];
- conv[1] = t[pos++];
- conv[2] = '\0';
- sscanf(conv, "%x", &hconv);
- v += ((char) hconv);
- break;
- // Certains navigateurs utilisent '+' pour indiquer ' ' (qui est illégal) au lieu
- // d'utiliser %20.
- case '+':
- v += ' ';
- pos++;
- break;
- default:
- v += t[pos++];
- }
- }
-// std::cerr << "Pushing HTTP variable: " << v << std::endl;
- Vars->Add(v);
- pos++;
- }
-}
-
-/*
- * Cette fonction renverra true si la méthode est une méthode POST.
- * Les Strings domain et file seront modifiées afin de renvoyer le domaine
- * et le fichier lut. La string s doit donner la première ligne de la requète,
- * c'est à dire la méthode demandée par le client.
- */
-
-bool ProcessRequest::ParseUri(String & file, String & domain, String & gvars, Handle * s) {
- String t, Uri;
- bool post = false;
- const char * p = 0;
- ssize_t sppos;
-
- *s >> t;
- std::cerr << _("Read Request (1): ") << t << std::endl;
-
- int IPos = t.strchr('?');
-
- gvars = "";
-
- if (IPos >= 0) {
- int HPos = t.strchr(' ', IPos);
- char * sdup = t.strdup(0, IPos - 1);
- gvars = t.extract(IPos + 1, HPos - 1);
- t = sdup;
- free(sdup);
- }
-
-// std::cerr << "New request: " << t << ", gvars = " << gvars << std::endl;
-
- bad = false;
-
- // p nous indiquera la position de la chaîne URL.
- switch (t[0]) {
- case 'P': /* POST? */
- if (t.extract(1, 4) == "OST ") {
- p = t.to_charp(5);
- post = true;
- } else {
-// std::cerr << "Error: unknow request.\n";
- bad = true;
- }
- break;
- case 'G': /* GET? */
- if (t.extract(1, 3) == "ET ") {
- p = t.to_charp(4);
- } else {
-// std::cerr << "Error: unknow request.\n";
- bad = true;
- }
- break;
- default:
-// std::cerr << "Error: unknow request.\n";
- bad = true;
- }
-
- if (!bad) {
- ssize_t poshttp, posslash;
- Uri = p;
- sppos = Uri.strrchr(' ');
- p = Uri.to_charp(0, sppos - 1);
- Uri = p;
- // On enlève tout le host spécifié éventuellement dans la requete.
- if ((poshttp = Uri.strstr("http://")) > 0) {
- Uri = Uri.to_charp(poshttp + 7);
- posslash = Uri.strchr('/');
- // Certains navigateurs indiqueront uniquement http://host comme URL.
- if (posslash >= 0) {
- host = Uri.extract(0, posslash - 1);
- Uri = Uri.to_charp(posslash);
- } else {
- host = Uri;
- Uri = "";
- }
- }
- posslash = Uri.strrchr('/');
- file = Uri.to_charp(posslash + 1);
- if (posslash > 0) {
- domain = Uri.to_charp(0, posslash - 1);
- } else {
- domain = "";
- }
- }
- return post;
-}
-
-/*
- * Ceci sert à rediriger le navigateur vers l'url de démarrage.
- */
-void ProcessRequest::SendRedirect(Handle * s) {
- *s << "HTTP/1.1 301 Moved Permanently" << endhl <<
- "Server: " << name << endhl <<
- "Location: http://" << host << "/bin/start" << endhl <<
- "Cache-Control: no-cache" << endhl <<
- "Connection: closed" << endhl <<
- "Content-Type: text/html" << endhl << endhl <<
- "<HTML><HEAD><TITLE>301 - Moved Permanently</TITLE></HEAD>" << endnl <<
- "<BODY><center><b><h2>You should be redirected to the " << endnl << endnl <<
- "<a href=\"http://" << host << "/bin/start\">start page</a></h2></b></center>" << endnl <<
- "</BODY></HTML>" << endnl;
-}
-
-/*
- * Nous envoyons les entetes de réponse HTTP.
- */
-
-void ProcessRequest::SendHeads(Handle * s, const String & mime, const String & extra, time_t lm) {
- time_t t = time(NULL);
- struct tm * ft = gmtime(&t);
- char buf[1024];
- strftime(buf, 1024, "%a, %d %b %Y %H:%M:%S GMT", ft);
- *s << "HTTP/1.1 200 OK" << endhl <<
- "Date: " << buf << endhl <<
- "Server: " << name << endhl;
- if (lm >=0) {
- ft = gmtime(&lm);
- strftime(buf, 1024, "%a, %d %b %Y %H:%M:%S GMT", ft);
- }
- *s << "Last-Modified: " << buf << endhl << extra <<
- "Connection: closed" << endhl <<
- "Content-Type: " << mime << endhl << endhl;
-}
-
-/*
- * Affichage d'une erreur 404.
- */
-
-void ProcessRequest::ShowError(Handle * s) {
- *s << "HTTP/1.1 404 Not Found" << endhl <<
- "Server: " << name << endhl <<
- "Cache-Control: no-cache" << endhl <<
- "Connection: closed" << endhl <<
- "Content-Type: text/html" << endhl << endhl <<
- "<HTML><HEAD><TITLE>404 - Error</TITLE></HEAD>" << endnl <<
- "<BODY><center><b><h2>The server was unable to process your query</h2></b></center>" << endnl <<
- "Click <A HREF=\"/\">here</A> to go the main page." <<
- "</BODY></HTML>" << endnl;
-}
-
-/*
- * Sert à déterminer le type mime à partir de l'extension du fichier.
- * Par défaut, nous mettons "text/plain".
- */
-
-String ProcessRequest::GetMime(const String & f) {
- String ext;
- size_t ppos;
-
- ppos = f.strrchr('.');
-
- if (ppos >= 0) {
- ext = f.extract(ppos + 1);
- if (ext == "jpg") return "image/jpeg";
- if (ext == "jpeg") return "image/jpeg";
- if (ext == "htm") return "text/html";
- if (ext == "html") return "text/html";
- if (ext == "gif") return "image/gif";
- if (ext == "png") return "image/png";
- if (ext == "class") return "application/octet-stream";
- }
-
- return "text/plain";
-}
-
-HttpServ::HttpServ(Action * ap, int port, const String & nname) throw (GeneralException) {
- bool r = true;
-
- p = ap;
- name = nname;
- localport = port;
-
-// std::cerr << "Initialising Mini HTTP-Server on port " << localport << std::endl;
-
- r = Listener.SetLocal("", port);
- if (!r) {
- throw GeneralException(_("Initialisation of the Mini HTTP-Server failed: can't bind"));
- }
-
- r = Listener.Listen();
-
- if (!r) {
- throw GeneralException(_("Initialisation of the Mini HTTP-Server failed: can't listen"));
- }
-
- Listener.SetNonBlock();
- WaitFor(&Listener, W4_STICKY | W4_READING);
-
-// std::cerr << "Mini HTTP-Server '" << name << "' ready and listening for port " << port << std::endl;
-}
-
-HttpServ::~HttpServ(void) {
- Listener.close();
-}
-
-int HttpServ::Do() throw (GeneralException) {
- try {
- Socket s = Listener.Accept();
- s.SetNonBlock();
- new ProcessRequest(p, s, name, localport);
- }
- catch (GeneralException) {
- }
- return TASK_ON_HOLD;
-}
-
-String HttpServ::GetName() {
- return String("Mini HTTP-Server '") + name + "'";
-}
+#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include "Socket.h" +#include "Action.h" +#include "HttpServ.h" +#include "Buffer.h" +#include "ReadJob.h" +#include "CopyJob.h" +#include "Task.h" +#include "gettext.h" + +String endhl = "\r\n", endnl = "\n"; + +class ProcessRequest : public Task { + public: + ProcessRequest(Action *, const Socket &, const String &, int); + virtual ~ProcessRequest() {} + virtual String GetName(); + protected: + virtual int Do() throw (GeneralException); + private: + String GetMime(const String &); + bool ParseUri(String &, String &, String &, Handle *); + void ParseVars(Handle *, int); + void ShowError(Handle *); + void SendHeads(Handle *, const String &, const String & = "", time_t = -1); + void SendRedirect(Handle *); + + String file, domain, t; + Buffer b; + Task * c, * a; + Action * f; + int len, localport; + Action * p; + Socket s; + + String name, host, gvars; + Variables * Vars, * Heads; + bool bad, hasvars, post; +}; + +ProcessRequest::ProcessRequest(Action * ap, const Socket & as, const String & aname, int aport) : localport(aport), p(ap), s(as), name(aname) { + SetBurst(); +} + +String ProcessRequest::GetName() { + return _("Processing HTTP request"); +} + +int ProcessRequest::Do() throw(GeneralException) { + switch (current) { + case 0: + if (!s.IsConnected()) return TASK_DONE; + + c = new ReadJob(&s, &b); + WaitFor(c); + current = 1; + Suspend(TASK_ON_HOLD); + + case 1: + delete c; + + bad = false; + +// std::cerr << "---- Got a request from handle " << s.GetHandle() << " \n"; + + post = ParseUri(file, domain, gvars, &b); + + Heads = new Variables(); + Vars = new Variables(); + + len = -1; + do { + int p; + b >> t; +// std::cerr << "Read Request (n): " << t << std::endl; + if ((t.strstr("Content-Length: ") == 0) || (t.strstr("Content-length: ") == 0)) { +// std::cerr << "Saw 'Content-Lenght:', reading length from '" << t.extract(16) << "'\n"; + len = t.extract(16).to_int(); + } + if (t.strstr("Host: ") == 0) { + host = t.extract(6); + } + + if ((p = t.strchr(':')) >= 0) { + String s = t.extract(0, p - 1); + s += '='; + s += t.extract(p + 2); + Heads->Add(s); + } + } while (t.strlen()); + +// std::cerr << "---- Processing it.\n"; + + hasvars = false; + + if (post) { + // On a pas eu de ligne 'Content-Length' mais on a eu une méthode POST. + // Cela est une erreur. + if (len == -1) { +// std::cerr << "Error: method POST but no Content-Length\n"; + bad = true; + } else { +// std::cerr << "Got a POST request. Parsing variables. (len = " << len << ")\n"; + // Les variables seront initialisées ici. + hasvars = true; + } + } + + current = 2; + if (hasvars && (len)) { + c = new CopyJob(&s, &b, len); + WaitFor(c); + Suspend(); + } else { + c = 0; + } + + case 2: + if (gvars != "") { + Buffer b2; + b2 << gvars; + ParseVars(&b2, gvars.strlen()); + } + if (hasvars) { + if (c) delete c; + ParseVars(&b, len); + } + + std::cerr << " Domain = '" << domain << "' - File = '" << file << "'\n"; + + if (!bad) { + // Nous vérifions le domaine. + if (domain != "") { + bad = true; + // Les domaines valides sont '/', '/bin' et '/image'. + if (domain == "/image") bad = false; + if (domain == "/bin") bad = false; + if (domain == "/") bad = false; + if (bad) { + std::cerr << _("Error: bad domain.\n"); + } + } else { + // L'url sans domaine ni fichier est valide. (cela arrive sur certains navigateurs...) + bad = (file != ""); + } + } + + a = 0; + + if (bad) { + ShowError(&b); + } else { + if (((domain == "") || (domain == "/")) && (file == "")) { + // Si le navigateur a demandé l'URL '/', alors on renvoie une notification + // de redirection. + SendRedirect(&b); + } else if (domain == "/bin") { + // Le domaine 'bin' est réservé aux actions. On cherche donc l'action à effectuer. + if ((f = p->Look4URL(file))) { + SendHeads(&b, "text/html"); + a = f->Do(Vars, Heads, &s); + } else { + ShowError(&b); + } + } else { + // Dans tous les autres cas de domaine, on cherche le fichier dans le répertoire datas. + // On utilise try au cas où le fichier n'existe pas et donc que le constructeur + // d'input renvoie une erreur. + try { + Handle * i = new Input(String("datas/") + file); + SendHeads(&b, GetMime(file), String("Accept-Ranges: bytes") + endhl + "Content-Length: " + (unsigned long long int) i->GetSize() + endhl, i->GetModif()); + i->SetNonBlock(); + a = new CopyJob(i, &s); + std::cerr << _("File found, dumping.\n"); + } + catch (IOGeneral e) { + ShowError(&b); + std::cerr << _("File not found, error showed.\n"); + } + } + } + + if (a) a->Stop(); + + delete Vars; + delete Heads; +// std::cerr << "---- Sending header buffer.\n"; + c = new CopyJob(&b, &s, -1, false); + WaitFor(c); + current = 3; + Suspend(); + + case 3: + delete c; + + if (a) { +// std::cerr << "---- Sending contents.\n"; + a->Restart(); + WaitFor(a); + current = 4; + Suspend(); + } + + case 4: + if (a) delete a; +// std::cerr << "---- End of Request.\n"; + } + return TASK_DONE; +} + +void ProcessRequest::ParseVars(Handle * s, int len) { + String t, v; + char conv[3], l; + int hconv, nbvars; + ssize_t pos = 0, next; + + t = ""; + for (int i = 0; i < len; i++) { + s->read(&l, 1); + t += l; + } +// std::cerr << "Post variables line: '" << t << "'\n"; + + + // Les variables sont sous la forme 'var1=val1&var2=val2&val3=var3'. Donc le nombre d'occurences + // du caractère '=' indique le nombre de variables. + nbvars = t.strchrcnt('='); + + for (int i = 0; i < nbvars; i++) { + // Les variables sont sous la forme 'var1=val1&var2=val2&val3=var3'. Donc on cherche le caractère + // & dans la chaine POST. + next = t.strchr('&', pos); + if (next < 0) next = t.strlen(); + v = ""; + while (pos != next) { + switch (t[pos]) { + // Le navigateur encode les caractères spéciaux à l'aide du format %XX où XX indique + // la valeur hexadécimale du caractère. Nous encodons surtout les caractères + // ' ', '=', '%', et '/' avec cette technique. + case '%': + pos++; + conv[0] = t[pos++]; + conv[1] = t[pos++]; + conv[2] = '\0'; + sscanf(conv, "%x", &hconv); + v += ((char) hconv); + break; + // Certains navigateurs utilisent '+' pour indiquer ' ' (qui est illégal) au lieu + // d'utiliser %20. + case '+': + v += ' '; + pos++; + break; + default: + v += t[pos++]; + } + } +// std::cerr << "Pushing HTTP variable: " << v << std::endl; + Vars->Add(v); + pos++; + } +} + +/* + * Cette fonction renverra true si la méthode est une méthode POST. + * Les Strings domain et file seront modifiées afin de renvoyer le domaine + * et le fichier lut. La string s doit donner la première ligne de la requète, + * c'est à dire la méthode demandée par le client. + */ + +bool ProcessRequest::ParseUri(String & file, String & domain, String & gvars, Handle * s) { + String t, Uri; + bool post = false; + const char * p = 0; + ssize_t sppos; + + *s >> t; + std::cerr << _("Read Request (1): ") << t << std::endl; + + int IPos = t.strchr('?'); + + gvars = ""; + + if (IPos >= 0) { + int HPos = t.strchr(' ', IPos); + char * sdup = t.strdup(0, IPos - 1); + gvars = t.extract(IPos + 1, HPos - 1); + t = sdup; + free(sdup); + } + +// std::cerr << "New request: " << t << ", gvars = " << gvars << std::endl; + + bad = false; + + // p nous indiquera la position de la chaîne URL. + switch (t[0]) { + case 'P': /* POST? */ + if (t.extract(1, 4) == "OST ") { + p = t.to_charp(5); + post = true; + } else { +// std::cerr << "Error: unknow request.\n"; + bad = true; + } + break; + case 'G': /* GET? */ + if (t.extract(1, 3) == "ET ") { + p = t.to_charp(4); + } else { +// std::cerr << "Error: unknow request.\n"; + bad = true; + } + break; + default: +// std::cerr << "Error: unknow request.\n"; + bad = true; + } + + if (!bad) { + ssize_t poshttp, posslash; + Uri = p; + sppos = Uri.strrchr(' '); + p = Uri.to_charp(0, sppos - 1); + Uri = p; + // On enlève tout le host spécifié éventuellement dans la requete. + if ((poshttp = Uri.strstr("http://")) > 0) { + Uri = Uri.to_charp(poshttp + 7); + posslash = Uri.strchr('/'); + // Certains navigateurs indiqueront uniquement http://host comme URL. + if (posslash >= 0) { + host = Uri.extract(0, posslash - 1); + Uri = Uri.to_charp(posslash); + } else { + host = Uri; + Uri = ""; + } + } + posslash = Uri.strrchr('/'); + file = Uri.to_charp(posslash + 1); + if (posslash > 0) { + domain = Uri.to_charp(0, posslash - 1); + } else { + domain = ""; + } + } + return post; +} + +/* + * Ceci sert à rediriger le navigateur vers l'url de démarrage. + */ +void ProcessRequest::SendRedirect(Handle * s) { + *s << "HTTP/1.1 301 Moved Permanently" << endhl << + "Server: " << name << endhl << + "Location: http://" << host << "/bin/start" << endhl << + "Cache-Control: no-cache" << endhl << + "Connection: closed" << endhl << + "Content-Type: text/html" << endhl << endhl << + "<HTML><HEAD><TITLE>301 - Moved Permanently</TITLE></HEAD>" << endnl << + "<BODY><center><b><h2>You should be redirected to the " << endnl << endnl << + "<a href=\"http://" << host << "/bin/start\">start page</a></h2></b></center>" << endnl << + "</BODY></HTML>" << endnl; +} + +/* + * Nous envoyons les entetes de réponse HTTP. + */ + +void ProcessRequest::SendHeads(Handle * s, const String & mime, const String & extra, time_t lm) { + time_t t = time(NULL); + struct tm * ft = gmtime(&t); + char buf[1024]; + strftime(buf, 1024, "%a, %d %b %Y %H:%M:%S GMT", ft); + *s << "HTTP/1.1 200 OK" << endhl << + "Date: " << buf << endhl << + "Server: " << name << endhl; + if (lm >=0) { + ft = gmtime(&lm); + strftime(buf, 1024, "%a, %d %b %Y %H:%M:%S GMT", ft); + } + *s << "Last-Modified: " << buf << endhl << extra << + "Connection: closed" << endhl << + "Content-Type: " << mime << endhl << endhl; +} + +/* + * Affichage d'une erreur 404. + */ + +void ProcessRequest::ShowError(Handle * s) { + *s << "HTTP/1.1 404 Not Found" << endhl << + "Server: " << name << endhl << + "Cache-Control: no-cache" << endhl << + "Connection: closed" << endhl << + "Content-Type: text/html" << endhl << endhl << + "<HTML><HEAD><TITLE>404 - Error</TITLE></HEAD>" << endnl << + "<BODY><center><b><h2>The server was unable to process your query</h2></b></center>" << endnl << + "Click <A HREF=\"/\">here</A> to go the main page." << + "</BODY></HTML>" << endnl; +} + +/* + * Sert à déterminer le type mime à partir de l'extension du fichier. + * Par défaut, nous mettons "text/plain". + */ + +String ProcessRequest::GetMime(const String & f) { + String ext; + size_t ppos; + + ppos = f.strrchr('.'); + + if (ppos >= 0) { + ext = f.extract(ppos + 1); + if (ext == "jpg") return "image/jpeg"; + if (ext == "jpeg") return "image/jpeg"; + if (ext == "htm") return "text/html"; + if (ext == "html") return "text/html"; + if (ext == "gif") return "image/gif"; + if (ext == "png") return "image/png"; + if (ext == "class") return "application/octet-stream"; + } + + return "text/plain"; +} + +HttpServ::HttpServ(Action * ap, int port, const String & nname) throw (GeneralException) { + bool r = true; + + p = ap; + name = nname; + localport = port; + +// std::cerr << "Initialising Mini HTTP-Server on port " << localport << std::endl; + + r = Listener.SetLocal("", port); + if (!r) { + throw GeneralException(_("Initialisation of the Mini HTTP-Server failed: can't bind")); + } + + r = Listener.Listen(); + + if (!r) { + throw GeneralException(_("Initialisation of the Mini HTTP-Server failed: can't listen")); + } + + Listener.SetNonBlock(); + WaitFor(&Listener, W4_STICKY | W4_READING); + +// std::cerr << "Mini HTTP-Server '" << name << "' ready and listening for port " << port << std::endl; +} + +HttpServ::~HttpServ(void) { + Listener.close(); +} + +int HttpServ::Do() throw (GeneralException) { + try { + Socket s = Listener.Accept(); + s.SetNonBlock(); + new ProcessRequest(p, s, name, localport); + } + catch (GeneralException) { + } + return TASK_ON_HOLD; +} + +String HttpServ::GetName() { + return String("Mini HTTP-Server '") + name + "'"; +} |