pub struct Request {
pub method: Method,
pub path: String,
pub version: String,
pub query: Query,
pub headers: Headers,
pub cookies: CookieJar,
pub body: Arc<Vec<u8>>,
pub address: SocketAddr,
pub socket: Arc<Mutex<TcpStream>>,
/* private fields */
}
Expand description
Http Request
Fieldsยง
ยงmethod: Method
Request method.
path: String
Request path (not tokenized).
The query string is not included, its in the query
field.
version: String
HTTP version string. Should usually be โHTTP/1.1โ.
query: Query
Request Query.
headers: Headers
Request headers.
Will not include cookies, which are in the cookies
field.
Request Cookies.
body: Arc<Vec<u8>>
Request body, as a static byte vec.
address: SocketAddr
Client socket address. If you are using a reverse proxy, this will be the address of the proxy (often localhost).
socket: Arc<Mutex<TcpStream>>
The raw tcp socket
Implementationsยง
sourceยงimpl Request
impl Request
sourcepub fn param(&self, name: impl AsRef<str>) -> Option<String>
pub fn param(&self, name: impl AsRef<str>) -> Option<String>
Get a path parameter by its name.
Example
server.route(Method::GET, "/greet/{name}", |req| {
// Get name Path param
// This is safe to unwrap because the router will only call this handler if the path param exists
let name = req.param("name").unwrap();
// Format a nice message
let message = format!("Hello, {}", name);
// Send Response
Response::new()
.text(message)
.content(Content::TXT)
});
Examples found in repository?
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
fn exec(&self) {
// Create a new Server instance on localhost port 8080
let mut server: Server = Server::<()>::new("localhost", 8081);
// Define a handler for GET "/greet/{name}"
// This will handel requests with anything where the {name} is
// This includes "/greet/bob", "/greet/fin"
server.route(Method::GET, "/greet/{name}", |req| {
// Get name path param
let name = req.param("name").unwrap();
// Make a nice Message to send
let message = format!("Hello, {}", name);
// Send Response
Response::new().text(message).content(Content::TXT)
});
// Define a greet route for Darren because he is very cool
// This will take priority over the other route as it is defined after
server.route(Method::GET, "/greet/Darren/", |_req| {
Response::new().text("Hello, Darren. You are very cool")
});
// Start the server
// This will block the current thread
server.start().unwrap();
}
More examples
18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
fn exec(&self) {
// Create a new Server instance on localhost port 8080
let mut server = Server::<()>::new(Ipv4Addr::LOCALHOST, 8080);
// Define a route to handel query string
// This will try to find a name value pair in the query string
server.route(Method::GET, "/", |req| {
// Format the response text
let text = format!(
"<h1>Hello, {}!</h1>",
// Get the query value of name and default to "Nobody" if not found
req.query.get("name").unwrap_or("Nobody")
);
Response::new().text(text).content(Content::HTML)
});
// Define another route
// This time to handle form data
server.route(Method::POST, "/form", |req| {
// The body of requests is not part of the req.query
// Instead it is part of the req.body but as a string
// We will need to parse it get it as a query
let body_data = Query::from_body(&String::from_utf8_lossy(&req.body));
let name = body_data.get("name").unwrap_or("Nobody");
let text = format!("<h1>Hello, {}</h1>", name);
// Create a new response, with the following default data
// - Status: 200
// - Data: OK
// - Headers: []
Response::new()
// Set the response body to be text
.text(text)
// Set the `Content-Type` header to be `text/html`
// Note: This could also be set with the Response::content method
.header(HeaderType::ContentType, "text/html")
});
// Define webpage with form
// The form data will be post to /form on submit
server.route(Method::GET, "/form", |_req| {
let page = r#"<form method="post">
<label for="name">Name:</label>
<input type="text" id="name" name="name"><br><br>
<input type="submit" value="Submit">
</form>"#;
Response::new().text(page).content(Content::HTML)
});
// Define a page with path params
server.route(Method::GET, "/greet/{name}", |req| {
// As this route would ever run without all the path params being filled
// It is safe to unwrap if the name is in the path
let data = format!("<h1>Hello, {}</h1>", req.param("name").unwrap());
Response::new().text(data).content(Content::HTML)
});
// You can now goto http://localhost:8080?name=John and should see "Hello, John"
// If you goto http://localhost:8080/form and submit the form you should see "Hello, {NAME}"
// Also goto http://localhost:8080/greet/John and you should see "Hello, John"
// Start the server
// This will block the current thread
server.start().unwrap();
}
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
fn main() {
set_log_level(Level::Trace);
let app = App::new(PathBuf::from("quotes.txt"));
app.load();
let mut server = Server::new(Ipv4Addr::LOCALHOST, 8080).state(app);
// Route to serve the homepage (page that has add quote form)
server.route(Method::GET, "/", |_| {
Response::new()
.text(String::new() + HEADER + HOME)
.content(Content::HTML)
});
// Route to handle creating new quotes.
// After successful creation the user will be redirected to the new quotes page.
server.stateful_route(Method::POST, "/api/new", |app, req| {
let form = Query::from_body(&String::from_utf8_lossy(&req.body));
let name =
url::decode(form.get("author").expect("No author supplied")).expect("Invalid author");
let body =
url::decode(form.get("quote").expect("No quote supplied")).expect("Invalid quote");
let quote = Quote {
name,
value: body,
date: now(),
};
let mut quotes = app.quotes.write().unwrap();
let id = quotes.len();
quotes.insert(id.to_string(), quote);
drop(quotes);
trace!(Level::Trace, "Added new quote #{id}");
app.save();
Response::new()
.status(Status::SeeOther)
.header(HeaderType::Location, format!("/quote/{id}"))
.text("Redirecting to quote page.")
});
server.stateful_route(Method::GET, "/quote/{id}", |app, req| {
let id = req.param("id").unwrap();
if id == "undefined" {
return Response::new();
}
let id = id.parse::<usize>().expect("ID is not a valid integer");
let quotes = app.quotes.read().unwrap();
if id >= quotes.len() {
return Response::new()
.status(Status::NotFound)
.text(format!("No quote with the id {id} was found."));
}
let quote = quotes.get(&id.to_string()).unwrap();
Response::new().content(Content::HTML).text(
String::new()
+ HEADER
+ "E
.replace("{QUOTE}", "e.value)
.replace("{AUTHOR}", "e.name)
.replace("{TIME}", &imp_date(quote.date)),
)
});
server.stateful_route(Method::GET, "/quotes", |app, _req| {
let mut out = String::from(HEADER);
out.push_str("<ul>");
for i in app.quotes.read().unwrap().iter() {
out.push_str(&format!(
"<li><a href=\"/quote/{}\">\"{}\" - {}</a></li>\n",
i.0, i.1.name, i.1.value
));
}
Response::new().text(out + "</ul>").content(Content::HTML)
});
// Note: In a production application you may want to multithread the server with the Server::start_threaded method.
server.start().unwrap();
}
21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
fn main() {
// Create Server
let mut server = Server::new("localhost", 8080).state(RwLock::new(Vec::new()));
// New paste interface
server.route(Method::GET, "/", |_req| {
Response::new().content(Content::HTML).text(
r#"
<form action="/new-form" method="post">
<input type="text" name="name" id="name" placeholder="Title">
<br />
<textarea id="body" name="body" rows="5" cols="33"></textarea>
<input type="submit" value="Submit" />
</form>
"#,
)
});
// New paste API handler
server.stateful_route(Method::POST, "/new", move |app, req| {
// Make sure paste data isn't too long
if req.body.len() > DATA_LIMIT {
return Response::new()
.status(Status::NotFound)
.text("Data too big!");
}
// Get the data as string
let body_str = String::from_utf8_lossy(&req.body).to_string();
// Get the name from the Name header
let name = req.headers.get("Name").unwrap_or("Untitled");
let paste = Paste {
name: name.to_owned(),
body: body_str,
time: Instant::now(),
};
// Push this paste to the pastes vector
let mut pastes = app.write().unwrap();
let id = pastes.len();
pastes.push(paste);
// Send Redirect response
Response::new()
.status(Status::MovedPermanently)
.header(HeaderType::Location, format!("/p/{id}"))
.text(format!("Redirecting to /p/{id}."))
});
// New paste form handler
server.stateful_route(Method::POST, "/new-form", |app, req| {
// Get data from response
let query = Query::from_body(String::from_utf8_lossy(&req.body).borrow());
let name = url::decode(query.get("name").unwrap_or("Untitled")).expect("Invalid name");
let body = url::decode(query.get("body").expect("No body supplied")).expect("Invalid body");
// Make sure paste data isn't too long
if body.len() > DATA_LIMIT {
return Response::new()
.status(Status::NotFound)
.text("Data too big!");
}
let paste = Paste {
name,
body,
time: Instant::now(),
};
// Push this paste to the pastes vector
let mut pastes = app.write().unwrap();
let id = pastes.len();
pastes.push(paste);
// Send Redirect response
Response::new()
.status(Status::MovedPermanently)
.text("Ok")
.header(HeaderType::Location, format!("/p/{}", id))
});
// Get pate handler
server.stateful_route(Method::GET, "/p/{id}", move |app, req| {
// Get is from path param
let id = req.param("id").unwrap().parse::<usize>().unwrap();
// Get the paste by id
let paste = &app.read().unwrap()[id];
// Send paste
Response::new().text(&paste.body)
});
// View all pastes
server.stateful_route(Method::GET, "/pastes", move |app, _req| {
// Starter HTML
let mut out = String::from(
r#"<a href="/">New Paste</a><meta charset="UTF-8"><table><tr><th>Name</th><th>Date</th><th>Link</th></tr>"#,
);
// Add a table row for each paste
for (i, e) in app.read().unwrap().iter().enumerate() {
out.push_str(&format!(
"<tr><td>{}</td><td>{}</td><td><a href=\"/p/{}\">๐</a></td></tr>",
e.name,
fmt_relative_time(e.time.elapsed().as_secs()),
i
));
}
// Send HTML
Response::new()
.text(format!("{}</table>", out))
.content(Content::HTML)
});
server.start().unwrap();
}
sourcepub fn body_str(&self) -> Cow<'_, str>
pub fn body_str(&self) -> Cow<'_, str>
Gets the body of the request as a string.
This uses the String::from_utf8_lossy
method, so it will replace invalid UTF-8 characters with the unicode replacement character (๏ฟฝ).
If you want to use a different encoding or handle invalid characters, use a string method on the body field.
Trait Implementationsยง
sourceยงimpl RealIp for Request
impl RealIp for Request
sourceยงfn real_ip_header(&self, header: impl Into<HeaderType>) -> IpAddr
fn real_ip_header(&self, header: impl Into<HeaderType>) -> IpAddr
header
into an IpAddr.
If the connection is not coming from localhost, the header isnโt found or the header contains an invalid IP address, the raw socket address will be returned. Read more