Skip to content

Commit

Permalink
meetup: Add HTTP server code we wrote during September meet-up (#36)
Browse files Browse the repository at this point in the history
The code and some instructions are added to the web-site.
  • Loading branch information
hds authored Sep 17, 2024
1 parent 45db189 commit f16c635
Show file tree
Hide file tree
Showing 5 changed files with 151 additions and 0 deletions.
10 changes: 10 additions & 0 deletions content/meetups/2024-09-17.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,13 @@ an HTTP server!

This will be a group implementation session, where we'll do our best to get a `curl`-compliant HTTP
server responding with something simple by the end of our lunch break.

#### The result

We managed to get a server working with some basic error handling as well as responding to a happy
path request with a body. The code has been left exactly as it was at the end of the meet-up, `dbg!`
statements and all!

The code can be found on GitHub [hds/lunch.rs](https://github.com/hds/lunch.rs):

- [http-for-lunch](https://github.com/hds/lunch.rs/tree/main/static/content/2024-09-17/http-for-lunch)
7 changes: 7 additions & 0 deletions static/content/2024-09-17/http-for-lunch/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions static/content/2024-09-17/http-for-lunch/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[package]
name = "http-for-lunch"
version = "0.1.0"
edition = "2021"

[dependencies]
48 changes: 48 additions & 0 deletions static/content/2024-09-17/http-for-lunch/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# HTTP for Lunch

This is an HTTP server which was built during the [September Rust for Lunch meetup](https://lunch.rs/meetups/2024-09-17/).

The code has been left exactly as it was at the end of the meet-up, `dbg!` statements and all!

## Building & running

Normal `cargo` behaviour:

```sh
cargo run
```

## Testing

We ran the following requests against the server.

Happy path:

```sh
$ curl -D - http://127.0.0.1:8080/hello
HTTP/1.1 200 Rust for Lunch
Content-Length: 13

Hello, World!
```

Method not allowed:

```sh
$ curl -X POST -D - http://127.0.0.1:8080/hello
HTTP/1.1 405 Method Not Allowed
```

HTTP version not supported:

```sh
$ curl --http1.0 -D - http://127.0.0.1:8080/hello
HTTP/1.1 505 HTTP Version not supported
```

Not found:

```sh
$ curl -D - http://127.0.0.1:8080/bye
HTTP/1.1 404 Not Found
```
80 changes: 80 additions & 0 deletions static/content/2024-09-17/http-for-lunch/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
use std::{
io::{self, Read, Write},
net::{TcpListener, TcpStream},
};

fn write_response_no_body(
stream: &mut TcpStream,
status_code: &str,
reason_phrase: &str,
) -> io::Result<()> {
write_response(stream, status_code, reason_phrase, None)
}

fn write_response(
stream: &mut TcpStream,
status_code: &str,
reason_phrase: &str,
body: Option<&str>,
) -> io::Result<()> {
let http_version = "HTTP/1.1";
write!(
stream,
"{http_version} {status_code} {reason_phrase}\r\n",
)?;

match body {
Some(body) => {
// Content length header
write!(stream, "Content-Length: {len}\r\n\r\n", len = body.len())?;

write!(stream, "{body}")
}
None => write!(stream, "\r\n"),
}
}

fn handle_client(mut stream: TcpStream) -> std::io::Result<()> {
let mut buffer = vec![0_u8; 1024];

let read_len = stream.read(&mut buffer)?;
println!("Read some bytes: {read_len}");

let request = std::str::from_utf8(&buffer).unwrap();
println!("Read this data: {request}");
let mut lines = request.lines();
let request_line = lines.next().unwrap();
let request_parts = request_line.split_whitespace().collect::<Vec<_>>();

let [method, uri, http_version] = request_parts.as_slice() else {
panic!("we'll sort this out later");
};

dbg!(method);
dbg!(uri);
dbg!(http_version);

if *http_version != "HTTP/1.1" {
return write_response_no_body(&mut stream, "505", "HTTP Version not supported");
}

if *method != "GET" {
return write_response_no_body(&mut stream, "405", "Method Not Allowed");
}

if *uri != "/hello" {
return write_response_no_body(&mut stream, "404", "Not Found");
}

write_response(&mut stream, "200", "Rust for Lunch", Some("Hello, World!"))
}

fn main() -> std::io::Result<()> {
let listener = TcpListener::bind("127.0.0.1:8080")?;

// accept connections and process them serially
for stream in listener.incoming() {
handle_client(stream?).unwrap();
}
Ok(())
}

0 comments on commit f16c635

Please sign in to comment.