Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Curl vs CPPREST

I am trying to access an URL, using CPPREST http_client :

http://www.20min.ch/rss/rss.tmpl?type=channel&get=68

I am receiving response code 302 for URL- redirection.

But when i try to access the same URL using CURL, I am receiving CURLE_OK.

Below are the 2 piece of code :

using CURL :

CURL *curl;
CURLcode res;
curl_global_init(CURL_GLOBAL_ALL);
curl = curl_easy_init();
if(curl){
    curl_easy_setopt(curl, CURLOPT_URL, "http://www.20min.ch/rss/rss.tmpl?type=channel&get=68");
    res = curl_easy_perform(curl);
    if(res != CURLE_OK)     {
        cout<<"failed";
    }
    else  {
        cout<<"success";
    }
    curl_easy_cleanup(curl);
}
curl_global_cleanup();

The output is : success

using CPPREST :

std::string url_= "http://www.20min.ch/rss/rss.tmpl?type=channel&get=68";
try
{
     http_client client1(U(url_));
     uri_builder builder1(U(""));
     client1.request(methods::GET, builder1.to_string()).then([=](http_response response)
     {
        cout<<"Response code is : "<<response.status_code();
     });
}
catch(std::exception& e)
{
    cout<<"response :"<<e.what();
}

The output is :: Response code is : 302

I do not understand why the two libs are behaving differently for same URL??

UPDATE :

I have also tried with :

http_client client1(utility::conversions::to_string_t(url_));

and

http_client client1(U("http://www.20min.ch/rss/rss.tmpl?type=channel&get=68"));

and

http_client client1(U("http://www.20min.ch/"));

but the response is same 302 with cpp rest. [ for cross checking bing example

is working fine]

UPDATE 2:

The method as explained by @Matt Weber seems very helpful and legit but i am continuously getting error code : 400 for that, So I tried the below things: I tried to set the host and port for the URL in uri_builder.

http_client client(U("http://www.20min.ch/rss/"));
uri_builder builder(U("/rss.tmpl"));
builder.append_query(U("type"), U("channel"));
builder.append_query(U("get"), U("68"));
builder.set_host(U("www.20min.ch"));
builder.set_port(U("80"));
client.request(methods::GET, builder.to_string()).then([=](http_response response)
{
     cout<<"Received response status code: "<<response.status_code();
});

but still same 302.

like image 643
Hummingbird Avatar asked Feb 07 '23 12:02

Hummingbird


1 Answers

The problem with the Rest SDK code is the http_client initialization:

    http_client client1(U(url_));

The U macro is for use with string literals to produce something from which a uri can be constructed. If you're on Windows, this shouldn't compile, because the macro expansion results in Lurl_. Apparently whatever this results in on your system leads to a request for something that responds with a 302.

There are a couple of options. One would be to simply use the literal directly:

    http_client client1(U("http://www.20min.ch/rss/rss.tmpl?type=channel&get=68"));

If you want to keep the std::string and initialize the client from that, you can convert to a utility::string_t from which the uri can be constructed.

    std::string url_= "http://www.20min.ch/rss/rss.tmpl?type=channel&get=68";
    http_client client1(utility::conversions::to_string_t(url_));

Once that is done, you'll likely find that you need to call the wait function on the continuation from request in order to actually see the expected output:

     client1.request(methods::GET, builder1.to_string()).then([](http_response response)
     {
        cout<<"Response code is : "<<response.status_code();
     }).wait(); // ensure that the response gets processed

EDIT:

The above is relevant for building on Windows, but has nothing to do with the 302 response.

On Linux, the request results in a 302 consistently. Looking at the request and response on the wire, a request from a Windows host gets a 200 and a request from a Linux host gets a 302. The reason is that in the Linux version, the host header includes a port number, which is what triggers the server to respond with a 302.

Windows request:

GET /rss/rss.tmpl?type=channel&get=68 HTTP/1.1\r\n
Connection: Keep-Alive\r\n
User-Agent: cpprestsdk/2.8.0\r\n
Host: www.20min.ch\r\n
\r\n

Linux request:

GET /rss/rss.tmpl?type=channel&get=68 HTTP/1.1\r\n
Host: www.20min.ch:80\r\n
User-Agent:cpprestsdk/2.8.0\r\n
Connection: Keep-Alive\r\n
\r\n

You can verify that this is the cause with wget:

$ wget --header="Host: www.20min.ch" -S "http://www.20min.ch/rss/rss.tmpl?type=channel&get=68"

HTTP/1.1 200 OK

$ wget --header="Host: www.20min.ch:80" -S "http://www.20min.ch/rss/rss.tmpl?type=channel&get=68" --max-redirect 0

HTTP/1.1 302 Found

The difference in the header is due to the different implementations. The WinHTTP client implementation does not add the Host header explicitly, presumably because it relies on WinHTTP to do that internally. The asio client implementation does add it, though.

        // Add the Host header if user has not specified it explicitly
        if (!ctx->m_request.headers().has(header_names::host))
        {
            request_stream << "Host: " << host << ":" << port << CRLF;
        }

So to get the expected behavior, the header can be set explicitly to avoid adding the port information:

std::string url_= "http://www.20min.ch/rss/rss.tmpl?type=channel&get=68";
http_client client1(utility::conversions::to_string_t(url_));
http_request request;
request.set_method(methods::GET);
request.headers().add(U("Host"), U("www.20min.ch"));
client1.request(request).then([](http_response response)
{
    std::cout<<"Response code is : "<<response.status_code();
}).wait();

With this change, I get a 200 OK on both Windows and Linux.

like image 149
Matt Weber Avatar answered Feb 19 '23 13:02

Matt Weber