How to get body of response with reqwest? - rust

I'm trying to send a GET request to the Binance API. But I'm getting this output in my terminal instead of the data:
Response { url: Url { scheme: "https", cannot_be_a_base: false, username: "", password: None, host: Some(Domain("api.binance.com")), port: None, path: "/api/v3/exchangeInfo", query: Some("symbol=BNBBTC"), fragment: None }, status: 200, headers: {"content-type": "application/json;charset=UTF-8", "content-length": "1515", "connection": "keep-alive", "date": "Thu, 23 Dec 2021 23:28:24 GMT", "server": "nginx", "vary": "Accept-Encoding", "x-mbx-uuid": "1244d760-2c41-46df-910f-b95c4a312bc2", "x-mbx-used-weight": "10", "x-mbx-used-weight-1m": "10", "strict-transport-security": "max-age=31536000; includeSubdomains", "x-frame-options": "SAMEORIGIN", "x-xss-protection": "1; mode=block", "x-content-type-options": "nosniff", "content-security-policy": "default-src 'self'", "x-content-security-policy": "default-src 'self'", "x-webkit-csp": "default-src 'self'", "cache-control": "no-cache, no-store, must-revalidate", "pragma": "no-cache", "expires": "0", "access-control-allow-origin": "*", "access-control-allow-methods": "GET, HEAD, OPTIONS", "x-cache": "Miss from cloudfront", "via": "1.1 08b9c2fd11813ffdb8fa03129d0a465d.cloudfront.net (CloudFront)", "x-amz-cf-pop": "FRA56-C2", "x-amz-cf-id": "EBp6UQUM3B2Lz0iAoPM88INjL4C0ugIgxmaoTPzi0Q4WPxfG46p8Yw=="} }
My code looks like this:
async fn main() {
let client = Client::new();
let res = client.get("https://api.binance.com/api/v3/exchangeInfo?symbol=BNBBTC")
// .header(USER_AGENT, "Mozilla/5.0 (Macintosh; Intel Mac OS X 12_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36")
// .header(CONTENT_TYPE, "application/json")
// .header(CACHE_CONTROL, "no-store")
// .header(PRAGMA, "no-cache")
.send().await;
println!("{:?}", res.unwrap());
}
What am I doing wrong?

The Response that you're printing is basically just the initial HTTP info (e.g. status and headers). You'll need to wait for the payload as well using methods depending on what you're expecting:
bytes/bytes_stream/chunk to get the raw data
text/text_with_charset to get the data as a string
json to deserialize the data into a structured type (see the docs for serde_json for more info)
In this case it looks like you're getting a JSON payload so using .json() into a deserializable type sounds like the right way to go, but if your only goal is to print it then .text() is probably the simpler approach.
async fn main() {
let client = Client::new();
let res = client
.get("https://api.binance.com/api/v3/exchangeInfo?symbol=BNBBTC")
.send()
.await
.expect("failed to get response")
.text()
.await
.expect("failed to get payload");
println!("{}", res);
}
Related:
Where is the body of a HTTP response stored? (with Rust + reqwest)

Related

Home Assistant - Request failed Error code 400

I made this function to make it possible to send announcements to my google home mini but im getting error code 400 (Bad Request) nonstop. I tried using multiple approaches to this.
In Home-Assistant, i have setup my Google Home and can play media through it but whenever i try it with the "google_say" API of Home-Assistant it doesn't work.
I also tried calling the Home-Assistant API over my Phone using an App called "API Client" but i got the same response.
function ttsGoogleHome(text) {
var ttsUrl = "http://127.0.0.1:8123/api/services/tts/google_say?api_password=<MY_PASSWORD>"
var varToken = "<MY_TOKEN>"
var postData = {"entity_id": "media_player.david", "message": `${text}`};
let axiosConfig = {
headers: {
'authorization': `Bearer ${varToken}`,
'Content-Type': 'application/json',
"Access-Control-Allow-Origin": "*",
}
};
axios.post(ttsUrl, postData, axiosConfig)
.then((res) => {
console.log("RESPONSE RECEIVED: ", JSON.stringify(res));
})
.catch((err) => {
console.log("AXIOS ERROR: ", JSON.stringify(err));
})
}
This is the response i get in the server:
{
"message": "Request failed with status code 400",
"name": "Error",
"stack": "Error: Request failed with status code 400\n
at createError (/home/pi/nodejs/node_modules/axios/lib/core/createError.js:16:15)\n
at settle (/home/pi/nodejs/node_modules/axios/lib/core/settle.js:17:12)\n
at IncomingMessage.handleStreamEnd (/home/pi/nodejs/node_modules/axios/lib/adapters/http.js:260:11)\n
at IncomingMessage.emit (events.js:388:22)\n
at endReadableNT (internal/streams/readable.js:1336:12)\n
at processTicksAndRejections (internal/process/task_queues.js:82:21)",
"config": {
"url": "http://127.0.0.1:8123/api/services/tts/google_say?api_password=<MY_PASSWORD>",
"method": "post",
"data": "{\"entity_id\":\"media_player.david\",\"message\":\"Erste Stunde Fach Deutsch Lehrer Schemmer Raum Schemmer\"}",
"headers": {
"Accept": "application/json, text/plain, */*",
"Content-Type": "application/json;charset=UTF-8",
"authorization": "Bearer <MY_TOKEN>",
"Access-Control-Allow-Origin": "*",
"User-Agent": "axios/0.21.1",
"Content-Length": 103
},
"transformRequest": [
null
],
"transformResponse": [
null
],
"timeout": 0,
"xsrfCookieName": "XSRF-TOKEN",
"xsrfHeaderName": "X-XSRF-TOKEN",
"maxContentLength": -1,
"maxBodyLength": -1
}
}
I found my error.
I used the wrong api link
here is the correct way to call it.
function ttsGoogleHome(text) {
var ttsUrl = "http://127.0.0.1:8123/api/services/tts/google_translate_say?api_password=APIPASSWORD"
var varToken = "TOKEN"
var postData = `{"entity_id": "media_player.david", "message": "${text}", "language": "de"}`;
let axiosConfig = {
data: null,
headers: {
'authorization': `Bearer ${varToken}`,
'Content-Type': 'application/json',
"Access-Control-Allow-Origin": "*",
}
};
axios.post(ttsUrl, postData, axiosConfig)
.then((res) => {
console.clear();
console.info("RESPONSE RECEIVED: ", JSON.stringify(res));
})
.catch((err) => {
console.clear();
console.error("AXIOS ERROR: ", JSON.stringify(err));
})
}
also here is my configuration.yaml:
# Configure a default steup of Home Assistant (frontend, api, etc)
# Text to speech
tts:
- platform: google_translate
- language: "de"
- service_name: google_say
-base_url: http://192.168.0.176:8123
group: !include groups.yaml
automation: !include automations.yaml
script: !include scripts.yaml
scene: !include scenes.yaml
homeassistant:
auth_providers:
- type: legacy_api_password
api_password: !secret http_password

Node Fetch request works locally but fails when deployed to heroku

I have written a small function that requests data from a "hidden" API. It is a simple GET request and it works flawlessly when I run locally, but I receive a 403 Forbidden response when I try to deploy to Heroku. The strange thing is it worked several days ago no issue.
address = https://a810-dobnow.nyc.gov/Publish/PublicPortalWrapper/WrapperService.svc/getPublicPortalPropertyDetailsGet/1%7C318%7CWEST%2088%20STREET%7C1
fetch(address, {
headers: {
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",
"accept-language": "en-US,en;q=0.9",
"cache-control": "max-age=0",
"sec-fetch-dest": "document",
"sec-fetch-mode": "navigate",
"sec-fetch-site": "none",
"sec-fetch-user": "?1",
"upgrade-insecure-requests": "1"
// cookie:
// "_ga=GA1.2.1003098932.1594177672; _abck=C3A49F4C33DC14B4AEC19A7EBC5ACB7A~0~YAAQiUMkF2ogZFZzAQAAsvKfZwQfXDkq7euJF0D2hRXAI2V1AruFzXLsOUSc59t7XNzo9iAXH5X1hH0WZkHU2/jyyCcQZO7iOKhK5SV68dIBzVnbK87jIJGLiP5SnbL5Vc9cb+jStzQUpnjOLG59Z1YwYX8pBBNF1xCmUE9p5JJGHt2eA/1A+szh3Fsd7Pa1Y/gNqCYlaMeG9KobxAhWrxPGSI8UqfJEY24rP53Sjkk2RnVQtOm4Ndym5VctS0E078oS7rO2ErVUciCd7KIm6uTqAIZaLCThpn38Lvv7Bw==~-1~-1~-1; WT_FPC=id=f89669c2-f54a-4a08-8417-488c541da028:lv=1595295049155:ss=1595295008235; ASP.NET_SessionId=fvhtl1mq5byqybp0vn4rvkk0; _gid=GA1.2.778374792.1596222780; bm_mi=8303530FA5B983C6A2C37AD6A50F1AB0~DwWIcg/T4TLuW4ckdSijBe3wsAObA+OJOKRuvmdxP64cGhS6O67EOei1JDxamYFX6tF8BcezG1nFQK8QcvDTvBA2ZwT0CifQcO7VSg9cAcLoYATKRjs0vahwRFMGb3hpm826BjjMbcMrdU0GW0U1JQxqNbtc5kFZBM3022kwyv0VGDzY3NNW7BWZHqToNvx2z6qHKJgnDb1V0TeZ/AjuFP89BX784OrYSTkaiSZLbg6BnM3HmCXznFFf0+IaMhxU2MgqrGwoITehpPQfuzZ2HGx1hhfWZ0xa0MGKZnZhd0w=; ak_bmsc=728816F52039F5DD770B7D4B6536D20717408D2C753F0000316D245FB7EF451C~plc8JV/GDGNtsmUQ/jrI8X7yLHGcKff9dnWlC62FcsMQEB3ENDDfKz8e1f/edVvmGAOdiXJdHstFeuQG18Sg95iSqHA8xYy6vqPUNz2DcmRmIK4ZqgsMgXgQ7SLSvxOxFTfsIkV83XG87pDK8HpYh6oO1sI33FGj5ZsRe2y8o88+cY/xI1CAuqtQNZbHf3SzhfkP0tzqeLtUoHuH8nZPyIluF4LxGSI9WhTndADPDMap+F9gPOm12ubgZRwcgJPKyl; NSC_mc_qfstjtuhspvq_dppljf=ffffffff9eb452ce45525d5f4f58455e445a4a42378b; bm_sv=CFE4F539604441CC66374C6DBD92BDD0~7PQFpr8AaDw9KWD30agg7TrTgydB1GbhFszaf7zAlHQJaWLcaclQ66BxK2PBdF3XoSvzzvutMQWGxTWg+TRgOudrUF1Gu8yUwwIv0IMv7imBGm1deHPcQCoDo3JsMZud2DJsOC35QEV60c4j8CuPjQ=="
},
referrer: "https://a810-dobnow.nyc.gov/publish/",
referrerPolicy: "no-referrer-when-downgrade",
body: null,
method: "GET",
mode: "cors"
})

API Gateway randomly switching responses from 403 to 404

I have API Gateway set up to serve some files from S3 bucket with Lambda. When I try to request non-existing files, API Gateway sometimes responds with 403 Forbidden (most of the times and doesn't even trigger Lambda function) and sometimes with 404 Not Found error (I'd like to trigger 404 in such cases).
My Lambda function is very simple:
exports.handler = async event => {
try {
const Bucket = 'testing-bucket';
const Key = `${event.documentType}/${event.requestParams.document}`;
const file = await s3.getObject({ Bucket, Key }).promise();
return {
body: file.Body.toString('base64'),
headers: {
'Content-Disposition': `attachment; filename=test.jpg`,
'Content-Length': file.ContentLength,
'Content-Type': file.ContentType,
},
statusCode: 200,
isBase64Encoded: true,
};
} catch (e) {
return {
body: JSON.stringify({ message: `[${e.code}] ${e.message}` }),
statusCode: e.statusCode,
};
}
};
IAM Role attached to Lambda function is configured in this way:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:GetObject",
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::testing-bucket",
"arn:aws:s3:::testing-bucket/*"
]
}
]
}
Caching is completely disabled in API Gateway and the command I've been trying to test this out is:
curl -X GET -H 'Authorization: 123xyz' -H 'Accept: image/jpeg' -H 'Cache-Control: no-cache' -I https://test.com/existing_folder/non-existing-file.xxx
Responses are:
HTTP/2 403
content-type: application/json
content-length: 60
date: Mon, 07 Oct 2019 10:32:30 GMT
x-amzn-requestid: ae870104-9045-4c23-9794-226992bad591
x-amzn-errortype: AccessDeniedException
x-amz-apigw-id: BMAZwGSyoAMFftw=
x-cache: Error from cloudfront
via: 1.1 ccf34ecc11e5579d8083b17d9d39a622.cloudfront.net (CloudFront)
x-amz-cf-pop: LHR62-C2
x-amz-cf-id: zgtgfJX9TQLcI8F2RLWdgTz-RN_1j7MXblQ1498ucoeFY3dhjitOdg==
and
HTTP/2 404
content-type: application/json
content-length: 59
date: Mon, 07 Oct 2019 10:32:31 GMT
x-amzn-requestid: 2de49681-4f21-4cd1-989c-9b36327badb1
x-amz-apigw-id: BMAZ5E52IAMFwEg=
x-amzn-trace-id: Root=1-5d9b143f-aadf0a24a5f60f4c939b77c0;Sampled=0
x-cache: Error from cloudfront
via: 1.1 be00537a2361673ea48963d6e04d04a1.cloudfront.net (CloudFront)
x-amz-cf-pop: LHR62-C2
x-amz-cf-id: 9VI26GH3-ZuJSQrEt5Fc7EjuMt8IV0TPzPwna8dvvr6UtsgiqwwIkw==
How to make API Gateway respond in consistent way?
UPDATE:
After observing API Gateway logs and trying to spam the same curl command for existing and non-existing files couple of times in a row, this was the output for non-existing file (timestamps are intact):
# curl -X GET -H 'Authorization: 123xyz' -H 'Accept: image/jpeg' -H 'cache-control: private, no-cache, no-store, max-age=1, s-maxage=1' https://my.url/foo/nobar
{
"requestId": "d19602e8-3a32-4445-b9e6-99f05a59fac4",
"ip": "redacted",
"caller": "-",
"user": "-",
"requestTime": "08/Oct/2019:00:05:03 +0000",
"httpMethod": "GET",
"resourcePath": "/foo/{bar}",
"status": "404",
"protocol": "HTTP/1.1",
"responseLength": "59"
}
# and
{
"requestId": "b33bf6c7-55db-4e1f-b4e4-b1e826139556",
"ip": "redacted",
"caller": "-",
"user": "-",
"requestTime": "08/Oct/2019:00:05:05 +0000",
"httpMethod": "GET",
"resourcePath": "/foo/{bar}",
"status": "403",
"protocol": "HTTP/1.1",
"responseLength": "60"
}
and for existing file:
# curl -X GET -H 'Authorization: 123xyz' -H 'Accept: image/jpeg' -H 'cache-control: private, no-cache, no-store, max-age=1, s-maxage=1' https://my.url/foo/bar
{
"requestId": "122ef31e-c587-470c-a0b5-51c6d9838fe4",
"ip": "redacted",
"caller": "-",
"user": "-",
"requestTime": "07/Oct/2019:23:58:35 +0000",
"httpMethod": "GET",
"resourcePath": "/foo/{bar}",
"status": "403",
"protocol": "HTTP/1.1",
"responseLength": "60"
}
# and then later
{
"requestId": "c8ad1b40-006f-4d03-9d10-c6d91e366380",
"ip": "redacted",
"caller": "-",
"user": "-",
"requestTime": "07/Oct/2019:23:59:58 +0000",
"httpMethod": "GET",
"resourcePath": "/foo/{bar}",
"status": "200",
"protocol": "HTTP/1.1",
"responseLength": "80280"
}
I finally got some time to get back to this issue and it looks like the problem was all along in "authorizer" function which had caching enabled, once disabled, my responses started to respond in a consistent way.
You can manage the response yourself. You can check if the file doesn't exist after your await and respond a 404. Something like this: (Code not tested)
exports.handler = async event => {
try {
const Bucket = 'testing-bucket';
const Key = `${event.documentType}/${event.requestParams.document}`;
const file = await s3.getObject({ Bucket, Key }).promise();
if (!file) {
return {
body: {error: 'File not found'},
headers: {
'Content-Type': 'application/json'
}
statusCode: 400,
};
}
return {
body: file.Body.toString('base64'),
headers: {
'Content-Disposition': `attachment; filename=test.jpg`,
'Content-Length': file.ContentLength,
'Content-Type': file.ContentType,
},
statusCode: 200,
isBase64Encoded: true,
};
} catch (e) {
return {
body: JSON.stringify({ message: `[${e.code}] ${e.message}` }),
statusCode: e.statusCode,
};
}
};
So when I wrote my lambda to process s3 downloads, I did stick with promise chaining so that I could debug easier...have you tried the other method?
return s3.getObject({ Bucket, Key }).promise().then((s3Response) => {
console.log(s3Response.Body.toString());
}).catch((err) => {
console.log(err);
});
I have a feeling there is something happening in your promise that's causing it to come back to quick and it's failing or something along those lines.

Google calendar API - create event 400, Parse Error

Using python 3.6, requests==2.22.0
Trying to create an event: documentation link
url = 'https://www.googleapis.com/calendar/v3/calendars/{}/events'.format(calendar_id)
data = {
"summary": "CALENDAR TESTING",
"location": "Some fake location",
"description": "This is a test",
"start": {
"dateTime": "2019-10-09T09:00:00-07:00",
"timeZone": "America/Los_Angeles",
},
"end": {
"dateTime": "2019-10-09T10:00:00-07:00",
"timeZone": "America/Los_Angeles",
},
}
headers = {
'Authorization': 'Bearer {}'.format(self.access_token.get('token')),
'Accept': 'application/json',
'Content-Type': 'application/json',
}
response = requests.post(url, data=data, headers=headers)
print("Response: ", response)
j = response.json()
print("json: ", j)
Output:
Response: <Response [400]>
Raw Response: {'error': {'errors': [{'domain': 'global', 'reason': 'parseError', 'message': 'Parse Error'}], 'code': 400, 'message': 'Parse Error'}}
request body:
summary=CALENDAR+TESTING&location=Some+fake+location&description=This+is+a+test&start=dateTime&start=timeZone&end=dateTime&end=timeZone
request headers:
{'User-Agent': 'python-requests/2.22.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': 'application/json', 'Connection': 'keep-alive', 'Authorization': 'Bearer ******', 'Content-Type': 'application/json', 'Content-Length': '135'}
Stack posts that did not fix the issue:
stack post - re not setting content type, which I am
stack post - re apostrophes vs double quotes, I switched to double quotes still error
I did notice that the timezone information is missing from the request body, but I am not sure why, and if that is the issue.
I am looking into this post right now
A simple solution to fix the problem:
import json
response = requests.post(url, data=json.dumps(data), headers=headers)

How to log cookies from a cookie jar?

How can I log cookies that are stored in a cookie jar using the request-promise npm module.
I have tried printing the cookie jar variable but as expected that does not work.
How I am creating the jar,
var request = require('request-promise');
var sess = request.jar()
The code sending the request,
request({url: myurl, jar: sess}, function () {
request(
{
url: 'myurl',
method: 'POST',
headers: [
{
"Accept": "application/json",
}
],
postData: {
"xqr":"1"
}
}
)
I expect all the cookies used to send my request to be printed out using console.log()
request uses tough-cookie internally. So you can easily access to tough-cookie store which is an abstract class and use its prototype function getAllCookies.
function logCookies(jar){
jar._jar.store.getAllCookies(function(err, cookieArray) {
if(err) throw new Error("Failed to get cookies");
console.log(JSON.stringify(cookieArray, null, 4));
});
}
And this will log all cookies and its properties.
[
{
"key": "1P_JAR",
"value": "1P_JAR_VALUE",
"expires": "2019-01-23T20:09:38.000Z",
"domain": "google.com",
"path": "/",
"hostOnly": false,
"creation": "2018-12-24T20:09:37.800Z",
"lastAccessed": "2018-12-24T20:09:38.097Z"
},
{
"key": "NID",
"value": "NID_VALUE",
"expires": "2019-06-25T20:09:38.000Z",
"domain": "google.com",
"path": "/",
"httpOnly": true,
"hostOnly": false,
"creation": "2018-12-24T20:09:37.802Z",
"lastAccessed": "2018-12-24T20:09:38.098Z"
}
]
If you only want to get raw cookie string you can just simply use
console.log(cookieArray.map(cookie => cookie.toString()))
And it will give you
[
'1P_JAR=1P_JAR_VALUE; Expires=Wed, 23 Jan 2019 20:15:02 GMT; Domain=google.com; Path=/',
'NID=NID_VALUE; Expires=Tue, 25 Jun 2019 20:15:02 GMT; Domain=google.com; Path=/; HttpOnly'
]

Resources