i am trying to create an API request to an AWS API, right now i fix the first issue that is related to generate de signature version 4, but no matter what i do, AWS response tell me that the signature is not welll generated, i already check the signature using nodejs code and works, according what i see and what AWS SUpport say, it is possible that some request headers are missing.
I am adding my QT Code i know that is not the best but is only an example, the function that i using to make the post request is loginWekall
#include "hash.h"
#include <QDateTime>
#include <QJsonObject>
#include <QJsonDocument>
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QBuffer>
#include <QNetworkReply>
CryptoHash::CryptoHash(QObject *parent) : QObject(parent)
{
}
QString CryptoHash::getKey(){
return this->key;
}
void CryptoHash::setKey(QString key){
this->key = key;
}
void CryptoHash::setSecret(QString secret){
this->secret = secret;
}
void CryptoHash::setRegion(QString region){
this->region = region;
}
void CryptoHash::setMethod(QString method){
this->method = method;
}
void CryptoHash::setEndpoint(QString endpoint){
this->endpoint = endpoint;
}
void CryptoHash::setHost(QString host){
this->host = host;
}
void CryptoHash::setURL(QString URL){
this->url = URL;
}
QString CryptoHash::getHash() {
return this->hashvalue;
}
const QByteArray CryptoHash::getSignatureKey(QString datestamp) {
QMessageAuthenticationCode code(QCryptographicHash::Sha256);
code.setKey(datestamp.toUtf8());
code.addData("AWS4" + this->key.toUtf8());
QByteArray kDate = code.result();
code.reset();
code.setKey(this->region.toUtf8());
code.addData(kDate);
QByteArray kRegion = code.result();
code.reset();
code.setKey(this->service.toUtf8());
code.addData(kRegion);
QByteArray kService = code.result();
code.reset();
code.setKey("aws4_request");
code.addData(kService);
const QByteArray kSignin = code.result();
return kSignin;
}
QByteArray CryptoHash::algorithmDesignation(const QCryptographicHash::Algorithm algorithm) {
switch (algorithm) {
case QCryptographicHash::Md4: return "AWS4-HMAC-MD4";
case QCryptographicHash::Md5: return "AWS4-HMAC-MD5";
case QCryptographicHash::Sha1: return "AWS4-HMAC-SHA1";
case QCryptographicHash::Sha224: return "AWS4-HMAC-SHA224";
case QCryptographicHash::Sha256: return "AWS4-HMAC-SHA256";
case QCryptographicHash::Sha384: return "AWS4-HMAC-SHA384";
case QCryptographicHash::Sha512: return "AWS4-HMAC-SHA512";
default:
Q_ASSERT_X(false, Q_FUNC_INFO, "invalid algorithm");
return "invalid-algorithm";
}
}
QByteArray CryptoHash::canonicalHeader(const QByteArray &headerName, const QByteArray &headerValue) {
QByteArray header = headerName.toLower() + ':';
const QByteArray trimmedHeaderValue = headerValue.trimmed();
bool isInQuotes = false;
char previousChar = '\0';
for (int index = 0; index < trimmedHeaderValue.size(); ++index) {
char thisChar = trimmedHeaderValue.at(index);
header += thisChar;
if (isInQuotes) {
if ((thisChar == '"') && (previousChar != '\\'))
isInQuotes = false;
} else {
if ((thisChar == '"') && (previousChar != '\\')) {
isInQuotes = true;
} else if (isspace(thisChar)) {
while ((index < trimmedHeaderValue.size()-1) &&
(isspace(trimmedHeaderValue.at(index+1))))
++index;
}
}
previousChar = thisChar;
}
return header;
}
QByteArray CryptoHash::canonicalHeaders(const QNetworkRequest &request, QByteArray * const signedHeaders) {
Q_CHECK_PTR(signedHeaders);
signedHeaders->clear();
QMap<QByteArray,QByteArray> headers;
foreach (const QByteArray &rawHeader, request.rawHeaderList()) {
headers.insert(rawHeader.toLower(), request.rawHeader(rawHeader));
}
headers.insert("host", request.url().host().toUtf8());
headers.insert("x-amz-date", this->amz.toUtf8());
QByteArray canonicalHeaders;
for (QMap<QByteArray,QByteArray>::const_iterator iter = headers.constBegin(); iter != headers.constEnd(); ++iter) {
canonicalHeaders += canonicalHeader(iter.key(), iter.value()) + '\n';
if (!signedHeaders->isEmpty()) *signedHeaders += ';';
*signedHeaders += iter.key();
}
return canonicalHeaders;
}
QByteArray CryptoHash::canonicalRequest(const QNetworkAccessManager::Operation operation,
const QNetworkRequest &request,
const QByteArray &payload,
QByteArray * const signedHeaders) {
//qInfo() <<"payload";
//qInfo() << payload;
//qInfo() << QCryptographicHash::hash(payload, QCryptographicHash::Sha256).toHex();
return this->method.toUtf8() + '\n' +
this->endpoint.toUtf8() + '\n' +
"\n" + canonicalHeaders(request, signedHeaders) + '\n' +
*signedHeaders + '\n' +
QCryptographicHash::hash(payload, QCryptographicHash::Sha256).toHex();
}
QByteArray CryptoHash::credentialScope(const QDateTime &date, const QString ®ion, const QString &service) {
return this->dstamp.toUtf8() + '/' + region.toUtf8() + '/' + service.toUtf8() + "/aws4_request";
}
QByteArray CryptoHash::stringToSign(const QByteArray &algorithmDesignation, const QString &requestDate,
const QByteArray &credentialScope, const QByteArray &canonicalRequest) {
return algorithmDesignation + '\n' +
this->amz.toUtf8() + '\n' +
credentialScope + '\n' +
QCryptographicHash::hash(canonicalRequest, QCryptographicHash::Sha256).toHex();
}
QByteArray CryptoHash::signingKey( const QDate &date,
const QString ®ion, const QString &service) {
const QCryptographicHash::Algorithm hashAlgorithm = QCryptographicHash::Sha256;
return QMessageAuthenticationCode::hash("aws4_request",
QMessageAuthenticationCode::hash(service.toUtf8(),
QMessageAuthenticationCode::hash(region.toUtf8(),
QMessageAuthenticationCode::hash(this->dstamp.toUtf8(), "AWS4"+this->secret.toUtf8(),
hashAlgorithm), hashAlgorithm), hashAlgorithm), hashAlgorithm);
}
void debugRequest(QNetworkRequest request, QByteArray data = QByteArray())
{
qDebug() << request.url().toString();
const QList<QByteArray>& rawHeaderList(request.rawHeaderList());
foreach (QByteArray rawHeader, rawHeaderList) {
qDebug() << request.rawHeader(rawHeader);
}
qDebug() << data;
}
QString CryptoHash::loginWekall(const QString AWSV4, const QString timestamp){
QNetworkAccessManager *manager = new QNetworkAccessManager(this);
QUrl url("https://asdasdasd.execute-api.us-east-1.amazonaws.com/master/users/login");
QNetworkRequest request(url);
QJsonObject obj;
obj["email"] = "user#test.co";
obj["password"] = "password";
QJsonDocument doc(obj);
QByteArray data = doc.toJson();
// FIXME for debug
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
request.setRawHeader("Content-Type", "application/json");
request.setRawHeader("Authorization", AWSV4.toUtf8());
request.setRawHeader("X-Amz-Date", this->amz.toUtf8());
request.setRawHeader("Host","g298zqijvh.execute-api.us-east-1.amazonaws.com");
request.setRawHeader("Accept","application/json, text/plain, */*");
qDebug() << request.url().toString();
const QList<QByteArray>& rawHeaderList(request.rawHeaderList());
foreach (QByteArray rawHeader, rawHeaderList) {
qDebug() << request.rawHeader(rawHeader);
}
qDebug() << data;
QNetworkReply *reply = manager->post(request, data);
QObject::connect(reply, &QNetworkReply::finished, [=](){
if(reply->error() == QNetworkReply::NoError){
QString contents = QString::fromUtf8(reply->readAll());
qDebug() << contents;
}
else{
qInfo()<< QString::fromUtf8(reply->readAll());
QString err = reply->errorString();
qDebug() << err;
}
reply->deleteLater();
});
return "true";
}
QString CryptoHash::getDate(){
return this->amz;
}
QString CryptoHash::hash(){
const QDateTime datetime;
QDateTime* dtime = new QDateTime();
QString amzdate = dtime->currentDateTimeUtc().toString("yyyyMMddThhmmssZ").toUtf8();
QString datestamp = dtime->currentDateTimeUtc().toString("yyyyMMdd").toUtf8();
QJsonObject data;
this->amz = amzdate;
//this->amz = "20210306T142037Z";
this->dstamp = datestamp;
qInfo() <<"amztime";
qInfo() << amzdate;
qInfo() <<"date";
qInfo() << datestamp;
data["email"] = "user#test.co";
data["password"] = "password";
QJsonDocument doc(data);
QByteArray strData(doc.toJson(QJsonDocument::Compact));
qInfo() << "JSON parameters";
qInfo() << strData;
const QByteArray algorithmDesignation = this->algorithmDesignation(QCryptographicHash::Sha256);
const QString endpoint = this->endpoint;
QString signa = "host;x-amz-date";;
const QByteArray credentialScope = this->credentialScope(datetime, this->region, this->service);
QString access = "ACCESSKEYID/";
this->scope = access.toUtf8() + this->dstamp+ "/us-east-1/execute-api/aws4_request";
qInfo() << "credentialScope";
qInfo() << credentialScope;
QByteArray signedHeaders = signa.toUtf8();
qInfo() << "signedHeaders";
qInfo() << signedHeaders;
const QUrl URL("https://asdasd.execute-api.us-east-1.amazonaws.com/master/users/login");
const QNetworkRequest request(URL);
const QByteArray canonicalRequest = this->canonicalRequest(QNetworkAccessManager::Operation::PostOperation, request, strData, &signedHeaders);
qInfo() << "canonical request";
qInfo() << canonicalRequest;
const QByteArray stringToSign = this->stringToSign(algorithmDesignation, this->amz, credentialScope, canonicalRequest);
qInfo() << "stringToSign";
qInfo() << stringToSign;
const QByteArray signingKey = this->signingKey(datetime.date(), this->region, this->service);
qInfo() << "signinKey";
qInfo() << signingKey;
const QByteArray signature = QMessageAuthenticationCode::hash(stringToSign, signingKey, QCryptographicHash::Sha256);
qInfo() << "signature";
qInfo() << signature;
this->hashvalue = algorithmDesignation + " Credential=" + this->key.toUtf8() + '/' + credentialScope +
", SignedHeaders=" + signedHeaders + ", Signature=" + signature.toHex();
qInfo() << "AWS SIGNATURE VERSION 4";
qInfo() << this->hashvalue;
this->loginWekall(hashvalue, this->amz);
return 0;
}
CryptoHash::~CryptoHash(){};
Related
I used a DownloadManager class like that but the problem is that for large file I have a wrong size 65536KB instead of 51328022 bytes. There are something wrong in the saveDisk method.
ps: I used Qt 5.6 cuz I need to run the app on Windows Vista & XP
bool DownloadManager::saveToDisk(const QString &filename, QIODevice *data)
{
LogManager* logmgr = LogManager::GetInstance();
FileManager* filemgr = FileManager::GetInstance();
QFileInfo fileinfo(filename);
QString dirpath = fileinfo.absoluteDir().absolutePath();
if (!filemgr->DirectoryIsPresent(dirpath))
{
if (!filemgr->CreateDirectory(dirpath)) {
logmgr->Log(LOG_LEVEL_ERROR, QString("cannot create directory (") + dirpath + ")");
}
}
QFile file(filename);
if (!file.open(QIODevice::WriteOnly)) {
const QString errorText = QString("Could not open ") + filename + QString(" for writing:") + file.errorString();
logmgr->Log(LOG_LEVEL_ERROR, errorText);
return false;
}
file.write(data->readAll());
file.close();
return true;
}
void DownloadManager::downloadFinished(QNetworkReply *reply)
{
LogManager* logmgr = LogManager::GetInstance();
if (m_currentDownloads.contains(reply))
{
QUrl url = reply->url();
if (reply->error() != QNetworkReply::NoError) {
m_nbFailedDownload++;
const QString errorText = QString("Download of ")+ url.toString() +" failed: " + reply->errorString() + " (" + QString::number(reply->error()) + ")";
logmgr->Log(LOG_LEVEL_ERROR, errorText);
} else {
m_nbSucceededDownload++;
QString filename = saveFileName(url);
if (saveToDisk(filename, reply))
{
const QString infoText = QString("Download of ") + url.toString() + " succeeded (saved to " + filename + ")";
logmgr->Log(LOG_LEVEL_INFO, infoText);
}
}
m_currentDownloads.removeAll(reply);
reply->deleteLater();
}
int total = m_nbTotalDownload == 0? 1:m_nbTotalDownload;
emit onProgress((m_nbFailedDownload + m_nbSucceededDownload) * 100 / total);
if (m_currentDownloads.isEmpty()){
logmgr->Log(LOG_LEVEL_INFO, "DownloadManager downloads finished");
emit onFinished();
}
}
You should call DownloadManager::saveToDisk by signal QNetworkReply::readyRead
void DownloadManager::onDownloadReadyRead( QNetworkReply *reply )
{
...
saveToDisk( filename, reply );
...
}
void DownloadManager::saveToDisk( QNetworkReply* reply )
{
...
file.write( reply->read( reply_->bytesAvailable() ) );
...
}
It's ok. I replaced this line :
file.write(data->readAll());
file.close();
return true;
by
ByteArray ba = data->readAll();
int nbTries = 99;
int n = ba.size();
int idx = 0;
while (n > 0 && nbTries > 0)
{
int ret = file.write(ba.data() + idx, std::min(16*1024, n));
if (ret > 0)
{
n -= ret;
idx += ret;
}
else
{
nbTries --;
}
}
file.close();
return nbTries > 0;
I wrote several chunks instead of one big
I'm trying to find a solution that in case that a validation is not successful because there is more then one error in the XML file the Qt MessageHandler(line, column, description etc. ) is able to show every error in the XML data not just the first one that occurs in the XML file.
Example:
I have an error in line: 65 (see pic)
but there are also errors in line :78,83,95 but it dose not show it only shows the first one.
Is there a solution for this case? And if yes how?
My Code looks like this:
MessageHandler messageHandler;
QFile xsdfile("....xsd");
xsdfile.open(QIODevice::ReadOnly);
QXmlSchema schema;
schema.setMessageHandler(&messageHandler);
bool errorOccurred = false;
if (schema.load(&xsdfile, QUrl::fromLocalFile(xsdfile.fileName())) == false)
errorOccurred = true;
else
{
QXmlSchemaValidator xmlvalidator(schema);
QFile xmlfile("......xml");
xmlfile.open(QIODevice::ReadOnly);
if (!xmlvalidator.validate(&xmlfile, QUrl::fromLocalFile(xmlfile.fileName())))
errorOccurred = true;
xmlfile.close();
}
xsdfile.close();
if (errorOccurred) {
QString qs = messageHandler.statusMessage();
cout << "Line: " << messageHandler.line() << "\n" << "Row: " << messageHandler.column() << "\n" << "ErrorMessage: ";
std::cout << qs.toUtf8().constData() << std::endl;
return -1;
}
else {
return 0;
}
And my MessageHandler class looks like this:
class MessageHandler : public QAbstractMessageHandler
{
public:
MessageHandler()
: QAbstractMessageHandler(0)
{
}
QString statusMessage() const
{
return m_description;
}
int line() const
{
return m_sourceLocation.line();
}
int column() const
{
return m_sourceLocation.column();
}
protected:
virtual void handleMessage(QtMsgType type, const QString &description,
const QUrl &identifier, const QSourceLocation &sourceLocation)
{
Q_UNUSED(type);
Q_UNUSED(identifier);
m_description = description;
m_sourceLocation = sourceLocation;
}
private:
QString m_description;
QSourceLocation m_sourceLocation;
};
Thanks :)
I think you need to write a custom validator to catch all errors. You can modify this example code to catch all errors.
struct NodeProperties
{
NodeProperties()
{
values.resize(names.size());
}
std::vector<int> values;
static QStringList names;
static QString rootName;
static QString childName;
};
QString NodeProperties::rootName = "static_route";
QString NodeProperties::childName = "molenet";
QStringList NodeProperties::names = QList<QString>() << "node_id"
<< "nextHop_id"
<< "sink_id"
<< "delay_before_send"
<< "delay_before_sleep";
QList<NodeProperties> nodesProperties;
bool parseXMLFile()
{
QXmlStreamReader xmlStreamReader;
QString xmlFile;
xmlFile = QFileDialog::getOpenFileName(this,
tr("Open XML file"), QDir::currentPath(), tr("XML (*.xml)"));
if(xmlFile.isEmpty())
return false;
QFile file(xmlFile);
if (!file.open(QFile::ReadOnly | QFile::Text))
{
QMessageBox::warning(this,tr("Error: Cannot read file ..."),
(xmlFile+" " + file.errorString() ));
return false;
}
nodesProperties.clear();
xmlStreamReader.setDevice(&file);
xmlStreamReader.readNext();
while(!xmlStreamReader.atEnd())
{
if(xmlStreamReader.isStartElement())
{
if(xmlStreamReader.name() == NodeProperties::rootName)
xmlStreamReader.readNext();
if(xmlStreamReader.name() == NodeProperties::childName)
{
xmlStreamReader.readNext();
if(xmlStreamReader.isCharacters() )
xmlStreamReader.readNext();
if(xmlStreamReader.isStartElement())
{
NodeProperties prop;
for(int i = 0; i < NodeProperties::names.size(); ++i)
{
if(xmlStreamReader.isCharacters() )
{
xmlStreamReader.readNext();
--i;
continue;
}
if(xmlStreamReader.name() == NodeProperties::names[i])
{
prop.values[i] = xmlStreamReader.readElementText().toInt();
}
xmlStreamReader.readNext();
}
nodesProperties.push_back(prop);
}
}
}
xmlStreamReader.readNext();
}
file.close();
if (xmlStreamReader.hasError())
{
QMessageBox::warning(this,tr("Error: Failed to parse file "),
(xmlFile+" " + xmlStreamReader.errorString() ));
nodesProperties.pop_back();
}
else if (file.error() != QFile::NoError)
{
qDebug() << "Error: Cannot read file " << qPrintable(xmlFile)
<< ": " << qPrintable(file.errorString());
}
//createGraphVizFile(xmlFile);
this->xmlFile = xmlFile;
return true;
}
The XML file is:
<?xml version="1.0"?>
<static_route>
<molenet>
<node_id> 1 </node_id>
<nextHop_id> 2 </nextHop_id>
<sink_id> 254 </sink_id>
<delay_before_send> 2 </delay_before_send>
<delay_before_sleep> 30 </delay_before_sleep>
</molenet>
<molenet>
<node_id> 2 </node_id>
<nextHop_id> 3 </nextHop_id>
<sink_id> 254 </sink_id>
<delay_before_send> 4 </delay_before_send>
<delay_before_sleep> 30 </delay_before_sleep>
</molenet>
</static_route>
I'd like to save custom class to XML using QSettings. But I always get XML without structure members.
#include <QCoreApplication>
#include <QtCore/qdatastream.h>
#include <qxmlstream.h>
#include <qdebug.h>
#include <QtCore/QSettings>
#include <QMetaType>
struct Interface_struct
{
QString name;
QString ip;
};
Q_DECLARE_METATYPE(Interface_struct)
QDataStream& operator <<(QDataStream& out, const Interface_struct& s)
{
out << s.name << s.ip;
return out;
}
QDataStream& operator >>(QDataStream& in, Interface_struct& s)
{
in >> s.name;
in >> s.ip;
return in;
}
static bool readXmlFile(QIODevice &device, QSettings::SettingsMap &map)
{
qDebug()<< "read";
QXmlStreamReader reader(&device);
QString key;
while(!reader.atEnd())
{
reader.readNext();
if( reader.isStartElement() && reader.tokenString() != "Settings")
{
if( reader.text().isNull() )
{
// key = Settings
if(key.isEmpty())
{
key = reader.tokenString();
}
// key = Settings/Intervall
else
{
key += "/" + reader.tokenString();
}
}
else
{
map.insert(key, reader.text().data());
}
}
}
return true;
}
static bool writeXmlFile(QIODevice &device, const QSettings::SettingsMap &map)
{
qDebug()<< "write";
QXmlStreamWriter writer(&device);
writer.writeStartDocument("1.0");
writer.writeStartElement("Settings");
foreach(QString key, map.keys())
{
foreach(QString elementKey, key.split("/"))
{
writer.writeStartElement(elementKey);
}
writer.writeCharacters(map.value(key).toString());
writer.writeEndElement();
}
writer.writeEndElement();
writer.writeEndDocument();
return true;
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
qRegisterMetaType<Interface_struct>("Interface_struct");
qRegisterMetaTypeStreamOperators<Interface_struct>("Interface_struct");
{
Interface_struct s;
s.name = QString("br03000");
s.ip = QString("172.16.222.5");
const QSettings::Format xml_format =
QSettings::registerFormat("xml", readXmlFile, writeXmlFile);
if(xml_format == QSettings::InvalidFormat)
{
qDebug() << "InvalidFormat!";
return 0;
}
QSettings::setPath(xml_format, QSettings::UserScope, "/home/farit/test/");
QSettings settings(xml_format, QSettings::UserScope, "xml_cfg");
settings.setValue("network", QVariant::fromValue(s));
}
{
QSettings::Format xml_format =
QSettings::registerFormat("xml", readXmlFile, writeXmlFile);
QSettings::setPath(xml_format, QSettings::UserScope, "/home/farit/test/");
QSettings settings(xml_format, QSettings::UserScope, "xml_cfg");
QVariant value = settings.value("network");
Interface_struct interface = value.value<Interface_struct>();
qDebug() << "TEST: " << interface.name << interface.ip;
}
return 0;
}
I get this output:
read
write
read
TEST: "" ""
Press <RETURN> to close this window...
And XML looks like this:
<?xml version="1.0" encoding="UTF-8"?><Settings><network></network></Settings>
How can I save structure members of custom class to XML using QSettings?
UPDATE: I'm sorry, I forgot to mention, that is supposed to be done in Qt4.
this is my method and it does not work.
void ApplicationUI::Post(const QString &id,const QString &name,const QString &surname,const QString &grade, const QString &dob,const QString &language,const QString &school, const QString &gender,const QString &cellno,const QString ®istrationDate)
{
this->connection = new QNetworkAccessManager(this);
connect(this->connection, SIGNAL(finished(QNetworkReply*)), this, SLOT(postFinished()));
QUrl url("http://172.27.15.7/BB10_Scripts/Registration.php");
QUrl postParams;
postParams.addQueryItem("id",id);
postParams.addQueryItem("name", name);
postParams.addQueryItem("surname",surname);
postParams.addQueryItem("grade",grade);
postParams.addQueryItem("dob",dob);
postParams.addQueryItem("language",language);
postParams.addQueryItem("school",school);
postParams.addQueryItem("gender",gender);
postParams.addQueryItem("cellno",cellno);
postParams.addQueryItem("registrationDate",registrationDate);
QByteArray data;
data.append(postParams.data);
data.remove(0,1);
QNetworkRequest req;
req.setUrl(url);
req.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
this->connection->post(req,postParams.encodedQuery());());
bool ok = QObject::connect(connection, SIGNAL(finished(QNetworkReply*)), this, SLOT(postFinished()));
Q_ASSERT(ok);
Q_UNUSED(ok);
qDebug() << "Connection is success : ? : " << ok;
}
i get the message
Connection is success : ? : true
Process 476930267 (CascadesProject) terminated SIGSEGV code=1 fltno=11 >ip=79f70ce2 >(/base/usr/lib/qt4/lib/libQtNetwork.so.4.8.5#_ZNK13QNetworkReply5errorEv+0x9) >mapaddr=00060ce2. ref=00000004 bdslot=1
postParams.addQueryItem("id", id);
postParams.addQueryItem("name", name);
postParams.addQueryItem("surname", surname);
postParams.addQueryItem("grade", grade);
postParams.addQueryItem("dob", dob);
postParams.addQueryItem("language", language);
postParams.addQueryItem("school", school);
postParams.addQueryItem("gender", gender);
postParams.addQueryItem("cellno", cellno);
postParams.addQueryItem("registrationDate",registrationDate);
QNetworkRequest request;
request.setUrl(QUrl("http://172.27.15.7/BB10_Scripts/Registration.php?"));
bool res;
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
Q_UNUSED(res);
this->reply = connection->post(request, postParams.encodedQuery());
res = QObject::connect(this->reply, SIGNAL(finished()),this, SLOT(postFinished()));
Q_ASSERT(res);
Q_UNUSED(res);
qDebug() << "Connection is success : ? : " << res;
I am attempting to build my own custom QAbstractNetworkCache implementation for use with QNetworkAccessManager.
I am having trouble with QAbstractNetworkCache::insert(QIODevice *device); inside this method, the device always arrives with 0 bytes to read from.
As I understand, the QIODevice* that is returned from QAbstractNetworkCache::prepare(const QNetworkCacheMetaData &metaData) is going to be filled with data and used as a parameter to QAbstractNetworkCache::insert(QIODevice *device) method once QNetworkAccessManager finishes downloading.
So I have prepared a QBuffer to be this container, but whenever QAbstractNetworkCache::insert(QIODevice *device) is getting called, it always arrives with nothing in it (device->bytesAvailable() == 0)
QIODevice* NetworkCachePrivate::prepare(const QNetworkCacheMetaData &metaData) {
if (!metaData.isValid() || !metaData.url().isValid() || cacheDir.isEmpty()) return 0;
QIODevice* device = 0;
QString hash = hexMD5(metaData.url().toString());
QScopedPointer<QBuffer> buffer(new QBuffer);
if (buffer->open(QIODevice::ReadWrite))
{
qDebug() << "BUFFER READY";
device = buffer.take();
deviceMapping[device] = qMakePair(hash, metaData);
}
return device;
}
void NetworkCachePrivate::insert(QIODevice *device) {
if (deviceMapping.contains(device))
{
QPair<QString, QNetworkCacheMetaData> pair = deviceMapping[device];
QString fileName;
fileName += cacheDir;
fileName += QLatin1String("/");
fileName += pair.first;
qDebug() << "DEVICE BYTES" << device->bytesAvailable(); //ALWAYS 0!!!! :(
QFile file(fileName);
if (file.open(QIODevice::WriteOnly))
{
qint64 size = file.write(device->readAll());
if (size <= 0)
{
file.remove();
}
else
{
qDebug() << "FILE WROTE " << size;
cacheSize += size;
}
}
deviceMapping.remove(device);
delete device;
}
}
QNetworkCacheMetaData NetworkCachePrivate::metaData (const QUrl &url ) {
QString fileName;
fileName += cacheDir;
fileName += QLatin1String("/");
fileName += hexMD5(url.toString());
QNetworkCacheMetaData data;
if (!QFile::exists(fileName))
return data;
data.setUrl(url);
data.setExpirationDate(QDateTime::currentDateTime().addYears(1));
return data;
}
As peppe also pointed out in the comment, you need to seek the QBuffer to the beginning after the write and before the read as per the documentation:
QBuffer allows you to access a QByteArray using the QIODevice interface. The QByteArray is treated just as a standard random-accessed file. Example:
QBuffer buffer;
char ch;
buffer.open(QBuffer::ReadWrite);
buffer.write("Qt rocks!");
*** buffer.seek(0); ***
buffer.getChar(&ch); // ch == 'Q'
buffer.getChar(&ch); // ch == 't'
buffer.getChar(&ch); // ch == ' '
buffer.getChar(&ch); // ch == 'r'
To be more concrete for your code, you would need to insert the statement like this:
...
device.seek(0);
qDebug() << "DEVICE BYTES" << device->bytesAvailable(); //NOT 0 ANYMORE!!!! :)
...