What HTTP Method does EventSource use to open a connection? - javascript

While in other questions people claimt EventSource is fairly well documented I have found it to be more implied then explicit in some cases.
My understanding is that when you initialise an EventSource object in JS it opens a connection to your server using the specified URI.
Is this connection initiated using GET?
(Not sure if this constitutes a second question) Is it possible to use/force another HTTP Method (POST)?

The request method when using the EventSource interface is a GET request. You can include a query string in the URL passed to the constructor and parse the query string at the server.
const stream = "data: event stream\n\n";
const blob = new Blob([stream], {type:"text/event-stream"});
const blobURL = URL.createObjectURL(blob);
const es = new EventSource(blobURL);
es.onmessage = e => {
console.log(e.data);
}
es.onerror = e => {
es.close();
}

Related

Problems with Blob acquisition when receiving data through a web-socket

In the process of communicating with the server using a web socket, the data is handed over to the server to Json.
And in the process of receiving data through onmessage, the data came into Blob, not Json or string.
I couldn't find a way to change this Blob to Json, so I posted a question here.
Is there any way to receive the data in string or Json?
Or how to change Blob to string or Json.
The server is written in c++ and sent as a string.
useEffect(() => {
//var socket = new WebSocket("ws://172.30.1.50:65432/websocket");
const socket = new WebSocket("ws://ip:port/websocket"); //For convenience, I wrote down ip and port.
socket.Type = "blob"
socket.onopen = function (event) {
socket.send(JSON.stringify({ 'CMD': 'test' }))
console.log("connetion?");}
socket.close
socket.onmessage = function (event) {
console.log((event.data))
}, []})
The WebSocket protocol supports two kinds of data messages: text and binary. When a text message is received by Javascript, it is deserialised as a string; when a binary message is received, it is deserialised as either an ArrayBuffer or a Blob, depending on the value of the websocket's binaryType property (the default is to return a Blob).
The fact that you got a Blob indicates that the server sent a binary message. This is unusual for a JSON message, so either the server is very unusual, or the protocol is not based on JSON.
In any case, you can convert r data into a string by calling the async .text() method:
let data = await event.data.text();
If the contents is indeed JSON, then you may parse it as usual:
let value = JSON.parse(data);

Download Zip file PushStreamContent Javascript

i'am asking the about the right way to downlaod a PushStreamContent present in Post Request ,
i already preapred the backend request , something like this
private static HttpClient Client { get; } = new HttpClient();
public HttpResponseMessage Get()
{
var filenamesAndUrls = new Dictionary<string, string>
{
{ 'README.md', 'https://raw.githubusercontent.com/StephenClearyExamples/AsyncDynamicZip/master/README.md' },
{ '.gitignore', 'https://raw.githubusercontent.com/StephenClearyExamples/AsyncDynamicZip/master/.gitignore'},
};
var result = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new PushStreamContent(async (outputStream, httpContext, transportContext) =>
{
using (var zipStream = new ZipOutputStream(outputStream))
{
foreach (var kvp in filenamesAndUrls)
{
zipStream.PutNextEntry(kvp.Key);
using (var stream = await Client.GetStreamAsync(kvp.Value))
await stream.CopyToAsync(zipStream);
}
}
}),
};
result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment") { FileName = "MyZipfile.zip" };
return result;
}
and in the front part , i used axios to send Post request and with the result i create blob to download it (i modified the backend to support Post)
but the download take much time and i think this a wrong way to use PushStreamContent and i should use EventSource or something like this.
Thank you.
After few days of search , there are two solution :
Change the download request to Get Request instead of Post.
Use Fetch instead of axios http request and with the response send it to streamsaver package , it's really amazing and instantly start the download on the fly.
I agree with houssem about changing it to a get request.
I'm the creator of StreamSaver and occasionally i search for ppl talking about it and help someone in need. I often have to tell ppl that it's better to use the server to save files rather than using StreamSaver. StreamSaver is meant for client generated content (good for stuff like WebTorrent or webcam recording)
Download can only happen when you navigate the resource. That means you can't use ajax (xhr, fetch, axios, etc) to trigger a download
a <a href>, <iframe>, location.href = all works fine, but if you really need it to be a post request, then you can also submit a <form> but you will have to do it with application/multipart or URLEncoded and not with a json request or anything else. the con of this is that you can't use custom request headers like a authentication header (unless you use service worker to add them)
in such cases it is better with cookies that gets sent along every request

Get page URL parameters from a service worker

How do I get page URL with parameters from a service worker?
I have tried self.registration.scope but that doesn't include the parameters.
I'm not clear as to whether you're asking about getting the service worker script's URL, or the URLs of all of the client pages that are open under the service worker's scope. So... here's how to do both:
// Get a URL object for the service worker script's location.
const swScriptUrl = new URL(self.location);
// Get URL objects for each client's location.
self.clients.matchAll({includeUncontrolled: true}).then(clients => {
for (const client of clients) {
const clientUrl = new URL(client.url);
}
});
In either of those cases, once you have a URL object, you can use its searchParams property if you're interested in the query parameters:
if (url.searchParams.get('key') === 'value') {
// Do something if the URL contains key=value as a query parameter.
}
You can get waiting.scriptURL or active.scriptURL, pass result to URL() constructor, get .search property of object
navigator.serviceWorker.register("sw.js?abc=123")
.then(function(reg) {
const scriptURL = reg.waiting && reg.waiting.scriptURL || reg.active.scriptURL;
const url = new URL(scriptURL);
const queryString = url.search;
console.log(queryString);
}).catch(function(err) {
console.log("err", err);
});
ServiceWorker.scriptURL can give you the URL and its parameters as well.
Then, the following step is to get a ServiceWorker object, and it depends on where you would like to use the URL parameters.
In the worker script, use self.serviceWorker.scriptURL. e.g.
const searchParams = new URLSearchParams(self.serviceWorker.scriptURL);
In the page script, use scriptURL with navigator.serviceWorker.ready. e.g.
const serviceWorkerRegistration = await navigator.serviceWorker.ready;
const activeServiceWorker = serviceWorkerRegistration.active;
const searchParams = new URLSearchParams(activeServiceWorker.scriptURL);
However, you might want to get a registration object from register API, but the above code snippet should work as well.

How to test Request objects' properties in node

I have a function that returns a new Request object;
import * as _url from 'url';
// pathname starts with '/content/'
const isContentUrl = (path) => /^\/content\//.test(path);
export default function(url) {
let urlObj = _url.parse(url);
if (isContentUrl(urlObj.pathname)) {
urlObj.pathname = `/offline${urlObj.pathname}`;
}
return new Request(_url.format(urlObj), {
credentials: 'same-origin',
headers: {
'x-requested-with': 'sw'
}
});
}
Now I'm writing unit tests for this function and although I know there isn't actually much that changes here but say for example the headers could change for whatever reason, how can I assert parts of the request object like the headers, credentials or the URL?
Is there a nice way to be able to parse it for testing?
Ideally I'd like to do something like
it('should return a Request object with the correct headers', () => {
const url = '/content/2c509c50-e4ba-11e6-9645-c9357a75844a';
const request = offlineContent(url);
const result = request.headers;
const expected = {'x-requested-with': 'sw'};
expect(result).to.eql(expected);
});
in my test
Assuming that request here the request HTTP client, the returned object is an instance of the Request constructor defined here.
If you follow through the code, you will see that headers provided are available simply through an object member and thus headers, and other members being attached to self can be easily introspected through javascript.
In addition, the http module through which requests are dispatched is available as self.httpModule and can be mocked by an implementation that is compliant with node http module and requests dispatched through the library can be intercepted through spies.
MDN details the methods on the Request and Headers object. Unfortunately I haven't found a way to convert this to an object that I could assert with a deep equal. But I can use these to assert against.
request.url;
request.credentials;
request.headers.get('x-requested-with');
Unfortunately the Headers.getAll() method has been deprecated so in order to fail tests if someone added a new header I have asserted against;
Array.from(request.headers.keys()).length

EventSource and basic http authentication

Does anyone know if it is possible to send basic http authentication credentials with EventSource?
I'm looking for a solution to the same problem. This post here says this:
Another caveat is that as far as we know, you cannot change the HTTP
headers when using EventSource, which means you have to submit an
authorization query string param with the value that you would have
inserted using HTTP Basic Auth: a base64 encoded concatenation of your
login and a token.
Here is the code from the post:
// First, we create the event source object, using the right URL.
var url = "https://stream.superfeedr.com/?";
url += "&hub.mode=retrieve";
url += "&hub.topic=http%3A%2F%2Fpush-pub.appspot.com%2Ffeed";
url += "&authorization=anVsaWVuOjJkNTVjNDhjMDY5MmIzZWFkMjA4NDFiMGViZDVlYzM5";
var source = new EventSource(url);
// When the socket has been open, let's cleanup the UI.
source.onopen = function () {
var node = document.getElementById('sse-feed');
while (node.hasChildNodes()) {
node.removeChild(node.lastChild);
}
};
// Superfeedr will trigger 'notification' events, which corresponds
// exactly to the data sent to your subscription endpoint
// (webhook or XMPP JID), with a JSON payload by default.
source.addEventListener("notification", function(e) {
var notification = JSON.parse(e.data);
notification.items.sort(function(x, y) {
return x.published - y.published;
});
notification.items.forEach(function(i) {
var node = document.getElementById('sse-feed');
var item = document.createElement("li");
var t = document.createTextNode([new Date(i.published * 1000), i.title, i.content].join(' '));
item.appendChild(t);
node.insertBefore(item, node.firstChild);
// We add the element to the UI.
});
});
If your talk about cookies (not http auth):
EventSource uses http, so cookies are sent with the EventSource connection request.
Http auth should be supported as any other http url, although from the spec CORS+http auth is not supported.
Nowadays there is a NPM package to change the HTTP Header
https://www.npmjs.com/package/eventsource
This library is a pure JavaScript implementation of the EventSource
client. The API aims to be W3C compatible.
You can use it with Node.js or as a browser polyfill for browsers that
don't have native EventSource support.
You can use event-source-polyfill to add headers like this
import { EventSourcePolyfill } from 'event-source-polyfill';
new EventSourcePolyfill(`/api/liveUpdate`, {
headers: {
Authorization: `Bearer 12345`,
'x-csrf-token': `xxx-xxx-xxx`,
},
});
EventSource is about the server sending events to the client. I think you need bidirectional communication for authentication. How would you otherwise send the actual credentials?
WebSockets, however, can achieve that. Is that what you are looking for?
Update:
You can achieve what you want by utilizing cookies, as pointed out by 4esn0k. Cookies are sent along with the initial request that the browser makes to establish the connection. So, just make sure you set the session identifier for the cookie before launching any EventSource connections.

Categories

Resources