0

It's a HTTP request sending method. When the goal website responses, httpFinished() will be called.

void HTTPClientBase:: HttpRequestGet()
{
    network_manager.setProxy(proxy);
    QNetworkRequest network_request;

    network_request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
    network_request.setUrl(URL);


    reply = network_manager.get(network_request);
    connect(reply, SIGNAL(finished(QNetWorkReply*)), this, SLOT(httpFinished(QNetWorkReply*)));
}

void HTTPClientBase::httpFinished(QNetWorkReply* reply)
{
    // How do I know this reply comes from which proxy?
}

I can call HttpRequestGet() in a loop.

static HTTPClientBase myClient;
for(int i=0; i<20; i++)
{
  myClient.setUrl("https:\\www.google.com");
  myClient.setProxy("123.123.123.123:1234"); // The proxy changes every time in this loop.
  myClient.HttpRequestGet();
}

When HTTPClientBase::httpFinished(QNetWorkReply* reply) is called, How do I know this reply comes from which proxy? }

Zhang
  • 3,030
  • 2
  • 14
  • 31

1 Answers1

2

As per the QNetworkReply document, you can get the corresponding request using the member function QNetworkReply::request().

Anyway, QNetworkRequest has not member function of setProxy.

But if you are setting proxy for QNetworkAccessManager you can have a pointer to the corresponding manager by QNetworkReply::manager().

Notice the connect command. finished() has no QNetworkReply* argument so your connect command will also fail and you have to change HTTPClientBase::httpFinished(QNetWorkReply* reply) to HTTPClientBase::httpFinished().

...
    connect(reply, SIGNAL(finished()), this, 
    SLOT(httpFinished()));
}

void HTTPClientBase::httpFinished()
{
    QNetworkReply* reply = qobject_cast<QNetworkReply*>(sender());
    if(reply){
        QNetworkAccessManager * manager = reply->manager();
        QNetworkProxy proxy = manager->proxy();
        // now you have the proxy 
    }

}

As you see, you have to use sender() to obtain the actual signal sender. You need to create different QNetworkAccessManager instances for each proxy you have. If you have a proxy pool, create your QNetworkAccessManager instances first and choose them according to your specific needs.

If you don't want to create a new QNetworkAccessManager for each proxy, you can do something like this:

void HTTPClientBase:: HttpRequestGet()
{
    network_manager.setProxy(proxy);
    QNetworkRequest network_request;

    network_request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
    network_request.setUrl(URL);

    reply = network_manager.get(network_request);
    //new member variable: QHash<QNetworkReply*,QString> m_RequestToProxy;
    m_RequestToProxy.insert(reply,proxy);

    connect(reply, SIGNAL(finished()), this, SLOT(httpFinished()));
}

void HTTPClientBase::httpFinished()
{
    QNetworkReply* reply = qobject_cast<QNetworkReply*>(sender());
    if(reply){
        QString proxy = m_RequestToProxy.take(reply);
        //check if proxy is valid
        //and then do whatever you want
    }
}

And another better solution is to set a property of reply and get it in slot.

void HTTPClientBase:: HttpRequestGet()
{
    network_manager.setProxy(proxy);
    QNetworkRequest network_request;

    network_request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
    network_request.setUrl(URL);

    reply = network_manager.get(network_request);
    reply->setProperty("_proxy_",proxy);

    connect(reply, SIGNAL(finished()), this, SLOT(httpFinished()));
}

void HTTPClientBase::httpFinished()
{
    QNetworkReply* reply = qobject_cast<QNetworkReply*>(sender());
    if(reply){
        QVariant v_proxy = reply->property("_proxy_");
        //check if proxy is valid
        if(v_proxy.isValid()){
             QString proxy = v_proxy.toString();
             //and then do whatever you want
        }

    }
}
Soheil Armin
  • 2,725
  • 1
  • 6
  • 18
  • It's my fault. setProxy is a method of QNetworkAccessManager. – Zhang Dec 31 '19 at 08:27
  • My problem is `network_manager.setProxy(proxy);` will be called 20 times in less than 1 second. But httpFinished will be called after 2 or 3 seconds. When I call `manager->proxy()` in httpFinished(), I think it would be the last proxy (not corresponding one). I'll do a test for this. – Zhang Dec 31 '19 at 08:44
  • network_manager is a member variable value. All requests use the same one. – Zhang Dec 31 '19 at 08:44
  • That's why I say, you have to create a pool of ```QNetworkAccessManager```. You can also create a QHash to keep track of which request belongs to which proxy but I would not recommend it. – Soheil Armin Dec 31 '19 at 08:48
  • just added a new approach to do it with a single ```QNetworkAccessManager``` – Soheil Armin Dec 31 '19 at 09:28
  • Great solution! I had thought to add a rawheader (like `_proxy_`) into the request, but it seemed lame. – Zhang Jan 02 '20 at 01:18
  • 1
    Another solution, is to connect the `finished` signal to a stateful lambda that captures the used proxy (as well as a pointer to the `QNetworkReply` object) if one wants to avoid bloating the class `HTTPClientBase` with additional members. – Mike Jan 03 '20 at 15:42
  • The problem is essentially similar to the one presented in [this question](https://stackoverflow.com/q/22641306), where the user has a grid of buttons that have their `clicked` signal connected to a single slot, and is trying to determine the location of the pushed button when that slot is invoked (similar to determining the proxy used in our network request when the slot `httpFinished` gets invoked). There is [a comprehensive answer](https://stackoverflow.com/a/22642431/2666212) that provides a lot of solutions there. – Mike Jan 03 '20 at 15:53