Skip to content

Commit ae4df47

Browse files
committed
preload update merge to typescript
2 parents f2d44df + e969876 commit ae4df47

File tree

11 files changed

+164
-166
lines changed

11 files changed

+164
-166
lines changed

SSEController.js

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
const http = require('http');
2+
// native browser api that we are bringing in to work in a node environmnet
3+
const EventSource = require('eventsource');
4+
5+
const SSEController = {};
6+
7+
// keep reference to what will be our EventSource that listens for SSE's
8+
let sse;
9+
10+
SSEController.createStream = (reqResObj, options, event) => {
11+
// got options from httpController
12+
const { headers } = options;
13+
// because EventSource cannot access headers, we are making a regular get request to SSE server to get its headers, and then passing those headers into function where we will be connecting our EventSource, there will a time delay between the time the user opens the request and the server sends back its first response. We keep reference to the time the first request was made to account for that time difference later on.
14+
const startTime = Date.now();
15+
16+
http.get(headers.url, {
17+
headers,
18+
agent: false,
19+
}, (res) => {
20+
// update info in reqResObj to reflect fact that connection was succesful
21+
reqResObj.response.headers = {...res.headers};
22+
reqResObj.connection = 'open';
23+
reqResObj.connectionType = 'SSE';
24+
// invoke function that will create an EventSource
25+
SSEController.readStream(reqResObj, event, Date.now() - startTime);
26+
});
27+
};
28+
29+
SSEController.readStream = (reqResObj, event, timeDiff) => {
30+
// EventSource listens for SSE's and process specially formatted data from them, as well as adding other useful information
31+
sse = new EventSource(reqResObj.url);
32+
// event listeners
33+
sse.onopen = () => console.log(`SSE at ${reqResObj.url} opened!`);
34+
// this is where incoming messages are processed
35+
sse.onmessage = (message) => {
36+
// message is not a javascript object, so we spread its contents into one
37+
const newMessage = { ...message };
38+
// this is where where account for any time lost between the first AJAX request and the creation of the EventSource
39+
newMessage.timeReceived = Date.now() - timeDiff;
40+
// add processed message to events array on reqResObj
41+
reqResObj.response.events.push(newMessage);
42+
// ...and send back to renderer process to be added to the store
43+
return event.sender.send('reqResUpdate', reqResObj);
44+
};
45+
sse.onerror = (err) => {
46+
console.log('there was an error in SSEController.readStream', err)
47+
};
48+
};
49+
50+
module.exports = SSEController;

httpMainController.js

Lines changed: 72 additions & 150 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,15 @@ const http2 = require("http2");
88
const setCookie = require("set-cookie-parser");
99

1010
//Included Functions
11+
const SSEController = require('./SSEController');
1112

1213
// openHTTPconnection(reqResObj, connectionArray)
1314
// establishHTTP2Connection(reqResObj, connectionArray)
1415
// attachRequestToHTTP2Client(client, reqResObj, connectionArray)
15-
// sendToMainForFetch(args)
16+
// makeFetch(args)
1617
// establishHTTP1connection(reqResObj, connectionArray)
1718
// parseFetchOptionsFromReqRes(reqResObject)
18-
// handleSingleEvent(response, originalObj, headers)
19+
// addSingleEvent(response, originalObj, headers)
1920
// handleSSE(response, originalObj, headers)
2021
// parseSSEFields(rawString)
2122
// cookieFormatter(setCookie(response.cookies))
@@ -79,9 +80,9 @@ const httpController = {
7980
// --------------------------------------------------
8081
else {
8182
// console.log('New HTTP2 Conn:', reqResObj.host);
82-
83+
console.log('no pre-existing http2 found')
8384
const id = Math.random() * 100000;
84-
const client = http2.connect(reqResObj.host);
85+
const client = http2.connect(reqResObj.host, () => console.log('connected!'));
8586

8687
// push HTTP2 connection to array
8788
const http2Connection = {
@@ -173,9 +174,9 @@ const httpController = {
173174

174175
reqStream.on("response", (headers, flags) => {
175176
// first argumnet of callback to response listener in ClientHttp2Stream is an object containing the receieved HTTP/2 Headers Object, as well as the flags associated with those headers
176-
177+
console.log('headers is : ', headers)
177178
// SSE will have 'stream' in the 'content-type' heading
178-
isSSE = headers["content-type"].includes("stream");
179+
isSSE = (headers["content-type"] && headers["content-type"].includes("stream"));
179180

180181
if (isSSE) {
181182
reqResObj.connection = "open";
@@ -186,20 +187,15 @@ const httpController = {
186187
}
187188
reqResObj.isHTTP2 = true;
188189
reqResObj.timeReceived = Date.now();
189-
// for purpose of explaining time bug
190-
console.log('this is the time inside the response event listener : ', Date.now())
191190
reqResObj.response.headers = headers;
192191

193192
// if cookies exists, parse the cookie(s)
194193
if (setCookie.parse(headers["set-cookie"])) {
195-
console.log('made it into cookies')
196194
reqResObj.response.cookies = this.cookieFormatter(
197195
setCookie.parse(headers["set-cookie"])
198196
);
199197
// send back reqResObj to renderer so it can update the redux store
200198
event.sender.send('reqResUpdate', reqResObj);
201-
202-
this.handleSingleEvent([], reqResObj, event);
203199
}
204200
});
205201

@@ -249,29 +245,63 @@ const httpController = {
249245
event.sender.send('reqResUpdate', reqResObj);
250246
} else {
251247
reqResObj.connection = "closed";
252-
reqResObj.response.events.push(data);
248+
reqResObj.response.events.push(JSON.parse(data));
253249
// send back reqResObj to renderer so it can update the redux store
254250
event.sender.send('reqResUpdate', reqResObj);
255251
}
256252
});
257253
},
258254
// ----------------------------------------------------------------------------
259255

260-
// sendToMainForFetch(args) {
261-
// return new Promise((resolve) => {
262-
// ipcRenderer.send("http1-fetch-message", args);
263-
// ipcRenderer.on("http1-fetch-reply", (event, result) => {
264-
// resolve(result);
265-
// });
266-
// });
267-
// },
268-
256+
makeFetch(args) {
257+
return new Promise((resolve) => {
258+
// ipcRenderer.send("http1-fetch-message", args);
259+
// ipcRenderer.on("http1-fetch-reply", (event, result) => {
260+
// resolve(result);
261+
// });
262+
const { method, headers, body } = args.options;
263+
264+
fetch2(headers.url, { method, headers, body })
265+
.then((response) => {
266+
const headers = response.headers.raw();
267+
// check if the endpoint sends SSE
268+
// add status code for regular http requests in the response header
269+
270+
if (headers["content-type"][0].includes("stream")) {
271+
// invoke another func that fetches to SSE and reads stream
272+
// params: method, headers, body
273+
resolve({
274+
headers,
275+
body: { error: "This Is An SSE endpoint" },
276+
})
277+
}
278+
headers[":status"] = response.status;
279+
280+
const receivedCookie = headers["set-cookie"];
281+
headers.cookies = receivedCookie;
282+
283+
const contents = /json/.test(response.headers.get("content-type"))
284+
? response.json()
285+
: response.text();
286+
contents
287+
.then((body) => {
288+
console.log('returned out of fetching')
289+
resolve({
290+
headers,
291+
body
292+
});
293+
})
294+
.catch((error) => console.log("ERROR", error));
295+
296+
})
297+
.catch((error) => console.log(error));
298+
});
299+
},
269300
// ----------------------------------------------------------------------------
270301

271302
establishHTTP1connection(event, reqResObj, connectionArray) {
272-
//XXXXXXXXXXXXXXX
273303

274-
// start off by clearing existing response data
304+
// start off by clearing existing response data, and make note of when response was created
275305
reqResObj.response.headers = {};
276306
reqResObj.response.events = [];
277307
reqResObj.connection = "pending";
@@ -284,41 +314,27 @@ const httpController = {
284314
connectionArray.splice(i, 1);
285315
}
286316
});
317+
287318
const openConnectionObj = {
288-
abort: new AbortController(),
289319
protocol: "HTTP1",
290320
id: reqResObj.id,
291321
};
292322
connectionArray.push(openConnectionObj);
293323

294324
const options = this.parseFetchOptionsFromReqRes(reqResObj);
295-
options.signal = openConnectionObj.abort.signal;
296325

297326
//--------------------------------------------------------------------------------------------------------------
298327
// Check if the URL provided is a stream
299328
//--------------------------------------------------------------------------------------------------------------
300-
301-
// if isSSE is true, then node-fetch to stream,
302329
if (reqResObj.request.isSSE) {
303-
// invoke another func that fetches to SSE and reads stream
304-
// params: method, headers, body
305-
const { method, headers, body } = options;
306-
307-
fetch2(headers.url, { method, headers, body }).then((response) => {
308-
const heads = {};
309-
for (const entry of response.headers.entries()) {
310-
heads[entry[0].toLowerCase()] = entry[1];
311-
}
312-
reqResObj.response.headers = heads;
313-
this.handleSSE(response, reqResObj, heads);
314-
});
315-
}
316-
// if not SSE, talk to main to fetch data and receive
317-
else {
318-
// send information to the NODE side to do the fetch request
319-
this.sendToMainForFetch({ options })
330+
// if so, send us over to SSEController
331+
SSEController.createStream(reqResObj, options, event)
332+
// if not SSE, talk to main to fetch data and receive
333+
} else {
334+
this.makeFetch({ options })
320335
.then((response) => {
321336
// Parse response headers now to decide if SSE or not.
337+
322338
const heads = response.headers;
323339
reqResObj.response.headers = heads;
324340

@@ -336,11 +352,12 @@ const httpController = {
336352
reqResObj.response.cookies = this.cookieFormatter(
337353
setCookie.parse(theResponseHeaders.cookies)
338354
);
339-
// send back reqResObj to renderer so it can update the redux store
340-
event.sender.send('reqResUpdate', reqResObj);
341-
342-
this.handleSingleEvent(body, reqResObj, event);
355+
343356
}
357+
// update reqres object to include new event
358+
reqResObj = this.addSingleEvent(body, reqResObj);
359+
// send back reqResObj to renderer so it can update the redux store
360+
event.sender.send('reqResUpdate', reqResObj);
344361
})
345362
.catch((err) => {
346363
reqResObj.connection = "error";
@@ -392,110 +409,15 @@ const httpController = {
392409

393410
// ----------------------------------------------------------------------------
394411

395-
handleSingleEvent(response, reqResObj, event) {
396-
const newObj = JSON.parse(JSON.stringify(reqResObj));
397-
newObj.connection = "closed";
398-
newObj.connectionType = "plain";
399-
newObj.timeReceived = Date.now();
400-
console.log('Date.now() inside handleSingleEvent : ', Date.now())
401-
console.log('this is what was added : ', newObj.timeReceived)
402-
newObj.response.events.push(response);
403-
// send back reqResObj to renderer so it can update the redux store
404-
event.sender.send('reqResUpdate', reqResObj);
405-
},
406-
407-
// ----------------------------------------------------------------------------
408-
409-
/* handle SSE Streams for HTTP1.1 */
410-
handleSSE(response, originalObj, headers) {
411-
const reader = response.body.getReader();
412-
let data = "";
413-
read();
414-
415-
const newObj = JSON.parse(JSON.stringify(originalObj));
416-
417-
// okay to set these after the read since read is async
418-
newObj.timeReceived = Date.now();
419-
newObj.response.headers = headers;
420-
newObj.response.events = [];
421-
newObj.connection = "open";
422-
newObj.connectionType = "SSE";
423-
424-
const decoder = new TextDecoder("utf-8");
425-
function read() {
426-
reader.read().then((obj) => {
427-
// check if there is new info to add to data
428-
if (decoder.decode(obj.value) !== "") {
429-
data += decoder.decode(obj.value);
430-
}
431-
432-
// check if there are double new lines to parse...
433-
let couldBeEvents = true;
434-
const wouldBeTimeReceived = Date.now();
435-
while (couldBeEvents) {
436-
const possibleEventArr = data.split(/\n\n/g);
437-
438-
// if the array has a match, send it to be parsed, and send back to store
439-
if (possibleEventArr && possibleEventArr[0]) {
440-
const receivedEventFields = httpController.parseSSEFields(
441-
possibleEventArr[0]
442-
);
443-
receivedEventFields.timeReceived = wouldBeTimeReceived;
444-
445-
newObj.response.events.push(receivedEventFields);
446-
store.default.dispatch(actions.reqResUpdate(newObj));
447-
448-
// splice possibleEventArr, recombine with \n\n to reconstruct original,
449-
// minus what was already parsed.
450-
possibleEventArr.splice(0, 1);
451-
data = possibleEventArr.join("\n\n");
452-
}
453-
// if does not contain, end while loop
454-
else {
455-
couldBeEvents = false;
456-
}
457-
}
458-
459-
// base case
460-
if (obj.done) {
461-
} else {
462-
read();
463-
}
464-
});
465-
}
412+
addSingleEvent(event, reqResObj) {
413+
console.log('event in addSingleEvent :', event)
414+
// adds new event to reqResObj and returns it so obj can be sent back to renderer process
415+
reqResObj.timeReceived = Date.now();
416+
reqResObj.response.events.push(event);
417+
// returns updated reqResObj
418+
return reqResObj;
466419
},
467420

468-
parseSSEFields(rawString) {
469-
return (
470-
rawString
471-
// since the string is multi line, each for a different field, split by line
472-
.split("\n")
473-
// remove empty lines
474-
.filter((field) => field !== "")
475-
// massage fields so they can be parsed into JSON
476-
.map((field) => {
477-
const fieldColonSplit = field
478-
.replace(/:/, "&&&&")
479-
.split("&&&&")
480-
.map((kv) => kv.trim());
481-
482-
const fieldObj = {
483-
[fieldColonSplit[0]]: fieldColonSplit[1],
484-
};
485-
return fieldObj;
486-
})
487-
.reduce((acc, cur) => {
488-
// handles if there are multiple fields of the same type, for example two data fields.
489-
const key = Object.keys(cur)[0];
490-
if (acc[key]) {
491-
acc[key] = `${acc[key]}\n${cur[key]}`;
492-
} else {
493-
acc[key] = cur[key];
494-
}
495-
return acc;
496-
}, {})
497-
);
498-
},
499421

500422
cookieFormatter(cookieArray) {
501423
return cookieArray.map((eachCookie) => {

main.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ const protoParserFunc = require("./src/client/protoParser.js");
4747
require("./menu/mainMenu");
4848
// require http controller file
4949
require('./httpMainController.js')();
50+
// require('./SSEController.js')();
5051

5152

5253
// configure logging

0 commit comments

Comments
 (0)