How can we use electron.protocol.interceptFileProtocol with only certain paths, leaving other requests untouched? - javascript

I'd like to intercept certain HTTP requests and replace them with files. So I thought I could use electron.protocol.interceptFileProtocol like so:
protocol.interceptFileProtocol('http', (request, callback) => {
// intercept only requests to "http://example.com"
if (request.url.startsWith("http://example.com")) {
callback("/path/to/file")
}
// otherwise, let the HTTP request behave like normal.
// But how?
})
How do we allow other http requests other than http://example.com to continue working as normal?

When using protocol.interceptXXXXProtocol(scheme, handler), we are intercepting scheme protocol and uses handler as the protocol’s new handler which sends a new XXXX request as a response, as said in the doc here.
However, doing so totally breaks the initial handler for this specific protocol, which we would need after handling the callback execution. Thus, we just need to restore it back to its initial state, so that it can continue working as normal :)
Let's use: protocol.uninterceptProptocol(scheme)
protocol.interceptFileProtocol('http', (request, callback) => {
// intercept only requests to "http://example.com"
if (request.url.startsWith("http://example.com")) {
callback("/path/to/file")
}
// otherwise, let the HTTP request behave like normal.
protocol.uninterceptProtocol('http');
})

Not sure if there is a way to do this exactly? but I did something similar which is to use session.defaultSession.webRequest.onBeforeRequest
See: https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/webRequest
something like
session.defaultSession.webRequest.onBeforeRequest({urls: ['http://example.com']}, function(details, callback) {
callback({
redirectURL: 'file://' + this.getUrl(details.url)
});
});
If you need more than a redirect you could redirect to your own custom protocol (eg. a url like mycustomprotocol://...). You can implement your own protocol handler with protocol.registerStringProtocol, etc.
I used both onBeforeRequest and registerStringProtocol separately in electron without issues so far but never both together - should work together though I geuss.

Related

Cypress: How to check query params in fetch request?

I am trying to intercept a fetch request and assert that the payload / query params are correct. I have searched the cypress documentation but everything mentioned is setting query params. I for example, need to assert that a fetch request such as https://helloWorld.com/proxy/service has query parameters /payload of ?service=request&layers=demo. Is there a way to do this?
I've tried almost everything but something similar to this is what I'm shooting for. Any ideas?
cy.location("https://helloWorld/proxy/service").should((loc) => {
expect(loc.search).to.eq('?service=request&layers=demo')
})
Setting up an intercept can check the shape of the request
cy.intercept('https://helloworld.com/proxy/service').as('requestWithQuery')
// trigger the request
cy.wait('#requestWithQuery').then(interception => {
expect(interception.req.body.query.includes('service=request').to.eq(true)
})
I'm not sure if the assertion above is exactly what you need, but put a console.log(interception.req) to check out what you need to assert.
The intercept can also specify the query
cy.intercept({
pathname: 'https://helloworld.com/proxy/service',
query: {
service: 'request',
layers: 'demo'
},
}).as('requestWithQuery')
// trigger the request
cy.wait('#requestWithQuery')
By the way your use of cy.location() is incorrect, you would use
cy.location('search').should('eq', '?service=request&layers=demo')
// or
cy.location().should((loc) => {
expect(loc.href).to.eq(
'https://helloworld/proxy/service?service=request&layers=demo'
)
})
But the app would already have to have navigated to https://helloWorld/proxy/service and it's not clear from your question if that is happening.
Catching "helloWorld/proxy/service"
When your app uses fetch, the URL is converted to lower case.
So sending fetch('https://helloWorld/proxy/service') can be intercepted with
cy.intercept('https://helloworld/proxy/service') // all lower-case url
There's a clue in the Cypress log, the logged fetch is show as being all lower-case characters
(fetch) GET https://helloworld/proxy/service
BaseUrl and intercept
When baseUrl is a different domain from the intercept hostname, you can specify it with an additional option, although in practice I found that it also works with the full URL as shown above.
cy.intercept('/proxy/service*', { hostname: 'https://helloworld' })

webRequest API: How to get the requestId of a new request?

The chrome.webRequest API has the concept of a request ID (source: Chrome webRequest documention):
Request IDs
Each request is identified by a request ID. This ID is unique within a browser session and the context of an extension. It remains constant during the the life cycle of a request and can be used to match events for the same request. Note that several HTTP requests are mapped to one web request in case of HTTP redirection or HTTP authentication.
You can use it to correlate the requests even across redirects. But how do you initially get hold off the id when start a new request with fetch or XMLHttpRequest?
So far, I have not found anything better than to use the URL of the request as a way to make the initial link between the new request and the requestId. However, if there are overlapping requests to the same resource, this is not reliable.
Questions:
If you make a new request (either with fetch or XMLHttpRequest), how do you reliably get access to the requestId?
Does the fetch API or XMLHttpRequest API allow access to the requestId?
What I want to do is to use the functionality provided by the webRequest API to modify a single request, but I want to make sure that I do not accidentally modify other pending requests.
To the best of my knowledge, there is no direct support in the fetch or XHMLHttpRequest API. Also I'm not aware of completely reliable way to get hold of the requestId.
What I ended up doing was installing a onBeforeRequest listener, storing the requestId, and then immediately removing the listener again. For instance, it could look like this:
function makeSomeRequest(url) {
let listener;
const removeListener = () => {
if (listener) {
chrome.webRequest.onBeforeRequest.removeListener(listener);
listener = null;
}
};
let requestId;
listener = (details) => {
if (!requestId && urlMatches(details.url, url)) {
requestId = details.requestId;
removeListener();
}
};
chrome.webRequest.onBeforeRequest.addListener(listener, { urls: ['<all_urls>'] });
// install other listeners, which can then use the stored "requestId"
// ...
// finally, start the actual request, for instance
const promise = fetch(url).then(doSomething);
// and make sure to always clean up the listener
promise.then(removeListener, removeLister);
}
It is not perfect, and matching the URL is a detail that I left open. You could simply compare whether the details.url is identical to url:
function urlMatches(url1, url2) {
return url1 === url2;
}
Note that it is not guaranteed that you see the identical URL, for instance, if make a request against http://some.domain.test, you will see http://some.domain.test/ in your listener (see my other question about the details). Or http:// could have been replaced by https:// (here I'm not sure, but it could be because of other extensions like HTTPS Everywhere).
That is why the code above should only be seen as a sketch of the idea. It seems to work good enough in practice, as long as you do not start multiple requests to the identical URL. Still, I would be interested in learning about a better way to approach the problem.

Which IP does a method use (user or server) on an API request?

I want to use an API in my meteor app. The API is restricted to a few requests per second per unique IP.
Does anyone know if the server IP or the user IP is used, when I make an API call in Meteor.methods like this
Meteor.methods({
searchTerm: function (term, lang) {
var parameters = {
"api_key": Meteor.settings.API
};
try {
var result = HTTP.call("GET", apiLink, { params: parameters });
return result.data;
} catch (e) {
return e;
}
}
}
Thanks in advance.
As already noted in the comments, if this code (the methods call itself) is runs on the server, then the method call (later with Meteor.call) is like a remote procedure call and the HTTP will be executed on the server only. If, however, this code, the methods call, is invoked on both the client and the server, then that defines a stub (http://docs.meteor.com/#/full/methods_header). That stub is executed in parallel on the client and the server. It is meant to help with latency compensation. I don't think you want that in this case though, since you are more concerned with the number of API requests. So I would suggest to leave it where it is right now (in the server folder somewhere). That way you can be sure that it will only execute on the server and not the client, and hence use the server IP.

Server side redirect in backbone

I have model object:
module.exports = {
redirectTo: function(params, callback) {
callback(null, 'home/redirect_to');
}
}
Can I redirect user without loading page?
It will depend on variables, so redirecting not always needed. The only thing I need now is redirecting from server-side.
Server side redirection can be done by simply replying HTTP 302 to client.
If what you are asking is "How to go around server side redirection so that your Backbone callback can still be triggered?", then my solution will be following:
For example, when an user type a url http://yousite.com/blog/someid on his browser, and you want to use Backbone callback to handle his request. Then you can let your server return:
<script> window.location="http://yoursite.com#blog/someid"</script>
Note we replace a / into #.
Or, you can let your server return (fragment allowed in Location):
HTTP/1.1 302 Found
Location: http://yoursite.com#blog/someid
Then your Backbone callback will be triggered.
It can be done using this.redirectTo(url)

Meteor: what is the final Location/URL after following redirects?

Meteor's HTTP package is a wrapper around mikeal's request, and it supports the followRedirects option. But how can one find out what the final URL is, after the 3xx redirect responses have been followed (and the request didn't fail because of lack of a cookie jar)?
With request, the final URL is in response.request.href. But with Meteor... ?
Here's the Meteor code:
if (Meteor.isServer) {
Meteor.startup(function () {
var url = 'http://google.com';
var result = HTTP.call("HEAD", url, {
followRedirects: true
});
console.log(result); // nothing here hints at the final URL
});
}
I've created a package that does this - http-more.
Turns out Meteor doesn't pass back the request object within the response, and given the history of rejected PRs concerning enhancements to the HTTP package, I've just implemented that option separately.

Categories