So I've taken up the challenge of doing webhooks in c++ and I wanted to just get some help with the post requests. Here is the code I have at the moment, I wanted to send embeds through post requests in C++.
Here is my code along with errors and all, the webhook is still active if you want to test yourself. Am trying to keep it all using windows librarys on purpose.
#include <winsock2.h>
#include <ws2tcpip.h>
#include <string>
#pragma warning(disable:4996)
#pragma comment(lib, "ws2_32.lib")
using namespace std;
int Plug(string address, string port, SOCKET* csock) {
WSADATA WSAData;
WSAStartup(MAKEWORD(2, 0), &WSAData);
PADDRINFOA result;
ADDRINFOA hints;
ZeroMemory(&hints, sizeof(ADDRINFO));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
int res = getaddrinfo(address.c_str(), port.c_str(), &hints, &result);
*csock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (SOCKET_ERROR == connect(*csock, (SOCKADDR*)result->ai_addr, sizeof(SOCKADDR))) return WSAGetLastError();
return 0;
}
void Unplug(SOCKET* csock) {
closesocket(*csock);
WSACleanup();
}
string PostRequest(string host, string query, string data) {
string req = "POST " + query + " HTTP/1.1" + "\r\n";
req += "Host: " + host + "\r\n";
req += "Content-Type: application/x-www-form-urlencoded\r\n";
req += "Content-Length: " + to_string(data.length()) + "\r\n";
req += "Connection: Close";
req += "\r\n\r\n" + data + "\r\n\r\n";
SOCKET s;;
Plug(host, "80", &s);
Send(&s, req);
while (GetAvailable(&s) == 0) Sleep(10);
string result = Receive(&s);
//if (result.find("\r\n\r\n") == string::npos && DEBUG) return result;
//if (result.find("\r\n\r\n") == string::npos) return "PR_INVALID_RESPONSE";
//result = result.substr(result.find("\r\n\r\n") + 4, string::npos);
Unplug(&s);
return result;
}
int main() {
//https://discordapp.com/api/webhooks/705211476553629747/zwzqZMnTTgLtHBm3kc_DvvD71IW9FfE4ur-PQlkgeZhd56cT7UjSJCWI-V8wPiEUWV2w
std::cout<<PostRequest("162.159.129.233","/api/webhooks/705249405141516360/s8ioXr6IZEeuPnMi1O37CmY3o5pUZcu6ho7aIJdieSAqgGyTXOZjkOZdMNe1uJre6dto","{'embeds': [{'description': '**ERROR**: `TESTSTRING`\n', 'color': 4508791}]}");
}
However when I make the post requests it is unable to send my content and instead returns 400. I've tried a few more things but Im wondering if its my query?
HTTP/1.1 301 Moved Permanently
Cache-Control: max-age=3600
Cf-Ray: 58bd304dec1ff2c0-WAW
Cf-Request-Id: 026a1c84ad0000f2c054113200000001
Date: Thu, 30 Apr 2020 00:36:28 GMT
Expires: Thu, 30 Apr 2020 01:36:28 GMT
Location: MYWEBHOOKURL
Server: cloudflare
Set-Cookie: __cfruid=5adc23f36cce3f0a398b8f9e91429b4349ef9314-1588206988; path=/; domain=.discordapp.com; HttpOnly
Vary: Accept-Encoding
Content-Length: 0
Connection: close
And lets say I use discords actual ip instead of resolving it I end up getting
this instead
HTTP/1.1 403 Forbidden
Cache-Control: max-age=15
Cf-Ray: 58bd37782e6dffbc-WAW
Cf-Request-Id: 026a20ff1a0000ffbcc89df200000001
Content-Type: text/plain; charset=UTF-8
Date: Thu, 30 Apr 2020 00:41:21 GMT
Expires: Thu, 30 Apr 2020 00:41:36 GMT
Server: cloudflare
Set-Cookie: __cfduid=d4cb17401215362929759e2da8110f0e31588207281; expires=Sat, 30-May-20 00:41:21 GMT; path=/; domain=.162.159.129.233; HttpOnly; SameSite=Lax
Vary: Accept-Encoding
Content-Length: 16
Connection: close
error code: 1003
If you have any ideas dont hesitate to put them down below, ive been lost on this for a long time.
You could just use D++ to post to webhooks:
https://dpp.dev/classdpp_1_1webhook.html
In fact you could do everything with this lib instead of rolling your own solution.
Related
I wrote code that should query the google.com web page and display its contents, but it doesn't work as intended.
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
int main()
{
int sockfd;
struct sockaddr_in destAddr;
if((sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1){
fprintf(stderr, "Error opening client socket\n");
close(sockfd);
return;
}
destAddr.sin_family = PF_INET;
destAddr.sin_port = htons(80);
destAddr.sin_addr.s_addr = inet_addr("64.233.164.94");
memset(&(destAddr.sin_zero), 0, 8);
if(connect(sockfd, (struct sockaddr *)&destAddr, sizeof(struct sockaddr)) == -1){
fprintf(stderr, "Error with client connecting to server\n");
close(sockfd);
return;
}
char *httprequest1 = "GET / HTTP/1.1\r\n"
"Host: google.com\r\n"
"\r\n";
char *httprequest2 = "GET / HTTP/1.1\r\n"
"Host: http://www.google.com/\r\n"
"\r\n";
char *httprequest3 = "GET / HTTP/1.1\r\n"
"Host: http://www.google.com/\r\n"
"Upgrade-Insecure-Requests: 1\r\n"
"Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9\r\n"
"User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36\r\n"
"\r\n";
char *httprequest = httprequest2;
printf("start send\n");
int send_result = send(sockfd, httprequest, strlen(httprequest), 0);
printf("send_result: %d\n", send_result);
#define bufsize 1000
char buf[bufsize + 1] = {0};
printf("start recv\n");
int bytes_readed = recv(sockfd, buf, bufsize, 0);
printf("end recv: readed %d bytes\n", bytes_readed);
buf[bufsize] = '\0';
printf("-- buf:\n");
puts(buf);
printf("--\n");
return 0;
}
If I send httprequest1, I get this output:
gcc -w -o get-google get-google.c
./get-google
start send
send_result: 36
start recv
end recv: readed 528 bytes
-- buf:
HTTP/1.1 301 Moved Permanently
Location: http://www.google.com/
Content-Type: text/html; charset=UTF-8
Date: Fri, 09 Sep 2022 11:52:16 GMT
Expires: Sun, 09 Oct 2022 11:52:16 GMT
Cache-Control: public, max-age=2592000
Server: gws
Content-Length: 219
X-XSS-Protection: 0
X-Frame-Options: SAMEORIGIN
<HTML><HEAD><meta http-equiv="content-type" content="text/html;charset=utf-8">
<TITLE>301 Moved</TITLE></HEAD><BODY>
<H1>301 Moved</H1>
The document has moved
here.
</BODY></HTML>
--
In httprequest2, I specified the parameter Host: and I got the following this output:
gcc -w -o get-google get-google.c
./get-google
start send
send_result: 48
start recv
end recv: readed 198 bytes
-- buf:
HTTP/1.1 400 Bad Request
Content-Length: 54
Content-Type: text/html; charset=UTF-8
Date: Fri, 09 Sep 2022 11:53:19 GMT
Connection: close
<html><title>Error 400 (Bad Request)!!1</title></html>
--
Then I try copy headers from browser and after httprequest3 I got same result as for httprequest2.
How can I get the full page?
It should be Host: www.google.com and not Host: http://www.google.com/
However, it might not give you the home page. Google wants you to use HTTPS, so it'll probably redirect you to https://www.google.com/ and you won't be able to implement HTTPS fully yourself (you'll have to use a library like OpenSSL)
I'm struggeling with doing a GET request passing a query string. Everytime I run it I get 410 gone response, I checked if the link got deleted but it's still accessible.
My code:
CURLUcode rc;
CURLU* url = curl_url();
CURL* handle = curl_easy_init();
CURLcode res;
struct curl_slist* list = NULL;
if (handle) {
rc = curl_url_set(url, CURLUPART_HOST, "www.example.com", 0);
rc = curl_url_set(url, CURLUPART_QUERY,"b=sashio", CURLU_APPENDQUERY);
rc = curl_url_set(url, CURLUPART_QUERY, "id=me_ZwNjBQRlZGL0AwR0ZQNjAQR1AQZ3At==", CURLU_APPENDQUERY);
rc = curl_url_set(url, CURLUPART_SCHEME, "http", 0);
rc = curl_url_set(url, CURLUPART_PATH, "/en/m.php?", 0);
curl_easy_setopt(handle, CURLOPT_CURLU, url);
list = curl_slist_append(list, "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9");
curl_easy_setopt(handle, CURLOPT_HTTPHEADER, list);
curl_easy_setopt(handle, CURLOPT_HTTPGET, 1L);
curl_easy_setopt(handle, CURLOPT_VERBOSE, 1L);
curl_easy_setopt(handle, CURLOPT_ENCODING, "");
}
res = curl_easy_perform(handle);
Verbose output :
* Mark bundle as not supporting multiuse
* HTTP 1.0, assume close after body
< HTTP/1.0 410 Gone
< Date: Wed, 12 Aug 2020 19:41:23 GMT
< Server: Apache
< X-UA-Compatible: IE=EmulateIE9
< Last-Modified: Wed, 12 Aug 2020 19:41:23 GMT
< Cache-Control: private, must-revalidate, max-age=0
< Etag: "1804121564-00010031-BZGZ0AGt5BQt2AGD1ZQZ1AmV"
< Set-Cookie: PHPSESSID=tp3mbkk7148cm989areejhhd90; path=/
< Expires: Thu, 19 Nov 1981 08:52:00 GMT
< Pragma: no-cache
< Content-Length: 192
< Connection: close
< Content-Type: text/html
<
* Closing connection 0
Thanks for everyone specially #AndreasWenzel. The browser was sending a cookie to the server like AndreasWenzel said. So in the headers I added the cookie, now I receive a correct response.
Have a nice day!
I've been trying to send HTTP POST Request to facebook without success i get this response from the server :
HTTP/1.1 400 Bad Request Content-Type: text/html; charset=utf-8 Date:
Sat, 10 Dec 2016 21:28:17 GMT Connection: close Content-Length: 2959
Facebook | Error
Sorry, something went wrong We're working on it and we'll get it fixed
as soon as we can
My code
#include <iostream>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <unistd.h>
#include <netdb.h>
#include <fstream>
using namespace std;
int main()
{
int s, error;
struct sockaddr_in addr;
s = socket(AF_INET, SOCK_STREAM, 0);
if(s <0)
{
cout<<"Error 01: creating socket failed!\n";
close(s);
return 1;
}
addr.sin_family = AF_INET;
addr.sin_port = htons(80);
inet_aton("31.13.90.36",&addr.sin_addr);
error = connect(s,(sockaddr*)&addr,sizeof(addr));
if(error!=0)
{
cout<<"Error 02: conecting to server failed!\n";
close(s);
return 1;
}
const int msgSize = 1042;
char msg[] = "POST /login.php?login_attempt=1 \r\n"
"HTTP/1.1\r\n"
"HOST: facebook.com\r\n\r\n"
"Content-type: application/x-www-form-urlencoded\r\n"
"Content-Length: 41\r\n"
"email=lel#gmail.com&pass=test123&submit=1\r\n" ;
char answ[1042];
//cin.getline(&msg[0],256);
send(s,msg,strlen(msg),0);
ssize_t len;
while((len = recv(s, msg, strlen(msg), 0)) > 0)
{
cout.write(msg, len);
std::cout << std::flush;
}
if(len < 0)
{
cout << "error";
}
close(s);
}
What I did wrong?
There are several errors in your message. This is what you send according to your code:
1 POST /login.php?login_attempt=1 \r\n
2 HTTP/1.1\r\n
3 HOST: facebook.com\r\n\r\n
4 Content-type: application/x-www-form-urlencoded\r\n
5 Content-Length: 41\r\n
6 email=lel#gmail.com&pass=test123&submit=1\r\n
Instead it should be like this:
1 POST /login.php?login_attempt=1 HTTP/1.1\r\n
2 HOST: facebook.com\r\n
3 Content-type: application/x-www-form-urlencoded\r\n
4 Content-Length: 41\r\n
5 \r\n
6 email=lel#gmail.com&pass=test123&submit=1
In detail:
Line 1 and 2 should be a single line, i.e. "method path HTTP-version"
Line 3 should not contain multiple \r\n
instead the empty line \r\n should be after all HTTP headers (new line 5)
the body in line 6 should contain only the data covered by the content-length. The 41 bytes you set there don't include the \r\n you send
Apart from that you don't properly parse the response and instead expect the server to close the connection once the response is done. Since you are using HTTP/1.1 you implicitly use persistent connections (HTTP keep-alive) so the server might actually wait for more requests within the same TCP connection and not close the connection immediately.
I really recommend that you study the standard of HTTP instead of guessing how the protocol might work.
I am fetching some websites with sockets by making a http request and reading the response header like this:
char buffer[1000];
while ((bytesReceived = tcpSocket.Receive(buffer, 1000-1)) > 0)
{
buffer[bytesReceived] = '\0';
myFile << buffer;
memset(buffer, 0, 1000);
}
This is the receive function:
int fsx::TcpSocket::Receive(char* _buffer, size_t _length)
{
int iResult = recv(this->socketHandler, _buffer, _length, 0);
if (iResult >= 0)
{
return iResult;
}
else
{
return SOCKET_ERROR;
}
}
And this part of the response im getting:
HTTP/1.1 200 OK
Date: Tue, 22 Sep 2015 10:46:10 GMT
Content-Type: text/html; charset=utf-8
Transfer-Encoding: chunked
Connection: close
Set-Cookie: __cfduid=d01e9db42c5332c444d5105c2cd9fd9e01442918769; expires=Wed, 21-Sep-16 10:46:09 GMT; path=/; domain=.stackoverflow.com; HttpOnly
Cache-Control: public, no-cache="Set-Cookie", max-age=60
Cf-Railgun: 2b57bd3274 5.38 0.314316 0030 3350
Expires: Tue, 22 Sep 2015 10:47:09 GMT
Last-Modified: Tue, 22 Sep 2015 10:46:09 GMT
Vary: *
X-Frame-Options: SAMEORIGIN
X-Request-Guid: 9921fd42-6fd5-4a34-a839-c87d26b2f39a
Set-Cookie: prov=e6796729-38a7-4754-af17-96349ae78010; domain=.stackoverflow.com; expires=Fri, 01-Jan-2055 00:00:00 GMT; path=/; HttpOnly
Server: cloudflare-nginx
CF-RAY: 229d6ca79fef05b5-ARN
3b19 //<------------- WHAT THE HECK IS THIS?
<!DOCTYPE html>
<html itemscope itemtype="http://schema.org/QAPage">
<head>
As you can see, im getting this characters '3b19' at the end of the response header, what is that? Its a different set of characters every single time and I can't seem to find them at: http://www.stackoverflow.com/questions/12691882/how-to-send-and-receive-data-socket-tcp-c-c which is the page that im fetching.
It is a length of the content send used in "chunked encoding".
RFC2616 3.6.1 Chunked Transfer Coding is describing about "chunked encoding".
I am writing an application for work that needs to perform HTTP requests to a server, and get a response, in JSON back.
At the moment, my code connects to the server, and gets a response back, which is great. However, I also need to send data to the server, which will process it, and send me the jSON.
My problem is that I am able to send, thanks to POSTFIELDS, only a single "field", and won't get any response if I insert more that one.
The code is the following:
// writefunc works, so there is no point adding its code in here
size_t Simulator::writefunc(void *ptr, size_t size, size_t nmemb, struct string *buffer_in);
void Simulator::move(Player *player)
{
std::ostringstream ss_url;
ss_url << API_URL << "functionCalled";
char const *url = ss_url.str().c_str();
std::ostringstream ss_postfields;
// This will not work - And all values do NOT contain any space
// The server do NOT receive any data (in POST and GET)
ss_postfields << "user_name=" << player->getName()
<< "&user_secret=" << player->secret()
<< "&app_id=" << player->getApp_id()
<< "&lat=" << player->getLocation()->getLat()
<<"&lon=" << player->getLocation()->getLon();
// This will not work either
// ss_postfields << "user_name=nicolas&app_id=2";
// This works and will send the data to the server, which will receive and process it.
// ss_postfields << "user_name=" << player->getName();
const char *postfields = ss_postfields.str().c_str();
CURL *curl_handle;
curl_global_init(CURL_GLOBAL_ALL);
curl_handle = curl_easy_init();
if(curl_handle){
struct string s;
init_string(&s);
curl_easy_setopt(curl_handle, CURLOPT_URL, url);
curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, writefunc);
curl_easy_setopt(curl_handle, CURLOPT_NOPROGRESS, 1L);
curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, &s);
curl_easy_setopt(curl_handle, CURLOPT_POSTFIELDS, postfields);
curl_easy_setopt(curl_handle, CURLOPT_POSTFIELDSIZE, strlen(postfields));
curl_easy_setopt(curl_handle, CURLOPT_POST, 1L);
CURLcode res = curl_easy_perform(curl_handle);
if(CURLE_OK != res)
{
printf("Error: %s\n", strerror(res));
exit(0);
}
printf("%s\n", s.ptr);
curl_easy_cleanup(curl_handle);
curl_global_cleanup();
}
}
I thought I would also give the output of CURLOPT_HEADER and CURLOPT_VERBOSE for when I send only 1 value, or multiple values:
When I Send One value only:
* About to connect() to localhost port 8888 (#0)
* Trying ::1...
* connected
* Connected to localhost (::1) port 8888 (#0)
> POST [api_url] HTTP/1.1
Host: localhost:8888
Accept: */*
Content-Length: 22
Content-Type: application/x-www-form-urlencoded
* upload completely sent off: 22 out of 22 bytes
< HTTP/1.1 200 OK
< Date: Sun, 24 Nov 2013 17:25:14 GMT
< Server: Apache
< X-Powered-By: PHP/5.3.20
< Cache-Control: no-cache
< X-Debug-Token: a41727
< Transfer-Encoding: chunked
< Content-Type: application/json
<
* Connection #0 to host localhost left intact
0
HTTP/1.1 200 OK
Date: Sun, 24 Nov 2013 17:25:14 GMT
Server: Apache
X-Powered-By: PHP/5.3.20
Cache-Control: no-cache
X-Debug-Token: a41727
Transfer-Encoding: chunked
Content-Type: application/json
[ OUTPUT FROM SERVER HERE ]
* Closing connection #0
And when I send multiple values:
* About to connect() to localhost port 8888 (#0)
* Trying ::1...
* connected
* Connected to localhost (::1) port 8888 (#0)
> POST [api_url] HTTP/1.1
Host: localhost:8888
Accept: */*
Content-Length: 1
Content-Type: application/x-www-form-urlencoded
* upload completely sent off: 1 out of 1 bytes
< HTTP/1.1 200 OK
< Date: Sun, 24 Nov 2013 17:30:13 GMT
< Server: Apache
< X-Powered-By: PHP/5.3.20
< Cache-Control: no-cache
< X-Debug-Token: d1947e
< Transfer-Encoding: chunked
< Content-Type: application/json
<
* Connection #0 to host localhost left intact
0
HTTP/1.1 200 OK
Date: Sun, 24 Nov 2013 17:30:13 GMT
Server: Apache
X-Powered-By: PHP/5.3.20
Cache-Control: no-cache
X-Debug-Token: d1947e
Transfer-Encoding: chunked
Content-Type: application/json
[ OUTPUT FROM SERVER HERE ]
* Closing connection #0
Well you seem to be missing an equals sign!
ss_postfields << "user_name=" << player->getName()
<< "&user_secret=" << player->secret()
<< "&app_id=" << player->getApp_id()
<< "&lat=" << player->getLocation()->getLat()
<<"&lon=" << player->getLocation()->getLon();
instead of
ss_postfields << "user_name=" << player->getName()
<< "&user_secret" << player->secret()
<< "&app_id=" << player->getApp_id()
<< "&lat=" << player->getLocation()->getLat()
<<"&lon=" << player->getLocation()->getLon();
Since I was in a rush, I rewrote everything using HTTP sockets. But I could not be done with this issue, so I went back to it, and finally found the problem. It seems like cUrl does not really like streams.
I thought I would update this post in the case anyone is having the same issue:
Therefore, something like the following will NOT work:
std::ostringstream ss_postfields;
// This will not work - And all values do NOT contain any space
// The server do NOT receive any data (in POST and GET)
ss_postfields << "user_name=" << player->getName()
<< "&user_secret=" << player->getSecret();
const char *postfields = ss_postfields.str().c_str();
// ...
curl_easy_setopt(curl_handle, CURLOPT_POSTFIELDS, postfields);
CURLcode res = curl_easy_perform(curl_handle);
However, and bizarrely, this will work:
std::string str_postfields;
std::string user_name = player->getName();
std::string user_secret = player->getSecret();
std::string str_postfields = "user_name=" +user_name +"&user_secret=" +user_secret;
const char *postfields = str_postfields.c_str();
// ...
curl_easy_setopt(curl_handle, CURLOPT_POSTFIELDS, postfields);
CURLcode res = curl_easy_perform(curl_handle);