fix server and finish the SCGI client for Qt
authorKonrad Rosenbaum <konrad@silmor.de>
Thu, 31 Jan 2013 21:23:32 +0000 (22:23 +0100)
committerKonrad Rosenbaum <konrad@silmor.de>
Thu, 31 Jan 2013 21:23:32 +0000 (22:23 +0100)
qtbase/include/interface.h
qtbase/include/server.h
qtbase/src/interface.cpp
qtbase/src/server.cpp
qtbase/src/server_p.h
qtbase/src/transaction.cpp
qtbase/src/wobnam.cpp
qtbase/src/wobnam.h
qtbase/src/wobnam_p.h [new file with mode: 0644]
qtbase/wbase.pro

index f65d866..8085a1f 100644 (file)
 
 #include <WOb>
 
-class QNetworkAccessManager;
+class WobNetworkAccessManager;
 class QNetworkRequest;
 class QNetworkReply;
 class WServerRequest;
 class WServerReply;
 
+/// base class of all interfaces
 class WInterface:public QObject
 {
        Q_OBJECT
@@ -135,7 +136,7 @@ class WInterface:public QObject
                unsigned short m_proxyport;
                int m_wtimeout;
                LogLevel loglvl;
-               QNetworkAccessManager*m_netman;
+               WobNetworkAccessManager*m_netman;
 };
 
 #endif
index 3f47861..2e69b67 100644 (file)
@@ -15,28 +15,42 @@ class QTcpServer;
 class QLocalServer;
 class WInterface;
 
+///Encapsulates the request as it comes in via (S)CGI
 class WServerRequest
 {
        public:
+               ///instantiates an empty request
                WServerRequest();
+               ///copies a request
                WServerRequest(const WServerRequest&);
                
+               ///copies a request
                WServerRequest& operator=(const WServerRequest&);
                
+               ///checks HTTP level header for its existence, in (S)CGI these are prefixed with "HTTP_" -> so hasHeader("Host") checks for the CGI header/variable HTTP_HOST to exist
                bool hasHeader(const QString&)const;
+               ///returns true if the given low-level variable/header exists
                bool hasCgiHeader(const QString&)const;
+               ///returns the value of the given HTTP level header
                QByteArray header(const QString&)const;
+               ///returns the value of the given low-level variable/header
                QByteArray cgiHeader(const QString&)const;
-               
+               ///returns the request body, or an empty array if there was none
                QByteArray bodyData()const;
                
+               ///returns true if this is a valid SCGI request
                bool isValid()const;
                
+               ///dumps the request in a human readable format
                QByteArray dump()const;
                
+               ///returns the path (Request-Uri) that has been requested by the client
                QString pathInfo()const;
+               ///returns the (virtual) Host that the client wants to access
+               QString hostInfo()const;
                
        protected:
+               ///used by WServerReceiver to instantiate requests
                WServerRequest(const QByteArray&);
                void setBody(const QByteArray&);
                friend class WServer;
@@ -48,20 +62,29 @@ class WServerRequest
 };
 Q_DECLARE_METATYPE(WServerRequest);
 
+///reply object for the server side: contains the data sent back to the client
 class WServerReply
 {
        public:
+               ///creates an invalid reply (500 Server Error)
                WServerReply();
-               WServerReply(const WServerReply&);
-               WServerReply& operator=(const WServerReply&);
+               ///copies the reply object
+               WServerReply(const WServerReply&)=default;
+               ///copies the reply object
+               WServerReply& operator=(const WServerReply&)=default;
                
+               ///sets the return status of the reply
                bool setStatus(short code,QString str);
+               ///sets a specific header, Content-Length cannot be overridden
                bool setHeader(const QString&,const QString&);
+               ///converts the string to UTF-8 and sets it as the message body
                void setBody(const QString&);
+               ///sets the message body and the Content-Length header accordingly
                void setBody(const QByteArray&);
                
        protected:
                friend class WServer;
+               /// \internal converts the reply to the SCGI wire format ready to be sent
                QByteArray toWireFormat()const;
        private:
                short mStatCode;
@@ -70,31 +93,65 @@ class WServerReply
 };
 
 /** Represents an SCGI server as a proxy to server side interfaces.
+ * 
+ * Use the cgi2scgi helper to translate between CGI and SCGI.
 */
 class WServer:public QObject
 {
        Q_OBJECT
        public:
-               WServer(const QString&,QObject*parent=0);
-               WServer(const QHostAddress&,unsigned short,QObject*parent=0);
-               virtual ~WServer();
+               /** creates a new server listening to a local socket 
+                * 
+                * \param path the (file) name of the socket (or named pipe on Windows)
+                * \param removeIfExist if true the socket file is removed before trying to create it again (this is a good idea on Unix/Linux, since existing sockets cannot be created)
+                * \param parent the owning QObject of this server */
+               WServer(const QString&path,bool removeIfExist=true,QObject*parent=0);
+               /** creates a new server listening to a TCP socket
+                * 
+                * \param addr the address to listen on
+                * \param port the TCP port to listen on
+                * \param parent owning QObject of this server */
+               WServer(const QHostAddress&addr,unsigned short port,QObject*parent=0);
+               ///deletes the server object
+               virtual ~WServer()=default;
                
+               ///returns true if this is a working server (i.e. the constructor did not have errors)
                bool isActive()const{return tcpserv!=0 || locserv!=0;}
                
+               ///returns the current receive timeout in seconds
+               ///this is the time that a request has to be completely received
                int receiveTimeout()const{return mrecvtimeout;}
+               
        public slots:
-               void registerInterface(const QString&,WInterface*);
-               void registerStatic(const QString&,const QString&);
+               /** registers an interface to handle a specific path
+                * 
+                * \param path the relative path inside the server's address space
+                * \param ifc the interface that handles this path */
+               void registerInterface(const QString&path,WInterface*ifc);
+               /** registers a specific content to be served on a path
+                * 
+                * \param path the relative path inside the server's address space
+                * \param content the page content to deliver */
+               void registerStatic(const QString&path,const QString&content);
+               ///deletes whatever registration exists at a specific path
                void unregisterPath(const QString&);
+               ///registers a custom page for a 404 Not Found error
                void register404(const QString&);
+               ///registers a custom page for a 400 Invalid Request error
                void register400(const QString&);
+               ///sets the receive timeout of the server
                void setReceiveTimeout(int);
+               ///restricts the hosts that can connect to this server
                void restrictSourceHosts(const QPair<QHostAddress,int>&);
+               ///enable or disable the /debug path, if enabled a request at that path simply returns the content of the request as a response
                void enableDebugUrl(bool enable=true);
                
        private slots:
+               /// \internal called when a new connection is available
                void newConnection();
+               /// \internal creates a WServerReceiver object to receive and parse the connection
                void handleConnection(QIODevice*);
+               /// \internal called to actually handle requests once they are fully received
                void handleRequest(WServerRequest,QIODevice*);
        private:
                void handleRequestHelper(WServerRequest,QIODevice*);
index ce7c553..fcc2f6d 100644 (file)
@@ -8,11 +8,12 @@
 
 #include <QMutex>
 #include <QMutexLocker>
-#include <QNetworkAccessManager>
 #include <QNetworkReply>
 #include <QNetworkRequest>
 #include <QNetworkProxy>
 
+#include "wobnam.h"
+
 QMap<QString,WInterface*> WInterface::inst;
 
 static QMutex mtx(QMutex::Recursive);
@@ -34,7 +35,7 @@ WInterface::WInterface(QString name)
                delete inst[name];
        }
        inst.insert(name,this);
-       m_netman=new QNetworkAccessManager(this);
+       m_netman=new WobNetworkAccessManager(this);
        connect(m_netman,SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>)), this,SLOT(sslErrors(QNetworkReply*,QList<QSslError>)));
 }
 
index dc0ee0c..81f3162 100644 (file)
 #include <QTcpServer>
 #include <QTcpSocket>
 #include <QTimer>
+#include <QFile>
 
 static int metaid=qRegisterMetaType<WServerRequest>();
 
 #define ERR404 "<h1>Path does not exist</h1>\nSorry, the path you requested does not exist.\n"
 #define ERR400 "<h1>Invalid Request</h1>\nSorry, the request was invalid.\n"
 
-WServer::WServer(const QString&path,QObject*parent)
+WServer::WServer(const QString&path,bool removeIfExist,QObject*parent)
 :QObject(parent)
 {
        mrecvtimeout=30000;
        tcpserv=0;
        mdebugenabled=false;
+       if(removeIfExist){
+               QFile::remove(path);
+       }
        locserv=new QLocalServer(this);
        if(!locserv->listen(path)){
                qDebug()<<"Error creating server socket on path"<<path;
@@ -55,9 +59,6 @@ WServer::WServer(const QHostAddress&host,unsigned short port,QObject*parent)
        err404=ERR404;
        err400=ERR400;
 }
-WServer::~WServer()
-{
-}
 
 void WServer::enableDebugUrl(bool enable){mdebugenabled=enable;}
 
@@ -111,12 +112,12 @@ void WServer::newConnection()
        if(tcpserv!=0){
                while(tcpserv->hasPendingConnections()){
                        QTcpSocket*sock=tcpserv->nextPendingConnection();
-                       qDebug()<<"new TCP connection from"<<sock->peerAddress()<<"port"<<sock->peerPort();
+//                     qDebug()<<"new TCP connection from"<<sock->peerAddress()<<"port"<<sock->peerPort();
                        if(sourcehost.second>0){
                                if(!sock->peerAddress().isInSubnet(sourcehost)){
                                        sock->close();
                                        sock->deleteLater();
-                                       qDebug()<<"dropping TCP connection";
+//                                     qDebug()<<"dropping TCP connection";
                                        continue;
                                }
                        }
@@ -125,7 +126,7 @@ void WServer::newConnection()
        }
        if(locserv!=0){
                while(locserv->hasPendingConnections()){
-                       qDebug()<<"new Local connection";
+//                     qDebug()<<"new Local connection";
                        handleConnection(locserv->nextPendingConnection());
                }
        }
@@ -152,16 +153,18 @@ static bool qstringlonger(const QString&s1,const QString&s2)
 
 void WServer::handleRequestHelper(WServerRequest rq,QIODevice*sock)
 {
+//     qDebug()<<"handling request for host"<<rq.hostInfo()<<"path"<<rq.pathInfo();
+//     qDebug()<<rq.dump();
        //reject invalid requests outright
        if(!rq.isValid()){
-               qDebug()<<"rejecting invalid request with code 400";
+//             qDebug()<<"rejecting invalid request with code 400";
                sock->write(QByteArray("Status: 400 Invalid Request\r\nContent-Type: text/html; charset=utf-8\r\n\r\n")+err400.toUtf8());
                return;
        }
        QString path=rq.pathInfo();
        //check for debug mode
        if(mdebugenabled && pathMatch("/debug",path)){
-               qDebug()<<"debug: dumping request data as response";
+//             qDebug()<<"debug: dumping request data as response";
                sock->write(QByteArray("Status: 200 Ok\r\nContent-Type: text/plain\r\n\r\n"));
                sock->write(rq.dump());
                return;
@@ -174,21 +177,21 @@ void WServer::handleRequestHelper(WServerRequest rq,QIODevice*sock)
                if(pathMatch(k[i],path)){
                        //find a static match
                        if(statcontent.contains(k[i])){
-                               qDebug()<<"returning static content";
+//                             qDebug()<<"returning static content";
                                sock->write(QByteArray("Status: 200 Ok\r\nContent-Type: text/html; charset=utf-8\r\n\r\n"));
                                sock->write(statcontent[k[i]].toUtf8());
                                return;
                        }else
                        //find dynamic match
                        if(iface.contains(k[i])){
-                               qDebug()<<"returning dynamic content";
+//                             qDebug()<<"returning dynamic content";
                                WServerReply rp=iface[k[i]]->execute(rq);
                                sock->write(rp.toWireFormat());
                                return;
                        }
                }
        //no match, send a 404
-       qDebug()<<"no match found, sending 404";
+//     qDebug()<<"no match found, sending 404";
        sock->write(QByteArray("Status: 404 File not found\r\nContent-Type: text/html; charset=utf-8\r\n\r\n")+err404.toUtf8());
 }
 void WServer::handleRequest(WServerRequest rq,QIODevice*sock)
@@ -261,10 +264,14 @@ QByteArray WServerRequest::cgiHeader(const QString&h)const
 }
 QString WServerRequest::pathInfo()const
 {
-       QString p=QString::fromLatin1(cgiHeader("PATH_INFO"));
+       QString p=QString::fromLatin1(cgiHeader("Request-Uri"));
        if(p=="")return "/";
        else return p;
 }
+QString WServerRequest::hostInfo()const
+{
+       return QString::fromLatin1(header("Host"));
+}
 QByteArray WServerRequest::bodyData()const
 {
        return mbody;
@@ -292,18 +299,6 @@ WServerReply::WServerReply()
        mStatCode=500;
        mStatStr="Internal Error";
 }
-WServerReply::WServerReply(const WServerReply&r)
-{
-       operator=(r);
-}
-WServerReply& WServerReply::operator=(const WServerReply&r)
-{
-       mStatCode=r.mStatCode;
-       mStatStr=r.mStatStr;
-       mBody=r.mBody;
-       mHead=r.mHead;
-       return *this;
-}
 bool WServerReply::setStatus(short code,QString str)
 {
        //check the return code
@@ -390,7 +385,7 @@ void WServerReceiver::receiveMore()
        //read
        qint64 ba=sock->bytesAvailable();
        if(ba<=0)return;
-       qDebug()<<"reading next"<<ba<<"bytes";
+//     qDebug()<<"reading next"<<ba<<"bytes";
        buffer+=sock->read(ba);
        //interpret
        if(mode==StartMode){
@@ -399,7 +394,7 @@ void WServerReceiver::receiveMore()
                        explen=buffer.left(p).toInt();
                        buffer=buffer.mid(p+1);
                        mode=HeaderMode;
-                       qDebug()<<"proceeding to header of size"<<explen;
+//                     qDebug()<<"proceeding to header of size"<<explen;
                }
        }
        if(mode==HeaderMode){
@@ -415,19 +410,19 @@ void WServerReceiver::receiveMore()
                                mode=BodyMode;
                        else
                                mode=Completed;
-                       qDebug()<<"header complete next is"<<explen;
+//                     qDebug()<<"header complete next is"<<explen;
                }
        }
        if(mode==BodyMode){
                if(buffer.size()>explen){//this is not an off-by-one, see below
                        request.setBody(buffer.mid(1,explen));
-                       qDebug()<<"body completed";
+//                     qDebug()<<"body completed";
                        mode=Completed;
                }
        }
        //check for completion
        if(mode==Completed){
-               qDebug()<<"request completed, now handling it";
+//             qDebug()<<"request completed, now handling it";
                emit readyForProcess(request,sock);
                sock=0;
                deleteLater();
index 96e278c..c763aac 100644 (file)
@@ -8,16 +8,23 @@
 
 #include "server.h"
 
+/// \internal helper class for WServer - helps with receiving and parsing SCGI requests
 class WServerReceiver:public QObject
 {
        Q_OBJECT
        public:
-               WServerReceiver(WServer*,QIODevice*);
+               ///constructs a new receiver
+               /// \param srv the server this receiver belongs to
+               /// \param sock the socket to handle
+               WServerReceiver(WServer*srv,QIODevice*sock);
+               ///deletes the receiver object and its socket if it had an error or timeout
                virtual ~WServerReceiver();
-       public slots:
+       private slots:
+               /// \internal helper to receive data
                void receiveMore();
                
        signals:
+               ///returns the completely parsed request back to the server to handle it
                void readyForProcess(WServerRequest,QIODevice*);
                
        private:
index 9e4b196..b239a1b 100644 (file)
@@ -93,8 +93,9 @@ void WTransaction_Private::startQuery(QString hreq,QByteArray data)
        QUrl url=iface->url();
        int port=url.port();
        if(port<=0){
-               if(url.scheme().toLower()=="http")port=80;
-               else port=443;
+               const QString&s=url.scheme().toLower();
+               if(s=="http")port=80;
+               else if(s=="https")port=443;
        }
        if(m_log)m_log->setInfo(QString("New HTTP Request %1 URL %2\n\tto host=%3 port=%4 scheme=%5")
                .arg(hreq)
index 853b2d9..f8923dd 100644 (file)
@@ -4,11 +4,20 @@
 //
 
 #include "wobnam.h"
+#include "wobnam_p.h"
 
-#include <QSslSocket>
+#include <QStringList>
+#include <QDebug>
+#include <QUrlQuery>
 
 #include <stdlib.h>
 
+WobNetworkAccessManager::WobNetworkAccessManager(QObject* parent)
+       : QNetworkAccessManager(parent)
+{
+}
+
+
 static inline QByteArray translateOperation(QNetworkAccessManager::Operation op)
 {
        switch(op){
@@ -25,39 +34,44 @@ QNetworkReply* WobNetworkAccessManager::createRequest(QNetworkAccessManager::Ope
 {
        //check whether it is scgi or scgissl
        QUrl url=req.url();
-       if(url.scheme()=="scgi" || url.scheme()=="scgissl"){
+       if(url.scheme()=="scgi" || url.scheme()=="scgissl" || url.scheme()=="scgilocal"){
                return new WobScgiNetworkReplyImpl(req,translateOperation(op),outgoingData,this);
        }
        return QNetworkAccessManager::createRequest(op, req, outgoingData);
 }
 
-QNetworkReply* WobNetworkAccessManager::QNetworkAccessManager::sendCustomRequest(const QNetworkRequest& req, const QByteArray& op, QIODevice* data)
+QNetworkReply* WobNetworkAccessManager::sendCustomRequest(const QNetworkRequest& req, const QByteArray& op, QIODevice* data)
 {
        //check whether it is scgi or scgissl
        QUrl url=req.url();
-       if(url.scheme()=="scgi" || url.scheme()=="scgissl"){
+       if(url.scheme()=="scgi" || url.scheme()=="scgissl" || url.scheme()=="scgilocal"){
                return new WobScgiNetworkReplyImpl(req,op,data,this);
        }
        return QNetworkAccessManager::sendCustomRequest(req, op, data);
 }
 
 
-WobScgiNetworkReplyImpl::WobScgiNetworkReplyImpl(const QNetworkRequest& req_, QByteArray op_, QIODevice* input, QObject* parent)
-       : QNetworkReply(parent),req(req_),op(op_)
+WobScgiNetworkReplyImpl::WobScgiNetworkReplyImpl(QNetworkRequest req, QByteArray op_, QIODevice* input, QObject* parent)
+       : QNetworkReply(parent),op(op_)
 {
+       setRequest(req);
        offset=0;
        readMode=HeadRead;
+       setOpenMode(QIODevice::ReadOnly|QIODevice::Unbuffered);
+       //get input
+       if(input)incontent=input->readAll();
        //create connection
        if(req.url().scheme()=="scgi"){
-               sock=new QTcpSocket(this);
-               connect(sock,SIGNAL(error(QAbstractSocket::SocketError)),
+               QTcpSocket* tsock=new QTcpSocket(this);
+               sock=tsock;
+               connect(tsock,SIGNAL(error(QAbstractSocket::SocketError)),
                        this,SLOT(sockerror(QAbstractSocket::SocketError)));
-               connect(sock,SIGNAL(connected()),
+               connect(tsock,SIGNAL(connected()),
                        this,SLOT(startwrite()));
-               connect(sock,SIGNAL(disconnected()),
+               connect(tsock,SIGNAL(disconnected()),
                        this,SLOT(sockabort()));
-               sock->connectToHost(req.url().host(),req.url().port(9080));
-       }else{
+               tsock->connectToHost(req.url().host(),req.url().port(9080));
+       }else if(req.url().scheme()=="scgissl"){
                QSslSocket *ssock=new QSslSocket(this);
                sock=ssock;
                connect(sock,SIGNAL(error(QAbstractSocket::SocketError)),
@@ -68,11 +82,29 @@ WobScgiNetworkReplyImpl::WobScgiNetworkReplyImpl(const QNetworkRequest& req_, QB
                        this,SLOT(startwrite()));
                connect(sock,SIGNAL(disconnected()),
                        this,SLOT(sockabort()));
-               sock->connectToHost(req.url().host(),req.url().port(9443));
+               ssock->connectToHostEncrypted(req.url().host(),req.url().port(9443));
+       }else{
+               //create socket
+               QLocalSocket *lsock=new QLocalSocket(this);
+               sock=lsock;
+               connect(lsock,SIGNAL(error(QLocalSocket::LocalSocketError)),
+                       this,SLOT(lsockerror(QLocalSocket::LocalSocketError)));
+               connect(lsock,SIGNAL(connected()),
+                       this,SLOT(startwrite()));
+               connect(lsock,SIGNAL(disconnected()),
+                       this,SLOT(sockabort()));
+               //parse url
+               QUrl url=req.url();
+               QUrlQuery query(url);
+               const QString path=query.queryItemValue("_lpath_");
+               query.removeQueryItem("_lpath_");
+               url.setQuery(query);
+               req.setUrl(url);
+               setRequest(req);
+               //connnect
+               lsock->connectToServer(path);
        }
        connect(sock,SIGNAL(readyRead()),this,SLOT(sockread()));
-       //get input
-       if(input)incontent=input->readAll();
 }
 
 
@@ -87,6 +119,19 @@ qint64 WobScgiNetworkReplyImpl::readData(char* data, qint64 maxSize)
        return mmax;
 }
 
+qint64 WobScgiNetworkReplyImpl::bytesAvailable() const
+{
+       return outcontent.size()-offset;
+}
+
+qint64 WobScgiNetworkReplyImpl::size()const
+{
+       return outcontent.size();
+}
+
+void WobScgiNetworkReplyImpl::abort(){QNetworkReply::close();}
+void WobScgiNetworkReplyImpl::close(){QNetworkReply::close();}
+
 void WobScgiNetworkReplyImpl::ignoreSslErrors()
 {
        QSslSocket *ssock=qobject_cast<QSslSocket*>(sock);
@@ -100,18 +145,19 @@ void WobScgiNetworkReplyImpl::startwrite()
        head.append('\0').append(QByteArray::number(incontent.size())).append('\0');
        head.append("SCGI").append('\0').append("1").append('\0');
        head.append("REQUEST_METHOD").append('\0').append(op).append('\0');
-       QByteArray uri=req.url().toString(QUrl::RemoveScheme|QUrl::RemoveAuthority|QUrl::EncodeSpaces|QUrl::RemoveFragment).toLatin1();
+       const QUrl url=request().url();
+       QByteArray uri=url.toString(QUrl::RemoveScheme|QUrl::RemoveAuthority|QUrl::EncodeSpaces|QUrl::RemoveFragment).toLatin1();
        head.append("REQUEST_URI").append('\0').append(uri).append('\0');
-       head.append("HOST").append('\0').append(req.url().host()).append('\0');
+       head.append("HTTP_HOST").append('\0').append(url.host()).append('\0');
        //generate remaining headers
-       const QList<QByteArray>&hnames=req.rawHeaderList();
+       const QList<QByteArray>&hnames=request().rawHeaderList();
        for(int i=0;i<hnames.size();i++){
                const QByteArray hname=hnames[i].toUpper().replace('-','_');
                if(hname=="CONTENT_LENGTH" || hname=="SCGI" || 
                   hname=="REQUEST_METHOD"||hname=="REQUEST_URI" || hname=="HOST")
                        continue;
-               head.append(hname).append('\0');
-               head.append(req.rawHeader(hnames[i])).append('\0');
+               head.append("HTTP_"+hname).append('\0');
+               head.append(request().rawHeader(hnames[i])).append('\0');
        }
        //generate netstring from headers
        head.prepend(QByteArray::number(head.size())+":");
@@ -137,7 +183,7 @@ void WobScgiNetworkReplyImpl::sockread()
                        if(inhead[pos]=='\n')cn++;
                        pos++;
                }
-               incontent=inhead.mid(pos);
+               outcontent=inhead.mid(pos);
                inhead=inhead.left(pos).trimmed();
                //switch mode
                readMode=ContentRead;
@@ -147,12 +193,25 @@ void WobScgiNetworkReplyImpl::sockread()
                        const QByteArray head=heads[i].trimmed();
                        pos=head.indexOf(':');
                        if(pos<0)continue;
-                       setRawHeader(head.left(pos).trimmed(),head.mid(pos+1).trimmed());
+                       const QString hname=head.left(pos).trimmed();
+                       const QString hval=head.mid(pos+1).trimmed();
+                       if(hname.toLower()=="status"){
+                               setAttribute(QNetworkRequest::HttpStatusCodeAttribute, hval.left(3).toInt());
+                               setAttribute(QNetworkRequest::HttpReasonPhraseAttribute, hval.mid(3).trimmed());
+                       }else
+                               setRawHeader(hname.toLatin1(),hval.toLatin1());
                }
        }else{
-               incontent+=sock->read(avl);
+               outcontent+=sock->read(avl);
                emit downloadProgress(incontent.size(),header(QNetworkRequest::ContentLengthHeader).toLongLong());
        }
+       //are we done yet?
+       if(readMode==ContentRead){
+               qint64 len=header(QNetworkRequest::ContentLengthHeader).toInt();
+               if(len>0 && outcontent.size()>=len){
+                       finishUp();
+               }
+       }
 }
 
 void WobScgiNetworkReplyImpl::sockabort()
@@ -162,7 +221,7 @@ void WobScgiNetworkReplyImpl::sockabort()
        sockread();
        //now go in peace
        sock->close();
-       emit finished();
+       finishUp();
 }
 
 static inline QNetworkReply::NetworkError translateError(QAbstractSocket::SocketError err)
@@ -201,5 +260,34 @@ static inline QNetworkReply::NetworkError translateError(QAbstractSocket::Socket
 
 void WobScgiNetworkReplyImpl::sockerror(QAbstractSocket::SocketError err)
 {
+       if(err==QAbstractSocket::RemoteHostClosedError && readMode==ContentRead){
+               finishUp();
+               return;
+       }
+       qDebug()<<"WobScgiNetworkReplyImpl received network socket error"<<(int)err;
+       emit error(translateError(err));
+}
+
+static inline QNetworkReply::NetworkError translateError(QLocalSocket::LocalSocketError err)
+{
+       return translateError((QAbstractSocket::SocketError)err);
+}
+
+void WobScgiNetworkReplyImpl::lsockerror(QLocalSocket::LocalSocketError err)
+{
+       if(err==QLocalSocket::PeerClosedError && readMode==ContentRead){
+               qint64 len=header(QNetworkRequest::ContentLengthHeader).toLongLong();
+               if(len<=0)setRawHeader("Content-Length",QByteArray::number(outcontent.size()));
+               finishUp();
+               return;
+       }
+       qDebug()<<"WobScgiNetworkReplyImpl received local socket error"<<(int)err;
        emit error(translateError(err));
+}
+
+void WobScgiNetworkReplyImpl::finishUp()
+{
+       if(isFinished())return;
+       setFinished(true);
+       emit finished();
 }
\ No newline at end of file
index a80f151..580443a 100644 (file)
@@ -8,48 +8,29 @@
 
 
 #include <QNetworkAccessManager>
-#include <QNetworkReply>
-#include <QNetworkRequest>
-#include <QTcpSocket>
-
+class QNetworkReply;
+
+/** Extended version of QNetworkAccessManager that can handle SCGI directly.
+ * 
+ * This is used by PACK to be able to connect to Qt based PACK servers directly
+ * instead of tunneling through a web server and/or CGI.
+ * 
+ * URL formats:
+ * scgi://host:port/path -> TCP
+ * scgissl://host:port/path -> SSL
+ * scgilocal://dummyhost/path?_lpath_=/path/to/sock -> local socket, the socket path name is encoded in the _lpath_ attribute
+ */
 class WobNetworkAccessManager:public QNetworkAccessManager
 {
 protected:
+       ///creates a request for a standard operation
        virtual QNetworkReply * createRequest(Operation op, const QNetworkRequest & req, QIODevice * outgoingData = 0);
 public:
+       ///creates a new Network Access Manager
+       explicit WobNetworkAccessManager(QObject* parent = 0);
+       ///creates a request with a custom operation
        virtual QNetworkReply * sendCustomRequest(const QNetworkRequest & request, const QByteArray & verb, QIODevice * data = 0);
 };
 
 
-class WobScgiNetworkReplyImpl:public QNetworkReply
-{
-public:
-       WobScgiNetworkReplyImpl(const QNetworkRequest&,QByteArray op,QIODevice*, QObject* parent);
-       void abort(){}
-       qint64 bytesAvailable() const {return outcontent.size()-offset;}
-       bool isSequential() const{return true;}
-
-public slots:
-       virtual void ignoreSslErrors();
-    
-protected:
-       qint64 readData(char *data, qint64 maxSize);
-
-private slots:
-       void sockerror(QAbstractSocket::SocketError);
-       void startwrite();
-       void sockabort();
-       void sockread();
-
-private:
-       QByteArray outcontent,incontent,inhead;
-       qint64 offset;
-       QNetworkRequest req;
-       QTcpSocket*sock;
-       QByteArray op;
-       
-       enum ReadMode {HeadRead,ContentRead}readMode;
-};
-
-
 #endif
\ No newline at end of file
diff --git a/qtbase/src/wobnam_p.h b/qtbase/src/wobnam_p.h
new file mode 100644 (file)
index 0000000..2434cd2
--- /dev/null
@@ -0,0 +1,65 @@
+// Copyright (C) 2013 by Konrad Rosenbaum <konrad@silmor.de>
+// protected under the GNU LGPL version 3 or at your option any newer.
+// See COPYING.LGPL file that comes with this distribution.
+//
+
+#ifndef WOB_NAM_P_H
+#define WOB_NAM_P_H
+
+
+#include <QNetworkReply>
+#include <QNetworkRequest>
+#include <QSslSocket>
+#include <QLocalSocket>
+
+/// \internal SCGI client reply
+class WobScgiNetworkReplyImpl:public QNetworkReply
+{
+       Q_OBJECT
+public:
+       /// creates a new reply handler/receiver
+       WobScgiNetworkReplyImpl(QNetworkRequest,QByteArray op,QIODevice*, QObject* parent);
+       ///abort the reception (just closes the stream, does not actually abort anything yet)
+       void abort()override;
+       ///closes the reply stream, call this only if you are no longer interested in the answer
+       void close()override;
+       ///returns the amount of body data available to be read
+       qint64 bytesAvailable() const override;
+       bool isSequential() const override{return true;}
+       ///returns the complete size of the body
+       qint64 size()const override;
+
+public slots:
+       ///relays SSL errors
+       virtual void ignoreSslErrors()override;
+    
+protected:
+       ///implementation of reading body data
+       qint64 readData(char *data, qint64 maxSize) override;
+
+private slots:
+       /// \internal TCP socket errors
+       void sockerror(QAbstractSocket::SocketError);
+       /// \internal Local socket errors
+       void lsockerror(QLocalSocket::LocalSocketError);
+       /// \internal starts writing request data
+       void startwrite();
+       /// \internal the connection was aborted/closed by the peer
+       void sockabort();
+       /// \internal more response data available for reading
+       void sockread();
+
+private:
+       QByteArray outcontent,incontent,inhead;
+       qint64 offset;
+       QIODevice*sock;
+       QByteArray op;
+       
+       enum ReadMode {HeadRead,ContentRead}readMode;
+       
+       /// \internal helper to finish the response and signal the owner
+       void finishUp();
+};
+
+
+#endif
\ No newline at end of file
index 3f942cf..570fde4 100644 (file)
@@ -23,7 +23,8 @@ HEADERS += \
        include/interface.h \
        include/server.h \
        src/server_p.h \
-       src/wobnam.h
+       src/wobnam.h \
+       src/wobnam_p.h
 
 SOURCES += \
        src/object.cpp \
@@ -34,4 +35,6 @@ SOURCES += \
        src/wobnam.cpp
 
 INCLUDEPATH += include src
-DEPENDPATH += include src
\ No newline at end of file
+DEPENDPATH += include src
+
+*g++* { QMAKE_CXXFLAGS += -std=c++11 }
\ No newline at end of file