-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
meetup: Add HTTP server code we wrote during September meet-up (#36)
The code and some instructions are added to the web-site.
- Loading branch information
Showing
5 changed files
with
151 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(()) | ||
} |