Skip to content

Commit c5a28aa

Browse files
authored
Update request body parsing and file streaming exemples
1 parent b0cb1f2 commit c5a28aa

File tree

3 files changed

+119
-139
lines changed

3 files changed

+119
-139
lines changed

examples/FileStreaming.js

Lines changed: 49 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,98 +1,97 @@
1-
// This is an example of streaming files
1+
/* This is an example of streaming files */
22

33
const uWS = require('../dist/uws.js');
44
const fs = require('fs');
5-
65
const port = 9001;
7-
const bigFileName = 'absolutPathTo/bigVideo.mp3';
6+
7+
const smallFileType = 'application/json';
8+
const smallFileName = 'absolutPathTo/smallFile.json';
9+
const smallFileCachedBuffer = fs.readFileSync(smallFileName);
10+
console.log('Small file size is: '+ smallFileCachedBuffer.length +' bytes');
11+
12+
const bigFileType = 'video/mpeg';
13+
const bigFileName = 'absolutPathTo/bigFile.mp3';
814
const bigFileSize = fs.statSync(bigFileName).size;
9-
console.log('Video size is: '+ bigFileSize +' bytes');
15+
console.log('Big file size is: '+ bigFileSize +' bytes');
16+
17+
let lastStreamIndex = 0;
18+
let openStreams = 0;
1019

11-
// Stream data to res
20+
/* Helper function to stream data */
1221
/** @param {import('node:Stream').Readable} readStream */
13-
const streamData = (res, readStream, totalSize, onFinished) => {
14-
let chunkBuffer; // Actual chunk being streamed
15-
let totalOffset = 0; // Actual chunk offset
22+
const streamData = (res, readStream, totalSize, onSucceed) => {
23+
let chunkBuffer; /* Actual chunk being streamed */
24+
let totalOffset = 0; /* Actual chunk offset */
25+
26+
/* Send actual chunk to client */
1627
const sendChunkBuffer = () => {
1728
const [ok, done] = res.tryEnd(chunkBuffer, totalSize);
1829
if (done) {
19-
// Streaming finished
30+
/* Streaming finished */
2031
readStream.destroy();
21-
onFinished();
32+
onSucceed();
2233
} else if (ok) {
23-
// Chunk send success
34+
/* Chunk send succeed */
2435
totalOffset += chunkBuffer.length;
25-
// Resume stream if it was paused
36+
/* Resume stream if it was paused */
2637
readStream.resume();
2738
} else {
28-
// Chunk send failed (client backpressure)
29-
// onWritable will be called once client ready to receive new chunk
30-
// Pause stream
39+
/* Chunk send failed (client backpressure)
40+
* onWritable will be called once client ready to receive new chunk
41+
* Pause stream to wait client */
3142
readStream.pause();
3243
}
3344
return ok;
3445
};
3546

36-
// Attach onAborted handler because streaming is async
37-
res.onAborted(() => {
38-
readStream.destroy();
39-
onFinished();
40-
});
41-
42-
// Register onWritable callback
43-
// Will be called to drain client backpressure
47+
/* Register onWritable callback
48+
* Will be called to drain client backpressure */
4449
res.onWritable((offset) => {
4550
const offsetDiff = offset - totalOffset;
4651
if (offsetDiff) {
47-
// If start of the chunk was successfully sent
48-
// We only send the missing part
52+
/* If start of the chunk was successfully sent
53+
* We only send the missing part */
4954
chunkBuffer = chunkBuffer.subarray(offsetDiff);
5055
totalOffset = offset;
5156
}
52-
// Always return if resend was successful or not
57+
/* Always return if resend was successful or not */
5358
return sendChunkBuffer();
5459
});
5560

56-
// Register callback for stream events
61+
/* Register callback for stream events */
5762
readStream.on('error', (err) => {
5863
console.log('Error reading file: '+ err);
59-
// res.close calls onAborted callback
60-
res.close();
64+
/* res.close calls onAborted callback */
65+
res.cork(() => res.close());
6166
}).on('data', (newChunkBuffer) => {
6267
chunkBuffer = newChunkBuffer;
63-
// Cork before sending new chunk
68+
/* Cork before sending new chunk */
6469
res.cork(sendChunkBuffer);
6570
});
6671
};
6772

68-
let lastStreamIndex = 0;
69-
let openStreams = 0;
70-
7173
const app = uWS./*SSL*/App({
7274
key_file_name: 'misc/key.pem',
7375
cert_file_name: 'misc/cert.pem',
7476
passphrase: '1234'
77+
}).get('/smallFile', (res, req) => {
78+
/* !! Use this only for small files !!
79+
* May cause server backpressure and bad performance
80+
* For bigger files you have to use streaming method */
81+
res.writeHeader('Content-Type', smallFileType).end(smallFileCachedBuffer);
7582
}).get('/bigFile', (res, req) => {
7683
const streamIndex = ++ lastStreamIndex;
7784
console.log('Stream ('+ streamIndex +') was opened, openStreams: '+ (++ openStreams));
78-
res.writeHeader('Content-Type', 'video/mpeg');
79-
// Create read stream with fs.createReadStream
80-
streamData(res, fs.createReadStream(bigFileName), bigFileSize, () => {
81-
// On streaming finished (success/error/onAborted)
82-
console.log('Stream ('+ streamIndex +') was closed, openStreams: '+ (-- openStreams));
85+
const readStream = fs.createReadStream(bigFileName);
86+
/* Attach onAborted handler because streaming is async */
87+
res.onAborted(() => {
88+
readStream.destroy();
89+
console.log('Stream ('+ streamIndex +') failed, openStreams: '+ (-- openStreams));
90+
});
91+
res.writeHeader('Content-Type', bigFileType);
92+
streamData(res, readStream, bigFileSize, () => {
93+
console.log('Stream ('+ streamIndex +') succeed, openStreams: '+ (-- openStreams));
8394
});
84-
85-
}).get('/smallFile', (res, req) => {
86-
// !! Use this only for small files !!
87-
// May cause server backpressure and bad performance
88-
// For bigger files you have to use streaming
89-
try {
90-
const fileBuffer = fs.readFileSync('absolutPathTo/smallData.json');
91-
res.writeHeader('Content-Type', 'application/json').end(fileBuffer);
92-
} catch (err) {
93-
console.log('Error reading file: '+ err);
94-
res.writeStatus('500 Internal Server Error').end();
95-
}
9695
}).listen(port, (token) => {
9796
if (token) {
9897
console.log('Listening to port ' + port);

examples/ParseJsonOrFormBody.js

Lines changed: 0 additions & 89 deletions
This file was deleted.

examples/ParseRequestBody.js

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/* This is an example of parsing request body (JSON / URL-encoded form) */
2+
3+
const uWS = require('../dist/uws.js');
4+
const querystring = require('node:querystring');
5+
const port = 9001;
6+
7+
/* Helper function to parse request body */
8+
const parseRequestBody = (res, callback) => {
9+
let buffer = Buffer.alloc(0);
10+
/* Register data callback */
11+
res.onData((ab, isLast) => {
12+
/* Concat every buffer */
13+
buffer = Buffer.concat([buffer, Buffer.from(ab)]);
14+
if (isLast) callback(buffer);
15+
});
16+
};
17+
18+
const app = uWS./*SSL*/App({
19+
key_file_name: 'misc/key.pem',
20+
cert_file_name: 'misc/cert.pem',
21+
passphrase: '1234'
22+
}).get('/jsonAPI', (res, req) => {
23+
/* Attach onAborted handler because body parsing is async */
24+
res.onAborted(() => {
25+
console.log('Request aborted!');
26+
});
27+
parseRequestBody(res, (bodyBuffer) => {
28+
try {
29+
const parsedJson = JSON.parse(bodyBuffer.toString());
30+
console.log('Valid JSON: ');
31+
console.log(parsedJson);
32+
res.cork(() => {
33+
res.end('Thanks for this json!');
34+
});
35+
} catch {
36+
console.log('Invalid JSON or no data at all!');
37+
res.cork(() => {
38+
res.writeStatus('400 Bad Request').end();
39+
});
40+
}
41+
});
42+
}).post('/formPost', (res, req) => {
43+
/* Attach onAborted handler because body parsing is async */
44+
res.onAborted(() => {
45+
console.log('Request aborted!');
46+
});
47+
parseRequestBody(res, (bodyBuffer) => {
48+
let formData;
49+
try { formData = querystring.parse(bodyBuffer.toString()); }
50+
catch { formData = null; }
51+
if (formData && formData.myData) {
52+
console.log('Valid form body: ');
53+
console.log(formData);
54+
res.cork(() => {
55+
res.end('Thanks for your data!');
56+
});
57+
} else {
58+
console.log('Invalid form body or no data at all!');
59+
res.cork(() => {
60+
res.end('Invalid form body');
61+
});
62+
}
63+
});
64+
}).listen(port, (token) => {
65+
if (token) {
66+
console.log('Listening to port ' + port);
67+
} else {
68+
console.log('Failed to listen to port ' + port);
69+
}
70+
});

0 commit comments

Comments
 (0)