I'm trying to send a POST request to an HTTPS server (Jazz server) which expects a client certificate. In particular, this request concerns a login attempt to a Jazz server.
I'm using Qt 6.3.1 and my project is developed in C++.
I've tried many solutions but I couldn't get any response from the server: this is the result of QNetworkReply::error() function
Error: QNetworkReply::ProtocolInvalidOperationError - "Error transferring https://rtc.***.com/qm/authenticated/j_security_check - server replied: Bad Request"
One of my latest implementations is the following:
const static QString BASE_URL = "https://rtc.***.com/";
const static QString QM_AUTH_URL = "qm/authenticated/j_security_check";
void MainWindow::login(QString u, QString p) {
uname = u;
pwd = p;
netManager = new QNetworkAccessManager(this);
// Read the SSL certificate
QFile file("./myCert.cer");
if(!file.open(QIODevice::ReadOnly)) {
qDebug()<<"File opening error!";
return;
}
// Create a certificate object
const QSslCertificate certificate(file.readAll());
// Add this certificate to all SSL connections
QSslConfiguration conf = QSslConfiguration::defaultConfiguration();
conf.addCaCertificate(certificate);
QSslConfiguration::setDefaultConfiguration(conf);
sslsock = new QSslSocket(this);
if(!sslsock->isOpen() || !sslsock->isEncrypted() || !sslsock->isValid()) {
connect(sslsock, &QSslSocket::sslErrors, this, &MainWindow::printSslErrors);
connect(sslsock, &QSslSocket::encrypted, this, &MainWindow::socketConnected);
sslsock->setLocalCertificate(certificate);
sslsock->setSslConfiguration(conf);
sslsock->connectToHostEncrypted("rtc.***.com", 443);
} else {
socketConnected();
}
void MainWindow::printSslErrors(const QList<QSslError> &err) { //never called
for(int i=0; i<err.size(); i++) {
qDebug()<<"Error "<<(i+1)<<": "<<err[i].error()<<" - "<<err[i].errorString();
}
sslsock->ignoreSslErrors(err);
}
void MainWindow::socketConnected() {
qDebug()<<"SSL handshake completed!";
qDebug()<<"sslsock->isEncrypted(): "<<sslsock->isEncrypted();
qDebug()<<"sslsock->isOpen(): "<<sslsock->isOpen();
qDebug()<<"sslsock->isReadable(): "<<sslsock->isReadable();
qDebug()<<"sslsock->isValid(): "<<sslsock->isValid();
qDebug()<<"sslsock->isWritable(): "<<sslsock->isWritable();
qDebug()<<sslsock->peerCertificate() << " cert";
netManager->setStrictTransportSecurityEnabled(true);
QUrlQuery params;
params.addQueryItem("j_username", uname);
params.addQueryItem("j_password", pwd);
QNetworkRequest req(QUrl(BASE_URL+QM_AUTH_URL));
QSslConfiguration conf = QSslConfiguration::defaultConfiguration();
req.setSslConfiguration(conf);
req.setRawHeader("Accept", "application/xml");
req.setHeader(QNetworkRequest::KnownHeaders::ContentTypeHeader, "application/x-www-form-urlencoded");
repl = netManager->post(req, params.query(QUrl::FullyEncoded).toUtf8());
connect(repl, &QNetworkReply::finished, this, &MainWindow::finishReading);
connect(repl, &QNetworkReply::sslErrors, this, &MainWindow::printSslErrors);
}
void MainWindow::readData() {
dataBuffer.clear();
dataBuffer.append(repl->readAll());
}
void MainWindow::finishReading() {
readData();
#if DEBUG
std::ofstream dataBuffFile, reqFile;
reqFile.open("request.txt", std::ios::out);
if(reqFile) {
reqFile<<"RawHeaderList size: "<<repl->request().rawHeaderList().size()<<std::endl;
for(int i=0; i<repl->request().rawHeaderList().size(); i++) {
reqFile<<repl->request().rawHeaderList().at(i).data()<<std::endl;
}
reqFile<<"Accept: "<<repl->request().rawHeader("Accept").data()<<std::endl;
reqFile<<"Content-Type: "<<repl->request().rawHeader("Content-Type").data()<<std::endl;
reqFile<<"Content-Length: "<<repl->request().rawHeader("Content-Length").data()<<std::endl;
reqFile<<"URL: "<<repl->request().url().toDisplayString().toStdString()<<std::endl;
reqFile.close();
} else {
qDebug()<<"Error in file opening!";
}
dataBuffFile.open("dataBuffer.txt", std::ios::out);
if(dataBuffFile) {
dataBuffFile<<dataBuffer.data();
dataBuffFile.close();
} else {
qDebug()<<"Error in file opening!";
}
#endif
if(repl->error() != QNetworkReply::NoError) {
qDebug()<<"Error: "<<repl->error()<<" - "<<repl->errorString();
warnMsg(this, "Error", QString("Request[Error] : %1").arg(repl->errorString()));
} else {
//TO_DO: CONVERT DATA
dataBuffer.clear();
}
}
SSL handshake is ok, I have no problems setting SSL configuration and using OpenSSL.
I simply can't login with the jazz server, which replies with "bad request".
I've tried also to make an HTTPS request to "www.google.com", but the outcome is -more or less- the same.
What is the methodology to be able to make HTTPS requests with Qt/C++?
Related
My grpc client code has 2 services, and it calls the Stat service from within the Delete service (and others not listed here), to populate the message properties before querying the server to delete it. The issue is, when I pass my path value from Stat to get_stat, it no longer exists. Just curious if there is a better way to structure my code to prevent this from happening? Very new to grpc and c++.
Client:
StatusCode DFSClientNodeP1::Stat(const std::string &filename, void *file_status)
{
Status return_status;
grpc::ClientContext context;
dfs_service::Empty request;
dfs_service::File_Metadata response;
if (file_status != NULL)
response = *(static_cast<dfs_service::File_Metadata *>(file_status));
response.set_filename(filename.c_str());
std::cout << "CLIENT: " << response.filename() << std::endl;
return_status = this->service_stub->get_status(&context, request, &response);
return return_status.error_code();
}
StatusCode DFSClientNodeP1::Delete(const std::string &filename)
{
StatusCode return_status;
grpc::ClientContext context;
dfs_service::File_Metadata request;
dfs_service::Empty response;
return_status = Stat(filename, &request);
if (return_status == 0)
{
return this->service_stub->delete_file(&context, request, &response).error_code();
}
else if (return_status == 5)
{
return return_status;
}
else
{
return StatusCode::CANCELLED;
}
}
Server:
Status get_status(ServerContext *context, const dfs_service::Empty *request, dfs_service::File_Metadata *response) override
{
std::cout << "SERVER: " << response->filename() << std::endl;
struct stat result;
if (stat(this->WrapPath(response->filename()).c_str(), &result) == 0)
{
response->set_filesize(result.st_size);
response->set_blocksize(result.st_blksize);
// metadata.set_creation_time(result.st_ctim);
response->set_modified_time(result.st_mtime);
return Status::OK;
}
else
{
return Status(StatusCode::NOT_FOUND, "File Not Found");
}
}
Output:
Client: TEST.jpg
Server:
In gRPC, the request message is sent from the client to the server, and the response message is sent from the server to the client. What you're doing here is sending an empty message from the client to the server. Your client code is setting the filename field in the response message before it gets the response from the server, but that entire message is getting overwritten by the actual server response.
What you need to do here is pass the filename as a field in the request message rather than making it a field in the response message.
https://github.com/cesanta/mongoose/blob/master/examples/websocket-server/main.c
#include "mongoose.h"
static const char *s_listen_on = "ws://localhost:80020";
static const char *s_web_root = ".";
// This RESTful server implements the following endpoints:
// /websocket - upgrade to Websocket, and implement websocket echo server
// /api/rest - respond with JSON string {"result": 123}
// any other URI serves static files from s_web_root
static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
if (ev == MG_EV_OPEN) {
// c->is_hexdumping = 1;
} else if (ev == MG_EV_HTTP_MSG) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_http_match_uri(hm, "/websocket")) {
// Upgrade to websocket. From now on, a connection is a full-duplex
// Websocket connection, which will receive MG_EV_WS_MSG events.
mg_ws_upgrade(c, hm, NULL);
} else if (mg_http_match_uri(hm, "/rest")) {
// Serve REST response
mg_http_reply(c, 200, "", "{\"result\": %d}\n", 123);
} else {
// Serve static files
struct mg_http_serve_opts opts = {.root_dir = s_web_root};
mg_http_serve_dir(c, ev_data, &opts);
}
} else if (ev == MG_EV_WS_MSG) {
// Got websocket frame. Received data is wm->data. Echo it back!
struct mg_ws_message *wm = (struct mg_ws_message *) ev_data;
mg_ws_send(c, wm->data.ptr, wm->data.len, WEBSOCKET_OP_TEXT);
}
(void) fn_data;
}
int main(void) {
struct mg_mgr mgr; // Event manager
mg_mgr_init(&mgr); // Initialise event manager
printf("Starting WS listener on %s/websocket\n", s_listen_on);
mg_http_listen(&mgr, s_listen_on, fn, NULL); // Create HTTP listener
for (;;) mg_mgr_poll(&mgr, 1000); // Infinite event loop
mg_mgr_free(&mgr);
return 0;
}
I want to create a websocket server, but I am getting an error while running this project.
I tried on different ports but the result did not change.
Error is:
mongoose.c:2774:mg_listen Failed: ws://localhost:80020, errno 0
Failed
I am trying to set a mongoose web server v3.3 with a self-signed SSL certificate. I know how to do it without SSL but I want to implement HTTPS.
I have implemented something like this:
void *event_handler(enum mg_event event,
struct mg_connection *conn) {
const struct mg_request_info *request_info = mg_get_request_info(conn);
static void* done = "done";
if (event == MG_NEW_REQUEST) {
if (strcmp(request_info->uri, "/hello") == 0) {
// handle c[renderer] request
if(strcmp(request_info->request_method, "GET") != 0) {
// send error (we only care about HTTP GET)
mg_printf(conn, "HTTP/1.1 %d Error (%s)\r\n\r\n%s",
500,
"we only care about HTTP GET",
"we only care about HTTP GET");
// return not null means we handled the request
return done;
}
// handle your GET request to /hello
char* content = "Hello World!";
char* mimeType = "text/plain";
int contentLength = strlen(content);
mg_printf(conn,
"HTTP/1.1 200 OK\r\n"
"Cache: no-cache\r\n"
"Content-Type: %s\r\n"
"Content-Length: %d\r\n"
"\r\n",
mimeType,
contentLength);
mg_write(conn, content, contentLength);
return done;
}
}
// in this example i only handle /hello
mg_printf(conn, "HTTP/1.1 %d Error (%s)\r\n\r\n%s",
500, /* This the error code you want to send back*/
"Invalid Request.",
"Invalid Request.");
return done;
}
// No suitable handler found, mark as not processed. Mongoose will
// try to serve the request.
return NULL;
}
int main(int argc, char **argv) {
const char *options[] = {
"ssl_certificate", "cert.pem",
"listening_ports", "443s",
"num_threads", "10",
NULL
};
static struct mg_context *ctx;
ctx = mg_start(&event_handler, options);
if(ctx == NULL) {
exit(EXIT_FAILURE);
}
puts("Server running, press enter to exit\n");
getchar();
mg_stop(ctx);
return EXIT_SUCCESS;
}
The problem is I am not able to access my server from the web browser. I think the problem is that the first event my callback receives is MG_INIT_SSL but I do not know how to handle or process it. Could anybody please help?
Firstly, I believe you should not have to handle other events than MG_NEW_REQUEST in your event handler.
I would also debug using openssl:
openssl s_client -connect <hostname:port>
to see that the SSL connection gets set up properly.
In any case, Cesanta does provide a complete working example for you to use:
https://github.com/cesanta/mongoose/tree/master/examples/simplest_web_server_ssl
I want to create a Server for MJPEGs and I found this tutorial: http://www.bogotobogo.com/Qt/Qt5_QTcpServer_Client_Server.php. But when I connect from a Web Browser like Chrome (Microsoft Telnet works fine), it shows connection reset.
After creating the server, I would like to show MJPEGs on my browser (like an IP Camera does) using this: How to Create a HTTP MJPEG Streaming Server With QTcp-Server Sockets?
Here's my Server.cpp (A little modified) -
#include "stdafx.h"
#include "TCPServer.h"
TcpServer::TcpServer(QObject *parent) :
QObject(parent)
{
server = new QTcpServer(this);
connect(server, SIGNAL(newConnection()),
this, SLOT(newConnection()));
if (!server->listen(QHostAddress::Any, 9999))
qDebug() << "Server could not start";
else
qDebug() << "Server started!";
}
void TcpServer::newConnection()
{
QTcpSocket *socket = server->nextPendingConnection();
QByteArray header = "HTTP/1.1 200 OK\r\n";
socket->write(header);
QByteArray ContentType = "Content-Type: text/html\r\n";
socket->write(ContentType);
QByteArray Body = "Test";
socket->write(Body);
socket->flush();
socket->close();
}
After LOTS of trial and error (and luck), I found the solution. The code should end like this -
...
socket->flush();
socket->waitForBytesWritten(10000);
socket->close();
EDIT -
After trying again, I found out this - There should be an extra \r\n after the headers. So this will work -
void TcpServer::newConnection()
{
QTcpSocket *socket = server->nextPendingConnection();
QByteArray header = "HTTP/1.1 200 OK\r\n";
socket->write(header);
QByteArray ContentType = "Content-Type: text/html\r\n\r\n\*Here is the edit*\";
socket->write(ContentType);
QByteArray Body = "Test";
socket->write(Body);
socket->flush();
socket->close();
}
I'm writing a proxy server, http part is ready, but are having problems with https.
I created a certificate and private key (as I understood, without it will not work) in this way:
OpenSSL> req-x509-newkey rsa: 2048-keyout server.key-nodes-days 365-out server.csr
I did a simple QTcpServer that is passed a socketDescriptor to the created object on newIncomingConnection().
In constructor of my object I did:
sock = new QSslSocket();
connect (sock,SIGNAL(readyRead()),this,SLOT(onQuery()));
connect(sock,SIGNAL(disconnected()),this,SLOT(deleteLater()));
connect(sock,SIGNAL(error(QAbstractSocket::SocketError)),this,SLOT(onError(QAbstractSocket::SocketError)));
connect(sock,SIGNAL(sslErrors(QList<QSslError>)),this,SLOT(sltSslErrors(QList<QSslError>)));
...
Load key and cert
...
sock->setProtocol(QSsl::AnyProtocol);
QSslKey sslKey(key, QSsl::Rsa);
QSslCertificate sslCert(cert);
sock->setPrivateKey(sslKey);
sock->setLocalCertificate(sslCert);
sock->setSocketDescriptor(socketDesc);
sock->startServerEncryption();
if(!sock->waitForEncrypted(30000)) {
qDebug()<<"wait for encrypted failed";
}
On connect in console I see "wait for encrypted failed" and socket emited signal error() with QAbstractSocket::SslHandshakeFailedError.
Could you give advice on what else to do that would be to establish the ssl connection without error ?
I believe you need to call setSocketDescriptor before calling the setPrivateKey and setLocalCertificate methods.
Below is code that I've used to create a HTTPS Server Socket, extending QTcpServer.
void SslServer::incomingConnection(qintptr socketDescriptor)
{
QSslSocket *serverSocket = new QSslSocket;
if (serverSocket->setSocketDescriptor(socketDescriptor)) {
QFile keyFile(<sslKeyFileName>);
if (!keyFile.open(QIODevice::ReadOnly)) {
delete serverSocket;
return;
}
QSslKey key(&keyFile, QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey);
if (key.isNull()) {
delete serverSocket;
return;
}
keyFile.close();
serverSocket->setPrivateKey(key);
// to prevent asking for client certificate.
serverSocket->setPeerVerifyMode(QSslSocket::VerifyNone);
serverSocket->setLocalCertificate(<certificateFileName>);
serverSocket->startServerEncryption();
if (serverSocket->waitForEncrypted(3000)) {
// this will emit a newConnection() signal
addPendingConnection(serverSocket);
} else {
qDebug() << "Encryption Failed.";
delete serverSocket;
}
} else {
delete serverSocket;
}
}