/**stage the transaction is in*/
enum Stage {
Uninitialized,///<transaction has not started yet
+ Request,///<transaction is running
Success,///<transaction ended successfully
- Error///<transaction ended with an error
+ Error,///<transaction ended with an error
+// Timeout,///<transaction timed out
};
/**returns the stage the transaction is in*/
- virtual Stage stage()const{return m_stage;}
+ virtual Stage stage()const;
/**returns whether the transaction had an error while executing*/
- virtual bool hasError()const{return m_stage==Error;}
+ virtual bool hasError()const;
/**returns the error type (usually the component causing it)*/
- virtual QString errorType()const{return m_errtype;}
+ virtual QString errorType()const;
/**returns the (translated) human readable error string*/
virtual QString errorString()const;
/**returns the interface that is used for the transaction*/
- virtual QString interface()const{return m_iface;}
+ virtual QString interface()const;
+
+ /**deconstructor*/
+ virtual ~WTransaction();
protected:
/**internal: construct the transaction*/
WTransaction(QString iface=QString());
/**internal: execute a query on the web, used by subclasses*/
virtual QByteArray executeQuery(QString,QByteArray);
- /**internal logger class*/
- class WTLog{
- public:
- WTLog(WTransaction*,const QString&);
- ~WTLog();
- void setRequ(const QString&,const QString&,int);
- void setResp(const QString&,const QString&,int);
- void setError(const QString&);
- void setInfo(const QString&);
- private:
- QByteArray req,rsp;
- QString trn;
- WTransaction*parent;
- int lvl;
- };
private slots:
/**internal: triggers when the transaction times out*/
virtual void webTimeout();
/**internal: triggers when the response data is available*/
- virtual void webReady(int,bool);
+ virtual void webReady();
+ /**internal: triggers when the response errors out*/
+ virtual void webError();
signals:
/**this signal is raised when the transaction finished executing*/
void webFinished();
protected:
- friend class WTransaction::WTLog;
- Stage m_stage;
- QString m_errtype,m_errstr,m_iface,m_wobstatus;
- int m_httpid;
- WTLog*m_log;
+ class Log;
+ class Private;
+ friend class WTransaction::Log;
+ Private *d;
};
#endif
//
//
+#include "WTransaction_Private"
#include "WTransaction"
#include "WInterface"
#include <QCoreApplication>
#include <QDebug>
#include <QEventLoop>
-#include <QHttp>
+#include <QNetworkAccessManager>
+#include <QNetworkReply>
+#include <QNetworkRequest>
+#include <QStringList>
#include <QTimer>
#include <QUrl>
WTransaction::WTransaction(QString ifc)
{
- m_stage=Uninitialized;
- m_httpid=-1;
- m_iface=ifc;
- m_log=0;
+ d=new Private;
+ d->m_stage=Uninitialized;
+ d->m_iface=ifc;
+ d->m_log=0;
}
WTransaction::WTransaction(const WTransaction&t)
:WHelper()
{
- m_stage=t.m_stage;
- m_errstr=t.m_errstr;
- m_errtype=t.m_errtype;
- m_iface=t.m_iface;
- m_httpid=-1;
- m_log=0;
+ d=new Private;
+ d->m_stage=t.d->m_stage;
+ d->m_errstr=t.d->m_errstr;
+ d->m_errtype=t.d->m_errtype;
+ d->m_iface=t.d->m_iface;
+ d->m_log=0;
+}
+
+WTransaction::~WTransaction()
+{
+ if(d)delete d;
+ d=0;
}
WTransaction& WTransaction::operator=(const WTransaction&t)
{
- m_stage=t.m_stage;
- m_errstr=t.m_errstr;
- m_errtype=t.m_errtype;
- m_iface=t.m_iface;
- m_httpid=-1;
+ d->m_stage=t.d->m_stage;
+ d->m_errstr=t.d->m_errstr;
+ d->m_errtype=t.d->m_errtype;
+ d->m_iface=t.d->m_iface;
return *this;
}
return r;
}
+static inline QString hd2str(const QList<QNetworkReply::RawHeaderPair>&hl)
+{
+ QString r;
+ for(int i=0;i<hl.size();i++)
+ r+=QString::fromAscii(hl[i].first)+"="+QString::fromAscii(hl[i].second)+"\n";
+ return r;
+}
+
+static QList<QNetworkReply::RawHeaderPair> rqpairs(const QNetworkRequest&req)
+{
+ QList<QByteArray>hds=req.rawHeaderList();
+ QList<QNetworkReply::RawHeaderPair>ret;
+ for(int i=0;i<hds.size();i++)
+ ret<<QNetworkReply::RawHeaderPair(hds[i],req.rawHeader(hds[i]));
+ return ret;
+}
+
QByteArray WTransaction::executeQuery(QString hreq,QByteArray data)
{
- QHttp req;
- connect(&req,SIGNAL(requestFinished(int,bool)),this,SLOT(webReady(int,bool)));
+ QNetworkRequest req;
//set up request
QEventLoop loop(this);
connect(this,SIGNAL(webFinished()),&loop,SLOT(quit()));
- WInterface *iface=WInterface::instance(m_iface);
+ //find interface
+ WInterface *iface=WInterface::instance(d->m_iface);
if(!iface){
- if(m_log)m_log->setError("cannot find interface.");
+ if(d->m_log)d->m_log->setError("cannot find interface.");
else qDebug("Error: transaction cannot find interface.");
- m_stage=Error;
- m_errtype="_iface";
- m_errstr=tr("interface not found");
+ d->m_stage=Error;
+ d->m_errtype="_iface";
+ d->m_errstr=tr("interface not found");
return QByteArray();
}
- connect(&req,SIGNAL(sslErrors(const QList<QSslError>&)),iface,SLOT(sslErrors(const QList<QSslError>&)));
+ //set target
QUrl url=iface->url();
int port=url.port();
if(port<=0){
if(url.scheme().toLower()=="http")port=80;
else port=443;
}
- if(m_log)m_log->setInfo(QString("New HTTP Request %1 URL %2\n\tto host=%3 port=%4 scheme=%5")
+ if(d->m_log)d->m_log->setInfo(QString("New HTTP Request %1 URL %2\n\tto host=%3 port=%4 scheme=%5")
.arg(hreq)
.arg(url.toString())
.arg(url.host())
.arg(port)
.arg(url.scheme()));
- QHttp::ConnectionMode conm;
- if(url.scheme().toLower()=="http")conm=QHttp::ConnectionModeHttp;
- else conm=QHttp::ConnectionModeHttps;
- req.setHost(url.host(),conm,port);
- if(iface->useProxy())req.setProxy(iface->proxyHost(),iface->proxyPort(),iface->proxyUser(),iface->proxyPassword());
- QString pathspec=url.path();
- if(pathspec=="")pathspec="/";
- if(url.encodedQuery()!="")pathspec+="?"+url.encodedQuery();
- QHttpRequestHeader hrh("POST",pathspec);
- QString hostspec=url.host();
- if(url.port()>0)hostspec+=":"+QString::number(port);
- hrh.setValue("Host",hostspec);
+ req.setUrl(url);
+ //set headers
QMap<QString,QString>hdrs=iface->headers(hreq);
QStringList hdrn=hdrs.keys();
for(int i=0;i<hdrn.size();i++)
- hrh.setValue("X-"+hdrn[i],hdrs[hdrn[i]]);
- hrh.setValue("X-WobRequest",hreq);
- hrh.setContentLength(data.size());
- hrh.setContentType("application/x-webobject; charset=UTF-8");
- m_httpid=req.request(hrh,data);
- if(m_log){
- m_log->setRequ(hrh.toString(),esc(data),m_httpid);
- m_log->setInfo(QString("HTTP ID %1").arg(m_httpid));
+ req.setRawHeader("X-"+hdrn[i].toAscii(),hdrs[hdrn[i]].toAscii());
+ req.setRawHeader("X-WobRequest",hreq.toAscii());
+ req.setHeader(QNetworkRequest::ContentLengthHeader,QByteArray::number(data.size()));
+ req.setHeader(QNetworkRequest::ContentTypeHeader,"application/x-webobject; charset=UTF-8");
+ //send it (with body)
+ QNetworkReply*rsp=iface->post(req,data);
+ //log request
+ if(d->m_log){
+ d->m_log->setRequ(hd2str(rqpairs(req)),esc(data));
}
+ //make sure response is deleted
+ QObject rspown;
+ rsp->setParent(&rspown);
+ //we are requesting now...
+ d->m_stage=Request;
/////////////////////////////////////////////////////////////////////
//start loop
QTimer tmr;
tmr.setSingleShot(true);tmr.start(iface->webTimeout()*1000);
connect(&tmr,SIGNAL(timeout()),this,SLOT(webTimeout()));
+ connect(rsp,SIGNAL(finished()),this,SLOT(webReady()));
+ connect(rsp,SIGNAL(error(QNetworkReply::NetworkError)),this,SLOT(webError()));
loop.exec();
tmr.stop();tmr.disconnect(SIGNAL(timeout()),this,SLOT(webTimeout()));
/////////////////////////////////////////////////////////////////////
//process result
- if(m_stage==Error && m_errtype=="_timeout"){
+ if(d->m_stage==Error && d->m_errtype=="_timeout"){
//it did not finish yet, caught a timeout.
- req.abort();
- m_errstr=tr("Web Request timed out.");
- if(m_log)m_log->setError(QString("Request %1 timed out.").arg(m_httpid));
+ rsp->abort();
+ d->m_errstr=tr("Web Request timed out.");
+ if(d->m_log)d->m_log->setError("Request timed out.");
return QByteArray();
}else
//finished with low-level error?
- if(m_stage==Error){
- m_errstr="HTTP Error: "+req.errorString();
- if(m_log)m_log->setError(QString("Request %2 finished with HTTP level error: %1").arg(m_errstr).arg(m_httpid));
+ if(d->m_stage==Error){
+ d->m_errstr="HTTP Error: "+rsp->errorString();
+ if(d->m_log)d->m_log->setError(QString("Request finished with HTTP level error: %1").arg(d->m_errstr));
return QByteArray();
}
//get data
- QHttpResponseHeader rsph=req.lastResponse();
- m_wobstatus=rsph.value("X-WobResponse-Status");
- m_wobstatus=m_wobstatus.replace("\"","").trimmed().toLower();
- QByteArray rspdata=req.readAll();
- if(m_log)m_log->setResp(rsph.toString(),esc(rspdata),m_httpid);
+ d->m_wobstatus=rsp->rawHeader("X-WobResponse-Status");
+ d->m_wobstatus=d->m_wobstatus.replace("\"","").trimmed().toLower();
+ QByteArray rspdata=rsp->readAll();
+ if(d->m_log)d->m_log->setResp(hd2str(rsp->rawHeaderPairs()),esc(rspdata));
//check for high level error
- if(rsph.statusCode()!=200){
- m_errstr=tr("HTTP Error, return code %1 %2") .arg(rsph.statusCode()).arg(rsph.reasonPhrase());
- m_errtype="_HTTP";
- m_stage=Error;
- if(m_log)m_log->setError(QString("Request %2 finished with HTTP level error: %1").arg(m_errstr).arg(m_httpid));
+ int statcode=rsp->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
+ if(statcode!=200){
+ d->m_errstr=tr("HTTP Error, return code %1 %2") .arg(statcode).arg(rsp->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString());
+ d->m_errtype="_HTTP";
+ d->m_stage=Error;
+ if(d->m_log)d->m_log->setError(QString("Request finished with HTTP level error: %1").arg(d->m_errstr));
return QByteArray();
}
- if(m_stage!=Error)m_stage=Success;
+ if(d->m_stage==Request){
+ d->m_stage=Error;
+ d->m_errtype="_internal";
+ d->m_errstr="Ooops: still in Request phase at end of response handling.";
+ }
//remaining high level errors are handled by the generated code
return rspdata;
}
void WTransaction::webTimeout()
{
- if(m_httpid<0)return;
+ if(d->m_stage!=Request)return;
// qDebug("web timeout");
- m_stage=Error;
- m_errtype="_timeout";
+ d->m_stage=Error;
+ d->m_errtype="_timeout";
emit webFinished();
}
-void WTransaction::webReady(int i,bool e)
+void WTransaction::webReady()
{
- if(i!=m_httpid)return;
- if(m_log)m_log->setInfo(QString("finished request %1").arg(i));
- if(e){
- m_stage=Error;
- m_errtype="_web";
- }
+ if(d->m_stage!=Request)return;
+ if(d->m_log)d->m_log->setInfo(QString("finished request"));
+ d->m_stage=Success;
+ d->m_errtype="";
+ emit webFinished();
+}
+
+void WTransaction::webError()
+{
+ if(d->m_stage!=Request)return;
+ if(d->m_log)d->m_log->setInfo(QString("error in request"));
+ d->m_stage=Error;
+ d->m_errtype="_web";
emit webFinished();
}
QString WTransaction::errorString()const
{
- return QCoreApplication::translate("php::",m_errstr.toUtf8(),0,QCoreApplication::UnicodeUTF8);
+ return QCoreApplication::translate("php::",d->m_errstr.toUtf8(),0,QCoreApplication::UnicodeUTF8);
}
+WTransaction::Stage WTransaction::stage()const{return d->m_stage;}
+bool WTransaction::hasError()const{return d->m_stage==Error;}
+QString WTransaction::errorType()const{return d->m_errtype;}
+QString WTransaction::interface()const{return d->m_iface;}
+
-WTransaction::WTLog::WTLog(WTransaction*p,const QString&s)
+WTransaction::Log::Log(WTransaction*p,const QString&s)
{
parent=p;
- parent->m_log=this;
+ parent->d->m_log=this;
trn=s;
- WInterface*in=WInterface::instance(p->m_iface);
+ WInterface*in=WInterface::instance(p->d->m_iface);
if(in)lvl=in->logLevel();
else lvl=WInterface::LogOnError;
if(lvl>WInterface::LogNone)
qDebug("Running transaction %s",s.toAscii().data());
}
-WTransaction::WTLog::~WTLog()
+WTransaction::Log::~Log()
{
- parent->m_log=0;
+ parent->d->m_log=0;
}
-void WTransaction::WTLog::setRequ(const QString&h,const QString&d,int i)
+void WTransaction::Log::setRequ(const QString&h,const QString&d)
{
if(lvl<=WInterface::LogInfo)return;
- QByteArray r="RequestID="+QString::number(i).toAscii()
- +"\n--Header--\n"+h.trimmed().toAscii()
+ QByteArray r="Request"
+ "\n--Header--\n"+h.trimmed().toAscii()
+"\n--Body--\n"+d.trimmed().toAscii()+"\n--End--";
if(lvl==WInterface::LogDetailed)
qDebug("Transaction %s request: %s",trn.toAscii().data(),r.data());
req=r;
}
-void WTransaction::WTLog::setResp(const QString&h,const QString&d,int i)
+void WTransaction::Log::setResp(const QString&h,const QString&d)
{
if(lvl<=WInterface::LogInfo)return;
- QByteArray r="RequestID="+QString::number(i).toAscii()
- +"\n--Header--\n"+h.trimmed().toAscii()
+ QByteArray r="Response"
+ "\n--Header--\n"+h.trimmed().toAscii()
+"\n--Body--\n"+d.trimmed().toAscii()+"\n--End--";
if(lvl==WInterface::LogDetailed)
qDebug("Transaction %s response: %s",trn.toAscii().data(),r.data());
rsp=r;
}
-void WTransaction::WTLog::setInfo(const QString&s)
+void WTransaction::Log::setInfo(const QString&s)
{
if(lvl>=WInterface::LogInfo)
qDebug("Transaction %s info: %s",trn.toAscii().data(),s.toAscii().data());
}
-void WTransaction::WTLog::setError(const QString&s)
+void WTransaction::Log::setError(const QString&s)
{
//bail out if no logging
if(lvl<WInterface::LogMinimal)return;
QString hcd;
QString scd;
hcd="#include \""+m_transbase+"\"\n";
+ hcd+="#include \"WTransaction_Private\"\n";
scd+="#include \"WInterface\"\n#include <QCoreApplication>\n";
for(int i=0;i<in.size();i++){
QString tp=qtobjtype(trn,in[i],WocQtOut::In);
//query method implemented
scd+="void "+cn+"::netquery()\n{\n";
- scd+="\tWTLog log(this,\""+trn.name()+"\");\n";
+ scd+="\tLog log(this,\""+trn.name()+"\");\n";
scd+="\tQDomDocument doc;QDomElement root=doc.createElement(\"WobRequest-"+trn.name()+"\");\n";
scd+="\tQDomElement tmp;\n";
- scd+="\tWInterface *iface=WInterface::instance(m_iface);\n";
- scd+="\tif(iface==0){m_errtype=\"_iface\";m_errstr=\"interface not found\";m_stage=Error;log.setError(m_errstr);return;}\n";
+ scd+="\tWInterface *iface=WInterface::instance(d->m_iface);\n";
+ scd+="\tif(iface==0){d->m_errtype=\"_iface\";d->m_errstr=\"interface not found\";d->m_stage=Error;log.setError(d->m_errstr);return;}\n";
//encode input
scd+=trnInput(trn);
scd+="\tdoc.appendChild(root);\n";
code+="\tdoc=QDomDocument();\n";
code+="\tQString emsg;int eln,ecl;\n";
code+="\tif(!doc.setContent(rba,&emsg,&eln,&ecl)){\n";
- code+="\t\tm_stage=Error;m_errtype=\"_iface\";m_errstr=QString(QCoreApplication::translate(\"WobTransaction\",\"XML result parser error line %1 col %2: %3\")).arg(eln).arg(ecl).arg(emsg);\n\t\tlog.setError(m_errstr);\n\t\treturn;\n\t}\n";
+ code+="\t\td->m_stage=Error;d->m_errtype=\"_iface\";d->m_errstr=QString(QCoreApplication::translate(\"WobTransaction\",\"XML result parser error line %1 col %2: %3\")).arg(eln).arg(ecl).arg(emsg);\n\t\tlog.setError(d->m_errstr);\n\t\treturn;\n\t}\n";
code+="\troot=doc.documentElement();\n";
//decide where to go, error handling
- code+="\tif(m_wobstatus!=\"ok\"){\n\t\tm_stage=Error;m_errtype=\"_server\";m_errstr=\"unknown server error\";\n";
+ code+="\tif(d->m_wobstatus!=\"ok\"){\n\t\td->m_stage=Error;d->m_errtype=\"_server\";d->m_errstr=\"unknown server error\";\n";
code+="\t\tQString terr=root.text().trimmed();\n";
- code+="\t\tif(terr==\"\"){\n\t\t\tlog.setError(m_errstr);\n\t\t\treturn;\n\t\t}\n";
- code+="\t\tm_errtype=root.attribute(\"type\",\"_server\");\n";
- code+="\t\tm_errstr=terr;\n\t\tlog.setError(m_errstr);\n\t\treturn;\n\t}\n";
+ code+="\t\tif(terr==\"\"){\n\t\t\tlog.setError(d->m_errstr);\n\t\t\treturn;\n\t\t}\n";
+ code+="\t\td->m_errtype=root.attribute(\"type\",\"_server\");\n";
+ code+="\t\td->m_errstr=terr;\n\t\tlog.setError(d->m_errstr);\n\t\treturn;\n\t}\n";
code+="\tQList<QDomElement> nl;\n";
//parse parameters
for(int i=0;i<sl.size();i++){
if(trn.isListType(t)){
code+="\tfor(int i=0;i<nl.size();i++){\n";
if(trn.isObjectType(t)){
- code+="\t\ttry{out_"+sl[i]+".append("+qtobjtype(trn,sl[i],WocQtOut::Out)+"(nl.at(i).toElement()));}catch(WException e){m_stage=Error;m_errtype=e.component();m_errstr=e.error();log.setError(m_errstr);}\n";
+ code+="\t\ttry{out_"+sl[i]+".append("+qtobjtype(trn,sl[i],WocQtOut::Out)+"(nl.at(i).toElement()));}catch(WException e){d->m_stage=Error;d->m_errtype=e.component();d->m_errstr=e.error();log.setError(d->m_errstr);}\n";
}else if(trn.isIntType(t)){
code+="\t\tout_"+sl[i]+".append(nl.at(i).toElement().text().toInt());\n";
}else if(trn.isBoolType(t)){
}else{
code+="\tif(nl.size()>0){\n";
if(trn.isObjectType(t)){
- code+="\t\ttry{out_"+sl[i]+"="+qtobjtype(trn,sl[i],WocQtOut::Out)+"(nl.at(0).toElement());}catch(WException e){m_stage=Error;m_errtype=e.component();m_errstr=e.error();log.setError(m_errstr);}\n";
+ code+="\t\ttry{out_"+sl[i]+"="+qtobjtype(trn,sl[i],WocQtOut::Out)+"(nl.at(0).toElement());}catch(WException e){d->m_stage=Error;d->m_errtype=e.component();d->m_errstr=e.error();log.setError(d->m_errstr);}\n";
}else if(trn.isBlobType(t)){
code+="\t\tout_"+sl[i]+"=QByteArray::fromBase64(nl.at(0).toElement().text().toAscii());\n";
}else{//can only be string