I need to write a full http request for invoke a SOAP service. I don't have libraries for soap request so i need to write full HTTP packet. this is how i've proceeded (i'm programming an arduino board):
String body = HttpRequestBody("33", "77%");
client.println("POST /dataserver HTTP/1.1");
client.println("Accept: text/xml, multipart/related");
client.println("Content-Type: text/xml; charset=utf-8");
client.println("SOAPAction: \"http://example.com/Functions/SendDataRequest\"");
client.println("User-Agent: Arduino WiFiShield");
client.println("Content-Length: "+body.length());
client.println("Host: arduino-data-server.appspot.com/dataserver");
client.println("Connection: Keep-Alive");
client.println();
client.println(body);
client represent connection to my webservice. This is HttpRequestBody function:
String HttpRequestBody(String v1, String v2) {
Serial.println("Generating xml message...");
String res = "";
res += "<?xml version=\"1.0\"?>\n\r";
res +="<S:Envelope xmlns:S=\"http://schemas.xmlsoap.org/soap/envelope/\"\n\r";
res +="<S:Body>\n\r";
res +="<ns2:sendData xmlsn:ns2=\"http://example.com\">\n\r";
res +="<arg0>"+v1+"</arg0>\n\r";
res +="<arg1>"+v2+"</arg1>\n\r";
res +="</ns2:sendData>\n\r";
res +="</S:Body>\n\r";
res +="</S:Envelope>\n\r";
Serial.println(res);
return res;
}
But something gone wrong and i can't contact webserver. Webserver works and it's reacable, because if i change POST into GET, on webservice log, i see connection. How can i fix it?
In HttpRequestBody you are allocating: String res = ""; and then updating it. Finally, you return res. But, res was allocated on the stack of HttpRequestBody (??), you are NOT guaranteed that it will be there after HttpRequestbody terminates.
You probably need to do the C++ equivalent of malloc used in C code, in order to ensure that res is on the heap and does not get freed.
Related
This question already has an answer here:
Differ between header and content of http server response (sockets)
(1 answer)
Closed 1 year ago.
I'm making this socket HTTP client (very basic). When recv()'ing response data from example.com it works fine and writes it all to a buffer but when I try to revc any bigger amounts of data it stops at around 1500 bytes.
Right now all I'm trying to do is get the response written into the buffer (headers and all). Not trying to parse anything. But that isn't working. It works for a few iterations but then stops or hangs. I'm asking for help identifying the issue with this receive_response() function that causes these behaviors.
This is the function that revc's the HTTP response:
void tcp_client::receive_response(char *buffer) {
int bytes_recv = 0;
int total_bytes_recv = 0;
for (;;) {
bytes_recv = recv(sock, &buffer[total_bytes_recv], CHUNK_SIZE, 0);
if (bytes_recv <= 0) {
break;
} else {
total_bytes_recv += bytes_recv;
}
}
}
The main function:
int main(int argc, char **argv) {
http_client http;
char response[100000] = {0};
http.connect_to_host("go.com", 80);
http.send_request("GET / HTTP/1.1\r\n\r\n");
http.receive_response(response);
std::cout << response << std::endl;
return 0;
}
Thank you
You seem to expect the server to close the connection after the response is transmitted. A typical HTTP 1.1 server doesn't do that by default; they keep the connection open for further requests, unless the client explicitly asks otherwise via Connection: close header.
So, you receive all the data, and then the next recv call is sitting there, waiting for more data to arrive.
An HTTP 1.1 client is expected to detect the end of response via Content-Length header, or by decoding a chunked response as indicated by Transfer-Encoding: chunked header.
I am new to the Boost library. I try to create an Rest HTTP request using the Boost::http library.
My question is how can i simply assign the JSON payload to the http request.
the following code snippet shows my current try which connects successfully but the payload is not assigned.
http::request<http::string_body> req{ http::verb::post, LOGIN_PATH, 10 };
req.set(beast::http::field::content_type, "application/json");
req.body() = std::move(serviceUser);
// Send the HTTP request to the remote host
http::write(stream, req);
// This buffer is used for reading and must be persisted
beast::flat_buffer buffer;
// Declare a container to hold the response
http::response<http::dynamic_body> res;
// Receive the HTTP response
http::read(stream, buffer, res);
// Write the message to standard out
std::cout << res << std::endl;
return "OK";
The following snippet shows the JSON message.
std::stringstream strStream;
strStream << "{\"userName\" : lena, \"password\" : liebe }";
serviceUser = strStream.str();
Can you give me a simple example please. Importent to mention is that i use the Boost library version 1.70.
I would like to periodically http GET some value from a website.
with beast, the code is like:
// Set up an HTTP GET request message
http::request<http::string_body> req{http::verb::get, target, version};
req.set(http::field::host, host);
req.set(http::field::user_agent, BOOST_BEAST_VERSION_STRING);
http::response<http::string_body> res;
while (true) {
// Send the HTTP request to the remote host
http::write(stream, req);
res = {};
// Receive the HTTP response
http::read(stream, buffer, res);
// Write the message to standard out
std::cout << res << std::endl;
}
But in the loop,
res = {}
brought one temporary object creation, one move/copy assignment and the destroy of the temporary object.
I am wondering if there is a better way to avoid these unnecessary costs。
Just remove the offending line and move the declaration of res inside the while loop. res will then be created and destroyed each time round the loop:
// Set up an HTTP GET request message
http::request<http::string_body> req{http::verb::get, target, version};
req.set(http::field::host, host);
req.set(http::field::user_agent, BOOST_BEAST_VERSION_STRING);
while (true) {
// Send the HTTP request to the remote host
http::write(stream, req);
// Receive the HTTP response
http::response<http::string_body> res;
http::read(stream, buffer, res);
// Write the message to standard out
std::cout << res << std::endl;
}
You can replace res = {}; with res.clear(); or drop it altogether.
The read free function instantiates a parser that takes ownership of all response resources (by move)¹. In its constructor, it unconditionally clears the message object anyways:
You can use a debugger to trace through these lines with a simple tester like this, which is exactly what I did.
¹ in the end the message is moved back into the response object passed, so it works out
Here's what I would do
http::read(stream, buffer, res);
// move the response object to the processing function
process(std::move(res)); // res will be as clean as newly constructed after being moved
I have a MYSQL database linked to a webpage which, through some PHP code pushes pertinent data using JSON to a blank page. I am trying to grab this data using C++ and sockets and verifying a Key using the URL data.
I have a method of doing this using C# and a simple WebClient().DownloadString but I am having issues translating it to C++.
I have tried using some of the other libs mentioned around stackoverflow but haven't had the luck I was hoping for. Any help would be greatly appreciated.
char* SocketRequest_URL(char* URL, char* Key, int sign, char* Path = "")
{
char *begin = bufferReturn;
char *end = begin + sizeof(bufferReturn);
std::fill(begin, end, 0);
Host = gethostbyname(URL);
SocketAddress.sin_addr.s_addr = *((unsigned long*)Host->h_addr);
SocketAddress.sin_family = AF_INET;
SocketAddress.sin_port = SERVER_PORT;
Socket = socket(AF_INET, SOCK_STREAM, 0);
if (connect(Socket, (struct sockaddr *)&SocketAddress, sizeof(SocketAddress)) != CELL_OK) {
return "CONNECTION ERROR";
}
strcpy(RequestBuffer, "GET /");
strcat(RequestBuffer, Path);
char sign_Buf[15];
sprintf(sign_Buf, "%d", sign);
strcat(RequestBuffer, sign_Buf);
strcat(RequestBuffer, "/");
strcat(RequestBuffer, Key);
strcat(RequestBuffer, " HTTP/1.0\r\nHOST: ");
strcat(RequestBuffer, URL);
strcat(RequestBuffer, "\r\nUser-Agent: Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36");
strcat(RequestBuffer, "\r\n\r\n");
send(Socket, RequestBuffer, strlen(RequestBuffer), 0);
while (recv(Socket, bufferReturn, 1024, 0) > 0)
{
return bufferReturn;
}
}
Essentially, the page I am trying to pull data from has information pushed to it. I want to grab that data and save it. I can't even seem to get connected to the link.
I would not recommend to use socket API for HTTP/S interactions. Here is a list of the C/C++ libraries/stacks that may be used for that, for example:
WinInet
WinHttp
Qt
POCO
Boost.Asio
libcurl
Also, you should add if condition for socket API functions and check returning values which could give you a tip about what's happening in your code. And your code returns from the function after first successful call to the recv that is incorrect as you must expect as much calls to recv as needed, until it returns -1.
My Goal: I'm trying to submit a post request
Issue: The post request is not being sent or is not formatted correctly or missing some information. I'm not sure about the exact problem.
What is a Successful Post Request: A successful post request to the website will change the user's Country location, this is part of the website feature.
Current Program Output: I get a 200 -OK response and No errors
Additional information: In my code below, I am making 2 requests.
From the first request, i receive and read an html page to find the RequestDigest value. This value is needed to perform a post request to a SharePoint website.
For the second request, I use the RequestDigest Value found and add it in post request and change the user's country to Canada ("CA").
Note: I'm new to C++ / MFC and I have been stuck on the problem for over a week! any help and/or guidance will be greatly appreciated
void CUserPassDiag::Connect(){
CInternetSession session(_T("My Session"));
CHttpConnection* pServer = NULL;
CHttpFile* pFile = NULL;
char *szBuff = new char[100000];
try
{
// First Request to retrieve the Digest value
CString strServerName = _T("boration.ptt.ca");
CString headers = _T("Content-Type: application/x-www-form-urlencoded\r\n");
headers += _T("Host: boration.ptt.ca\r\n");
headers += _T("Method: POST\r\n");
headers += _T("Pragma: no-cache\r\n");
headers += _T("Accept: application/x-ms-application, image/jpeg, application/xaml+xml, image/gif, image/pjpeg, application/x-ms-xbap, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, *//*\r\n");
headers += _T("UserAgent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; Trident/7.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET CLR 1.1.4322; .NET4.0C; .NET4.0E; InfoPath.3\r\n");
headers += _T("Keep-Alive: true\r\n");
headers += _T("Accept-Language: en-CA\r\n");\
headers += _T("Referer: http://boration.ptt.ca/team/physicallocation/_Layouts/Ptt.PhysicalLocationWidget/Page.aspx\r\n");
// Headers Ready
CString szHeaders = _T(headers);
DWORD dwRet;
// URL
CString strObject = _T("/team/D135/SitePages/PttHome.aspx");
// get connection with username and password
pServer = session.GetHttpConnection(strServerName, INTERNET_DEFAULT_HTTP_PORT, _T("user"), _T("pass"));
// open request
pFile = pServer->OpenRequest(CHttpConnection::HTTP_VERB_POST, strObject);
pFile->AddRequestHeaders(szHeaders);
pFile->SendRequest();
pFile->QueryInfoStatusCode(dwRet); // response is 200 OK
// Data is retrieve and can be queried for digest value
pFile->Read(szBuff,100000);
CString digest = CString(szBuff);
std::string str = digest.GetString();
std::smatch match;
std::regex re("0x(\\d|[A-Z]).*0000");
if (std::regex_search(str, match, re)){
digest = CString(std::string(match[0]).c_str());
}
// close the server and chttpfile
pServer->Close();
pFile->Close();
/*
* Second Request Preperation
*/
// Post data - CA stands for Canada and this value is added to the post reuquest to change the users country
CString country = "CA";
// add a new header
CString xRequestDigest = "X-RequestDigest: " + digest + _T("\r\n");
headers += xRequestDigest;
// formatting the digest value to be placed in the post request
digest.Replace(_T(" "),_T("+"));
digest.Replace(_T(":"),_T("%3A"));
digest.Replace(_T(","),_T("%2C"));
// new URL
strObject = _T("/team/physicallocation/_Layouts/Ptt.PhysicalLocationWidget/Page.aspx");
// post data to be sent
CString strFormData = _T("__REQUESTDIGEST=" + digest + "&__VIEWSTATE=%2FwEPDwUKLTM0MDQ5MzI3MQ9kFgJmD2QWAgIBDxYCHgVjbGFzcwUtbXMtZGlhbG9nIG1zLWRpYWxvZy1uciBtcy1kaWFsb2cgbXMtZGlhbG9nLW5yFgICAw9kFg4CEQ9kFgRmD2QWBAIBD2QWAgIBDxYCHgdWaXNpYmxlaBYCZg9kFgQCAg9kFgQCAQ8WAh8BaGQCAw8WCB4TQ2xpZW50T25DbGlja1NjcmlwdAWPAWphdmFTY3JpcHQ6Q29yZUludm9rZSgnVGFrZU9mZmxpbmVUb0NsaWVudFJlYWwnLDEsIDEsICdodHRwOlx1MDAyZlx1MDAyZnhjb2xsYWJvcmF0aW9uLnB3Yy5jYVx1MDAyZnRlYW1cdTAwMmZwaHlzaWNhbGxvY2F0aW9uJywgLTEsIC0xLCAnJywgJycpHhhDbGllbnRPbkNsaWNrTmF2aWdhdGVVcmxkHihDbGllbnRPbkNsaWNrU2NyaXB0Q29udGFpbmluZ1ByZWZpeGVkVXJsZB4MSGlkZGVuU2NyaXB0BSFUYWtlT2ZmbGluZURpc2FibGVkKDEsIDEsIC0xLCAtMSlkAgMPDxYKHglBY2Nlc3NLZXkFAS8eD0Fycm93SW1hZ2VXaWR0aAIFHhBBcnJvd0ltYWdlSGVpZ2h0AgMeEUFycm93SW1hZ2VPZmZzZXRYZh4RQXJyb3dJbWFnZU9mZnNldFkC6wNkZAIDD2QWAgIBD2QWAgIDD2QWAgIBDzwrAAUBAA8WAh4PU2l0ZU1hcFByb3ZpZGVyBRFTUFNpdGVNYXBQcm92aWRlcmRkAgEPZBYEAgMPZBYCZg9kFgJmDxQrAANkBSNodHRwOi8vbXlzaXRlLnB3Yy5jYTo4MC9QZXJzb24uYXNweAUYaHR0cDovL215c2l0ZS5wd2MuY2E6ODAvZAIFDw8WBB4EVGV4dAUeTGF1bmNoIHRoZSBEZXZlbG9wZXIgRGFzaGJvYXJkHwFoZGQCFw9kFgICAQ9kFgJmD2QWAgIBDw9kFgYfAAUibXMtc2J0YWJsZSBtcy1zYnRhYmxlLWV4IHM0LXNlYXJjaB4LY2VsbHBhZGRpbmcFATAeC2NlbGxzcGFjaW5nBQEwZAIbD2QWAgIBDxAWAh8BaGQUKwEAZAIhD2QWAmYPZBYCAgEPDxYGHgtOYXZpZ2F0ZVVybAWBAWphdmFzY3JpcHQ6IFNob3dEaWFsb2coJ2h0dHA6Ly94Y29sbGFib3JhdGlvbi5wd2MuY2EvdGVhbS9waHlzaWNhbGxvY2F0aW9uL19MYXlvdXRzL1BXQy5QaHlzaWNhbExvY2F0aW9uV2lkZ2V0L0NvdW50cnlQYWdlLmFzcHgnKR4HVG9vbFRpcAUyQ291bnRyeTogQ2FuYWRhDQpFeHBpcmVzIGluOiAyMyAgSG91ciAgNTcgIE1pbnV0ZXMeCEltYWdlVXJsBTkvX2xheW91dHMvc3RhdGljL1BXQy5QaHlzaWNhbExvY2F0aW9uV2lkZ2V0L2ltYWdlcy9DQS5wbmdkZAInD2QWAmYPZBYCAgEPDxYCHwwFFlRFQ0hOSUNBTCBEQVRBIEFMTE9XRURkZAJFD2QWAgIJD2QWAgIBDw8WAh8BaGQWAgIDD2QWAmYPZBYCAgMPZBYCAgUPDxYEHgZIZWlnaHQbAAAAAAAAeUABAAAAHgRfIVNCAoABZBYCAgEPPCsACQEADxYEHg1QYXRoU2VwYXJhdG9yBAgeDU5ldmVyRXhwYW5kZWRnZGQCWQ9kFgICAQ9kFgYCAQ8PFgIfAWhkFgoCAQ8PFgIfDAUvQ29uZmlybSB5b3VyIGN1cnJlbnQgcGh5c2ljYWwgbG9jYXRpb24gY291bnRyeS5kZAIDDw8WAh8MBRlBcmUgeW91IHN0aWxsIGluIENhbmFkYSA%2FZGQCBQ8PFgIfDAXlAVRvIGRldGVybWluZSBpZiB0ZWNobmljYWwgZGF0YSBpcyBhY2Nlc3NpYmxlLCB5b3VyIGN1cnJlbnQgcGh5c2ljYWwgbG9jYXRpb24gbXVzdCBiZSBwcm92aWRlZC4gPGJyLz48YnIvPg0KQWxsIHZhbHVlcyBwcm92aWRlZCBhcmUgbG9nZ2VkIGFuZCByZXRhaW5lZCBmb3IgYXVkaXRpbmcgcHVycG9zZXMuPGJyLz48YnIvPk1ha2Ugc3VyZSB0byB1cGRhdGUgbG9jYXRpb24gd2hlbiB0cmF2ZWxsaW5nLiBkZAIHDw8WAh8MBQNZZXNkZAIJDw8WAh8MBQJOb2RkAgMPDxYCHwFnZBYUZg8PFgIfDAU1UGxlYXNlIHNlbGVjdCB5b3VyIGN1cnJlbnQgcGh5c2ljYWwgbG9jYXRpb24gY291bnRyeS5kZAIBDw8WAh8MBQhDb3VudHJ5OmRkAgIPDxYCHwwFC1VzZXIgTmFtZSA6ZGQCAw8PFgIfDAUdUmFuYSwgVGFpbW9vciAgICAgICAgICAgICBQV0NkZAIEDw8WAh8MBQZEYXRlIDpkZAIFDw8WAh8MBQtIb3N0IE5hbWUgOmRkAgYPDxYCHwwFGUY1LU1haW5JUC1pbnRlcm5hbC5wd2MuY2FkZAIIDw8WAh8MBeUBVG8gZGV0ZXJtaW5lIGlmIHRlY2huaWNhbCBkYXRhIGlzIGFjY2Vzc2libGUsIHlvdXIgY3VycmVudCBwaHlzaWNhbCBsb2NhdGlvbiBtdXN0IGJlIHByb3ZpZGVkLiA8YnIvPjxici8%2BDQpBbGwgdmFsdWVzIHByb3ZpZGVkIGFyZSBsb2dnZWQgYW5kIHJldGFpbmVkIGZvciBhdWRpdGluZyBwdXJwb3Nlcy48YnIvPjxici8%2BTWFrZSBzdXJlIHRvIHVwZGF0ZSBsb2NhdGlvbiB3aGVuIHRyYXZlbGxpbmcuIGRkAgoPDxYCHwwFB0NvbmZpcm1kZAILDw8WAh8MBQZDYW5jZWxkZAIFD2QWBAIBDw8WAh8MBQdXYXJuaW5nZGQCBQ8PFgIfDAUFQ2xvc2VkZGQShQ6UJsf5SBoFkPM2ZpdvDrHQ0w%3D%3D&__EVENTVALIDATION=%2FwEWBwKO0pUjAqmflsILAs2u9bkDAr36nu0CAo3uhpUDAubLxrYNAtT449AGHxcQwoL9xE%2FsJbw1%2BBKH5DPplNg%3D&ctl00%24PlaceHolderSearchArea%24ctl01%24ctl00=http%3A%2F%2Fxcollaboration.pwc.ca%2Fteam%2Fphysicallocation&InputKeywords=Search+this+site...&ctl00%24PlaceHolderSearchArea%24ctl01%24ctl04=0&countries="+country+"&ctl00%24PlaceHolderMain%24inpHide="+country+"&ctl00%24PlaceHolderMain%24btnSave=Confirm&__spText1=&__spText2=");
// Open connection
pServer = session.GetHttpConnection(strServerName, INTERNET_DEFAULT_HTTP_PORT, _T("user"), _T("pass"));
pFile = pServer->OpenRequest(CHttpConnection::HTTP_VERB_POST, strObject);
// add 'content-length' header
CString contentLength;
contentLength.Format("Content-Length: %d",strFormData.GetLength());
contentLength+= "\r\n";
headers += _T(contentLength);
szHeaders = _T(headers);
// add the header
pFile->AddRequestHeaders(szHeaders);
//make a new request
bool res = pFile->SendRequest(szHeaders,(LPVOID)(LPCSTR)strFormData ,strFormData.GetLength());
pFile->QueryInfoStatusCode(dwRet);
TRACE("\n%d\n",res);
}catch(CInternetException* e){
TCHAR sz[1024];
e->GetErrorMessage(sz,1024);
_tprintf_s(_T("ERROR! %s\n"), sz);
e->Delete();
}
session.Close();
}
Your code seems fine, make sure you are getting the digest value from the right address.
Code looks good but I'm missing a pFile->EndRequest(); -- but that shouldn't be the cause.
If I were you, I'd check the server responses i.e. have a look at the servers access log. You need to be more specific here so people can help. If you get a 200 response, it might be a server problem as well.