Why not take a look at µBlitz.js? #1217
Replies: 4 comments
-
|
I forgot to mention, that I am planning to make a testing tool, which lets you "trace" how the µWS functionality in your project works. You would just run some additional code and have all your "forgotten onAborted" traced and mentioned. And yes, I edited this message to show, that this testing would eliminate the risk for community to hate debugging apps like here #1209 |
Beta Was this translation helpful? Give feedback.
-
Right you can't use "middlewares" in uWS because "middlewares" assume some very poor algorithms. Also, they are not really necessary if you know how to use regular functions.
Exactly, this has always been the idea. You don't need to implement a body parser as "a middleware" if you know how to use functions. Rather than have uWS adopt a bad algorithmic design, people need to learn how to use functions. This is kind of how my examples always assumed people would do it anyways; if you look at JsonPost.js example, it literally has a function named readJson. That's 1 single line of code to read a body in uWS (without relying on "middlewares"). This readJson is basically the same exact thing as your accumulateBody function. The obvious way forward is to make a library of these commonly used functions, optimize them, and then have people use them. Rather than modify uWS to become Express and their shitty "middleware" interface. Btw, If y ou're building an app and you can't wrap your head around a function like "accumulateBody" rather than a middleware doing the same, then you're a lost cause anyways. |
Beta Was this translation helpful? Give feedback.
-
|
Thanks for response! As for now, anyone who reads this, you are welcome to text me in case you want to save your idea for uWS (Node.js side) for an easy access in the future. |
Beta Was this translation helpful? Give feedback.
-
|
I would like to finally centralise the file-streaming mechanism and publish to @ublitzjs/static (don't look there now, it is totally different from what I would like to write now), so I need an advice from uWS users/devs. import { type HttpResponse } from "@ublitzjs/core";
import { type FileHandle, open } from "node:fs/promises";
var chunkSize = 64 * 1024
var drainEvent = Symbol()
type returnErrMessage =
| `can't open`
| `read failed`
| `wrong size`
/**
* @returns undefined - everything ok, res.finished == true
* @returns "can't open" - file coudln't be opened, status was not written and response was not closed. Handle this case manually with "500" or else.
* @returns "read failed" - response was forcefully closed.
* @returns "wrong size" - file is smaller that the ranges, you passed. Response is closed forcefully.
* */
export async function sendFile(
res: HttpResponse,
path: string,
fileData: { start: number; end: number; whole?: number },
headers?: (res: HttpResponse)=>any
): Promise<returnErrMessage | undefined> {
var fd: FileHandle = await open(path, "r").catch(()=>-1 as const) as any
if((fd as any) === -1) return `can't open` // typescript can't tell that below this "if(fd===-1)" is NOT -1
if (res.aborted) { await fd.close().catch(()=>{}); return }
var potentialContentOffset = 0
var actualContentOffset = 0
var CL: number = fileData.end - fileData.start + 1;
res.emitter.once("abort", ()=>{ closePromise = fd.close().catch(()=>{}) })
var closePromise: Promise<any> | undefined
var beforeDrainResponseOffset: number = 0;
var writeResults: [boolean, boolean]
var aheadPromise: Promise<Buffer<ArrayBuffer> | null>
var currentIsbuf1 = true
var buf1: Buffer<ArrayBuffer> | undefined
var buf2: Buffer<ArrayBuffer> | undefined
if (CL > chunkSize) {
let secondBufferRemains = CL - chunkSize;
let memory = Buffer.allocUnsafe(chunkSize + (secondBufferRemains > chunkSize ? chunkSize : secondBufferRemains))
buf1 = Buffer.from(memory.buffer, 0, chunkSize)
buf2 = Buffer.from(memory.buffer, chunkSize)
} else buf1 = Buffer.allocUnsafe(CL)
var currentBuffer: Buffer<ArrayBuffer> | null
var readPromise = tryReadNext()
var errMessage: returnErrMessage | undefined
async function tryReadNext(): Promise<Buffer<ArrayBuffer> | null> {
var val = currentIsbuf1 ? buf1! : buf2!;
currentIsbuf1 = !currentIsbuf1;
var currentPotentialContnentOffset = potentialContentOffset
potentialContentOffset += chunkSize;
if(potentialContentOffset > CL) {
val = Buffer.from(val.buffer, val.byteOffset); potentialContentOffset = CL;
}
var result = await fd.read(val, 0, val.byteLength, currentPotentialContnentOffset + fileData.start).catch(()=>{return null});
if(res.aborted) return null;
else if(!result) {
errMessage = `read failed`; res.close(); return null;
} else if (result.bytesRead != val.length) {
errMessage = `wrong size`; res.close(); return null;
}
actualContentOffset += result.bytesRead
return val
}
res.onWritable((offset) => {
writeResults = res.tryEnd(currentBuffer!.subarray(offset - beforeDrainResponseOffset), CL)
res.finished = writeResults[1]
var drained = writeResults[0] || writeResults[1]
if (drained) res.emitter.emit(drainEvent)
return drained
})
function toWait(resolve: ()=>void) { res.emitter.once(drainEvent, resolve) }
function corkedCB() { writeResults = res.tryEnd(currentBuffer!, CL) }
var startedReadingAhead: boolean = false
res.cork(()=>{
if(fileData.whole && (fileData.whole != fileData.end + 1 || fileData.start > 0)) {
res
.writeStatus("206")
.writeHeader(
"Accept-Ranges: bytes\r\nContent-Range",
`bytes ${fileData.start}-${fileData.end}/${fileData.whole}`
);
}
if (headers) headers(res)
})
do {
startedReadingAhead = potentialContentOffset < CL
if (startedReadingAhead) { aheadPromise = tryReadNext(); }
currentBuffer = await readPromise;
if(!currentBuffer || res.aborted) break;
if (actualContentOffset == CL) {
closePromise = fd.close().catch(()=>{});
//clean sooner
if (currentIsbuf1) buf1 = undefined;
else buf2 = undefined;
}
if (startedReadingAhead) { readPromise = aheadPromise!; }
res.cork(corkedCB)
res.finished = writeResults![1]
if(!writeResults![0] && !res.finished) {
beforeDrainResponseOffset = res.getWriteOffset();
await new Promise<void>(toWait)
if(res.aborted) break;
}
} while (!res.finished);
if(closePromise) await closePromise!;
return errMessage;
} |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
This is a utilitarian / utility library for µWebSockets.js
µBlitz.js
µWS.js is a great project, however on the outside, to be sincere, it lacks features. Do you always write that "Buffer.concat" when receiving a payload, or did you find a way to register several callbacks for "onAborted", that allows you make separate handlers for asynchronous functionality?
I believe that most of you have once compared Express.js, full of middlewares, with µWS.js. Here middlewares can't be used in a reliable or fast way (hyper-express copies all headers), mainly due to "request" object being cleared after the first async job.
What about meta-framework path of improvement? I don't think so as well. In C++ we have "templates" to make our dependency modules adapt for different scenarious, but in JIT js... These are constant "if" calls, slowing down our app. Creating meta-framework means making a universal tool for everyone, but for js it is too much.
I have spent some time to think about an improvement for µWebSockets.js, but from the Node.js side and DX perspective. My conclusion is: why don't we just use utilities? Utilities are created to perform one task, hence without overhead. If we don't like one, we can use another and let some build tool remove unused stuff.
As for now, there are "core" (abort handlers, additional typescript support) and "payload" (multipart requests + body accumulation) completely stable, as I have put some time into testing it. Other ones probably "work", but only with versions < 1.0.0 of two packages mentioned above. You can look at least into typescript description I have put there, just so you could understand their value. Next one coming is "static".
All modules are published on npm under @ublitzjs organization (again, core and payload are ones stable) and github is here: https://github.com/ublitzjs
Got any ideas / wishes / threats (I wish you didn't)? I am open for further discussions
Beta Was this translation helpful? Give feedback.
All reactions