Skip to content

Commit 495257c

Browse files
committed
meetup: Add HTTP server code we wrote during September meet-up
The code and some instructions are added to the web-site.
1 parent 45db189 commit 495257c

File tree

5 files changed

+151
-0
lines changed

5 files changed

+151
-0
lines changed

content/meetups/2024-09-17.md

+10
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,13 @@ an HTTP server!
2626

2727
This will be a group implementation session, where we'll do our best to get a `curl`-compliant HTTP
2828
server responding with something simple by the end of our lunch break.
29+
30+
#### The result
31+
32+
We managed to get a server working with some basic error handling as well as responding to a happy
33+
path request with a body. The code has been left exactly as it was at the end of the meet-up, `dbg!`
34+
statements and all!
35+
36+
The code can be found on GitHub [hds/lunch.rs](https://github.com/hds/lunch.rs):
37+
38+
- [http-for-lunch](https://github.com/hds/lunch.rs/tree/main/static/content/2024-09-17/http-for-lunch)

static/content/2024-09-17/http-for-lunch/Cargo.lock

+7
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
[package]
2+
name = "http-for-lunch"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
[dependencies]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# HTTP for Lunch
2+
3+
This is an HTTP server which was built during the [September Rust for Lunch meetup](https://lunch.rs/meetups/2024-09-17/).
4+
5+
The code has been left exactly as it was at the end of the meet-up, `dbg!` statements and all!
6+
7+
## Building & running
8+
9+
Normal `cargo` behaviour:
10+
11+
```sh
12+
cargo run
13+
```
14+
15+
## Testing
16+
17+
We ran the following requests against the server.
18+
19+
Happy path:
20+
21+
```sh
22+
$ curl -D - http://127.0.0.1:8080/hello
23+
HTTP/1.1 200 Rust for Lunch
24+
Content-Length: 13
25+
26+
Hello, World!
27+
```
28+
29+
Method not allowed:
30+
31+
```sh
32+
$ curl -X POST -D - http://127.0.0.1:8080/hello
33+
HTTP/1.1 405 Method Not Allowed
34+
```
35+
36+
HTTP version not supported:
37+
38+
```sh
39+
$ curl --http1.0 -D - http://127.0.0.1:8080/hello
40+
HTTP/1.1 505 HTTP Version not supported
41+
```
42+
43+
Not found:
44+
45+
```sh
46+
$ curl -D - http://127.0.0.1:8080/bye
47+
HTTP/1.1 404 Not Found
48+
```
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
use std::{
2+
io::{self, Read, Write},
3+
net::{TcpListener, TcpStream},
4+
};
5+
6+
fn write_response_no_body(
7+
stream: &mut TcpStream,
8+
status_code: &str,
9+
reason_phrase: &str,
10+
) -> io::Result<()> {
11+
write_response(stream, status_code, reason_phrase, None)
12+
}
13+
14+
fn write_response(
15+
stream: &mut TcpStream,
16+
status_code: &str,
17+
reason_phrase: &str,
18+
body: Option<&str>,
19+
) -> io::Result<()> {
20+
let http_version = "HTTP/1.1";
21+
write!(
22+
stream,
23+
"{http_version} {status_code} {reason_phrase}\r\n",
24+
)?;
25+
26+
match body {
27+
Some(body) => {
28+
// Content length header
29+
write!(stream, "Content-Length: {len}\r\n\r\n", len = body.len())?;
30+
31+
write!(stream, "{body}")
32+
}
33+
None => write!(stream, "\r\n"),
34+
}
35+
}
36+
37+
fn handle_client(mut stream: TcpStream) -> std::io::Result<()> {
38+
let mut buffer = vec![0_u8; 1024];
39+
40+
let read_len = stream.read(&mut buffer)?;
41+
println!("Read some bytes: {read_len}");
42+
43+
let request = std::str::from_utf8(&buffer).unwrap();
44+
println!("Read this data: {request}");
45+
let mut lines = request.lines();
46+
let request_line = lines.next().unwrap();
47+
let request_parts = request_line.split_whitespace().collect::<Vec<_>>();
48+
49+
let [method, uri, http_version] = request_parts.as_slice() else {
50+
panic!("we'll sort this out later");
51+
};
52+
53+
dbg!(method);
54+
dbg!(uri);
55+
dbg!(http_version);
56+
57+
if *http_version != "HTTP/1.1" {
58+
return write_response_no_body(&mut stream, "505", "HTTP Version not supported");
59+
}
60+
61+
if *method != "GET" {
62+
return write_response_no_body(&mut stream, "405", "Method Not Allowed");
63+
}
64+
65+
if *uri != "/hello" {
66+
return write_response_no_body(&mut stream, "404", "Not Found");
67+
}
68+
69+
write_response(&mut stream, "200", "Rust for Lunch", Some("Hello, World!"))
70+
}
71+
72+
fn main() -> std::io::Result<()> {
73+
let listener = TcpListener::bind("127.0.0.1:8080")?;
74+
75+
// accept connections and process them serially
76+
for stream in listener.incoming() {
77+
handle_client(stream?).unwrap();
78+
}
79+
Ok(())
80+
}

0 commit comments

Comments
 (0)