A new crate: simple-server, a basic webserver

Hey all!

@ag_dubs and I have been working on a project, and today is its first release! :confetti_ball:

simple-server is a very standard webserver implementation. In some ways, it's a continuation of chapter 20 of the book, in some ways, it's "hey how close can we make the API to nodejs's createServer. In Node:

const http = require('http')
const port = 3000

const requestHandler = (request, response) => {
  console.log(request.url)
  response.end('Hello Node.js Server!')
}

const server = http.createServer(requestHandler)

server.listen(port, (err) => {
  if (err) {
    return console.log('something bad happened', err)
  }

  console.log(`server is listening on ${port}`)
})

With simple-server:

#[macro_use]
extern crate log;
extern crate env_logger;

extern crate simple_server;

use simple_server::Server;

fn main() {
    env_logger::init().unwrap();

    let host = "127.0.0.1";
    let port = "7878";

    let server = Server::new(|request, mut response| {
        info!("Request received. {} {}", request.method(), request.uri());
        Ok(response.body("Hello Rust!".as_bytes())?)
    });

    server.listen(host, port);
}

Similar levels of complexity, but a bit more Rust-like. :smile:

We've built simple-server on top of four crates: libstd, http, httparse, and scoped-threadpool. libstd for the networking, httparse to parse requests, http for all of the proper http types, and scoped-threadpool, as we're using blocking IO. Tokio is great, but we're going for utmost simplicity here, not maximum speed; you can reasonably read this crate and its dependencies and know what's going on.

A 0.1.0 release is up at https://crates.io/crates/simple-server, please kick the tires, open issues, etc!

53 Likes

I literally signed up for this forum so I could :purple_heart: this post. Really excited for this, @ag_dubs @steveklabnik! API looks great.

10 Likes

Alternatively, if you want something more akin to Python's SimpleHttpServer then this would be the Rust equivalent: https://github.com/TheWaWaR/simple-http-server

1 Like

Cool!
It's been my personal belief that Rust should have something like this! the "gateway drug" :syringe: towards using safe native software so to speak :smile:

The stringly-typing genuinely puzzles me though:

Why on earth would you make port stringly typed? :face_with_raised_eyebrow: If anything is a uint16, this is!
It even adds two "-quotes worth of extra typing vs let host = 7878... Or what am I missing?

I'm still on the fence about the host IP, on the one hand there's std::net::IpAddr, on the other hand it would add quite some boilerplate:

IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)); // verbose, guaranteed to work...
"127.0.0.1".parse().expect("couldn't parse IP address") // not much better...

Want me to open discussion github-issues for this, or rather discussion-here/coding-on-github?

6 Likes

And it should be documented what happens if the host string is not a valid host-name/IP: looks like Panics section is missing from the docs :wink:

1 Like

issue opened: #69

I've also opened a discussion issue for my stringly-typing concerns: #70

We can talk about it in-issue, but basically, it was about making things as simple as possible, so we didn't give much thought to it. Let's improve this!

1 Like

I expected something in that direction, that's why I made it a discussion issue, instead of a pull-request :slight_smile:
Taking the rest of the discussion to the github!

@juleskers There are better options. To get the infallibility of the first option, and the relative terseness of the parse option, You can do one of the following:

  1. [127, 0, 0, 0].into(), or for Ipv6: [0,0,0,0,0,0,0,1].into()
  2. On nightly, you can do:
    #![feature(ip_constructors)]
    Ipv4Addr::localhost().into();
    // Or for ::1 
    Ipv6Addr::localhost().into();
    

There's also an Ipv4Addr::unspecified() for 0.0.0.0, and Ipv6Addr::unspecified() for ::.

3 Likes

Thanks! :heart: That solves a lot of my concerns! I've added it to the issue.

Aren't ports allowed to be non-numeric in ipv6?

Whaaaat? That's the first I'd heard of it, and I can't imagine how it would fit in with the fixed header sizes of network packets.

Maybe string aliasses, but other than that? Would you happen to have some links for me? I'd love to dig in!

I couldn't find much - perhaps I was just thinking of string aliases.

1 Like

Thank you for building this!

I was looking for something with few dependencies to run on a low-end single-core device, and this looks like exactly what I need. Thanks!