Parse Exception from a simple POST request - c++

I am using sockets in C++ to communicate with Elasticsearch. I can communicate with my ELK instance (it is running in a docker compose, the docker files are here).
Here is the request that I am sending:
POST /twitter/_doc/1? HTTP/1.1
Content-Type: application/json; charset=UTF-8
Host: 127.0.0.1:9200
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36
'{ "post_date" : "2017-11-16T14:12:12", "message" : "trying out Elasticsearch FROM CPPPPP" }'
Connection: close
And here is the response that I am getting from the server:
HTTP/1.1 400 Bad Request
content-type: application/json; charset=UTF-8
content-length: 163
{"error":{"root_cause":[{"type":"parse_exception","reason":"request body is required"}],"type":"parse_exception","reason":"request body is required"},"status":400}
I can confirm that I am sending and receiving this data by counting the bytes being sent and received.
I am confused because when I put that request into the Kibana console, it runs fine.
Any ideas on what would be wrong with the request that I am sending?
Here is the C++ code where I create the request:
std::string post_http = "";
post_http += "POST /twitter/_doc/1? HTTP/1.1\n";
post_http += "Content-Type: application/json; charset=UTF-8\n";
post_http += "Host: ";
post_http += aURL;
post_http += ":9200\n";
post_http += "User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36\n";
post_http += "'{ \"post_date\" : \"2017-11-16T14:12:12\", ";
post_http += "\"message\" : \"trying out Elasticsearch FROM CPPPPP\" }'";
post_http += "\r\nConnection: close\r\n\r\n";
As far as I can tell it would be an issue with how I am creating the request in the code above.

Based on your code, it appears you mixed body inside headers.
Thus you actually have no body in your HTTP message.
Please see https://en.wikipedia.org/wiki/HTTP_message_body for a description of how an HTTP message is composed, specifically pay attention to the need pof having an EMPTY line between headers and body.
In your case, a solution may be to change your post_http string to:
std::string post_http = "";
post_http += "POST /twitter/_doc/1? HTTP/1.1\r\n";
post_http += "Content-Type: application/json; charset=UTF-8\r\n";
post_http += "Host: " + aURL + ":9200\r\n";
post_http += "User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36\r\n";
post_http += "Connection: close\r\n"
post_http += "\r\n"; // EMPTY line between header and body.
post_http += "'{ \"post_date\" : \"2017-11-16T14:12:12\", ";
post_http += "\"message\" : \"trying out Elasticsearch FROM CPPPPP\" }'";
I've also changed line endings to be all consistent "\r\n", as specified in the linked article.
It may also help to define a string const to give a name to the line ending sequence, e.g.:
const char* HTTP_LINE_END = "\r\n";

The problem was coming from not having a Content-Body in the header, as well as how long the Socket was waiting for a response. It seems that contrary to the WinSock documentation, you should not call the shutdown method if you are waiting for a response from the server.
Here is what I ended up having for my finished method:
assert(strcmp(aMethod, "GET") || strcmp(aMethod, "POST") || strcmp(aMethod, "PUT"));
// TODO: Investigate if I actually need to reconnect the socket every time a request is made?
ConnectSocket();
char Request[MAX_REQUEST_LEN];
strcpy_s(Request, MAX_REQUEST_LEN, aMethod);
strcat_s(Request, MAX_REQUEST_LEN, " ");
strcat_s(Request, MAX_REQUEST_LEN, aIndexParam);
strcat_s(Request, MAX_REQUEST_LEN, RequestHeader);
strcat_s(Request, MAX_REQUEST_LEN, "Content-Length: ");
strcat_s(Request, MAX_REQUEST_LEN, std::to_string(strlen(aMsg)).c_str());
strcat_s(Request, MAX_REQUEST_LEN, "\r\n");
strcat_s(Request, MAX_REQUEST_LEN, ConnectionClose);
strcat_s(Request, MAX_REQUEST_LEN, aMsg);
strcat_s(Request, MAX_REQUEST_LEN, "\r\n");
As you can see, I moved to just using char * instead of std::String, which gave me about a 30% performance boost. Also, There is now a Content-Length tag in the header.
This is what my final headers look like:
POST /twitter/_doc/ HTTP/1.1
Content-Type: application/json; charset=UTF-8
Content-Encoding: identity
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36
Host: HTTP/1.1
127.0.0.1:9200
Content-Length: 112
Connection: close
{ "user" : "kimchy", "post_date" : "2018-09-16T14:12:12", "message" : "trying out Elasticsearch FROM c++ BOII" }

Related

Count IP on URLs begins with "domain/product" in Apache access_logs

I try to count the access on a specific URL which begins every time with "shop/product?traffic=ads" with AWK, but I failed.
The following code gives me a counter how often an IP address has accessed these URL:
awk -F'[ "]+' '$7 == "/shop/product?traffic=ads" { ipcount[$1]++ }
END { for (i in ipcount) {
printf "%15s - %d\n", i, ipcount[i] } }' /var/www/vhosts/domain.com/logs/access_ssl_log
An example for the access_log (input-file) is here:
66.249.68.xx- - [19/Dec/2022:09:14:15 +0100] "GET /shop/other-product/1.0" 404 16996 "-" "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 5X Build/MMB29P) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.5304.xxx Mobile Safari/537.36 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)"
109.42.242.xxx - - [19/Dec/2022:09:14:55 +0100] "GET /shop/product?traffic=ads&gclid=Cj0KCQiAtICdBhCLARIsALUBFcFMmvFbA_1EyTTMRDp9IWhDXFA_HCeuEsIBXl886PoaAapen2KdussaAniSEALw_wcB HTTP/1.0" 200 30589 "https://www.google.com/" "Mozilla/5.0 (Linux; Android 11; SM-A515F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Mobile Safari/537.36"
80.187.75.xx - - [20/Dec/2022:06:40:12 +0100] "GET /shop/product HTTP/1.0" 200 10821 "https://www.example.com/shop/product?traffic=ads&gclid=EAIaIQobChMIg_Ks5vWF_AIVAgGLCh3k_gBKEAAYASAAEgKBOfD_BwE&dt=1671461107791" "Mozilla/5.0 (iPhone; CPU iPhone OS 16_0_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1"
The "gclid" and and the "dt"(session cookie) are dynamic.
I try to play with ^ after ads, before /shop, but there will be no results.
I want for example the following output:
6 Clicks from 109.42.242.xxx to /shop/product?traffic=ads&gclid=Cj0KCQiAtICdBhCLARIsALUBFcFMmvFbA_1EyTTMRDp9IWhDXFA_HCeuEsIBXl886PoaAapen2KdussaAniSEALw_wcB
1 Clicks from 80.187.75.xx to https://www.example.com/shop/product?traffic=ads&gclid=EAIaIQobChMIg_Ks5vWF_AIVAgGLCh3k_gBKEAAYASAAEgKBOfD_BwE&dt=1671461107791"
You can check if the string occurs in field 7 using index(), and then store the values of field 1 and field 7 with a space in between as the key, to retrieve both values in the END block by splitting on a space again.
awk -F'[ "]+' 'index($7, "/shop/product?traffic=ads") { ipcount[$1 " " $7]++ }
END { for (i in ipcount) {
parts = split(i, a, " ")
printf ipcount[i] " Clicks from " a[1] " to " a[2] "\n"
}
}' file
Test data
66.249.68.xx- - [19/Dec/2022:09:14:15 +0100] "GET /shop/other-product/1.0" 404 16996 "-" "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 5X Build/MMB29P) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.5304.xxx Mobile Safari/537.36 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)"
109.42.242.xxx - - [19/Dec/2022:09:14:55 +0100] "GET /shop/product?traffic=ads&gclid=Cj0KCQiAtICdBhCLARIsALUBFcFMmvFbA_1EyTTMRDp9IWhDXFA_HCeuEsIBXl886PoaAapen2KdussaAniSEALw_wcB HTTP/1.0" 200 30589 "https://www.google.com/" "Mozilla/5.0 (Linux; Android 11; SM-A515F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Mobile Safari/537.36"
109.42.242.xxx - - [19/Dec/2022:09:15:55 +0100] "GET /shop/product?traffic=ads&gclid=Cj0KCQiAtICdBhCLARIsALUBFcFMmvFbA_1EyTTMRDp9IWhDXFA_HCeuEsIBXl886PoaAapen2KdussaAniSEALw_wcB HTTP/1.0" 200 30589 "https://www.google.com/" "Mozilla/5.0 (Linux; Android 11; SM-A515F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Mobile Safari/537.36"
80.187.75.xx - - [20/Dec/2022:06:40:12 +0100] "GET /shop/product HTTP/1.0" 200 10821 "https://www.example.com/shop/product?traffic=ads&gclid=EAIaIQobChMIg_Ks5vWF_AIVAgGLCh3k_gBKEAAYASAAEgKBOfD_BwE&dt=1671461107791" "Mozilla/5.0 (iPhone; CPU iPhone OS 16_0_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1"
109.42.242.xxx - - [19/Dec/2022:09:15:55 +0100] "GET /shop/product?traffic=ads&gclid=Aj0KCQiAtICdBhCLARIsALUBFcFMmvFbA_1EyTTMRDp9IWhDXFA_HCeuEsIBXl886PoaAapen2KdussaAniSEALw_wcB HTTP/1.0" 200 30589 "https://www.google.com/" "Mozilla/5.0 (Linux; Android 11; SM-A515F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Mobile Safari/537.36"
Output
1 Clicks from 109.42.242.xxx to /shop/product?traffic=ads&gclid=Aj0KCQiAtICdBhCLARIsALUBFcFMmvFbA_1EyTTMRDp9IWhDXFA_HCeuEsIBXl886PoaAapen2KdussaAniSEALw_wcB
2 Clicks from 109.42.242.xxx to /shop/product?traffic=ads&gclid=Cj0KCQiAtICdBhCLARIsALUBFcFMmvFbA_1EyTTMRDp9IWhDXFA_HCeuEsIBXl886PoaAapen2KdussaAniSEALw_wcB
With your shown samples please try following awk code. Using match function to match regex \/shop\/product\?traffic=ads\S+(where escaped / to match literal /) and if match is found then creating an array value with index of $1 FS and matched value. In the END block of this program printing the values as per requirement.
awk '
match($7,/\/shop\/product\?traffic=ads\S+/){
value[$1 FS substr($7,RSTART,RLENGTH)]++
}
END{
for(i in value){
split(i,arr)
print value[i] " Clicks from " arr[1] " to " arr[2]
}
}
' Input_file

NestJS BadRequestException - Unexpected end of JSON input

Starting from few days ago, I've started to receive BadRequestException with Unexpected end of JSON input
This is the trace from NestJS:
mapExternalException(err) {
switch (true) {
// SyntaxError is thrown by Express body-parser when given invalid JSON (#422, #430)
// URIError is thrown by Express when given a path parameter with an invalid percentage
// encoding, e.g. '%FF' (#8915)
case err instanceof SyntaxError || err instanceof URIError:
return new common_1.BadRequestException(err.message); // <--- throws here
default:
return err;
}
}
Application is hosted on AWS and what's weird to me is the url from the request that you can see from sentry log:
{
data: {},
headers: {
accept-encoding: gzip,
content-length: 1024,
content-type: application/json,
host: IP_THAT_SEEMS_FROM_AMAZON,
user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36,
x-amzn-trace-id: Root=1-some-trace-id,
x-forwarded-for: SOME_IP,
x-forwarded-port: 443,
x-forwarded-proto: https
},
method: POST,
query_string: {},
url: https://IP_THAT_SEEMS_FROM_AMAZON/api/report
}
Is it possible that AWS is doing internal requests to /api/report endpoint, and if e.g. HTML is returned application throws Unexpected end of JSON input (because that's usual reason for that error when making requests)?
If anyone is familiar with what's happening, any help would be appreciated!

Oauth2-proxy - 404 error when redirecting to upstream url (Django application web page)

I'm trying to protect a Django application with oauth2-proxy
In the oauth2-proxy configuration: (version 7.2.1 or 7.3.0)
When the upstream url is set to something like this: --upstream="http://127.0.0.1:8000"
the redirection works fine. (and it returns a home page I have defined in the application )
But, if I use an upstream like this: --upstream="http://127.0.0.1:8000/hello"
it returns 404 error instead of the hello page that is also defined in the application
The page http://127.0.0.1:8000/hello is working fine when invoked directly and it returns "GET /hello HTTP/1.1" 200 136
So I would say it is not a problem with the page.
This is the command line I'm using:
oauth2-proxy.exe ^
--http-address=127.0.0.1:4180 ^
--email-domain=* ^
--cookie-secure=false ^
--cookie-secret=adqeqpioqr809718 ^
--upstream="http://127.0.0.1:8000/hello" ^
--redirect-url=http://127.0.0.1:4180/oauth2/callback ^
--oidc-issuer-url=http://127.0.0.1:28081/auth/realms/testrealm ^
--insecure-oidc-allow-unverified-email=true ^
--provider=keycloak-oidc ^
--client-id=oauth2_proxy ^
--ssl-insecure-skip-verify=true ^
--client-secret=L2znXLhGX4N0j3nsZYxDKfdYpXHMGDkX ^
--skip-provider-button=true
When the oauth2-proxy succeeds to redirect (--upstream="http://127.0.0.1:8000"), I get the page and the following output:
This is the output for the oauth2-proxy:
[2022/09/08 10:52:06] [proxy.go:89] mapping path "/" => upstream "http://127.0.0.1:8000"
[2022/09/08 10:52:06] [oauthproxy.go:148] OAuthProxy configured for Keycloak OIDC Client ID: oauth2_proxy
[2022/09/08 10:52:06] [oauthproxy.go:154] Cookie settings: name:_oauth2_proxy secure(https):false httponly:true expiry:168h0m0s domains: path:/ samesite: refresh:disabled
[2022/09/08 10:57:01] [oauthproxy.go:866] No valid authentication in request. Initiating login.
127.0.0.1:54337 - 9bbfcf75-da91-487a-a55e-40472e4adb23 - - [2022/09/08 10:57:01] 127.0.0.1:4180 GET - "/" HTTP/1.1 "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36 Edg/105.0.1343.27" 302 380 0.001
127.0.0.1:54337 - e0d8ed12-e4dd-4da6-9fbb-cf689fc53f8f - mail#gmail.com [2022/09/08 10:57:09] [AuthSuccess] Authenticated via OAuth2: Session{email:mail#gmail.com user:93547bcc-2441-414a-9149-c7533c4f5d23 PreferredUsername:testuser token:true id_token:true created:2022-09-08 10:57:09.789934 -0300 -03 m=+303.019857301 expires:2022-09-08 11:02:09.7839238 -0300 -03 m=+603.013847101 refresh_token:true groups:[role:offline_access role:uma_authorization role:default-roles-testrealm role:account:manage-account role:account:manage-account-links role:account:view-profile]}
[2022/09/08 10:57:09] [session_store.go:163] WARNING: Multiple cookies are required for this session as it exceeds the 4kb cookie limit. Please use server side session storage (eg. Redis) instead.
127.0.0.1:54337 - e0d8ed12-e4dd-4da6-9fbb-cf689fc53f8f - - [2022/09/08 10:57:09] 127.0.0.1:4180 GET - "/oauth2/callback?state=ahuKzCYr7jR4P4mmjniIt67TttZKyxGv4mLfEwKlQio%3A%2F&session_state=86ac9bd1-9756-4916-83e9-ec0496b5b767&code=df3940e5-58f5-49ac-a821-5607f0f2faae.86ac9bd1-9756-4916-83e9-ec0496b5b767.cd30a162-8e4d-4a2d-bff6-168e444aed92" HTTP/1.1 "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36 Edg/105.0.1343.27" 302 24 0.029
127.0.0.1:54337 - d58ace6e-afe9-4737-9b12-dbc17fdd0ca2 - mail#gmail.com [2022/09/08 10:57:09] 127.0.0.1:4180 GET / "/" HTTP/1.1 "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36 Edg/105.0.1343.27" 200 138 0.005
On the Django side I get:
**"GET / HTTP/1.1" 200 138**
When the oauth2-proxy fails to redirect --upstream="http://127.0.0.1:8000/hello"), I get the following output:
This is the output for the oauth2-proxy:
[2022/09/08 10:33:58] [proxy.go:89] mapping path "/hello" => upstream "http://127.0.0.1:8000/hello"
[2022/09/08 10:33:58] [oauthproxy.go:148] OAuthProxy configured for Keycloak OIDC Client ID: oauth2_proxy
[2022/09/08 10:33:58] [oauthproxy.go:154] Cookie settings: name:_oauth2_proxy secure(https):false httponly:true expiry:168h0m0s domains: path:/ samesite: refresh:disabled
[2022/09/08 10:37:20] [oauthproxy.go:866] No valid authentication in request. Initiating login.
127.0.0.1:53615 - 54c0f3d8-b3c0-4d48-8353-fe69be0e4500 - - [2022/09/08 10:37:20] 127.0.0.1:4180 GET - "/" HTTP/1.1 "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36 Edg/105.0.1343.27" 302 380 0.001
127.0.0.1:53615 - 0bec934e-05a3-4cc8-9306-fffc28597c8f - mail#gmail.com [2022/09/08 10:37:28] [AuthSuccess] Authenticated via OAuth2: Session{email:mail#gmail.com user:93547bcc-2441-414a-9149-c7533c4f5d23 PreferredUsername:testuser token:true id_token:true created:2022-09-08 10:37:28.6527488 -0300 -03 m=+210.486252601 expires:2022-09-08 10:42:28.6468518 -0300 -03 m=+510.480355601 refresh_token:true groups:[role:offline_access role:uma_authorization role:default-roles-testrealm role:account:manage-account role:account:manage-account-links role:account:view-profile]}
[2022/09/08 10:37:28] [session_store.go:163] WARNING: Multiple cookies are required for this session as it exceeds the 4kb cookie limit. Please use server side session storage (eg. Redis) instead.
127.0.0.1:53615 - 0bec934e-05a3-4cc8-9306-fffc28597c8f - - [2022/09/08 10:37:28] 127.0.0.1:4180 GET - "/oauth2/callback?state=nox0LM3fIlVU1kamoLBaktByeLCcIWiBvRLdHFIuhd4%3A%2F&session_state=808c0654-c9e7-4593-b5dc-95d3231438ea&code=e220414d-e949-4e2d-8d33-55de96f8f5d4.808c0654-c9e7-4593-b5dc-95d3231438ea.cd30a162-8e4d-4a2d-bff6-168e444aed92" HTTP/1.1 "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36 Edg/105.0.1343.27" 302 24 0.024
127.0.0.1:53615 - 9454773f-cade-46fe-870f-70d09fc49ffb - mail#gmail.com [2022/09/08 10:37:28] 127.0.0.1:4180 GET - "/" HTTP/1.1 "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36 Edg/105.0.1343.27" 404 19 0.000
On the Django side I get:
Nothing. As the Django app is never reached and so there are no logs.
Could you please help me find out what could be happening? I will really appreciate it!!
It doesn't seem to be a problem with the application, as the pages work fine when invoked directly.
If it is a mistake in my oauth2-proxy command line/configuration, I would appreciate someone points me to the error, so I can correct it.
Otherwise, any hint would also be much appreciated.
The only thing I've noticed in the logs of oauth2-proxy is that no matter what I put in the --upstream, the final GET (I think it is the redirection to the upstream) is as follows: GET - "/" ... it is the same in both attempts, and it only succeeds in the first one, because it matches the [proxy.go:89] mapping path "/"
The reason it was giving the 404 error, was that the configuration --upstreams points to a url to which the proxy is going to pass the request once authenticated, but it is not going to redirect to that address unless you specifically ask for it in the original request.
So the correct way of making the request is http://127.0.0.1:4180/hello, which is including the whole path to the endpoint you want to reach. (instead of for example http://127.0.0.1:4180 )

HTTP Response Format incorrect?

I was looking at creating WebServers with C++.
I followed this tutorial by Sloan Kelly, and referenced this answer here.
I wrote (improperly adapted) the following code (showing the sending part only):
std::stringstream make_response;
make_response << "HTTP/1.1 200 OK\r\n";
make_response << "Cache-Control: no-cache, private\r\n";
make_response << "Content-Type: text/html\r\n";
make_response << "Content-Length: 88\r\n";
make_response << "\r\n";
make_response << "Hello!";
std::string finished_response = make_response.str();
send(client, finished_response.c_str(), finished_response.length()+1, 0);
What is the problem here? When I connect to 127.0.0.1:8000, the screen is entirely blank.
I know that the request reaches me, as I have printed it in the console:
C:\Users\Jaideep Shekhar\OneDrive\Documents\Projects\ProjectC++\Library\v0.1\main>webserver.exe
Listening for incoming connections...
Client connected!
Client says: GET / HTTP/1.1
Host: 127.0.0.1:8000
Connection: keep-alive
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.75 Safari/537.36
Sec-Fetch-Mode: navigate
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3
Sec-Fetch-Site: cross-site
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
#_M
Client disconnected.
You can copy the whole source code from here and open up your terminal:
g++ webserver.cpp -o webserver.exe -lws2_32 && webserver.exe
Note that this code is for Windows OS.
Edit: Thanks to a comment, I solved the problem (see code below). Any improvements or problems in the code are welcome to be pointed out.
Edit2: How would I send images? I am opening the file opened with std::ios::binary, but it is not being recieved by the browser.
Thanks!
Okay, looks like the format and headers were all correct after all. I made a mistake in responding that I am sending 88 bytes of content, while I sent 5.
In fact, to avoid these issues, I have now put the HTML in its separate file.
The relevant part looks like this now:
// Transfer the whole HTML to a std::string
std::fstream grab_content("home.html");
std::stringstream make_content;
make_content << grab_content.rdbuf();
std::string finished_content;
finished_content = make_content.str();
// Create the HTTP Response
std::stringstream make_response;
make_response << "HTTP/1.1 200 OK\r\n";
make_response << "Cache-Control: no-cache, private\r\n";
make_response << "Content-Type: text/html\r\n";
make_response << "Content-Length: " << finished_content.length() << "\r\n";
make_response << "\r\n";
make_response << finished_content;
// Transfer it to a std::string to send
std::string finished_response = make_response.str();
// Send the HTTP Response
send(client, finished_response.c_str(), finished_response.length(), 0); // flag = 0

Python-Requests Post request failed with 403 Forbidden

I was trying to use python-requests to login https://www.custommade.com/, but it keeps giving me a "403 forbidden error". I got the post_url and content of payload from httpfox
import requests
post_url = 'https://www.custommade.com/secure/login/api/'
client = requests.session()
csrftoken = client.get('https://www.custommade.com/').cookies['csrftoken']
header_info = {
'User-Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.71 Safari/537.36',
'Content-type': 'application/json'
}
payload = {'_method':'login','csrftoken': csrftoken,'email': MYEMAIL,'password':MYPWS}
r = client.post(post_url, json=payload, headers = header_info)
print r.status_code
Could someone help? I tried to login other website and this works fine.
If you would print the reponse text you are getting you would see that they print error to you that you are not accepting cookies.
When you are doing something like this - always try to simulate the browser as best as possible - that means you have to set-up all the headers and also do the steps the browser does.
So first open the webpage in your browser. Open the dev tools, network tab.
Now click on the login -> you see that the browser does request to the /secure/proxy
So your program has to do it too. Than to the actual request. Ensure that your request looks so much as the request from the browser - check the headers. You can see that they send the token there. (btw they do not send it in the post data as you did in your script). Also they are probably checking some other headers, because when you remove them - it doesn't work. So easiest way is to put all the headers as the browser.
Don't forget about the cookies. But this is done automatically because you are using session from requests.
Anyway this is working code:
import requests
post_url = 'https://www.custommade.com/secure/login/api/'
client = requests.session()
client.get('https://www.custommade.com/')
r = client.get('https://www.custommade.com/secure/proxy/')
csrftoken = r.cookies['csrftoken']
header_info = {
"Host" : "www.custommade.com",
"Connection" : " keep-alive",
"Origin" : " https://www.custommade.com",
"User-Agent" : " Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36",
"Content-Type" : " application/x-www-form-urlencoded",
"Accept" : " */*",
"X-Requested-With" : " XMLHttpRequest",
"X-CSRFToken" : csrftoken,
"DNT" : " 1",
"Referer" : " https://www.custommade.com/secure/proxy/",
"Accept-Encoding" : " gzip, deflate, br",
"Accept-Language" : " en-US,en;q=0.8,cs-CZ;q=0.6,cs;q=0.4,sk;q=0.2,ru;q=0.2",
}
payload = {'_method':'login','email': 'sdfasdf#safs.com','password':'asfdasf', 'remember':True}
r = client.post(post_url, data=payload, headers = header_info)
print r.text
print r.status_code
Print:
{"errors": "Oops! Something went wrong. Please ensure you are sending JSON data."}
400
^^ Means the password is wrong