Giter Site home page Giter Site logo

w-henderson / humphrey Goto Github PK

View Code? Open in Web Editor NEW
32.0 32.0 3.0 1.17 MB

๐Ÿ“ก A Performance-Focused, Dependency-Free Web Server with WebSockets and JSON.

Home Page: https://humphrey.whenderson.dev

License: MIT License

Rust 97.87% Batchfile 0.37% Python 1.14% JavaScript 0.62%
hacktoberfest http http-server json rust web-server websocket websocket-server

humphrey's Introduction

๐Ÿ‘‹ Hi there, I'm William

I'm a Computer Science student at the University of Cambridge with a passion for solving problems by developing efficient, robust and maintainable software, built with performance in mind. I thrive on projects where every millisecond counts, and I find immense satisfaction in delivering performant and reliable solutions across diverse domains, from systems development to web applications, back-end infrastructure to mobile apps.

Please visit my portfolio and blog for more information.

๐Ÿ’ฌ Contact Me

If you want to get in touch, feel free to use any of the platforms below!

humphrey's People

Contributors

grinkers avatar robertpogue avatar ryanrussell avatar w-henderson 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

Watchers

 avatar  avatar

humphrey's Issues

Add `Host` header filtering for requests

It can be useful to run effectively different apps on one port, accessed from different domains. For example, Cloudflare hosts many web applications on fewer IP addresses, and the server knows what to do according to the Host header.

Proposed syntax:

let local_subapp = SubApp::new()
    .with_route("/different_route", local_handler);

let production_subapp = SubApp::new()
    .with_route("/different_route", production_handler);

let app = App::new()
    .with_route("/", home_handler)
    .with_host("localhost", local_subapp)
    .with_host("*", production_subapp)

with_route called on the main app adds a host-independent handler. Sub-app handlers take precedence over host-independent handlers, no matter the order they are specified in. In this example, when requesting "/", the program should first check production_subapp for a "/" handler, then fall back to the main app to find home_handler.

Refactor configuration to be more like Nginx

The current configuration only allows for one configuration for the server, whereas Nginx allows different settings for different routes. The configuration system needs to be completely rewritten to allow for a similar thing.

Possible New Configuration Example

server {
    address = "0.0.0.0"
    port = 80

    route /proxy/* {
        proxy = "http://127.0.0.1:8000"
    }

    route /* {
        directory = "/var/www"
    }
}

Add configuration options for different hosts

Use the new functionality from #41 to allow for host-level configuration.

Proposed syntax:

server {
    address "0.0.0.0"
    port    80
    threads 32

    # quotes are optional in host name
    host "localhost" {
        route /app/* {
            directory "/var/www/dev"
        }
    }

    host "*.example.com" {
        route /app/* {
            directory "/var/www/prod"
        }
    }

    # routes on the base server are applied to all hosts
    route / {
        redirect "/app/"
    }
}

Thread panics when calling `Address::from_headers`

When parsing proxy addresses in humphrey/src/http/address.rs, the following section of code can panic:

let mut proxies: Vec<IpAddr> = forwarded
    .split(',')
    .map(|s| IpAddr::from_str(s).unwrap()) // panics
    .collect();

This happens when the X-Forwarded-For header contains something other than a comma-separated list of IP addresses.

Add an `include` statement to the configuration format

This would import another .conf file into the place of the include statement. For example:

humphrey.conf

server {
    ...

    include "plugins.conf"
}

plugins.conf

plugins {
    some_plugin {
        ...
    }
}

Expanded server.conf

server {
    ...

    plugins {
        some_plugin {
            ...
        }
    }
}

Add support for CORS and the OPTIONS method

Currently, Humphrey does not allow cross-origin resource sharing and rejects the OPTIONS method. This should be changed, and the app given a with_cors(true | false) method.

Implement HTTP proxy

The current proxy implementation simply forwards every byte to the target address. A spec-compliant proxy needs to be implemented allowing for HTTP request forwarding with the X-Forwarded-For header.

Make the WebSocket proxy route-dependent

Use the functionality from #39 to allow WebSocket proxying to different targets on different routes.

Proposed syntax:

server {
    address "0.0.0.0"
    port    80
    threads 32

    route /ws {
        websocket "localhost:1234"
    }

    route /chat {
        websocket "localhost:5678"
    }
}

Add a path-aware route concept to the `App`

This would effectively allow users to define handlers which are aware of the endpoint to which they are attached. This could then be used for the serve_dir built-in handler and remove the need for the strip_prefix.

Improve `App::new` documentation

Clarify in the documentation that App::new requires Default to be implemented for the generic type State and that App::with_state should be used in cases where this is not possible.

Reduce boilerplate code required for cookies

This would involve specific methods for getting cookies from requests and setting cookies on responses. It would probably require at least a partial rewrite of the cookie system.

Make the WebSocket handler route-dependent

Currently, only one WebSocket handler can be set for the entire app. This should be changed so different routes can have different WebSocket handlers.

Current syntax:

let app = App::new()
    .with_websocket_handler(some_handler);

Proposed syntax:

let app = App::new()
    .with_websocket_handler("/chat_ws", some_handler)
    .with_websocket_handler("/*", default_handler);

Implement `Into<Response>` for a number of types

Some simple convenience implementations should be added for types such as String and Vec<u8>, allowing a simple 200 OK response to be automatically generated using some_string.into() instead of Response::new(StatusCode::OK, some_string).

Add a static content handler

Add some kind of first-party static content handler function to automatically serve static content within a directory, using the try_find_path function.

Better integrate JSON with Core and WebSocket

This would involve adding From implementations for automatically serializing JSON for transport in Responses and WebSocket Messages, as well as adding correct headers for the former.

Thread panics when getting peer address

In the client_handler function of humphrey/src/app.rs, the following line can panic.

let addr = stream.peer_addr().unwrap();

This is because the stream can become disconnected before the request is processed, which could happen as a result of high load or just becoming disconnected immediately after connecting.

Implement a monitoring feature

This would allow consumers of the Humphrey crate to monitor its internal performance and activity. This could then be integrated into Server in order to improve logging.

crates.io version bump request

https://docs.rs/humphrey_ws/latest/humphrey_ws/handler/fn.async_websocket_handler.html
This example doesn't compile when pulling from crates.io. I suspect it's just that crates.io needs a minor bump with

humphrey = { version = "^0.7.0", path = "../humphrey" }
? I think humphrey-auth and humprey-server might also need a version bump.

Thank you very much for this project, this is exactly what I was looking for! I can dig deeper if it's not just a simple minor version bump.

Fails to compile with the following dependencies with no lock-file:

[dependencies]
humphrey = "^0.7.0"
humphrey_ws = "^0.5"

but it does work from a local clone of master:

humphrey = { path = "../Humphrey/humphrey" }
humphrey_ws = { path = "../Humphrey/humphrey-ws" }

error:

error[E0277]: expected a `Fn<(humphrey::http::Request, humphrey::stream::Stream, Arc<_>)>` closure, found `impl Fn(humphrey::http::request::Request, humphrey::stream::Stream, Arc<_>)`
   --> src/main.rs:14:38
    |
14  |         .with_websocket_route("/ws", async_websocket_handler(websocket_app.connect_hook().unwrap()));
    |          --------------------        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected an `Fn<(humphrey::http::Request, humphrey::stream::Stream, Arc<_>)>` closure, found `impl Fn(humphrey::http::request::Request, humphrey::stream::Stream, Arc<_>)`
    |          |
    |          required by a bound introduced by this call
    |
    = help: the trait `Fn<(humphrey::http::Request, humphrey::stream::Stream, Arc<_>)>` is not implemented for `impl Fn(humphrey::http::request::Request, humphrey::stream::Stream, Arc<_>)`
    = note: expected a closure with arguments `(humphrey::http::request::Request, humphrey::stream::Stream, Arc<_>)`
               found a closure with arguments `(humphrey::http::Request, humphrey::stream::Stream, Arc<_>)`
    = note: required for `impl Fn(humphrey::http::request::Request, humphrey::stream::Stream, Arc<_>)` to implement `humphrey::app::WebsocketHandler<_>`
note: required by a bound in `App::<State>::with_websocket_route`
   --> /home/grinkers/.cargo/registry/src/index.crates.io-6f17d22bba15001f/humphrey-0.7.0/src/app.rs:364:12
    |
362 |     pub fn with_websocket_route<T>(mut self, route: &str, handler: T) -> Self
    |            -------------------- required by a bound in this associated function
363 |     where
364 |         T: WebsocketHandler<State> + 'static,
    |            ^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `App::<State>::with_websocket_route`
    ```

Fix bug with malformed empty responses

When requesting a route which serves zero bytes of content, the browser will hang indefinitely while the app believes it has finished serving the request.

Minimal reproducible example:

use humphrey::App;
use humphrey::http::{Request, Response, StatusCode};

fn main() {
    let app: App<()> = App::new()
        .with_stateless_route("/", home);
    app.run("0.0.0.0:80").unwrap();
}

fn home(request: Request) -> Response {
    Response::new(StatusCode::OK, "", &request)
}

Make `Response` creation more concise

Move current Response::new function to Response::default and have the new new function take in StatusCode, bytes and Request as parameters and perform all the compatibility fixes behind the scenes.

Write a "Getting Started" guide

This needs to be linked in the README. This would replace the book branch so should cover a decent amount of content (#25). The book branch should be deleted when done.

Humphrey Core 0.2 Roadmap

Humphrey Core 0.2 will include a number of breaking API changes to improve the user experience as well as adding new features to more easily support different use-cases.

Proposed Additions

Proposed Changes

Make `Connection` header handling managed by the app

Currently, every response must finish with with_request_compatibility(&request) in order to correctly reflect the Connection header of the request. This also sets the correct HTTP version for 1.0 and 1.1.

This should be brought into the app's main handler loop and automatically done on every response, except those with the Connection handler already set, for example if a connection is to be closed despite the client requesting it to be kept alive.

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.