PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : (HTTP) Server mit Qt4



TheDodger
13-12-2010, 05:28
Moin Moin!

Nach Jahren tauche ich mal wieder aus der Versenkung auf. :)
Es gab bisher einige wichtige Verwerfungen bei den Prioritäten ...
Aber nun hab ich wieder ein wenig mehr Zeit für meine Dinge.

Ich habe hier eine (selbstgeschriebene) WebApplication, welche mir zwar die Dienste leistet, die ich benötigte, die aber mittlerweile am der oberen Leistungsgrenze angekommen ist.

Da ich aktuell für ein neues Projekt plane und das eigentlich weiter nutzen möchte, aber das ganze mal von einer anderen Seite aus betrachten möchte, wollte ich einen (HTTP) Server unter Qt4 bauen. (Plumb ausgedrückt, ich will meine PHP-Anwendung nach Qt4/C++ konvertieren).
Vor allem um deutlich bessere Geschwindigkeiten zu erreichen.
Das ganze soll relative simple gestrickt sein.
Ich bin mir allerdings noch nicht ganz klar, wie das aufgebaut werden müsste .. ;)
Prinzipiell möchte ich einen Daemon/Server. Der soll - ähnlich einem Apachen - Requests entgegen nehmen, die abarbeiten und ein HTML Dokument ausliefern.
Dazu sollen die DB-Verbindungen persistent sein, Cookies/Sessions verarbeiten, etc.

Das ist der grobe Plan ...
Ich konnte nach einer Woche Recherche, bis auf rudimentäre Democodes, nicht wirklich was finden. :(

Vielleicht hat ja jemand von euch eine Idee, die mit weiter helfen kann?


Gruß aus Hamburg,
Bodo

PS: Ja, ich könnte einen Tomcat und Java nehmen, aber Java war, ist & bleibt (mir ein persöhnlicher) Graus.

zenobic
13-12-2010, 13:55
das hört sich interessant an.

Habe auch schon mal an so etwas gedacht, am Ende habe ich aber bisher nur mal eine Seite (Frontend) mit Datenbankabfragen in C als cgi mit apache umgesetzt.
Updaten der Datenbank mit Userverwaltung ist dann noch in php - wobei das Projekt noch nicht im produktiven Betrieb ist.

Flexibler als im C Code HTML zu generieren ist man mit einer Template Engine, wie clearsilver (wenn c++, ggf. moustache (https://github.com/mrtazz/plustache )).

Für mich ist PHP die Template Engine und man kann auch PHP Code generieren und das generierte HTML wiederrum auch cachen.

Eigener Webserver macht für mich eigentlich nur für bestimmte Zwecke Sinn, z.B: "deployen" um eine native Applikation mit HTTP zu erstellen, nur für Grafiken oder ide mit Debugger.

Warum nicht ein robuster, leichtgewichtiger Webserver mit FastCGI für eine Webapplikation?

Kann auch viel Performancegewinn einbringen ( http://marianoiglesias.com.ar/c/new-fun-project-a-c-web-framework/ ).

undefined
13-12-2010, 14:32
Sorry wenn das jetzt so hart rüberkommt ;) -- Forgot it !
Aber das wird mit qt4 nie richtig funktionieren weil die Dinge die Interessant währen einen laufenden X-Server benötigen. (das habe ich schon hinter mir ;) )
Es gibt aber eine andere Variante die ich selbst auch Verfolge und das ist "x-qt-plugin". Du findest Beispiele auf der Testseite von xhtmldbg (http://xhtmldbg.hjcms.de/testpage.html)

TheDodger
13-12-2010, 14:56
Sorry wenn das jetzt so hart rüberkommt ;) -- Forgot it !
Aber das wird mit qt4 nie richtig funktionieren weil die Dinge die Interessant währen einen laufenden X-Server benötigen. (das habe ich schon hinter mir ;) )

Also ich kann - ohne die div. Abhängigkeiten von X - eine reine Consolen-Anwendung mit Qt4 (nicht 3) schreiben.
Und die 4er Version bietet schon sehr schicke Klassen um den ganzen Netzwerkkram zu kapseln und nicht neu zu machen.


Es gibt aber eine andere Variante die ich selbst auch Verfolge und das ist "x-qt-plugin". Du findest Beispiele auf der Testseite von xhtmldbg (http://xhtmldbg.hjcms.de/testpage.html)
Danke, kann ich mir ja auch mal anschauen.

TheDodger
13-12-2010, 15:06
das hört sich interessant an.

Habe auch schon mal an so etwas gedacht, am Ende habe ich aber bisher nur mal eine Seite (Frontend) mit Datenbankabfragen in C als cgi mit apache umgesetzt.
Updaten der Datenbank mit Userverwaltung ist dann noch in php - wobei das Projekt noch nicht im produktiven Betrieb ist.

Flexibler als im C Code HTML zu generieren ist man mit einer Template Engine, wie clearsilver (wenn c++, ggf. moustache (https://github.com/mrtazz/plustache )).
Wenn dann kommt so oder so eine Template-Engine zum Einsatz. Ich bin ein Fan der Trennung von Logik & Design ...
Aber das geraffel um das Caching, Session, Cookies, Requestbehandlung und/oder einzelnen Modulen würde ich gern darüber abwickeln.



Für mich ist PHP die Template Engine und man kann auch PHP Code generieren und das generierte HTML wiederrum auch cachen.
PHP ist für *mich* primär eine Programmiersprache & eine PHP-Klasse eine Template-Engine.
bash kann AFAIK auch PHP & HTML erstellen und zur Not auch was cachen ... ;)

PHP stösst in meinem Fall an seine Grenzen, weshalb ich versuche das anders zu machen. :)
Lernen & Spaß haben gehört aber auch dazu ...


Eigener Webserver macht für mich eigentlich nur für bestimmte Zwecke Sinn, z.B: "deployen" um eine native Applikation mit HTTP zu erstellen, nur für Grafiken oder ide mit Debugger.

Warum nicht ein robuster, leichtgewichtiger Webserver mit FastCGI für eine Webapplikation?

Kann auch viel Performancegewinn einbringen ( http://marianoiglesias.com.ar/c/new-fun-project-a-c-web-framework/ ).
Ich hab mal nach dem clapp-Zeuch gesucht, aber zum Download scheint es nix zu geben. Von daher, kann ich es halt nicht bewerten ...

Vielleicht ist WebServer auch nicht ganz Typensicher von mir definiert ... es würde wohl eine Mischung zwischen WebServer & ApplicationServer sein ...

Mehrere davon parallel laufend auf unterschiedlichen Ports lauschend und von vorn vom Apachen/nginx befeuert.

anda_skoa
13-12-2010, 17:11
Sorry wenn das jetzt so hart rüberkommt ;) -- Forgot it !
Aber das wird mit qt4 nie richtig funktionieren weil die Dinge die Interessant währen einen laufenden X-Server benötigen. (das habe ich schon hinter mir ;) )


Das kann ich mir jetzt schwer vorstellen, hast du vielleicht ein Beispiel zu Hand?

Ciao,
_

TheDodger
14-12-2010, 05:54
Moin Moin!
Da ich aktuell für ein neues Projekt plane und das eigentlich weiter nutzen möchte, aber das ganze mal von einer anderen Seite aus betrachten möchte, wollte ich einen (HTTP) Server unter Qt4 bauen. (Plumb ausgedrückt, ich will meine PHP-Anwendung nach Qt4/C++ konvertieren).
Vor allem um deutlich bessere Geschwindigkeiten zu erreichen.
Das ganze soll relative simple gestrickt sein.
Ich bin mir allerdings noch nicht ganz klar, wie das aufgebaut werden müsste .. ;)
Prinzipiell möchte ich einen Daemon/Server. Der soll - ähnlich einem Apachen - Requests entgegen nehmen, die abarbeiten und ein HTML Dokument ausliefern.
Dazu sollen die DB-Verbindungen persistent sein, Cookies/Sessions verarbeiten, etc.

Das einzige, was ich zu dem Thema finden konnte ist das hier: qt-apps :: HTTP Server (http://qt-apps.org/content/show.php/Http+Server?content=132824)

Ich muß mir das aber erstmal zur Brust nehmen und schauen, ob das auch wirklich hilfreich für mich ist ...

Andere Vorschläge / Ideen sind noch immer gern gesehen ;)

undefined
14-12-2010, 06:25
Das kann ich mir jetzt schwer vorstellen, hast du vielleicht ein Beispiel zu Hand?

Ciao,
_
Beispiele habe ich keine mehr. Die habe ich irgendwann mal gelöscht.
Ich habe mal versucht mit Qt4 eine CGI basierte Webanwendung zu schreiben. Also mit QtCore, QtXml, xslt und cgi++. Ich war mehr damit beschäftigt, was kann ich nehmen als sonst was. Habe mich dann für die cxxtools (http://www.tntnet.org/index.html) entschieden.

msi
14-12-2010, 15:39
deine bisherige webanwendung optimieren wäre die andere alternative:

hier paar beispiele
- reverse proxy (evtl. edge side include)
- php accelerator
- mehr caching (zB auch memcache)

TheDodger
14-12-2010, 16:26
deine bisherige webanwendung optimieren wäre die andere alternative:

hier paar beispiele
- reverse proxy (evtl. edge side include)
- php accelerator
- mehr caching (zB auch memcache)

3x DONE
Die Anwendung wird seit ~3 Jahren aktiv entwickelt und dient mir als Spielwiese für eben diese Techniken.

Und sie ist aktuell nicht so skalierbar, wie ich sie für das neue Projekt benötige.

zenobic
15-12-2010, 15:30
Sind die beschrieben Skalierbarkeitsgrenzen mit PHP, Performanceprobleme?
Hast du facebooks HipHop schon ausprobiert?
HipHop soll laut Benchmarks von Rasmus einiges bringen ( http://about.digg.com/blog/rasmus-lerdorf-php-performance / http://talks.php.net/show/digg/20
).

nodejs (http://nodejs.org/) ist vielleicht noch interessant (beispielsweise falls "Comet" eine Anforderung ist).

WT und CppCMS sind bekannte Frameworks für C++, hat damit schon jemand Erfahrungen gesammelt?

TheDodger
16-12-2010, 08:26
Sind die beschrieben Skalierbarkeitsgrenzen mit PHP, Performanceprobleme?
Hast du facebooks HipHop schon ausprobiert?
HipHop soll laut Benchmarks von Rasmus einiges bringen

HipHop scheint interessant zu sein, wenn das Stück Software an einem Stück vorliegt.
Ist leider bei mir nicht der Fall. :)


WT und CppCMS sind bekannte Frameworks für C++, hat damit schon jemand Erfahrungen gesammelt?
WT hab ich angeschaut. Schon allein deshalb, weil es sich stark an Qt orientiert. Allerdings baut das wohl nur CGIs und ich habe es noch nicht mal geschafft (in vertretbarer Zeit) das 'Hello World' in den Browser zu zaubern.


Aber egal und um es noch einmal auf den Punkt zu bringen.
Ich möchte kein PHP mehr!

Aktuell versuche ich meine guten alten Qt3 Kenntnisse auf den aktuellen Stand zu bringen um mal wieder was mit C++ zusammen zu bauen (Mein letztes C++ Projekt scheint aus 2006 zu sein ... ich hab da noch Kommentare von/über anda_skoa drin :) )

TheDodger
16-12-2010, 08:29
Moin Moin!
Prinzipiell möchte ich einen Daemon/Server. Der soll - ähnlich einem Apachen - Requests entgegen nehmen, die abarbeiten und ein HTML Dokument ausliefern.
Dazu sollen die DB-Verbindungen persistent sein, Cookies/Sessions verarbeiten, etc.

Sooo ... ich habe ein kleines Proof-of-Concept am Start.
Ein Programm, welches einen TCP-Port aufreist und dort anfragen entgegen nimmt.
Eine simple GET-Anfrage funktioniert auch.

Jetzt brauche ich nur noch Session, Cookies & POST ... *seufts*

anda_skoa
18-12-2010, 12:13
Beispiele habe ich keine mehr. Die habe ich irgendwann mal gelöscht.

Mit Beispiel meinte ich eher weniger Beispielcode, viel mehr welche Aufgaben bzw. welche für deren Lösung verwendeter Klassen eine X Server Verbindung nötig gemacht haben.

Ähnlich wie andere hab ich schonmit Qt3 sehr erfolgreich Server- bzw. Konsolenanwendungen gemacht, also "headless" (ohne X11 Connection).

Mit Qt4 ist das noch leichter, QtCore/Network/usw linken nicht mal gegen libx11.

Insofern wäre es eben hilfreich zu wissen, auf welche interessanten Dinge du Bezug nimmst, für deren Umsetzung man eine GUI Resourcen benötigen würde.

Ciao,
_

anda_skoa
18-12-2010, 12:14
Jetzt brauche ich nur noch Session, Cookies & POST ... *seufts*

Falls du noch nach einer Qt basierten Templateengine suchst, vielleicht wäre Grantlee das was für dich: http://www.gitorious.org/grantlee/pages/Home

Ciao,
_

TheDodger
19-12-2010, 11:56
Falls du noch nach einer Qt basierten Templateengine suchst, vielleicht wäre Grantlee das was für dich: http://www.gitorious.org/grantlee/pages/Home

Ciao,
_

Danke für den Tipp!
Schau ich mir gern an.

Momentan versuche ich erstmal Plugins zum fliegen zu bekommen. Aktuell nehme ich dazu meine (mit deiner Hilfe entstandene) Klasse aus dem Jahre 2003 ... ;)
Aber ich glaube, ich komm wohl nicht darum, das ganze nochmal ganz von vorn zu machen ... :(

[edit]
Ja, so ist es.
Den Plugin-Kram muß ich quasi von NULL auf noch mal entwickeln.
Kann mir jemand Hilfe anbieten?

anda_skoa
19-12-2010, 19:24
Den Plugin-Kram muß ich quasi von NULL auf noch mal entwickeln.
Kann mir jemand Hilfe anbieten?

Dazu gibt es mittlerweile offizielle Doku von Nokia/Trolltech:
http://doc.qt.nokia.com/latest/plugins-howto.html

Siehe Sektion "low level API"

Ciao,
_

TheDodger
20-12-2010, 05:11
Dazu gibt es mittlerweile offizielle Doku von Nokia/Trolltech:
http://doc.qt.nokia.com/latest/plugins-howto.html

Siehe Sektion "low level API"

Ja, die hatte ich zwischenzeitlich auch gefunden.
Allerdings war ich gestern wohl nicht mehr voll da (und mein Zwerg wollte mehr Aufmerksamkeit), da hab ich nichts mehr funktionsfähiges hinbekommen.
Wahrscheinlich hab ich mich auch auf der Suche nach einem funktionierenden Codeschnippsel für eine PluginFactory total verrannt

[edit]
Ja, ausgeschlafen und mit anderen Ansätzen gehts dann doch :)
Hat sich einiges geändert, dass ich erstmal in das alte Hirn bekommen muß

TheDodger
20-12-2010, 08:39
Sooo ... ich habe ein kleines Proof-of-Concept am Start.
Ein Programm, welches einen TCP-Port aufreist und dort anfragen entgegen nimmt.
Eine simple GET-Anfrage funktioniert auch.

Ich stellt das mal Online download (http://boone-schulz.de/RIPServer.tgz) ...
Vielleicht kann das ja mal jemand gebrauchen / mir Tipps geben / helfen, etc. ...

zenobic
21-12-2010, 17:13
Nur eine Anregung:

In "env" könnte man z.B. session_id's reinlegen.
Zur jeder SessionId, könnte man die zugehörigen Werte serialisiert (z.B. QTScript JSON encoded) in env oder auf dem Filesystem ablegen (Server) sowie Client als Cookie oder Parameter.

Dafür würde ich "env" von einer globalen Konfigdatei entkoppeln,
da es schon readConfiguration gab, habe ich einen kleinen Patch:


http://pastebin.com/7RgDEMGd

(Es muss wenn es dir gefällt, noch die Signatur der 2 Funktionen in der Headerdatei aktualisiert werden).

TheDodger
21-12-2010, 20:31
Nur eine Anregung:

In "env" könnte man z.B. session_id's reinlegen.
Zur jeder SessionId, könnte man die zugehörigen Werte serialisiert (z.B. QTScript JSON encoded) in env oder auf dem Filesystem ablegen (Server) sowie Client als Cookie oder Parameter.

Dafür würde ich "env" von einer globalen Konfigdatei entkoppeln, da es schon readConfiguration gab, habe ich einen kleinen Patch:

http://pastebin.com/7RgDEMGd

(Es muss wenn es dir gefällt, noch die Signatur der 2 Funktionen in der Headerdatei aktualisiert werden).

Vielen Dank!
Das schau ich mir gleich (oder morgen früh) mal an! ;)

'env' kommt noch aus einem etwas älterem Projekt, da sind unter Garantie völlig Sinnfreie Dinge mit drin.

Sobald der Server mehrere Statische Dateien ausliefern kann, werd ich das mal mit siege stressen.
Ich weiß nämlich überhaupt nicht, ob er mehrere Anfragen parallel abarbeiten kann :)

In dem Sinne ... wäre es eigentlich sinnvoll, das ganze Ding mit Threads zu realisieren, oder weniger?

[EDIT]
Dann würde das mit den Plugins und QPluginLoader aber nicht mehr so funktionieren, richtig?
So wie ich das ganze verstanden hab, muß ich, beim Einsatz von Threads, die Plugins mit QLibrary laden, auch richtig?

zenobic
21-12-2010, 21:14
Vielen Dank!
Sobald der Server mehrere Statische Dateien ausliefern kann, werd ich das mal mit siege stressen.
Ich weiß nämlich überhaupt nicht, ob er mehrere Anfragen parallel abarbeiten kann :)

In dem Sinne ... wäre es eigentlich sinnvoll, das ganze Ding mit Threads zu realisieren, oder weniger?

Ich konnte es mir nicht verkneifen schon zu benchmarken und muss sagen, mit der Statusseite performt es jetzt schon (auch parallel) gut (es gibt ja auch Projekte die auch weitere Seiten in diesem Stil "hardcoden").

Da sind aber noch einige QT Meldungen die ich mir nicht genauer angesehen habe.


ab -n 1000 -c 5 http://127.0.0.1:8081/index.html

TheDodger
22-12-2010, 05:51
Ich konnte es mir nicht verkneifen schon zu benchmarken und muss sagen, mit der Statusseite performt es jetzt schon (auch parallel) gut (es gibt ja auch Projekte die auch weitere Seiten in diesem Stil "hardcoden").

Da sind aber noch einige QT Meldungen die ich mir nicht genauer angesehen habe.


ab -n 1000 -c 5 http://127.0.0.1:8081/index.html
Ahh, danke! :D
Du meinst bestimmt dieses hier:


Object: Cannot create children for a parent that is in a different thread.
(Parent is QUdpSocket(0x2a77470), parent's thread is QThread(0x71a540), current thread is QThread(0x8072d0)
Error: 4

Das sind Debug meldungen, die meine UDP-Debugkonsole empfangen könnte.
Wenn du in der 'Server.pro' das debug aus der config entfernts, sind die Meldungen auch weg.

Aber ab einer bestimmten Zeit liefert er nur noch 0 bytes große Dateien (mit einem 200er) aus.
Das ist dann eher "subobtimal" :(

TheDodger
22-12-2010, 15:12
Aber ab einer bestimmten Zeit liefert er nur noch 0 bytes große Dateien (mit einem 200er) aus.
Das ist dann eher "subobtimal" :(
Also das ständige öffnen und schließen von Dateien ist dann ja nicht ganz so perfomant, wie es scheint. :D
Da dachte ich bei mir, dass ich das doch eigentlich über ene QCache-Geschichte abbilden kann. Einmal geladen, dann in den Cache gesteckt und dann nur noch den genutzt ...

Aber QCache scheint ziemlich destruktiv zu sein.
Wenn ich den Inhalt einmal gelesen habe (QCache::take() ) dann ist der Cache wieder leer.
Auch der anderen Seite performat das jetzt auch nicht mehr so toll.
Dafür wird jetzt immer die ganze Datei ausgeliefert.
Sobald ich das mit dem Cache geschnallt hab, bau ich mal ein neues Archiv :D

zenobic
23-12-2010, 10:58
Vielen Dank!
In dem Sinne ... wäre es eigentlich sinnvoll, das ganze Ding mit Threads zu realisieren, oder weniger?


Mit fork ist es nicht nur einfacher, sondern soweit ich weiss auch stabiler, aber eben nicht performanter.
Wobei Performance auch mit I/O auf der Platte zu tun hat und somit die darunterliegenden Lowlevel/Kernelfunktionen eine grosse Rolle spielen.

Zumindest hörte ich, dass ein Apache2 (Standardkonfiguration) mit Threads abstürzen kann,
da ein Thread den Prozess lahm legen kann.
Ein forked Prozess nicht (Apache1 oder Apache2 mit pre-fork modul).

fork() gibt es soweit ich weiss nicht für QT, da es dies nicht plattformübergreifend gibt.

Jetzt ist es doch jeder Request ein Thread oder meinst du multithreaded (QtConcurrent) ?

TheDodger
23-12-2010, 12:28
Mit fork ist es nicht nur einfacher, sondern soweit ich weiss auch stabiler, aber eben nicht performanter.
Wobei Performance auch mit I/O auf der Platte zu tun hat und somit die darunterliegenden Lowlevel/Kernelfunktionen eine grosse Rolle spielen.

Deswegen wollte ich ja QCache nutzen um die I/O etwas zu entkoppeltn.

Zumindest hörte ich, dass ein Apache2 (Standardkonfiguration) mit Threads abstürzen kann, da ein Thread den Prozess lahm legen kann.
Ein forked Prozess nicht (Apache1 oder Apache2 mit pre-fork modul).

fork() gibt es soweit ich weiss nicht für QT, da es dies nicht plattformübergreifend gibt.

Jetzt ist es doch jeder Request ein Thread oder meinst du multithreaded (QtConcurrent) ?
Plattformübergreifend interessiert mich in dem Falle nicht die Bohne! ;)
Das Ding soll & wird ausschließlich auf Linux-Systemen laufen ...

Ich habe mit Threadprogrammierung absolut keine Erfahrung :/ bislang waren alle Anwendungen weniger Ressourcenfressend, oder hatten einfach nicht solche Timingprobleme.
Es würde also meine erste Threaded Anwendung werden ... *grussel*

Ich glaube, ich versuche das mit dem Cache (oder buffer, wie mans nennen will) hinzubekommen.
Vielleicht entschärft das ja schon meine Probleme?

anda_skoa
23-12-2010, 12:58
Wenn ich den Inhalt einmal gelesen habe (QCache::take() ) dann ist der Cache wieder leer.

Das ist auch die Aufgabe von take() :)

Zugriff auf ein Element mit QCache::operator[] oder QCache::object()

Analog zum verhalten von Qt3 QDict :D

Ciao,
_

TheDodger
23-12-2010, 17:54
Das ist auch die Aufgabe von take() :)

Zugriff auf ein Element mit QCache::operator[] oder QCache::object()

Analog zum verhalten von Qt3 QDict :D

Ciao,
_
Das erklärt einiges.
Aaaaber ... ich möchte mal zitieren:


T * QCache::take ( const Key & key )
Takes the object associated with key key out of the cache without deleting it. Returns a pointer to the object taken out, or 0 if the key does not exist in the cache.
The ownership of the returned object is passed to the caller.
See also remove().

Speziell ' Takes the object associated with key key out of the cache without deleting it. ' Das heisst für mich, eben, dass der Cache-Eintrag nicht gelöscht wird.

Mit ::object() rockt es dann wieder. :)
Danke!

anda_skoa
24-12-2010, 11:43
Speziell ' Takes the object associated with key key out of the cache without deleting it. ' Das heisst für mich, eben, dass der Cache-Eintrag nicht gelöscht wird.


Er wird auch nicht gelöscht, der Pointer auf das Objkt bleibt also gültig. Aber es wird eben aus dem Cache rausgenommen

Ciao,
_

TheDodger
25-12-2010, 16:19
Er wird auch nicht gelöscht, der Pointer auf das Objkt bleibt also gültig. Aber es wird eben aus dem Cache rausgenommen
Ahhh, eine Fehlinterpretation meinerseits ...
Sowas kann sich echt fix rächen ;)

Leider scheint auch ein insert() nicht so wie gewünscht zu funktionieren (jedenfalls, so, wie ich es gerade mache) ... ;)
Aber ich hab gerade gar keine Möglichkeit, da die Familie ihr Recht fordert. :)

Frohe Feiertage!

TheDodger
28-12-2010, 17:34
Ahhh, eine Fehlinterpretation meinerseits ...
Sowas kann sich echt fix rächen ;)

Leider scheint auch ein insert() nicht so wie gewünscht zu funktionieren (jedenfalls, so, wie ich es gerade mache) ...

Cache.h:


class RequestCache {

public:
RequestCache() { m_file = "" ; m_data = ""; }

void setFile( QString f ) { m_file = f; }
void setData( QByteArray d ) { m_data = d; }

QByteArray data() { return m_data; }

private:
QString m_file;
QByteArray m_data;
};

class RIPCache
: public QObject,
public ErrorHandler {

public:
RIPCache();
~RIPCache();

void insert( QString file, QByteArray d );

bool inCache( QString );
QByteArray fromCache( QString f );

private:
QCache<QString, RequestCache> m_cache;
RequestCache* r_cache;

};


Cache.cpp


RIPCache::RIPCache() {

r_cache = new RequestCache();
QCache<QString, RequestCache> m_cache(500000);
}

RIPCache::~RIPCache() {}


void RIPCache::insert( QString f, QByteArray d ) {

r_cache->setFile( f );
r_cache->setData( d );

Debug( QString( " size bevore '%1'" ).arg( m_cache.size() ).toAscii() );

m_cache.insert( f, r_cache );

Debug( QString( " size after '%1'" ).arg( m_cache.size() ).toAscii() );
}


Kurzauszüge des aktuellen Codes.
So bald ich eine 2. Datei in den Cache legen will, bekomme ich einen SegFault und das gesamte Gebilde stürtzt ab. :-(

Vielleicht sieht jemand von euch auf den ersten Blick meinen Denkfehler?

Gruß!
B.

jeebee
28-12-2010, 20:54
Also bei mir (nach dem Hinzufügen von 68 Zeilen Code ;) ) segfaultet es nicht, aber die Semantik ist sowieso falsch... Alle deine Cache-Einträge zeigen auf das selbe Element, nämlich den private class-member r_cache... Dieser enthält immer den Inhalt des zuletzt gecachten Files.

Flicken z.B. so:
void RIPCache::insert( QString f, QByteArray d ) {

RequestCache *r = new RequestCache();

r->setFile( f );
r->setData( d );

Debug( QString( " size bevore '%1'" ).arg( m_cache.size() ).toAscii() );

m_cache.insert( f, r_cache );

Debug( QString( " size after '%1'" ).arg( m_cache.size() ).toAscii() );
}Schöner wäre, wenn die Argumente für die Cache-Items direkt im Konstruktor übergeben werden... (also RequestCache *r = new RequestCache(f, d); ).

TheDodger
28-12-2010, 23:15
Also bei mir (nach dem Hinzufügen von 68 Zeilen Code ;) ) segfaultet es nicht, aber die Semantik ist sowieso falsch... Alle deine Cache-Einträge zeigen auf das selbe Element, nämlich den private class-member r_cache... Dieser enthält immer den Inhalt des zuletzt gecachten Files.

Danke!
Ich merke gerade, das zu viel guten Wein aus Schwiegervaterns Keller nicht unbedingt fördernd wirkt :}


Schöner wäre, wenn die Argumente für die Cache-Items direkt im Konstruktor übergeben werden... (also RequestCache *r = new RequestCache(f, d); ).
Ich hab das mal aufgegriffen und umgebaut & schon läuft es wie geschnitten Brot! :D


siege -v -b -t30s -c5 http://localhost:8080/index.html

Lifting the server siege.. done.
Transactions: 37332 hits
Availability: 100.00 %
Elapsed time: 29.11 secs
Data transferred: 2.60 MB
Response time: 0.00 secs
Transaction rate: 1282.45 trans/sec
Throughput: 0.09 MB/sec
Concurrency: 4.88
Successful transactions: 37332
Failed transactions: 0
Longest transaction: 0.12
Shortest transaction: 0.00


Flink. Naja, bei einer einzigen statischen Seite, die nix wirklich kann ... ;)

TheDodger
29-12-2010, 11:10
Ich hab mal eine aktuelle Version zum download (http://boone-schulz.de/RIPServer-20101229.tgz) auf den Server kopiert.

Fühlt euch frei ... ;)

TheDodger
04-01-2011, 15:49
Und da hat es mich wieder, das schwarze Loch ...

Ich hänge gerade beim auseinandernehmen des HTML-Headers fest, da ich gerade versuche einen POST/PUT Request (also das hochladen einer Datei) zusammen zu stümpern ...

Ich habe jetzt in einem QByteArray alles an HTML-Header, was mit der Browser schickt.
Nun möchte ich den aber nicht gerade Zeile für Zeile durchwühlen um herauszufinden, wann dort ein 'multipart/form-data; boundary=' drin vorkommt, bzw. die realen Daten ...

Gibt es dafür denn keinen einfachen, eleganten Weg?

TheDodger
05-01-2011, 18:20
Und da hat es mich wieder, das schwarze Loch ...

Ich hänge gerade beim auseinandernehmen des HTML-Headers fest, da ich gerade versuche einen POST/PUT Request (also das hochladen einer Datei) zusammen zu stümpern ...

Ich habe jetzt in einem QByteArray alles an HTML-Header, was mit der Browser schickt.
Nun möchte ich den aber nicht gerade Zeile für Zeile durchwühlen um herauszufinden, wann dort ein 'multipart/form-data; boundary=' drin vorkommt, bzw. die realen Daten ...

Gibt es dafür denn keinen einfachen, eleganten Weg?

Nach 2 harten Tagen Denksport hab ichs wohl ... nicht sehr elegant und wahrscheinlich auch eher suboptimal wenn es um performance geht, aber erstmal gehts.

TheDodger
07-01-2011, 10:46
Nach 2 harten Tagen Denksport hab ichs wohl ... nicht sehr elegant und wahrscheinlich auch eher suboptimal wenn es um performance geht, aber erstmal gehts.

Zu früh gefreut.
Bei (Binär)Dateien oberhalb von 250k scheint der Datenstrom nicht ganz beim Serverprozess angekommen zu sein.
Jedenfalls kann ich keine Dateien über 250k empfangen :(

v20110107 download (http://boone-schulz.de/RIPServer-20110107.tgz)

zenobic
10-01-2011, 00:19
v20110107 download (http://boone-schulz.de/RIPServer-20110107.tgz)

Was anderes ist mir aufgefallen, was leicht zu fixen sein kann,
wenn der GET Request auch direkt zerlegt werden soll?

Dann würde man bei "get.html?arg1=val1&arg2=val2" kein 404 erhalten.



// in QString Request::GET_Request() {
// ...
if( directory.exists( WebRoot ) && directory.isReadable() ) {

// readquery
bool hasQuery = false;
QString qs;
for (int j = 0; j < HTML_Request.size(); ++j) {
// if (hasQuery) qs += HTML_Request[j];
if (HTML_Request[j] == '?') {
hasQuery = true;
}
}

if (hasQuery) {
QStringList qsl = HTML_Request.split("?");
// ...filename = ...
// ...
// QStringList params = qsl.split("&");
} else
// filename = HTML_Request;



Und im Destruktor der "writeExampleConfiguration" Call überschreibt die Config.
Bei mir ist 8080 schon belegt, daher fande ich die "User" config praktisch.



RIPCoreApplication::~RIPCoreApplication() {
/* writeExampleConfiguration(); */
markUp();
}

TheDodger
10-01-2011, 09:47
Was anderes ist mir aufgefallen, was leicht zu fixen sein kann,
wenn der GET Request auch direkt zerlegt werden soll?

Dann würde man bei "get.html?arg1=val1&arg2=val2" kein 404 erhalten.

Danke!
Baue ich mit ein.


Und im Destruktor der "writeExampleConfiguration" Call überschreibt die Config.
Bei mir ist 8080 schon belegt, daher fande ich die "User" config praktisch.

Ich hatte das Thema QSettings bislang nicht so auf dem Schirm, da ich mich ja um das POST-Problem gekümmert hatte.
Meine alten configure-Klasse ist ja auch eher so ein alter Zopf, den ich mal unter Qt2 gebastelt hatte ... das Ding wird nun komplett entfernt.

TheDodger
10-01-2011, 10:09
Nachtrag meinerseits ...

Das hier:


for (int j = 0; j < HTML_Request.size(); ++j) {
// if (hasQuery) qs += HTML_Request[j];
if (HTML_Request[j] == '?') {
hasQuery = true;
}
}


Lässt sich effektiver so lösen:


hasQuery = ( HTML_Request.indexOf( '?' ) != -1 ) ? true : false;


:D

TheDodger
10-01-2011, 10:39
Zu früh gefreut.
Bei (Binär)Dateien oberhalb von 250k scheint der Datenstrom nicht ganz beim Serverprozess angekommen zu sein.
Jedenfalls kann ich keine Dateien über 250k empfangen :(

v20110107 download (http://boone-schulz.de/RIPServer-20110107.tgz)

Ich hab gerade noch gesehen, dass die die Daten, die via POST kommen, stark vom 'enctype' im <form></form> Block abhängen.
Somit reicht ja nicht mal ein simpler Parser, sondern damit wird das echt komplex ... :-(

zenobic
10-01-2011, 13:48
Mein Vorschlag mit dem GET Request war auch nicht ganz richtig,
mir fiel auf, dass im allgemeinen "Request handle", also vorher schon geparsed werden muss,
da dies genauso bei POST Requests der Fall sein kann.
Und man müsste noch nach Slash zerlegen und auf "is directory" prüfen,
da es auch sein kann dass der Query String (und resultierende enviroment variable: QUERY_STRING wenn cgi implementiert wird) Slashs enthält
( z.B: http://1.2.3.4/shop.cgi/categories/all ).


Ich hab gerade noch gesehen, dass die die Daten, die via POST kommen, stark vom 'enctype' im <form></form> Block abhängen.
Somit reicht ja nicht mal ein simpler Parser, sondern damit wird das echt komplex ... :-(

Ich dachte man kann es so realisieren,
dass alles was POST ist, auch ein potentieller Upload sein kann, also den POST Header parsen auf wenn vorhanden:
* "content-type" : auch "boundary=" enthalten ist, muss zerlegt werden.
* "content-disposition" : filename oder name und noch
* "content-transfer-encoding".


Gibt es denn einen Grund PUT zu implementieren?

Viele Webserver unterstützen nur POST, GET und HEAD, vorallem aus Sicherheitsgründen.

TheDodger
10-01-2011, 18:17
Mein Vorschlag mit dem GET Request war auch nicht ganz richtig,
mir fiel auf, dass im allgemeinen "Request handle", also vorher schon geparsed werden muss,
da dies genauso bei POST Requests der Fall sein kann.
Und man müsste noch nach Slash zerlegen und auf "is directory" prüfen,
da es auch sein kann dass der Query String (und resultierende enviroment variable: QUERY_STRING wenn cgi implementiert wird) Slashs enthält
( z.B: http://1.2.3.4/shop.cgi/categories/all ).

Das parsen des query-Strings habe ich aktuell noch nicht 100%ig implementiert bzw. durchdacht.
Aber ich denke, das dürfte nicht wirklich sooo kompliziert werden.
Ich hab mich mehr beim POST festgefressen. ;)



Ich dachte man kann es so realisieren,
dass alles was POST ist, auch ein potentieller Upload sein kann, also den POST Header parsen auf wenn vorhanden:
* "content-type" : auch "boundary=" enthalten ist, muss zerlegt werden.
* "content-disposition" : filename oder name und noch
* "content-transfer-encoding".


Gibt es denn einen Grund PUT zu implementieren?

Viele Webserver unterstützen nur POST, GET und HEAD, vorallem aus Sicherheitsgründen.
Ich will ja gar nicht PUT implementieren!
Das POST raubt mir schon genug Nerven ...

Hier mal ein paar Beispiele
#1 mit

<form action="post.html" name="form" enctype="multipart/form-data" method="post">
http://pastebin.de/13713
#2 mit

<form action="post.html" name="form" method="post">
http://pastebin.de/13714

Also 2 völlig verschiedene Wege um die POST-Variablen auszuwerten ...
Bei #1 fällt dann auch auf, dass 'Content-Disposition: form-data; ...' mehrmals vorkommt.
Ich bin ja so vorgegangen, dass ich vom letzten Eintrag anfange zu parsen. Scheinbar funktioniert das aber nicht wie gewünscht ...

zenobic
10-01-2011, 19:39
Also 2 völlig verschiedene Wege um die POST-Variablen auszuwerten ...
Bei #1 fällt dann auch auf, dass 'Content-Disposition: form-data; ...' mehrmals vorkommt.
Ich bin ja so vorgegangen, dass ich vom letzten Eintrag anfange zu parsen. Scheinbar funktioniert das aber nicht wie gewünscht ...

Ja, man braucht wahrscheinlich mindestens 2 Schritte um postdata zu parsen.

Aber dass sind, soweit ich weiss, auch die einzigen 2 Fälle die auch andere Webserver abfangen.

Ich glaube so könnte der Ablauf funktionieren:

Allgemein Header splitten auf ':' zum Auslesen.

Wenn POST dann (content-length speichern etc.) im
HEADER['content-type'] auf "boundary" oder "multipart" testen.

Wenn es kein Multipart ist und boundary enthält, kann man die Parameter (wie #1)
zerlegen.
Ansonsten boundary speichern ( #2 ), dann muss man parsen, z.B. mit spiltten nach boundary, diese beginnen mit
"\r\n--" + boundary und in einer Schleife analysieren, wenn dann ein filename enthalten ist, muss dies temporär gespeichert werden, z.B. nach /tmp mit einem "tmp name".

#1 Nach new 2 NewLines "\r\n\r\n" und als QUERY_STRING speichern,
(ggf. nach & und = zerlegen)

#2 mit boundary, dann boundaries zerlegen.
Aus der 'content-disposition' Line name und ggf. filename extrahieren,
ggf. alles nach Semikolon auf '=' splitten.
Wenn zwischen den Boundaries 'content-type' vorkommt, dann die eingelesen Chars (data)
als Datei speichern, ansonsten als Postdata in z.B. POST[name] = data .


Ich denke das kann auch richtig Spass machen :)

TheDodger
10-01-2011, 20:22
Wenn POST dann (content-length speichern etc.) im
HEADER['content-type'] auf "boundary" oder "multipart" testen.

Wenn es kein Multipart ist und boundary enthält, kann man die Parameter (wie #1) zerlegen.
Ansonsten boundary speichern ( #2 ), dann muss man parsen, z.B. mit spiltten nach boundary, diese beginnen mit
"\r\n--" + boundary und in einer Schleife analysieren, wenn dann ein filename enthalten ist, muss dies temporär gespeichert werden, z.B. nach /tmp mit einem "tmp name".

#1 Nach new 2 NewLines "\r\n\r\n" und als QUERY_STRING speichern, (ggf. nach & und = zerlegen)

#2 mit boundary, dann boundaries zerlegen.
Aus der 'content-disposition' Line name und ggf. filename extrahieren, ggf. alles nach Semikolon auf '=' splitten.
Wenn zwischen den Boundaries 'content-type' vorkommt, dann die eingelesen Chars (data) als Datei speichern, ansonsten als Postdata in z.B. POST[name] = data .

Ich denke das kann auch richtig Spass machen :)

Eigentlich mache ich es ja genau so, wie du es beschrieben hast ... (Mein Code ist in der 'MultipartData.cpp' ;) ) ... jedenfalls denke ich das.
Aber ich zweifle gerade daran ...

anda_skoa
10-01-2011, 20:24
Eventuell lässt sich auch QUrl zum parsen der GET URL heranziehen, das hätte u.A. hasQuery()

Ciao,
_

TheDodger
11-01-2011, 21:16
Ich denke das kann auch richtig Spass machen :)

Naja .. Spaß machen ... ;)

Scheinbar habe ich Probleme beim 'Auseinandernehmen' der Request-Daten:


[22:49:26:261] [ Debug] : HEADER: [595]
'POST /post.html HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2.13) Gecko/20110102 Gentoo Firefox/3.6.13
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.7,de-de;q=0.3
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-15,utf-8;q=0.7,*;q=0.7
Keep-Alive: 115
Connection: keep-alive
Referer: http://localhost:8080/
X-Behavioral-Ad-Opt-Out: 1
X-Do-Not-Track: 1
Content-Type: multipart/form-data; boundary=---------------------------1525561818437990015185174710
Content-Length: 278153'

[22:49:26:261] [ Debug] : Request: 'POST' - 'post.html' - 'HTTP/1.1'
[22:49:26:261] [ Debug] : Content-Disposition found ...
[22:49:26:261] [ Debug] : Request-Size '56749'
[22:49:26:261] [ClassDebug] : void MultiPart::init()
[22:49:26:262] [ Debug] : capacity 57344
[22:49:26:262] [ Debug] : i think, we can have 1 attachments
[22:49:26:262] [ Debug] : name 'file1'
[22:49:26:262] [ Debug] : filename '01ba2c47cf8ceb8a8d23e565dcec297b-d31oky4.jpg'
[22:49:26:262] [ Debug] : type 'image/jpeg'
[22:49:26:262] [ Debug] : size '56270'
[22:49:26:262] [ Debug] : datetime '10.01.2011-22:49:26:262'
[22:49:26:262] [ Debug] : md5 '684f129518e241c154fdaa5d5b8bba3d'

Nach 'Content-Length' müssten es ~280k sein.
Nach meinem auseinander-cut-en habe ich nur 56k übrig. :(

Ich krieg 'n Fön

update #1
Ich glaube fast, der socket in Request.cpp bekommt die Daten nicht fix genug und wird geschlossen, bevor alles bei ihm gelandet ist.
Gibt es keine Möglichkeit das anders zu gestalten?

update #2
aktuelle Version (http://boone-schulz.de/RIPServer-20110111.tgz)

TheDodger
14-01-2011, 10:36
Ich hab da eine Theorie, wieso das mit dem POST nicht so klappt ...
Liegt das evtl. daran, dass der Request in einem Thread abgearbeitet wird und der irgendwie Probleme mit dem syncronisieren der IP / Ports bekommt?

Denn anders kann ich mir das jetzt nicht mehr erklären.
Ich hab das jetzt auch erstmal weiter nach hinten verschoben um mit Cookie & Session weiterzumachen ...

TheDodger
18-01-2011, 09:41
Ich hab da eine Theorie, wieso das mit dem POST nicht so klappt ...
Liegt das evtl. daran, dass der Request in einem Thread abgearbeitet wird und der irgendwie Probleme mit dem syncronisieren der IP / Ports bekommt?
Ich habe mal unterschiedliche Wege ausprobiert, um an die Daten zu kommen:
#1


bool done = true;

do {

usleep(500);

pRawHeaderData.append( socket->readAll() );

done = socket->atEnd();
socket->flush();
} while( !done );


#2


while( socket->waitForDisconnected( 100 ) ) {

pRawHeaderData.append( socket->readLine() );
}


#3


while( socket->canReadLine() ) {

pRawHeaderData.append( socket->readLine() );
}


Nur um zu sehen, ob sich da irgendwas ändert.

Aber was soll ich sagen?
Das Ergebniss bleibt immer das gleiche ... :(

anda_skoa
18-01-2011, 12:00
Vielleicht eher so:



do {
while ( socket->waitForReadyRead( 100 ) ){
pRawHeaderData.append( socket->read( socket->bytesAvailable() ) );
}
} while (!socket->atEnd())


Ciao,
_

TheDodger
18-01-2011, 13:23
Vielleicht eher so:



do {
while ( socket->waitForReadyRead( 100 ) ){
pRawHeaderData.append( socket->read( socket->bytesAvailable() ) );
}
} while (!socket->atEnd())



Test ich nachher mal genauer.
Beim reinen c'n'p ist das eine Endlosschleife ;)

[edit]
Nein, es aendert sich nicht.
Ich hab auch gerade keine Moeglichkeit gefunden aus der do while schleife auszubrechen ...

TheDodger
20-01-2011, 09:19
Test ich nachher mal genauer.
Beim reinen c'n'p ist das eine Endlosschleife ;)

[edit]
Nein, es aendert sich nicht.
Ich hab auch gerade keine Moeglichkeit gefunden aus der do while schleife auszubrechen ...

Ich nehme alles zurück, was ich vorher behauptet habe!
Ich hab das ganze mal mit 'make clean' gesäubert und neu gebaut und siehe da ... jetzt gehts auch!
Was freu ich mich ;)

Danke!

TheDodger
29-01-2011, 14:09
Moin Moin!

Ich habe jetzt eine halbwegs lauffähige Version (download (http://boone-schulz.de/RIPServer-20110129.tgz)).

Momentan speichere ich die Sessions in einer sqlite-DB, bin mir aber nicht sicher, ob das als eine Performancebremse auswirken könnte.

PHP hat ja nicht wirklich was mit typsicheren Variablen am Hut, so dass man dort (in den Sessions) problemlos ganze Arrays reindrücken kann. Wenn ich diese Funktionalität noch implementieren will, wüsste ich noch gar nicht wie.
Mal abgesehen davon halte ich sie nicht gerade für sinnvoll. ;)

So, schaut euch das Ding an wenn ihr wollt.
Über Kritik und Anregungen bin ich echt dankbar, also übt keine Zurückhaltung! :D

TheDodger
30-01-2011, 13:19
Nachdem ich nun soweit bin, habe ich wohl noch ein einzigest Problem ... und zwar das Ding mit den Plugins.

Mein Wunschverhalten wäre dieses:
Beim Start des Servers werden die Verfügbaren Plugins geladen und können dann generell von überall her genutzt werden.

Aktuell scheine ich da aber an eine meiner (momentanen) Grenzen gestossen zu sein.
Wenn ich die Plugins in der Klasse RIPCoreApplication lese, kann ich diese in Request nicht benutzen, also muß ich die da wieder laden.

Und ich möchte nicht wirklich, dass die Plugins bei jedem Request neu geladen werden müssen. Dann brauch ich auch gar keine ...

Gibt es da für mich keine Elegantere Lösung?
Anda Skoa? Vielleicht hast du eine Idee? ;)

anda_skoa
30-01-2011, 15:58
Ich bin mit nicht ganz sicher, ob ich das Problem richtig verstehe.

In deiner Applikationsklasse lädst du die Plugins und hälst sie dann in einem Container?

Und in deiner Requestklasse hast du keine Zugriff auf diesen Container?

Ciao,
_

TheDodger
30-01-2011, 17:40
Ich bin mit nicht ganz sicher, ob ich das Problem richtig verstehe.

In deiner Applikationsklasse lädst du die Plugins und hälst sie dann in einem Container?

Und in deiner Requestklasse hast du keine Zugriff auf diesen Container?

Richtig.
Ich denke, es liegt daran, das der eigentliche Construktor des PluginLoaders privat und er eigentlich über eine statische PluginLoader::self() Funktion instanziert und erstellt wird.
self() ruft dann intern die (private) scan() Funktion auf.

Ich hab bislang noch keinen Hebel gefunden, dem Container eine Funktion mit zu geben, mit der die geladenen Plugins public zur Verfügung gestellt werden können.

anda_skoa
30-01-2011, 22:56
DAs versteh ich jetzt nicht ganz.

Kannst du nicht einfach


PluginLoader::self()->createForName(...);

aufrufen?

Ciao,
_

TheDodger
31-01-2011, 08:01
DAs versteh ich jetzt nicht ganz.

Kannst du nicht einfach


PluginLoader::self()->createForName(...);

aufrufen?


Stimmt, das funktioniert wunderbar!
Wahhh, da hab ich mich so dermassen verrannt, dass ich die einfachste Lösung nicht mehr gesehen habe :(

anda_skoa
06-02-2011, 10:59
Bin gerade über etwas gestolpert, dass dich vielleicht interessieren könnte:
https://github.com/nikhilm/qhttpserver

Ciao,
_

TheDodger
01-03-2011, 16:45
Bin gerade über etwas gestolpert, dass dich vielleicht interessieren könnte:
https://github.com/nikhilm/qhttpserver


Ahh, da schien jemand eine ähnlich Idee gehabt zu haben. ;)
Danke, dass werd ich mir bei Gelegenheit gern mal mit anschauen.

Zur Zeit kämpfe ich eher mit dem ausliefern von Bildern ... das scheint nicht ganz so zu funktionieren, wie ich es mir vorstelle. ;)
Reine HTML & Stylesheet Daten funktionieren