why timeout did not work in an asyncClient? - httpx

I create a asyncClient by async with httpx.AsyncClient(proxies=proxies,timeout=2.0)as client:
then I send a request
print("get cookie res start", f'xid{xid}')
cookieres = await client.get(url, headers=headers)
print("get cookie res ",f'xid{xid}',t-time.time())
sometimes the requests will cost more than 2s
get cookie res xid0 -4.54197096824646
and sometimes I have to wait several minutes and get an error like httpx.readError

Related

Getting statuscode = 400 while using http.get from dart/flutter on local ip address

My goal:
I want to make a get request to the RestApi that is running on the device in my local network to retrieve JSON data generated by the device.
What happens
The RestApi correctly responds to the browser call from every other device in the network. Bash's curl also works, however when I try to access the data through dart's http.get the program fails to retrieve the JSON data - I'm getting Statuscode 400.
Browser call result
What I have tried:
Messing with URL to make sure it's written correctly,
Setting up headers {"content" : "application/json"}
Using no (default) headers
Running the API call from separate dart file and from function embedded in flutter app. Both resulted in Statuscode 400, though flutter provided some more bug information:
Unhandled Exception: SocketException: OS Error: No route to host, errno = 113. I believe I have also seen the errno = 111 when I tried something else.
Using lower level HttpClient instead of Http
In the flutter app, I can connect to the data stored on firebase through http.get easily enough, but call to the device in local network results in the scenario described above.
Stand-alone dart file
import 'package:dart_http/dart_http.dart' as dart_http;
import 'package:http/http.dart' as http;
import 'dart:io';
import 'dart:convert';
main(List<String> arguments) async {
var url = 'http://192.168.0.5/api/device/state';
http.Response response1 =
await http.get(url, headers: {"content": "application/json"});
print('Response status: ${response1.statusCode}');
print('Response body: ${response1.body}');
print('Response body: ${response1.headers}');
print('Response body: ${response1.reasonPhrase}');
print('Response body: ${response1.request}');
HttpClient()
.getUrl(Uri.parse(
'http://192.168.0.5/api/device/state/')) // produces a request object
.then((request) => request.close()) // sends the request
.then((response) {
print(response);
response.transform(Utf8Decoder()).listen(print);
}); // transforms and prints the response
}
Call-embedded in flutter project
Future<Map<String, String>> getAirSensorStatus() async {
print("Getting Air Sensor");
http.Response response =
await http.get('http://192.168.0.5/api/device/state');
print(response);
print("Status code " + "${response.statusCode}");
try {
if (response.statusCode != 200 && response.statusCode != 201) {
print("Something went wrong. Status not 200 and not 201 for AirSensor");
return null;
}
final responseData = json.decode(response.body);
return responseData;
} catch (error) {
print(error);
return null;
}
}
I'm expecting to get statuscode 200 and JSON data in the response. Instead, only a response object is created and no connection established.
Could you help me figure out why is that?
The link to the documentation of the API I'm trying to access:
https://technical.blebox.eu/
The issue seems likely to be caused by the client unable to reach the local server on the request. HTTP error 400 is labelled as "Bad request" and "No route to host, errno = 113" usually caused by network error. A common fix to this issue is to ensure that the client is on the same network the local server is hosted in.

HTTP Request to Google Drive

I am trying to make an HTTP call to get the files content in drive using the below code:
string strbearer = 'Bearer '+strKey;
string strfileID = '1aIl4Sv**********hW';
Http http = new Http();
HttpRequest req = new HttpRequest();
req.setMethod('GET');
req.setEndpoint('https://www.googleapis.com/drive/v3/files/strfileID');
req.setHeader('content-type', 'text/plain');
req.setHeader('Authorization',strbearer);
req.setTimeout(60*1000);
HttpResponse resp = http.send(req);
but when i execute the above code i'm getting error as invalid credentials. please let me know if, this is the correct way to make a get request to drive using Apex(salesforce).

App Script sends 405 response when trying to send a POST request

I have published an app script publicly (Anyone, even anonymous) with a doPost method as follow,
function doPost(e){
var sheet = SpreadsheetApp.getActiveSheet();
var length = e.contentLength;
var body = e.postData.contents;
var jsonString = e.postData.getDataAsString();
var jsonData = JSON.parse(jsonString);
sheet.appendRow([jsonData.title, length]);
var MyResponse = "works";
return ContentService.createTextOutput(MyResponse).setMimeType(ContentService.MimeType.JAVASCRIPT);
}
When I sent a Post request with a JSON object with Advanced Rest Client it all works and return a 200 OK response. But when I try to send a post request with the react axios from a locally hosted react app it sends a 405 Response.
XMLHttpRequest cannot load https://script.google.com/macros/s/AKfycbzyc2CG9xLM-igL3zuslSmNY2GewL5seTWpMpDIQr_5eCod7_U/exec. Response for preflight has invalid HTTP status code 405
I have enabled cross origin resource sharing in the browser as well. The function that sends the POST request is as follow,
axios({
method:'post',
url:'https://script.google.com/macros/s/AKfycbzyc2CG9xLM-igL3zuslSmNY2GewL5seTWpMpDIQr_5eCod7_U/exec',
data: {
"title": 'Fred',
"lastName": 'Flintstone'
}
}).then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
You missed the important part:
Response for preflight has invalid HTTP status code 405.
Your browser is making a preflight request, which uses the OPTIONS HTTP method. This is to check whether the server will allow the POST request – the 405 status code is sent in the response to the OPTIONS request, not your POST request.
A CORS preflight request is a CORS request that checks to see if the CORS protocol is understood. Source
Additionally, for HTTP request methods that can cause side-effects on server's data (in particular, for HTTP methods other than GET, or for POST usage with certain MIME types), the specification mandates that browsers "preflight" the request, soliciting supported methods from the server with an HTTP OPTIONS request method, and then, upon "approval" from the server, sending the actual request with the actual HTTP request method. Source
Some requests don’t trigger a CORS preflight. Those are called "simple requests" in this article [...] Source
This article section details the conditions a request has to meet to be considered a "simple request".
[...] "preflighted" requests first send an HTTP request by the OPTIONS method to the resource on the other domain, in order to determine whether the actual request is safe to send. Cross-site requests are preflighted like this since they may have implications to user data. Source
This article section details the conditions which cause a request to be preflighted.
In this case, the following is causing the request to be preflighted:
[...] if the Content-Type header has a value other than the following:
application/x-www-form-urlencoded
multipart/form-data
text/plain
The value for the Content-Type header is set to application/json;charset=utf-8 by axios. Using text/plain;charset=utf-8 or text/plain fixes the problem:
axios({
method: 'post',
url: 'https://script.google.com/macros/s/AKfycbzyc2CG9xLM-igL3zuslSmNY2GewL5seTWpMpDIQr_5eCod7_U/exec',
data: {
title: 'Fred',
lastName: 'Flintstone',
},
headers: {
'Content-Type': 'text/plain;charset=utf-8',
},
}).then(function (response) {
console.log(response);
}).catch(function (error) {
console.log(error);
});
Another way for someone who has this problem in the future:
(in my case, using 'Content-Type': 'text/plain;charset=utf-8' doesn't work)
According to this doc https://github.com/axios/axios#using-applicationx-www-form-urlencoded-format
Instead of using text/plain;charset=utf-8 as the accepted answer, you can use application/x-www-form-urlencoded:
const axios = require('axios')
const qs = require('qs')
const url = 'https://script.google.com/macros/s/AKfycbzyc2CG9xLM-igL3zuslSmNY2GewL5seTWpMpDIQr_5eCod7_U/exec'
const data = {
"title": 'Fred',
"lastName": 'Flintstone'
}
const options = {
method: 'POST',
headers: { 'content-type': 'application/x-www-form-urlencoded' },
data: qs.stringify(data),
url
}
axios(options)
.then(function(response) {
console.log(response.data)
})
.catch(function(error) {
console.log(error)
})
I think you need to return JSON data. It is possible that you need to return JSONP to a request from a browser, but here is what I think you need to do:
return ContentService.createTextOutput(JSON.stringify({message: MyResponse})).setMimeType(ContentService.MimeType.JSON);
If that doesn't work, it is probably that you need to return JSONP to run in the browser. Here is some documentation to help you out: https://developers.google.com/apps-script/guides/content#serving_jsonp_in_web_pages

IP Camera - Streaming video with the basic authentication on Chrome

I have an Amcrest IP Camera. I would like to stream video to my web page.
Its url is rtsp://mycamera.com:5554/stream
In order to get the stream of the camera, the camera has a following API http://mycamera.com/video.mjpg
It requires the basic authentication to work.
By formatting the URL and putting it into the image tag like below, I can make it work on Firefox, Safari
<img src="http://username:password#mycamera.com/video.mjpg" />
However, Chrome (v.40 on mine) does not allow this kind of URL, the browser will promt the user password form to be filled, or simply refuses the request. Setting header Authorization: "Basic encode64(username, password)" in the script does not help, the URL always return 401 error (Unauthorized)
Sometimes, it raises the CORS error. If my website is running with HTTPS, the HTTP API call will get blocked by the browser.
Disable authentication of IP camera:
If the camera does not have that option on the admin panel, just add an user without a password with any username like "none", then we can setup like this
<iframe src="http://none:#mycamera.com/video.mjpg" width="1280" height="768"></iframe>
Without password, my Chrome no longer throws the user name/password. (tested on Chrome of Windows and MacOSX & Safari)
Proxy the output to my own website's server:
Download the framework CORS of the mohsen1
Install node js and run this server on the same with domain of the website. We need to edit the index.js file as below
var fs = require('fs');
var request = require('request');
var http = require('http');
var https = require('https');
var privateKey = fs.readFileSync('/var/private_key.pem', 'utf8');
var certificate = fs.readFileSync('/var/certificate.crt, 'utf8');
var credentials = {key: privateKey, cert: certificate};
var express = require('express');
var proxy_http_port = 9009
var proxy_https_port = 9443
var app = express();
// your express configuration here
var auth = "";
app.use(function (req, res, next) {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');
next();
});
var t = app.get('/', function (req, res, next) {
var username = "";
var password = "";
if (req.query.auth) {
username = req.query.auth.split(":")[0];
password = req.query.auth.split(":")[1];
auth = 'Basic ' + new Buffer(username + ':' + password).toString('base64');
res.header('Authorization', auth);
} else {
res.send('please specify auth query param');
}
if (req.query.url) {
var options = {
method: 'GET',
url: req.query.url,
headers: {
"Authorization": auth,
//'User-Agent': req.headers['user-agent']
},
encoding: null
};
var r = request(options, function callback(error, response, body) {
if (!error && response.statusCode == 200) {
res.write(body);
delete response.headers['access-control-allow-origin'];
}
}).pipe(res);
} else {
res.send('You have to specify URL query');
}
});
http.globalAgent.maxSockets = 100;
var httpServer = http.createServer(app);
var httpsServer = https.createServer(credentials, app);
httpServer.listen(proxy_http_port);
httpsServer.listen(proxy_https_port);
Encode the video stream URL below:
http://mycamera.com/video.mjpg
to:
http%3A%2F%2Fmycamera.com%2Fvideo.mjpg
The URL of stream video should be
https://mywebsite.com:9443/?auth=username:password&url=(put above encoded video URL here)
THe advantage of this approach is to bypass most of problems around and get rid of the errors(CORS, HTTPS) because my website will request to its own server
The drawback is the video would be fetch twice, it caused delay pretty much.
Error 401 means invalid credentials so there's a good chance you are simply not formatting the basic auth string correctly. The header format should be Authorization Basic [base64 encoded username:password] using a colon symbol : to separate the two before encoding that entire string into base 64.
For example the resulting header should look something like this:
Authorization: Basic QWxhZGRpbjpPcGVuU2VzYW1l
You can try installing the Chrome "Advanced REST Client" plugin and use that plugin to test accessing your URL link with basic auth parameters. If you can get it to work with the REST client plugin then go back and update your script to fix the format of the auth headers.
Link to Advanced REST Client:
https://chrome.google.com/webstore/detail/advanced-rest-client/hgmloofddffdnphfgcellkdfbfbjeloo?hl=en-US
Link to auth headers format info:
https://en.wikipedia.org/wiki/Basic_access_authentication

How to query third party JSON API from AWS Lambda function

I am working on a "Skill" for the new Amazon ECHO. The skill will allow a user to ask Alexa for information on the status and performance of an Enphase solar system. Alexa will respond with results extracted from the JSON based Enphase API. For example, the user could ask,
"Alexa. Ask Enphase how much solar energy I have produced in the last week."
ALEXA <"Your array has produced 152kWh in the last week.">
Problem is it has been years since I've programmed in JavaScript and this is my first time using AWS Lambda. I have not been very successful finding any information on how to embed a JSON query to a third party server within AWS Lambda function. Here is a relevant section of code in my Lambda function:
/**
* Gets power from Enphase API and prepares speach
*/
function GetPowerFromEnphase(intent, session, callback) {
var Power = 0;
var repromptText = null;
var sessionAttributes = {};
var shouldEndSession = false;
var speechOutput = "";
//////////////////////////////////////////////////////////////////////
// Need code here for sending JSON query to Enphase server to get power
// Request:
// https://api.enphaseenergy.com/api/v2/systems/67/summary
// key=5e01e16f7134519e70e02c80ef61b692&user_id=4d7a45774e6a41320a
// Response:
// HTTP/1.1 200 OK
// Content-Type: application/json; charset=utf-8
// Status: 200
// {"system_id":67,"modules":35,"size_w":6270,"current_power":271,
// "energy_today":30030,"energy_lifetime":59847036,
// "summary_date":"2015-03 04","source":"microinverters",
// "status":"normal","operational_at":1201362300,
// "last_report_at":1425517225}
//////////////////////////////////////////////////////////////////////
speechOutput = "Your array is producing " + Power + " kW, goodbye";
shouldEndSession = true;
// Setting repromptText to null signifies that we do not want to reprompt the user.
// If the user does not respond or says something that is not understood, the session
// will end.
callback(sessionAttributes,
buildSpeechletResponse(intent.name, speechOutput, repromptText,
shouldEndSession));
}
Some guidance would be much appreciated. Even if someone could point me in the right direction. Thanks!
Request is a very popular library for handling http requests in node.js. Here is an example of a POST using your data:
var request = require('request');
request({
url: 'https://api.enphaseenergy.com/api/v2/systems/67/summary',
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
key: '5e01e16f7134519e70e02c80ef61b692',
user_id: '4d7a45774e6a41320a'
})
}, function (error, response, body) {
if (!error && response.statusCode == 200) {
console.log('BODY: ', body);
var jsonResponse = JSON.parse(body); // turn response into JSON
// do stuff with the response and pass it to the callback...
callback(sessionAttributes,
buildSpeechletResponse(intent.name, speechOutput, repromptText,
shouldEndSession));
}
});
I don't have an example of ECHO/Alexa but here is an example of Lambda calling out to get weather data to send it to Slack