eddic / fastcgipp

fastcgi++: A C++ FastCGI and Web development platform:
https://fastcgipp.isatec.ca
GNU Lesser General Public License v3.0
310 stars 94 forks source link

tests/curl.cpp cannot POST data to another website #90

Closed hengbenkeji closed 3 years ago

hengbenkeji commented 3 years ago

First I thank you for your great work. Second, I describe the problem when I test the curl.cpp:

           ss << "\"data\":\"This is a test " << std::setfill('0') << std::setw(5) << id << "\"";
           proper.push_back(ss.str());
           ss.clear();
           ss.str(std::string());
           ss << "\"content-length\":\"20\"";
           proper.push_back(ss.str());
           ss.clear();
           ss.str(std::string());
           Fastcgipp::Curl<char> curl;
           curl << "This is a test " << std::setfill('0') << std::setw(5) << id++;
           curl.setCallback(std::bind(callback<char>, curl, proper, std::placeholders::_1));
           curl.setUrl("https://postman-echo.com/post");
           curl.addHeader("Content-Type: text/plain; charset=utf-8");
           curl.addHeader("Accept-Charset: utf-8");
           curler.queue(curl);

In my current work, I need to check user phone number through verification code. The POST data shown above cannot be recognized as a normal POST data by the SMS server. Then I tried to develop my own echo server, which can echo back the submitted POST or JSON data, showing that the above code cannot send formatted POST data that a typical server can reconginze. Then I look through your code, finding out that there were no typical methods like

curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data); // libcurl

So the two servers tested just show that they got a GET request instead of a composed POST request. After a further check, I found that size_t Fastcgipp::Curl_base::readCallback() was not called, so the POST data cannot be generated. Would you explain how to make sure readCallback() being called before the request is submitted to a third server? BTW, I did not find any method to check the ultimate raw POST request content (line by line style) to make sure the format conforms to https://tools.ietf.org/html/rfc7231#section-4.3.3. Hoping you can reply soon Regards PENG, Rui

eddic commented 3 years ago

If you insert data into the Curl object using the stream insertion operator (<< ) it gets sent as a POST request. The curl object doesn't use the easy setopt you're looking at. It uses a callback function that extracts the data from a non-contiguous buffer. Just follow what the above code is doing. It gets sent as a POST request.

hengbenkeji commented 3 years ago

Thank you for your quick reply. I kept trying using curl << REQUEST_BODY and finally it worked. The following code runs on the server, and can send JSON request to a third SMS server.

void OpenAPIHandler::response_get_info5( RequestPtr request ){ int id = 1; std::setlocale( LC_ALL, "en_US.utf8" ); const auto initialFds = openfds(); { Fastcgipp::Curler curler( 1 ); curler.start(); std::stringstream ss; std::vector proper; Fastcgipp::Curl curl;
curl.setCallback( std::bind( &OpenAPIHandler::booking_callback, this, request, curl, proper, std::placeholders::_1 ) ); curl.setUrl("http://yourdomain/echo.cgi"); curl.addHeader("Content-Type: application/json; charset=utf-8"); curl.addHeader("Accept-Charset: utf-8"); std::string requestBody = std::string( "\r\n\r\n{\r\n\r\n\"a\":1\r\n\r\n}" ); curl << requestBody << id++; curler.queue( curl ); curler.stop(); curler.join(); } }

hengbenkeji commented 3 years ago

many thanks to your quick reply. Problem solved

eddic commented 3 years ago

By the way you shouldn't need to set the content-length header. Curl should set that automatically based on the size passed to curl. This is at least what my tests have indicated. If you should happen to notice otherwise let me know.

hengbenkeji commented 3 years ago

It seems that I closed this post too soon. My SMS server could not recognize the request JSON as shown below. So I used https://postman-echo.com/post to debug the information. In the following code, I did not set the Content-Length, and I used application/json, and sent request to the API https://postman-echo.com/post.

void OpenAPIHandler::response_get_info5( RequestPtr request ){ int id = 1; std::setlocale( LC_ALL, "en_US.utf8" ); const auto initialFds = openfds(); { Fastcgipp::Curler curler( 1 ); curler.start(); std::stringstream ss; std::vector proper; Fastcgipp::Curl curl;
curl.setCallback( std::bind( &OpenAPIHandler::booking_callback, this, request, curl, proper, std::placeholders::_1 ) ); curl.setUrl("http://yourdomain/echo.cgi"); curl.addHeader("Content-Type: application/json; charset=utf-8"); curl.addHeader("Accept-Charset: utf-8"); std::string requestBody = std::string( "\r\n\r\n{\r\n\r\n\"a\":1\r\n\r\n}" ); curl << requestBody << id++; curler.queue( curl ); curler.stop(); curler.join(); } }

The API https://postman-echo.com/post responded as follows, failing to recognize the simples json {"a":1} as a json object(json":null").

response_data: {"args":{},"data":"\r\n\r\n0\r\n\r\n{\r\n\r\n\"a\":1\r\n\r\n}1","files":{},"form":{},"headers":{"x-forwarded-proto":"https","x-forwarded-port":"443","host":"postman-echo.com","x-amzn-trace-id":"Root=1-5ffbad5b-1fea46516f9a6aef388bed9a","content-length":"25","accept":"/","content-type":"application/json; charset=utf-8","accept-charset":"utf-8"},"json":null,"url":"https://postman-echo.com/post"}

I tried many times, including jsoncpp Json::Value, but with no luck. Would you explain how to format the data so as to let the server recognize the request body is a JSON object?

Thanks in advance

eddic commented 3 years ago

"\r\n\r\n0\r\n\r\n{\r\n\r\n"a":1\r\n\r\n}1" is absolutely not valid JSON. There is a number one at the end. This is coming the id++. I'm unclear why that's been left in. You may be inclined to read up on iostream usage.

hengbenkeji commented 3 years ago

You are right, sir. Now the server can recognize I am sending a JSON request. Thank you for your quick reply. Many thanks