I'm programming my ESP32 with the ArduinoIDE and have a problem with HTTP GET. What I'm doing:
the ESP32 connects as WiFi client to an existing WiFi network using a static, fixed IP
a webserver is started which provides a webpage for OTA firmware update -> this works, the webpage is accessible via the static IP
using HttpClient I try to GET an other, remote webserver, but this fails
This is the code I'm using for the HTTP GET call:
static WiFiClient wifi;
HttpClient wlanHttp=HttpClient(wifi,"my.server.tld");
wlanHttp.get("/setpos.php?id=DEADBEEF"); // -> this fails with error code -1
wlanHttp.responseStatusCode(); // follow-up error -1
wlanHttp.stop();
Any idea what goes wrong here?
The confusing part here is the ESP32 has a built in http client called HTTPClient. The one for Arduino is called HttpClient and I'd like to find the guy who decided on that name and see if he's okay. HTTPClient has a routine called getString() that is a lovely way to gather info from a json api call, but HttpClient won't compile with that because it has no clue what that is.
On ESp32 (if using the HTTPClient.h) the code should look like that:
static WiFiClient wifi;
HttpClient wlanHttp;
wlanHttp.begin("http://my.server.tld/setpos.php?id=DEADBEEF"); //Specify the URL
int httpCode = wlanHttp.GET(); //Make the request
if (httpCode > 0) { //Check for the returning code
if (httpCode == HTTP_CODE_OK) {
// get payload with http.getString();
Serial.println(httpCode);
// Serial.println(payload);
} else {
Serial.printf("[HTTP] GET... failed, error: %s\n", wlanHttp.errorToString(httpCode).c_str());
}
} else {
Serial.println("Error on HTTP request");
}
wlanHttp.end(); //Free the resources
In FTPS, the client connect to the server, the server sending a welcome message; thus, then client send command "AUTH TLS",the server set CertificatećPrivateKey and startServerEncryption; what should the client do to finish this ssl handshake?
Server:
if ("AUTH" == command && "TLS" == commandParameters.toUpper())
{
auth();
}
void FtpControlConnection::auth()
{
reply("234 Initializing SSL connection.");
SslServer::setLocalCertificateAndPrivateKey(socket);
socket->startServerEncryption();
}
void SslServer::setLocalCertificateAndPrivateKey(QSslSocket *socket)
{
socket->setPrivateKey(":/ssl/server.key", QSsl::Rsa, QSsl::Pem, "1234");
Q_ASSERT(!socket->privateKey().isNull());
socket->setLocalCertificate(":/ssl/server.crt");
Q_ASSERT(!socket->localCertificate().isNull());
}
void SslServer::incomingConnection(PortableSocketDescriptorType socketDescriptor)
{
QSslSocket *socket = new QSslSocket(this);
if (socket->setSocketDescriptor(socketDescriptor)) {
addPendingConnection(socket);
} else {
delete socket;
}
}
client
socket->connectToHost(hostNameEdit->text(), portBox->value());
socket->write("AUTH TLS\r\n");
socket->waitForConnected();
socket->startClientEncryption();
socket->waitForEncrypted();
I wish the client finish the ssl shakehand by using startClientEncryption, and then I can use the client socket to send command or file; but it does not work; I know the client socket and the server socket already connect, but how to add ssl on this?
U use QNetworkRequest to send post request. How can I get HTTP code of request? I send some request to server, on server I can see my request, but i have to check http code which server will return to application.
QNetworkRequest can not be used without QNetworkAccessManager that's responsible for making the actual request to the web server. Each request done by QNetworkAccessManager instance returns QNetworkReply where you should look for the status code from the server. It's located inside the QNetworkReply instance headers.
The request is asynchronous so it can be catch when signal is triggered.
Easiest example would be:
QNetworkAccessManager *manager = new QNetworkAccessManager(this);
connect(manager, SIGNAL(finished(QNetworkReply*)),
this, SLOT(replyFinished(QNetworkReply*)));
manager->get(QNetworkRequest(QUrl("http://qt-project.org")));
Then in the slot implementation:
void replyFinished(QNetworkReply *resp){
QVariant status_code = resp->attribute(QNetworkRequest::HttpStatusCodeAttribute);
status_code.is_valid(){
// Print or catch the status code
QString status = status_code.toString(); // or status_code.toInt();
qDebug() << status;
}
}
Have a look on the official documentation. It explains all in details.
QNetworkRequest
QNetworkAccessManager
I have a Client-Server application that was working with QTcpSocket. Now I would like to use an encrypted SSL connection instead, therefore I tried to switch to QSslSocket. But I can't establish a connection to server.
Here is the code for the client:
ConnectionHandler::ConnectionHandler(QString ip, int port, QObject *parent) : QObject(parent) {
// connect(this->socket, SIGNAL(connected()), this, SLOT(connected()));
connect(this->socket, SIGNAL(disconnected()), this, SLOT(disconnected()));
connect(this->socket, SIGNAL(readyRead()), this, SLOT(readyRead()));
connect(this->socket, SIGNAL(encrypted()), this, SLOT(encryptedReady()));
connect(this->socket, SIGNAL(sslErrors(const QList<QSslError> &)), this, SLOT(SSLerrorOccured(const QList<QSslError> &)));
connect(this->socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(socketError(QAbstractSocket::SocketError)));
connect(this->socket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(socketStateChanged(QAbstractSocket::SocketState)));
this->ip = ip;
this->port = port;
}
void ConnectionHandler::socketStateChanged(QAbstractSocket::SocketState state) {
qDebug() << state;
}
void ConnectionHandler::socketError(QAbstractSocket::SocketError) {
qDebug() << this->socket->errorString();
}
void ConnectionHandler::encryptedReady() {
qDebug() << "READY";
}
void ConnectionHandler::SSLerrorOccured(const QList<QSslError> &) {
qDebug() << "EEROR";
}
void ConnectionHandler::connectToServer() {
// this->socket->connectToHost(this->ip, this->port);
this->socket->connectToHostEncrypted(this->ip, this->port);
if (!this->socket->waitForConnected(5000)) {
this->socket->close();
this->errorMsg = this->socket->errorString();
}
}
void ConnectionHandler::connected() {
qDebug() << "connected";
this->connectedHostAddress = this->socket->peerAddress().toString();
this->connectionEstablished = true;
this->localIP = this->socket->localAddress().toString();
this->localPort = this->socket->localPort();
}
Here the one for the server:
ClientHandler::ClientHandler() {
this->socket->setProtocol(QSsl::SslV3);
this->socket->setSocketOption(QAbstractSocket::KeepAliveOption, true);
}
void ClientHandler::run() {
if (!this->fd)
return;
connect(this->socket, SIGNAL(readyRead()), this, SLOT(readyRead()));
connect(this->socket, SIGNAL(disconnected()), this, SLOT(disconnected()), Qt::DirectConnection);
connect(this->socket, SIGNAL(encrypted()), this, SLOT(encryptedReady()));
connect(this->socket, SIGNAL(sslErrors(const QList<QSslError> &)), this, SLOT(sslErrorOccured(const QList<QSslError> &)));
connect(this->socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(socketError(QAbstractSocket::SocketError)));
connect(this->socket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(socketStateChanged(QAbstractSocket::SocketState)));
if (!this->socket->setSocketDescriptor(this->fd)) {
emit error(socket->error());
return;
} else {
connect(this->socket, SIGNAL(encrypted()), this, SLOT(ready()));
this->socket->startServerEncryption();
}
this->peerIP = socket->peerAddress().toString();
QString tmp;
tmp.append(QString("%1").arg(socket->peerPort()));
this->peerPort = tmp;
QHostInfo::lookupHost(this->peerIP, this, SLOT(lookedUp(QHostInfo)));
}
void ClientHandler::socketStateChanged(QAbstractSocket::SocketState state) {
qDebug() << state;
}
void ClientHandler::socketError(QAbstractSocket::SocketError) {
qDebug() << this->socket->errorString();
}
void ClientHandler::setFileDescriptor(int fd) {
this->fd = fd;
}
void ClientHandler::ready() {
qDebug() << "READY";
}
void ClientHandler::sslErrorOccured(const QList<QSslError> &) {
qDebug() << "EEROR";
}
void ClientHandler::encryptedReady() {
qDebug() << "READY";
}
The output for the client I receive is:
QAbstractSocket::HostLookupState
QAbstractSocket::ConnectingState
QAbstractSocket::ConnectedState
"The remote host closed the connection"
QAbstractSocket::ClosingState
QAbstractSocket::UnconnectedState
and for the server:
QAbstractSocket::ConnectedState
"Error during SSL handshake: error:1408A0C1:SSL routines:SSL3_GET_CLIENT_HELLO:no shared cipher"
QAbstractSocket::UnconnectedState
Does anyone know how to fix this?
I assume that with non encrypted sockets everything is fine. So let's focus only on peculiarities of detailing with QSslSocket. I can share larger pieces or working code if needed. There is also a large story regarding SSL certificates that I will touch briefly here.
To start let's check you client on some external HTTP SSL servers, for example:
socket->connectToHostEncrypted("gmail.com", 443);
It should work immediately with default SSL protocol (without any setProtocol()). On the signal encrypted() you can write HTTP GET header and on the readyRead() the reply will come.
Now try to set socket->setProtocol(QSsl::SslV3); for "gmail.com". Expected result:
sslErrorOccured: ("The host name did not match any of the valid hosts for this certificate")
Note that it is not the error() signal but the sslErrors() signal notifying about certificate issues that can be ignored by client.
So, for simplicity let's work with default SSL protocol without using setProtocol().
Since the client is in working state we can move to the server. You were interrupted on the first level of SSL certification challenge. To start initializing SSL connection by the server you have to provide at least private encryption key and public certificate. That is your error:
"Error during SSL handshake: error:1408A0C1:SSL routines:SSL3_GET_CLIENT_HELLO:no shared cipher"
In the perfect case your certificate should be signed by an authentication authority company. In that case any client that has a list of such root certificates is able to check that your certificate is valid. However that service is paid and it may take few weeks. We can start with self signed certificate.
You can find various recipes for generating certificates, for example: "How to create a self-signed SSL Certificate which can be used for testing purposes or internal usage"
Short extract:
#Step 1: Generate a Private Key
openssl genrsa -des3 -out server.key 1024
#Step 2: Generate a CSR (Certificate Signing Request)
#Common Name (eg, your name or your server's hostname) []:example.com
openssl req -new -key server.key -out server.csr
#Step 3: Remove Passphrase from Key
cp server.key server.key.org
openssl rsa -in server.key.org -out server.key
#Step 4: Generating a Self-Signed Certificate
openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt
Now there are server.key and server.crt files. Those files should be passed by the server to QSslSocket before startServerEncryption():
socket->setPrivateKey("c:/test/ssl/cert/server.key", QSsl::Rsa);
socket->setLocalCertificate("c:/test/ssl/cert/server.crt");
Now the server can start SSL connection. You can test it with a browser. Typically browser will raise alarm that the connection is not trusted. That is because the certificate is self signed and it cannot protect from "man in the middle" attack. However, you can ask your browser to continue with that connection. So, on the server side you will have the HTTP GET header on the signalreadyRead().
Try to connect with the Qt client to that server. Now the error is raised by the client:
sslErrorOccured: ("The certificate is self-signed, and untrusted")
The server says in error(): "The remote host closed the connection". The client raised the SSL certificate error, but as with browsers we can continue with that connection. Put socket->ignoreSslErrors() in the sslErrors() signal handler:
void Client::sslErrorOccured(const QList<QSslError> & error) {
qDebug() << "sslErrorOccured:" << error;
socket->ignoreSslErrors(error);
}
That's it. Of course, your client should not accept all SSL certificate errors from all servers, since such errors mean that the connection is not really secure and can be hacked. It is just for testing. The object QSslError contains the certificate data, so it possible on your client side to accept only one specific self-signed certificate from your server and ignore all other such errors.
It is also possible to create your own 'authority root certificate' that you can manually write to your system. Then that certificate can be used for signing your server certificate. Your client will think that it is trusted connection, since it will be able to validate it by system root certificates.
Note that you can also have some issue with OpenSSL library. On Linux or OS X OpenSSL is in default setup, however for Windows it should be installed manually. Some compilation of OpenSSL may be already present in Windows system PATH, for example CMake has some limited OpenSSL library. However in general for Windows you should deploy OpenSSL together with your application.
I'm using HttpWebRequest to send many sync requests to the same HTTP URL on localhost, and I need to re-use TCP connections to prevent ephemeral port depletion.
But I don't seem to be able to get Keep-alive working.
Here's my code where I make the HTTP request:
var request = (HttpWebRequest) WebRequest.Create(url);
HttpWebResponse response = null;
request.Method = "GET";
request.KeepAlive = true;
try
{
response = (HttpWebResponse) request.GetResponse();
// ...
}
catch (Exception e)
{
// ...
}
finally
{
if (response != null)
{
response.Close();
}
}
I call the above code many times in a loop. All requests are successful, and return OK status code (200), but running netstat -ano reveals that there are MANY MANY connections in the TIME_WAIT state, where I expect it to re-use a single connection.
I tried calling both an IIS Express server on localhost (default ASP.NET MVC application) and a WebLogic application, with the same result.
I also tried HttpClient from .NET 4.5.
This quickly results in running out of ephemeral ports.
Is there anything wrong with what I'm doing? How can I make .NET re-use TCP connections?
Try response.Dispose(); instead of response.Close();