How to detect http response code? - video.js

I use video.js player with HLS.
I would like to detect HTTP response code for each failed request, which video.js makes to play a video. For example, if a response is 304 then my code needs to make a special action (show a message).
player.on('error', ...) does not provide such information. As well as tech level error.
I need something like:
player.on('request', function(response) { ... }

The best solution I found is wrapping video.xhr object with special handler.
Example of the code (using underscore.js library):
videojs.xhr = _.wrap(videojs.xhr, function(fn, options, callback) {
var wrapped_callback = _.wrap(callback, function(cb_fn, error, response) {
var args = _.rest(arguments, 1);
console.log('Error', error);
console.log('Response', response);
var res = cb_fn.apply(this, args);
return res;
});
return fn.apply(this, [options, wrapped_callback]);
});

Related

Angular 10 get webservice status with Await?

Just before login in the user, I need to test if the WebServce respond then if the system is in maintenance. On the WebService part (core3 .net) I got 2 functions:
HeartBeat that return: return Ok("OK");
MaintenanceInfo that return Return OK("No");
I display in real time the result of the 2 tests and if no problems, I display the login panel.
I need to do these tests in sequence, I was thinking doing it with await operator.
I got a TestHelperServie class with 2 functions that return bool. But I can't find how to pass from a HTTP subscribe function to a bool result. With true if I got the OK response and false if I got a timeout or another fail HTTP.
For now I do this:
async TestHeartBeat()
{
let Response:Boolean = false;
const headers = new HttpHeaders()
.set('Content-Type','application/json');
const options = {
headers: headers,
observe: "response" as const,
responseType: "json" as const
};
await this.http.get(`${environment.apiUrl}/TestController/HeartBeat`, options)
.subscribe(res=>{
Response = true;
},
error=>{
Response = false;
})
return Respone;
}
But the function does not wait for the http response.
How can I fix this?
Sorry for my newbies question, but I came from the c# world and we use await/async a lot. I don't think I can do that the same way in angular 10.
You can not await on Observables, you have to convert it to promise using toPromise()
return await this.http.get(....).toPromise().then(res=> Response = true).catch(err => Response = false);
Use rxjs pipe, map and catchError.
async TestHeartBeat()
{
const headers = new HttpHeaders()
.set('Content-Type','application/json');
const options = {
headers: headers,
observe: "response" as const,
responseType: "json" as const
};
return await this.http.get(`${environment.apiUrl}/TestController/HeartBeat`, options)
.pipe(
map(res => true),
catchError(error => false)
).toPromise();
}
Now you can call it like the following:
if (await TestHeartBeat()) {
// Display your panel
} else {
// Service is in maintenance
}

Handling HTTP status codes

I'd like to get you'r advice on this one.
Suppose I want to handle the errors/status codes I get from POST or GET methods in http protocol, I tried to implement something like this:
class Login extends IRequest<Map<String, dynamic>> {
Login(var email, var password) {
var data = {'email': email, 'password': password};
var url = API_PREFIX + LOGIN_ENDPOINT;
var body = json.encode(data);
var headers = {
'Content-type': CONTENT_TYPE,
};
}
Future<IResponse> login(var mail, var password) async {
final response = await http.post(url, headers: headers, body: body);
switch (response.statusCode) {
case OK:
case ACCEPTED:
final responseJson = json.decode(response.body);
String key = responseJson['login_key'];
String experial = responseJson['experial'];
var token = LoginToken(key, experial);
return token;
case NOT_FOUND:
return ErrorResponse(response.statusCode.toString());
}
}
}
I'm doing the request in an async way, and then check for the response code, but there are many cases to choose from , starting from 200 up to 500, which makes the code very messy and ugly,
Is there any easy way to work around this?
I have though about try / catch, but is it usable in this situation? (I know try catch are for exceptions, and here I just want the status code of the result...)
If you will take a look at the wiki you can see, that codes are divided into groups. 1xx means informational response, 2xx success and others.
With switch statement you can check, to which group received code belongs and continue or throw an error.
Other option is to add interceptors, and check response codes there.

Handling an authentication page returned by an axios request in vue

I have a vue app that sits behind a firewall, which controls authentication. When you first access the app you need to authenticate after which you can access the app and all is well until the authentication expires. From the point of view of my app I only know that the user needs to re-authenticate when I use axios to send off an API request and instead of the expected payload I receive a 403 error, which I catch with something like the following:
import axios from 'axios'
var api_url = '...'
export default new class APICall {
constructor() {
this.axios = axios.create({
headers: {
'Accept': 'application/json',
'Cache-Control': 'no-cache',
'Content-Type': 'application/json',
},
withCredentials: true,
baseURL: api_url
});
}
// send a get request to the API with the attached data
GET(command) {
return this.axios.get(command)
.then((response) => {
if (response && response.status === 200) {
return response.data; // all good
} else {
return response; // should never happen
}
}).catch((err) => {
if (err.message
&& err.message=="Request failed with status code 403"
&& err.response && err.response.data) {
// err.response.data now contains HTML for the authentication page
// and successful authentication on this page resends the
// original axios request, which is in err.response.config
}
})
}
}
Inside the catch statement, err.response.data is the HTML for the authentication page and successfully authenticating on this page automatically re-fires the original request but I can't for the life of me see how to use this to return the payload I want to my app.
Although it is not ideal from a security standpoint, I can display the content of err.response.data using a v-html tag when I do this I cannot figure out how to catch the payload that comes back when the original request is fired by the authentication page, so the payload ends up being displayed in the browser. Does anyone know how to do this? I have tried wrapping everything inside promises but I think that the problem is that I have not put a promise around the re-fired request, as I don't have direct control of it.
Do I need to hack the form in err.response.data to control how the data is returned? I get the feeling I should be using an interceptor but am not entirely sure how they work...
EDIT
I have realised that the cleanest approach is to open the form in error.response.data in a new window, so that the user can re-authenticate, using something like:
var login_window = window.open('about:blank', '_blank');
login_window.document.write(error.response.data)
Upon successful re-authentication the login_window now contains the json for the original axios get request. So my problem now becomes how to detect when the authentication fires and login_window contains the json that I want. As noted in Detect form submission on a page, extracting the json from the formatting window is also problematic as when I look at login_window.document.body.innerText "by hand" I see a text string of the form
JSON
Raw Data
Headers
Save
Copy
Collapse All
Expand All
status \"OK\"
message \"\"
user \"andrew\"
but I would be happy if there was a robust way of determining when the user submits the login form on the page login_window, after which I can resend the request.
I would take a different approach, which depends on your control over the API:
Option 1: you can control (or wrap) the API
have the API return 401 (Unauthorized - meaning needs to authenticate) rather than 403 (Forbidden - meaning does not have appropriate access)
create an authentication REST API (e.g. POST https://apiserver/auth) which returns a new authentication token
Use an Axios interceptor:
this.axios.interceptors.response.use(function onResponse(response) {
// Any status code that lie within the range of 2xx cause this function to trigger
// no need to do anything here
return response;
}, async function onResponseError(error) {
// Any status codes that falls outside the range of 2xx cause this function to trigger
if ("response" in error && "config" in error) { // this is an axios error
if (error.response.status !== 401) { // can't handle
return error;
}
this.token = await this.axios.post("auth", credentials);
error.config.headers.authorization = `Bearer ${this.token}`;
return this.axios.request(config);
}
return error; // not an axios error, can't handler
});
The result of this is that the user does not experience this at all and everything continues as usual.
Option 2: you cannot control (or wrap) the API
use an interceptor:
this.axios.interceptors.response.use(function onResponse(response) {
// Any status code that lie within the range of 2xx cause this function to trigger
// no need to do anything here
return response;
}, async function onResponseError(error) {
// Any status codes that falls outside the range of 2xx cause this function to trigger
if ("response" in error && "config" in error) { // this is an axios error
if (error.response.status !== 403) { // can't handle
return error;
}
if (!verifyLoginHtml(error.response.data)) { // this is not a known login page
return error;
}
const res = await this.axios.post(loginUrl, loginFormData);
return res.data; // this should be the response to the original request (as mentioned above)
}
return error; // not an axios error, can't handler
});
One solution is to override the <form>'s submit-event handler, and then use Axios to submit the form, which gives you access to the form's response data.
Steps:
Query the form's container for the <form> element:
// <div ref="container" v-html="formHtml">
const form = this.$refs.container.querySelector('form')
Add a submit-event handler that calls Event.preventDefault() to stop the submission:
form.addEventListener('submit', e => {
e.preventDefault()
})
Use Axios to send the original request, adding your own response handler to get the resulting data:
form.addEventListener('submit', e => {
e.preventDefault()
axios({
method: form.method,
url: form.action,
data: new FormData(form)
})
.then(response => {
const { data } = response
// data now contains the response of your original request before authentication
})
})
demo

How do I serve a file from S3 through Meteor Iron Router

My question is very similar to this one which describes how to serve a local file using Iron Router. I need to do the same, but instead of reading the file synchronously from disk, I need to get the file from S3 which is an asynchronous call.
The problem appears to be the fact that the action method has returned before the asynchronous s3.getObject completes giving me the following error.
Error: Can't render headers after they are sent to the client.
I'm assuming that Iron Router is generating the response for me when it realizes that I haven't handled the response in my action method, but I'm stumped about how to tell it to wait for my asynchronous call to finish.
Here is my code.
Router.map(function () {
this.route('resumeDownload', {
where: 'server',
path: '/resume/:_id',
action: function () {
var response = this.response;
var candidate = Candidates.findOne(this.params._id);
if (!candidate || !candidate.resumeS3Key) {
// this works fine because the method hasn't returned yet.
response.writeHead(404);
return response.end();
}
var s3 = new AWS.S3();
s3.getObject({Bucket: 'myBucket', Key: candidate.resumeS3Key}, function (err, data) {
if (err) {
// this will cause the error to be displayed
response.writeHead(500);
return response.end();
}
// this will also cause the error to be displayed
response.writeHead(200, {'Content-Type': data.ContentType});
response.end(data.Body);
});
}
});
});
I was able to solve this one myself. I needed to use a future in my action method.
Here is the working code.
Router.map(function () {
this.route('resumeDownload', {
where: 'server',
path: '/resume/:_id',
action: function () {
var response = this.response,
candidate = Candidates.findOne(this.params._id);
if (!candidate || !candidate.resumeS3Key) {
response.writeHead(404);
return response.end();
}
var Future = Npm.require('fibers/future'),
s3 = new AWS.S3(),
futureGetObject = Future.wrap(s3.getObject.bind(s3)),
data = futureGetObject({Bucket: 'myBucket', Key: candidate.resumeS3Key}).wait();
response.writeHead(200, {'Content-Type': data.ContentType});
response.end(data.Body);
}
});
});

How do I write a Node.js request to 3rd party API?

Does anyone have an example of an API response being passed back from a http.request() made to a 3rd party back to my clientSever and written out to a clients browser?
I keep getting stuck in what I'm sure is simple logic. I'm using express from reading the docs it doesn't seem to supply an abstraction for this.
Thanks
Note that the answer here is a little out of date-- You'll get a deprecated warning. The 2013 equivalent might be:
app.get('/log/goal', function(req, res){
var options = {
host : 'www.example.com',
path : '/api/action/param1/value1/param2/value2',
port : 80,
method : 'GET'
}
var request = http.request(options, function(response){
var body = ""
response.on('data', function(data) {
body += data;
});
response.on('end', function() {
res.send(JSON.parse(body));
});
});
request.on('error', function(e) {
console.log('Problem with request: ' + e.message);
});
request.end();
});
I would also recommend the request module if you're going to be writing a lot of these. It'll save you a lot of keystrokes in the long run!
Here is a quick example of accessing an external API in an express get function:
app.get('/log/goal', function(req, res){
//Setup your client
var client = http.createClient(80, 'http://[put the base url to the api here]');
//Setup the request by passing the parameters in the URL (REST API)
var request = client.request('GET', '/api/action/param1/value1/param2/value2', {"host":"[put base url here again]"});
request.addListener("response", function(response) { //Add listener to watch for the response
var body = "";
response.addListener("data", function(data) { //Add listener for the actual data
body += data; //Append all data coming from api to the body variable
});
response.addListener("end", function() { //When the response ends, do what you will with the data
var response = JSON.parse(body); //In this example, I am parsing a JSON response
});
});
request.end();
res.send(response); //Print the response to the screen
});
Hope that helps!
This example looks pretty similar to what you are trying to achieve (pure Node.js, no express):
http://blog.tredix.com/2011/03/partly-cloudy-nodejs-and-ifs.html
HTH