jgaa / restc-cpp

Modern C++ REST Client library
MIT License
615 stars 95 forks source link

Getting exception "Failed to connect (closed)" with simple example. #140

Closed bbogart closed 1 year ago

bbogart commented 1 year ago

Hello,

I'm using a simplified version of the "Fetch raw data" example:

#include <iostream>
#include "restc-cpp/restc-cpp.h"

using namespace std;
using namespace restc_cpp;

void DoSomethingInteresting(Context& ctx) {

    try {
        ctx.Get("http://jsonplaceholder.typicode.com/posts/1");
    } catch (const exception& ex) {
        clog << "Caught exception: " << ex.what() << endl;
    }
}

int main() {

    // Create a REST client
    auto RESTClient = RestClient::Create();

    // Create a co-routine that runs in a thread
    RESTClient->Process( DoSomethingInteresting );

    // Wait for the thread to finish and return.
    RESTClient->CloseWhenReady(true);

    return 0;
}

And always get the "Failed to connect (closed)" exception, but if I use wget on the same machine, the connection, url and returned json data seem fine:

$ wget -4 http://jsonplaceholder.typicode.com/posts/1 && cat 1
--2023-01-07 17:26:56--  http://jsonplaceholder.typicode.com/posts/1
Resolving jsonplaceholder.typicode.com (jsonplaceholder.typicode.com)... 104.21.55.162, 172.67.149.50
Connecting to jsonplaceholder.typicode.com (jsonplaceholder.typicode.com)|104.21.55.162|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 292 [application/json]
Saving to: ‘1’

1                   100%[===================>]     292  --.-KB/s    in 0s      

2023-01-07 17:26:57 (20.1 MB/s) - ‘1’ saved [292/292]

{
  "userId": 1,
  "id": 1,
  "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
  "body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"

Running on Ubuntu 20.04, boost 1.71.0-6ubuntu6, openssl 1.1.1f-1ubuntu2.16

I did compile restc-cpp with openssl (from ccmake):

OPENSSL_CRYPTO_LIBRARY /usr/lib/x86_64-linux-gnu/libcrypto.so
OPENSSL_INCLUDE_DIR /usr/include
OPENSSL_SSL_LIBRARY /usr/lib/x86_64-linux-gnu/libssl.so

Compiling my test code with:

g++ gitExample.cpp -o gitExample -ggdb -std=c++14 `curl-config --libs` -lrestc-cpp -lz -lssl -lcrypto -lpthread -lboost_system -lboost_program_options -lboost_filesystem -lboost_date_time -lboost_context -lboost_coroutine -lboost_chrono -lboost_log -lboost_thread -lboost_log_setup -lboost_regex -lboost_atomic -lpthread

It looks like the issue is that the connection cannot be made because the client is already closed, but changing the code to the following leads to the same exception:

#include <iostream>
#include "restc-cpp/restc-cpp.h"

using namespace std;
using namespace restc_cpp;

void DoSomethingInteresting(Context& ctx) {

    try {
        ctx.Get("http://jsonplaceholder.typicode.com/posts/1");
    } catch (const exception& ex) {
        clog << "Caught exception: " << ex.what() << endl;
    }
}

int main() {

    // Create a REST client
    auto RESTClient = RestClient::Create();

    // Create a co-routine that runs in a thread
    RESTClient->Process( DoSomethingInteresting );

    cout << "RESTClient->IsClosed: " << RESTClient->IsClosed() << endl;

    // Wait for the thread to finish and return.
    RESTClient->CloseWhenReady(true);

    cout << "RESTClient->IsClosed: " << RESTClient->IsClosed() << endl;

    return 0;
}

Prints:

RESTClient->IsClosed: 0 Caught exception: Failed to connect (closed) RESTClient->IsClosed: 1

Any hints as to what is going on?

jgaa commented 1 year ago

It seems like you found a bug. I can reproduce it here. If you use the RequestBuilder class to formulate your request, your application should work.

I have to add some new test-cases for the direct methods (this code actually fails in the test covering it, but that test is designed to just validate that the example code compiles) and fix it.

Thanks a lot for reporting this issue!

bbogart commented 1 year ago

OK! Thank you; also it would be great if there were a couple very simple complete examples that start from scratch, i.e. connect to end point, connect to end-point and print data, connect to endpoint and parse results, etc. building up to the examples already included.

jgaa commented 1 year ago

That's a good idea. I'll look into that when the bug is fixed.

bbogart commented 1 year ago

> If you use the RequestBuilder class to formulate your request, your application should work.

Hello Again,

It look me a little white to get back to this, but even with RequestBuilder I'm not having any luck, I'm just getting "Caught exception: Failed to connect" rather than "Caught exception: Failed to connect (closed)"

Tried the two following programs, same result:

#include <iostream>
#include "restc-cpp/restc-cpp.h"
#include "restc-cpp/RequestBuilder.h" 

using namespace std;
using namespace restc_cpp;

void DoSomethingInteresting(Context& ctx) {

    try {
        auto reply = RequestBuilder(ctx)
        .Get("https://jsonplaceholder.typicode.com/posts/1")
        .Execute();
    } catch (const exception& ex) {
        clog << "Caught exception: " << ex.what() << endl;
    }
}

int main() {

    // Create a REST client
    auto RESTClient = RestClient::Create();

    // Create a co-routine that runs in a thread
    RESTClient->Process( DoSomethingInteresting );

    cout << "RESTClient->IsClosed: " << RESTClient->IsClosed() << endl;

    // Wait for the thread to finish and return.
    //    RESTClient->CloseWhenReady(true);

    cout << "RESTClient->IsClosed: " << RESTClient->IsClosed() << endl;

    return 0;
}
#include <iostream>

#include <boost/lexical_cast.hpp>
#include <boost/fusion/adapted.hpp>

#include "restc-cpp/restc-cpp.h"
#include "restc-cpp/RequestBuilder.h"

using namespace std;
using namespace restc_cpp;

// C++ structure that match the JSON entries received
// from http://jsonplaceholder.typicode.com/posts/{id}
struct Post {
    int userId = 0;
    int id = 0;
    string title;
    string body;
};

// Since C++ does not (yet) offer reflection, we need to tell the library how
// to map json members to a type. We are doing this by declaring the
// structs/classes with BOOST_FUSION_ADAPT_STRUCT from the boost libraries.
// This allows us to convert the C++ classes to and from JSON.

BOOST_FUSION_ADAPT_STRUCT(
    Post,
    (int, userId)
    (int, id)
    (string, title)
    (string, body)
)

// The C++ main function - the place where any adventure starts
int main() {
    // Create an instance of the rest client
    auto rest_client = RestClient::Create();

    // Create and instantiate a Post from data received from the server.
    Post my_post = rest_client->ProcessWithPromiseT<Post>([&](Context& ctx) {
        // This is a co-routine, running in a worker-thread

        // Instantiate a Post structure.
        Post post;

        try {

            // Serialize it asynchronously. The asynchronously part does not really matter
            // here, but it may if you receive huge data structures.
            SerializeFromJson(post,

                // Construct a request to the server
                RequestBuilder(ctx)
                    .Get("http://jsonplaceholder.typicode.com/posts/1")

                    // Add some headers for good taste
                    .Header("X-Client", "RESTC_CPP")
                    .Header("X-Client-Purpose", "Testing")

                    // Send the request
                    .Execute());

        } catch (const exception& ex) {
            clog << "Caught exception: " << ex.what() << endl;
        }

        // Return the post instance trough a C++ future<>
        return post;
    })

    // Get the Post instance from the future<>, or any C++ exception thrown
    // within the lambda.
    .get();

    // Print the result for everyone to see.
    cout << "Received post# " << my_post.id << ", title: " << my_post.title;
}
jgaa commented 1 year ago

I committed a fix for this problem some days ago. Have you pullet the latest changes from github?

bbogart commented 1 year ago

Forgot to pull; I confirm things are working with and without using the request builder method.