ultimate-express 1.1.5 → 1.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -7
- package/package.json +1 -1
- package/src/application.js +1 -1
- package/src/index.js +0 -1
- package/src/response.js +41 -7
package/README.md
CHANGED
|
@@ -16,7 +16,7 @@ To make sure µExpress matches behavior of Express in all cases, we run all test
|
|
|
16
16
|
|
|
17
17
|
Similar projects based on uWebSockets:
|
|
18
18
|
|
|
19
|
-
- `express` on Bun - since Bun uses uWS for its HTTP module, Express is about 2.5 times faster than on Node.js with
|
|
19
|
+
- `express` on Bun - since Bun uses uWS for its HTTP module, Express is about 2.5 times faster than on Node.js with 27k req/sec instead of 10k req/sec normally, but still slower than µExpress at 70k req/sec because it doesn't do uWS-specific optimizations.
|
|
20
20
|
- `hyper-express` - while having a similar API to Express, it's very far from being a drop-in replacement, and implements most of the functionality differently. This creates a lot of random quirks and issues, making the switch quite difficult. Built in middlewares are also very different.
|
|
21
21
|
- `uwebsockets-express` - this library is closer to being a drop-in replacement, but misses a lot of APIs, depends on Express by calling it's methods under the hood and doesn't try to optimize routing by using native uWS router.
|
|
22
22
|
|
|
@@ -41,13 +41,8 @@ Also tested on a [real-world application](https://nekoweb.org) with templates, s
|
|
|
41
41
|
In a lot of cases, you can just replace `require("express")` with `require("ultimate-express")` and everything works the same. But there are some differences:
|
|
42
42
|
|
|
43
43
|
- `case sensitive routing` is enabled by default.
|
|
44
|
-
- Depending on how you send response, `Content-Length` header may be overwritten or not sent at all:
|
|
45
|
-
- - on simple responses with res.send(), res.json(), etc. it's set automatically (any value you set with res.set() is overwritten)
|
|
46
|
-
- - on streaming responses (piping, res.sendFile()) it's not sent because uWS uses chunked transfer encoding instead
|
|
47
|
-
- - on responses without body, it *is* sent (useful for HEAD requests)
|
|
48
44
|
- request body is only read for POST, PUT and PATCH requests by default. You can add additional methods by setting `body methods` to array with uppercased methods.
|
|
49
|
-
- For HTTPS, instead of doing this:
|
|
50
|
-
|
|
45
|
+
- For HTTPS, instead of doing this:
|
|
51
46
|
```js
|
|
52
47
|
const https = require("https");
|
|
53
48
|
const express = require("express");
|
|
@@ -96,6 +91,13 @@ Optimized routes can be up to 10 times faster than normal routes, as they're usi
|
|
|
96
91
|
|
|
97
92
|
4. By default, µExpress creates 1 (or 0 if your CPU has only 1 core) child thread to improve performance of reading files. You can change this number by setting `threads` to a different number in `express()`, or set to 0 to disable thread pool (`express({ threads: 0 })`). Threads are shared between all express() instances, with largest `threads` number being used. Using more threads will not necessarily improve performance. Sometimes not using threads at all is faster, please [test](https://github.com/wg/wrk/) both options.
|
|
98
93
|
|
|
94
|
+
## WebSockets
|
|
95
|
+
|
|
96
|
+
Since you don't create http server manually, you can't properly use http.on("upgrade") to handle WebSockets. To solve this, there's currently 2 options:
|
|
97
|
+
|
|
98
|
+
- There's currently another library in works: [Ultimate WS](https://github.com/dimdenGD/ultimate-ws) that implements `ws` module. It's same concept as this library, but for WebSockets: fast drop-in replacement for `ws` module with support for uExpress upgrades.
|
|
99
|
+
- You can simply use `app.uwsApp` to access uWebSockets.js `App` instance and call its `ws()` method directly.
|
|
100
|
+
|
|
99
101
|
## Compatibility
|
|
100
102
|
|
|
101
103
|
In general, basically all features and options are supported. Use [Express 4.x documentation](https://expressjs.com/en/4x/api.html) for API reference.
|
package/package.json
CHANGED
package/src/application.js
CHANGED
|
@@ -179,11 +179,11 @@ class Application extends Router {
|
|
|
179
179
|
|
|
180
180
|
#createRequestHandler() {
|
|
181
181
|
this.uwsApp.any('/*', async (res, req) => {
|
|
182
|
-
|
|
183
182
|
const request = new this._request(req, res, this);
|
|
184
183
|
const response = new this._response(res, request, this);
|
|
185
184
|
request.res = response;
|
|
186
185
|
response.req = request;
|
|
186
|
+
|
|
187
187
|
res.onAborted(() => {
|
|
188
188
|
const err = new Error('Request aborted');
|
|
189
189
|
err.code = 'ECONNABORTED';
|
package/src/index.js
CHANGED
package/src/response.js
CHANGED
|
@@ -98,27 +98,61 @@ module.exports = class Response extends Writable {
|
|
|
98
98
|
err.code = 'ECONNABORTED';
|
|
99
99
|
return this.destroy(err);
|
|
100
100
|
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
}
|
|
101
|
+
const isString = typeof chunk === 'string';
|
|
102
|
+
let isChunkedTransfer = true, totalSize;
|
|
104
103
|
this._res.cork(() => {
|
|
105
104
|
if(!this.headersSent) {
|
|
106
105
|
this.writeHead(this.statusCode);
|
|
107
106
|
this._res.writeStatus(this.statusCode.toString());
|
|
108
107
|
for(const header in this.headers) {
|
|
109
108
|
if(header === 'content-length') {
|
|
109
|
+
// if content-length is set, disable chunked transfer encoding, since size is known
|
|
110
|
+
isChunkedTransfer = false;
|
|
111
|
+
totalSize = parseInt(this.headers[header]);
|
|
110
112
|
continue;
|
|
111
113
|
}
|
|
112
114
|
this._res.writeHeader(header, this.headers[header]);
|
|
113
115
|
}
|
|
114
116
|
if(!this.headers['content-type']) {
|
|
115
|
-
this._res.writeHeader('content-type', 'text/html' + (
|
|
117
|
+
this._res.writeHeader('content-type', 'text/html' + (isString ? `; charset=utf-8` : ''));
|
|
116
118
|
}
|
|
117
119
|
this.headersSent = true;
|
|
118
120
|
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
121
|
+
if(!Buffer.isBuffer(chunk) && !(chunk instanceof ArrayBuffer)) {
|
|
122
|
+
chunk = Buffer.from(chunk);
|
|
123
|
+
chunk = chunk.buffer.slice(chunk.byteOffset, chunk.byteOffset + chunk.byteLength);
|
|
124
|
+
}
|
|
125
|
+
if(isChunkedTransfer) {
|
|
126
|
+
// chunked transfer encoding
|
|
127
|
+
this._res.write(chunk);
|
|
128
|
+
callback();
|
|
129
|
+
} else {
|
|
130
|
+
// fixed size transfer encoding
|
|
131
|
+
const lastOffset = this._res.getWriteOffset();
|
|
132
|
+
const [ok, done] = this._res.tryEnd(chunk, totalSize);
|
|
133
|
+
if(done) {
|
|
134
|
+
this.destroy();
|
|
135
|
+
this.socket.emit('close');
|
|
136
|
+
callback();
|
|
137
|
+
} else {
|
|
138
|
+
// still writing
|
|
139
|
+
if(!ok) {
|
|
140
|
+
// wait until uWS is ready to accept more data
|
|
141
|
+
this._res.onWritable((offset) => {
|
|
142
|
+
const [ok, done] = this._res.tryEnd(chunk.slice(offset - lastOffset), totalSize);
|
|
143
|
+
if(done) {
|
|
144
|
+
this.destroy();
|
|
145
|
+
this.socket.emit('close');
|
|
146
|
+
callback();
|
|
147
|
+
} else if(ok) {
|
|
148
|
+
callback();
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
} else {
|
|
152
|
+
callback();
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
122
156
|
});
|
|
123
157
|
}
|
|
124
158
|
writeHead(statusCode, statusMessage, headers) {
|