Git Product home page Git Product logo

uwebsockets-express's Introduction

uWebSockets.js + Express

Express API compatibility layer for uWebSockets.js.

Usage

import uWS from "uWebSockets.js"
import expressify from "uwebsockets-express"

const uwsApp = uWS.App();
const app = expressify(uwsApp);

// use existing middleware implementations!
app.use(express.json());
app.use('/', serveIndex(path.join(__dirname, ".."), { icons: true, hidden: true }))
app.use('/', express.static(path.join(__dirname, "..")));

// register routes
app.get("/hello", (req, res) => {
  res.json({ hello: "world!" });
});

app.listen(8000);

Compatibility coverage

  • ✅ Middleware support
  • ✅ Supports existing Express Router instances
  • Response API
    • ✅ res.headersSent
    • ✅ res.locals
    • ✅ res.append()
    • ✅ res.clearCookie()
    • ✅ res.cookie()
    • ✅ res.end()
    • ✅ res.get()
    • ✅ res.json()
    • ✅ res.jsonp()
    • ✅ res.location()
    • ✅ res.redirect()
    • ✅ res.render()
    • ✅ res.send()
    • ✅ res.sendFile()
    • ✅ res.sendStatus()
    • ✅ res.set()
    • ✅ res.status()
    • ✅ res.type()
    • ✅ res.vary()
    • ❌ res.app
    • ❌ res.attachment()
    • ❌ res.download()
    • ❌ res.format()
    • ❌ res.links()
  • Request API
    • ✅ req.baseUrl
    • ✅ req.body
    • ✅ req.ip
    • ✅ req.method
    • ✅ req.originalUrl
    • ✅ req.params
    • ✅ req.path
    • ✅ req.query
    • ✅ req.accepts()
    • ✅ req.get()
    • ✅ req.param()
    • ❌ req.app
    • ❌ req.cookies
    • ❌ req.fresh
    • ❌ req.hostname
    • ❌ req.ips
    • ❌ req.protocol
    • ❌ req.route
    • ❌ req.secure
    • ❌ req.signedCookies
    • ❌ req.stale
    • ❌ req.subdomains
    • ❌ req.xhr
    • ❌ req.acceptsCharsets()
    • ❌ req.acceptsEncodings()
    • ❌ req.acceptsLanguages()
    • ❌ req.is()
    • ❌ req.range()
    • ❌ req.pipe()

Middleware support

Disclaimer

Having an express compatibility layer on top of uWS is going to bring its performance slightly down. This library does not have performance as its only objective.

If you find potential improvements to make it more performant, feel free to send me a pull-request!

License

MIT

uwebsockets-express's People

Contributors

always-pixels avatar endel avatar hunkydoryrepair avatar levyks avatar ryanwillis avatar supertigerdev avatar theweiweiway avatar vidski avatar xrnss avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

uwebsockets-express's Issues

Cannot use uWebSockets-express with @colyseus/monitor

I followed alternative-express-compatibility-layer to employ uWebSockets.js in my Colyseus server. I tried to enable the monitoring module by the following code snippet but it failed:

const uwsTransport = new uWebSocketsTransport({});
const app = expressify(uwsTransport.app);

app.use(cors());
app.use(express.json());

const auth = basicAuth.default({
  users: {
    user: "password",
  },
  challenge: true,
});
app.use(auth);
app.use("/colyseus", monitor());

const gameServer = new Server({
  transport: new uWebSocketsTransport({}),
});

Querying to some-domain/colyseus/api just returned me with blank content.

What am I doing wrong with this code snippet?

HEAD requests not stripping body

Normally, express will route HEAD requests to the get handler if there is not an explicit head handler.
This library seems to do that appropriately as well.

However, what express does that this does not do, is strip the BODY response. uWebSockets-express seems to be sending the body back on a HEAD request.

This is particularly bad when used with the @colyseus/proxy, since the presence of the BODY creates an error event on the proxy, and the proxy, as a result, unregisters the server, and then subsequently tries every other proxy server, killing them 1 by 1. So, a single call to curl takes down anything running @colyseus/proxy and uWebSockets w/ express simulation pretty much totally.

curl -I http://your-colyseus-proxy/express-endpoint

Some help

I am thinking of transferring my current project to this library. I can make examples in return, but I need many things to start working in this lib. I'm not sure I can help in terms of code myself, since I've never written anything like this before.

I will be able to start by July 10th. I can also write performance tests and self-tests to check the functionality of some functions.

My current project is proprietary, if that matters.

Property 'socket' in type 'IncomingMessage' is not assignable to the same property in base type 'IncomingMessage'

Trying to build my application after adding this library results in a compiler error.

Running npm run build results in an error:

> tsc

node_modules/uwebsockets-express/lib/IncomingMessage.d.ts:25:5 - error TS2416: Property 'socket' in type 'IncomingMessage' is not assignable to the same property in base type 'IncomingMessage'.
  Type 'Socket' is missing the following properties from type 'Socket': write, connect, setEncoding, pause, and 43 more.

25     socket: Socket;
       ~~~~~~

Found 1 error in node_modules/uwebsockets-express/lib/IncomingMessage.d.ts:25

Dependency versions:

  • @colyseus/uwebsockets-transport - 0.14.28
  • uwebsockets-express - 1.2.2
  • express - 4.18.1
  • typescript - 4.7.4
  • node - 16.15.1

tsconfig:

I am not sure if this is relevant but it might be.

{
  "compilerOptions": {
    "outDir": "./dist",
    "rootDir": "src/",
    "target": "ES6",
    "module": "CommonJS",
    "moduleResolution": "Node",
    "strict": true,
    "strictNullChecks": false,
    "esModuleInterop": true,
    "experimentalDecorators": true,
    "removeComments": true,
    "resolveJsonModule": true,
  },
  "exclude": ["node_modules"],
  "include": ["./src/**/*.tsx", "./src/**/*.ts"]
}

Running my application with ts-node works fine, just compiling it fails.
Maybe I'm missing something out. :)

Thank you for this awesome library. It really saves time!

Sending status code is not working

Hi, first, thanks for this library! It is really helpful.

I found a possible issue when using the library, it seems that a route like this one:

    app.get('/test', (_req, res) => {
        return res.status(401).send()
    })

Is not sending the 401 status to the client.

I found this when trying to use this with express-basic-auth.

Using res.res.writeStatus(401).end() does work, but that means accessing the internal uWS Response object.

Another possible issue is that handlers created with router.post are also handling get requests:

router.get('/subpath', ...) // this also handles post requests - not just get
app.use('/path', router)

Full body not received

Here is the code which we have implemented

try {
    var uWebSockets_js_1 = require('uWebSockets.js');
    var uwebsockets_express_1 = require("uwebsockets-express");
    var uwsApp = uWebSockets_js_1.App();

    var app = (uwebsockets_express_1.default)(uwsApp);
    const server = http.createServer((req, res)=>{
      app(req, res)
    });

    app.listen(port, function (err) {
      app.post("/data", function (req, res) {
        console.log('body', req.body)
        res.json({ hello: "world!" });
      });
    });
  } catch (er) {
    console.log('er', er);
  }

Issue

Sometimes the full body is not received as req.body

Findings

  • Sometimes the full body is not received but sometimes received
  • If try with Postman or disable the web security browser its works fine but with the same origin (normal browser mode) is not working

Received body (which cause SyntaxError: Unexpected token , in JSON at position 2 due to body is not fully received)

body ","func":"var require = context.global.get('require'); \n\nmsg.payload = {\"status\":\"success\"};\n\nreturn msg;","outputs":1,"timeout":10000,"newThread":false,"noerr":0,"x":310,"y":220,"wires":[["d69ad624.954638"]]},{"id":"d69ad624.954638","type":"http response","z":"f1f6b604.8b97b","name":"","statusCode":"","headers":{},"x":500,"y":220,"wires":[]},{"id":"91d9c572.8a1bb8","type":"http in","z":"f1f6b604.8b97b","name":"","url":"/test","method":"get","upload":false,"swaggerDoc":"","x":110,"y":340,"wires":[["9739a876.c5deb8"]]},{"id":"9739a876.c5deb8","type":"function","z":"f1f6b604.8b97b","name":"","func":"var require = context.global.get('require'); \n\nmsg.payload = {\"status\":\"success\"};\n\nreturn msg;","outputs":1,"timeout":10000,"newThread":false,"noerr":0,"x":310,"y":340,"wires":[["e676b9c1.473868"]]},{"id":"e676b9c1.473868","type":"http response","z":"f1f6b604.8b97b","name":"","statusCode":"","headers":{},"x":500,"y":340,"wires":[]},{"id":"a2894126.7ee02","type":"http in","z":"f1f6b604.8b97b","name":"","url":"/test","method":"get","upload":false,"swaggerDoc":"","x":110,"y":400,"wires":[["802fcac3.58f838"]]},{"id":"802fcac3.58f838","type":"function","z":"f1f6b604.8b97b","name":"","func":"var require = context.global.get('require'); \n\nmsg.payload = {\"status\":\"success\"};\n\nreturn msg;","outputs":1,"timeout":10000,"newThread":false,"noerr":0,"x":310,"y":400,"wires":[["6bef2693.2788d8"]]},{"id":"6bef2693.2788d8","type":"http response","z":"f1f6b604.8b97b","name":"","statusCode":"","headers":{},"x":500,"y":400,"wires":[]},{"id":"b643fc2.3de89","type":"http in","z":"f1f6b604.8b97b","name":"","url":"/test","method":"get","upload":false,"swaggerDoc":"","x":110,"y":460,"wires":[["b36eb1a8.17c2b"]]},{"id":"b36eb1a8.17c2b","type":"function","z":"f1f6b604.8b97b","name":"","func":"var require = context.global.get('require'); \n\nmsg.payload = {\"status\":\"success\"};\n\nreturn msg;","outputs":1,"timeout":10000,"newThread":false,"noerr":0,"x":310,"y":460,"wires":[["f84a970.0d71968"]]},{"id":"f84a970.0d71968","type":"http response","z":"f1f6b604.8b97b","name":"","statusCode":"","headers":{},"x":500,"y":460,"wires":[]},{"id":"7e1f5f86.6c131","type":"http in","z":"f1f6b604.8b97b","name":"","url":"/test","method":"get","upload":false,"swaggerDoc":"","x":670,"y":40,"wires":[["8aa649f6.f06948"]]},{"id":"8aa649f6.f06948","type":"function","z":"f1f6b604.8b97b","name":"","func":"var require = context.global.get('require'); \n\nmsg.payload = {\"status\":\"success\"};\n\nreturn msg;","outputs":1,"timeout":10000,"newThread":false,"noerr":0,"x":870,"y":40,"wires":[["a645f8a6.52e358"]]},{"id":"a645f8a6.52e358","type":"http response","z":"f1f6b604.8b97b","name":"","statusCode":"","headers":{},"x":1060,"y":40,"wires":[]},{"id":"e80a8989.ea5278","type":"http in","z":"f1f6b604.8b97b","name":"","url":"/test","method":"get","upload":false,"swaggerDoc":"","x":670,"y":100,"wires":[["eb78714a.82d68"]]},{"id":"eb78714a.82d68","type":"function","z":"f1f6b604.8b97b","name":"","func":"var require = context.global.get('require'); \n\nmsg.payload = {\"status\":\"success\"};\n\nreturn msg;","outputs":1,"timeout":10000,"newThread":false,"noerr":0,"x":870,"y":100,"wires":[["48f1ae97.bdbdf"]]},{"id":"48f1ae97.bdbdf","type":"http response","z":"f1f6b604.8b97b","name":"","statusCode":"","headers":{},"x":1060,"y":100,"wires":[]},{"id":"6b3b8991.f8d3a8","type":"http in","z":"f1f6b604.8b97b","name":"","url":"/test","method":"get","upload":false,"swaggerDoc":"","x":670,"y":160,"wires":[["d617e9eb.e5a638"]]},{"id":"d617e9eb.e5a638","type":"function","z":"f1f6b604.8b97b","name":"","func":"var require = context.global.get('require'); \n\nmsg.payload = {\"status\":\"success\"};\n\nreturn msg;","outputs":1,"timeout":10000,"newThread":false,"noerr":0,"x":870,"y":160,"wires":[["23ec6d3e.5cbb52"]]},{"id":"23ec6d3e.5cbb52","type":"http response","z":"f1f6b604.8b97b","name":"","statusCode":"","headers":{},"x":1060,"y":160,"wires":[]}],"rev":"dd558e227813e71e4b213ee372a51251"}

@endel

500ms Content-Length timeout is not enough

We've run into occasional issues getting "request size did not match content length".

It is very sporadic, but ultimately determine that the timeout, which requires the full content is received within 500ms is causing problems.
I'm not sure if this is 500ms for the full content body, or if timeout resets with each packet received. Our payloads are around 1500 bytes, which is often 2 packets.

Scenarios where we end up getting this error:

  • Server under too much load. In this case, the timeout seems to happen before the packet is processed, although I cannot prove that. However, I would expect if server is overloaded, if the socket service took priority over the setTimeout, we wouldn't be getting these errors more during server overload. (Another possibility, servers are overloaded enough to cause packet loss due to buffer overflow).

  • network packet loss. If a client is having packet loss issues, the 2nd or 3rd packet of the request may come in more than 500ms after the initial packet. We get this more with users in Asia trying to hit our servers in US.

Would it be possible to make that timeout value configurable? Obviously, we don't want the client to hang forever with open socket because they sent 1 bad request, but in our case, it needs to be a little more patient.

get `expressify is not a functiuon` in .js file

when I use this package in Nodejs use .js file, node tell me expressify is not a functiuon .
sandbox


import uWS from "uWebSockets.js";
import expressify from "uwebsockets-express";

const uwsApp = uWS.App();

uwsApp.ws("/ws", {
  /* Options */
  compression: uWS.SHARED_COMPRESSOR,
  maxPayloadLength: 16 * 1024 * 1024,
  idleTimeout: 16,
  /* Handlers */
  open: (ws) => {},
  message: (ws, message, isBinary) => {
    /* Ok is false if backpressure was built up, wait for drain */
    let ok = ws.send(message, isBinary);
  },
  drain: (ws) => {
    console.log("WebSocket backpressure: " + ws.getBufferedAmount());
  },
  close: (ws, code, message) => {
    console.log("WebSocket closed");
  }
});

const app = expressify(uwsApp);

app.listen(9000, (token) => {
  if (token) {
    console.log("Listening to port " + 9000);
  } else {
    console.log("Failed to listen to port " + 9000);
  }
});

TypeError: expressify is not a function

example:
import uWS from "uWebSockets.js"
import expressify from "uwebsockets-express"

const uwsApp = uWS.App();
const app = expressify(uwsApp);

// use existing middleware implementations!
app.use(express.json());
app.use('/', serveIndex(path.join(__dirname, ".."), { icons: true, hidden: true }))
app.use('/', express.static(path.join(__dirname, "..")));

// register routes
app.get("/hello", (req, res) => {
res.json({ hello: "world!" });
});

app.listen(8000);

Error:
const app = expressify(uwsApp)
^
TypeError: expressify is not a function

Operation process:
1.npm i uWebSockets.js-express
2.npm i uNetworking/uWebSockets.js#v20.31.0
3. npm i express
4. npm i -D [email protected]

TypeError: res.flush is not a function

When using a flush to stream the video output, I'm facing res.flush is not a function.
Is there another way to accomplish the flush function, or is it possible for us to enable an express-style flush function?

Not working with Colyseus proxy

I am not sure if this is the right repo for reporting this. Sorry if it's not. 😄

Setup

  • Colyseus proxy running on Kubernetes
  • Game servers using uWebSockets transport layer

Problem

  • Game servers stopped registering themselves to the Colyseus proxy after switchting to uWebSockets transport
  • Requests to the proxy end up with error 503 and proxy prints out No proxy available! {}.
  • remove event gets fired from the game servers when shutting one down. (Proxy loggs the event.)
    → Seemingly they are able to communicate with the Redis presence.

The proxy was working fine before switching to the new transport layer.

Thanks in advance! 🙏

Corking

You need to implement corking.
Basically wrap the ServerResponse in a callback inside cork.

    res.cork(() => {
      res.writeStatus(...)
      res.writeHeader(...)
      res.write(...);
    })

Its easy to do and will improve performance significantly. If you are having troubles, I can open a PR.

From the lib:
Corking a response is a performance improvement in both CPU and network, as you ready the IO system for writing multiple chunks at once. By default, you're corked in the immediately executing top portion of the route handler. In all other cases, such as when returning from await, or when being called back from an async database request or anything that isn't directly executing in the route handler, you'll want to cork before calling writeStatus, writeHeader or just write. Corking takes a callback in which you execute the writeHeader, writeStatus and such calls, in one atomic IO operation. This is important, not only for TCP but definitely for TLS where each write would otherwise result in one TLS block being sent off, each with one send syscall.

Content-Length > actual size will result in Error: socket hang up

Sending a request, where the Content-Length header is bigger than the actual content size will result in a Error: socket hang up. In my opinion, it should throw a 400 Bad Request.

Example(Valid):

POST /users/login HTTP/1.1
Host: 127.0.0.1:2567
Host: 127.0.0.1:2567
User-Agent: UnityPlayer/2020.3.13f1 (UnityWebRequest/1.0, libcurl/7.75.0-DEV)
Accept: */*
Accept-Encoding: identity
Content-Type: application/x-www-form-urlencoded
X-Unity-Version: 2020.3.13f1
Content-Length: 38

email=mymail%40gmail.com&password=test

Result: 200 OK

Example(Invalid): Content-Length > 38

POST /users/login HTTP/1.1
Host: 127.0.0.1:2567
Host: 127.0.0.1:2567
User-Agent: UnityPlayer/2020.3.13f1 (UnityWebRequest/1.0, libcurl/7.75.0-DEV)
Accept: */*
Accept-Encoding: identity
Content-Type: application/x-www-form-urlencoded
X-Unity-Version: 2020.3.13f1
Content-Length: 39

email=mymail%40gmail.com&password=test

Result: Error: socket hang up

request size did not match content length

Description

I just replaced the express server with the uwebsocket-epxress. I am getting "request size did not match content length" for some of the our APIs. And those APIs are working fine with express. Anyone have chance to looked into it? It is blocking some of the priority Task. Thanks in advance :)

Express router middleware/route order issue

Hi there, thanks for the great package!

I noticed that when passing an express router to use, the order of middleware and routes are not preserved. Middleware are always executed before routes even when defined afterwards.

To Reproduce:
Hit reload in the browser pane of each of these examples and check the console.

uWebSockets-express middleware order: https://codesandbox.io/s/uwebsockets-express-middleware-order-issue-jcw6z

Express router middleware order: https://codesandbox.io/s/express-router-as-middleware-ordering-7rxgv

IncomingMessage and ServerResponse types aren't exported directly

It would be nice if we could import the types for * directly from 'uwebsockets-express' instead of having to type the entire path

import { IncomingMessage, ServerResponse } from 'uwebsockets-express';

instead of:

import { IncomingMessage } from 'uwebsockets-express/lib/IncomingMessage';
import { ServerResponse } from 'uwebsockets-express/lib/ServerResponse';

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.