ultimate-express 1.1.5 → 1.1.7

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 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 25k req/sec instead of 10k req/sec normally, but still slower than µExpress at 60k req/sec because it doesn't do uWS-specific optimizations.
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ultimate-express",
3
- "version": "1.1.5",
3
+ "version": "1.1.7",
4
4
  "description": "The Ultimate Express. Fastest http server with full Express compatibility, based on uWebSockets.",
5
5
  "main": "src/index.js",
6
6
  "scripts": {
@@ -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
@@ -22,7 +22,6 @@ const Request = require("./request.js");
22
22
  const Response = require("./response.js");
23
23
 
24
24
  Application.Router = function(options) {
25
-
26
25
  return new Router(options);
27
26
  }
28
27
 
package/src/response.js CHANGED
@@ -64,6 +64,8 @@ module.exports = class Response extends Writable {
64
64
  this.locals = {};
65
65
  this.aborted = false;
66
66
  this.statusCode = 200;
67
+ this.chunkedTransfer = true;
68
+ this.totalSize = 0;
67
69
  this.headers = {
68
70
  'keep-alive': 'timeout=10'
69
71
  };
@@ -98,15 +100,15 @@ module.exports = class Response extends Writable {
98
100
  err.code = 'ECONNABORTED';
99
101
  return this.destroy(err);
100
102
  }
101
- if(!Buffer.isBuffer(chunk)) {
102
- chunk = Buffer.from(chunk);
103
- }
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
+ this.chunkedTransfer = false;
111
+ this.totalSize = parseInt(this.headers[header]);
110
112
  continue;
111
113
  }
112
114
  this._res.writeHeader(header, this.headers[header]);
@@ -116,9 +118,41 @@ module.exports = class Response extends Writable {
116
118
  }
117
119
  this.headersSent = true;
118
120
  }
119
- const ab = chunk.buffer.slice(chunk.byteOffset, chunk.byteOffset + chunk.byteLength);
120
- this._res.write(ab);
121
- callback();
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(this.chunkedTransfer) {
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, this.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), this.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) {