Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Workers] Adds New Rust Examples #17302

Merged
merged 5 commits into from
Oct 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 73 additions & 1 deletion src/content/docs/workers/examples/basic-auth.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ tags:
languages:
- JavaScript
- TypeScript
- Rust
preview:
- true
pcx_content_type: example
Expand Down Expand Up @@ -258,4 +259,75 @@ export default {
} satisfies ExportedHandler<Env>;
```

</TabItem> </Tabs>
</TabItem> <TabItem label="Rust" icon="seti:rust">
```rs
use base64::prelude::*;
use worker::*;

#[event(fetch)]
async fn fetch(req: Request, env: Env, _ctx: Context) -> Result<Response> {
let basic_user = "admin";
// You will need an admin password. This should be
// attached to your Worker as an encrypted secret.
// Refer to https://developers.cloudflare.com/workers/configuration/secrets/
let basic_pass = match env.secret("PASSWORD") {
Ok(s) => s.to_string(),
Err(_) => "password".to_string(),
};
let url = req.url()?;

match url.path() {
"/" => Response::ok("Anyone can access the homepage."),
// Invalidate the "Authorization" header by returning a HTTP 401.
// We do not send a "WWW-Authenticate" header, as this would trigger
// a popup in the browser, immediately asking for credentials again.
"/logout" => Response::error("Logged out.", 401),
"/admin" => {
// The "Authorization" header is sent when authenticated.
let authorization = req.headers().get("Authorization")?;
if authorization == None {
let mut headers = Headers::new();
// Prompts the user for credentials.
headers.set(
"WWW-Authenticate",
"Basic realm='my scope', charset='UTF-8'",
)?;
return Ok(Response::error("You need to login.", 401)?.with_headers(headers));
}
let authorization = authorization.unwrap();
let auth: Vec<&str> = authorization.split(" ").collect();
let scheme = auth[0];
let encoded = auth[1];

// The Authorization header must start with Basic, followed by a space.
if encoded == "" || scheme != "Basic" {
return Response::error("Malformed authorization header.", 400);
}

let buff = BASE64_STANDARD.decode(encoded).unwrap();
let credentials = String::from_utf8_lossy(&buff);
// The username & password are split by the first colon.
//=> example: "username:password"
let credentials: Vec<&str> = credentials.split(':').collect();
let user = credentials[0];
let pass = credentials[1];

if user != basic_user || pass != basic_pass {
let mut headers = Headers::new();
// Prompts the user for credentials.
headers.set(
"WWW-Authenticate",
"Basic realm='my scope', charset='UTF-8'",
)?;
return Ok(Response::error("You need to login.", 401)?.with_headers(headers));
}

let mut headers = Headers::new();
headers.set("Cache-Control", "no-store")?;
Ok(Response::ok("🎉 You have private access!")?.with_headers(headers))
}
_ => Response::error("Not Found.", 404),
}
}
```
</TabItem> </Tabs>
127 changes: 126 additions & 1 deletion src/content/docs/workers/examples/cors-header-proxy.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ languages:
- JavaScript
- TypeScript
- Python
- Rust
pcx_content_type: example
title: CORS header proxy
sidebar:
Expand Down Expand Up @@ -436,4 +437,128 @@ async def on_fetch(request):
return raw_html_response(demo_page)
```

</TabItem> </Tabs>
</TabItem> <TabItem label="Rust" icon="seti:rust">
```rs
use std::{borrow::Cow, collections::HashMap};
use worker::*;

fn raw_html_response(html: &str) -> Result<Response> {
Response::from_html(html)
}
async fn handle_request(req: Request, api_url: &str) -> Result<Response> {
let url = req.url().unwrap();
let mut api_url2 = url
.query_pairs()
.find(|x| x.0 == Cow::Borrowed("apiurl"))
.unwrap()
.1
.to_string();
if api_url2 == String::from("") {
api_url2 = api_url.to_string();
}
let mut request = req.clone_mut()?;
*request.path_mut()? = api_url2.clone();
if let url::Origin::Tuple(origin, _, _) = Url::parse(&api_url2)?.origin() {
(*request.headers_mut()?).set("Origin", &origin)?;
}
let mut response = Fetch::Request(request).send().await?.cloned()?;
let headers = response.headers_mut();
if let url::Origin::Tuple(origin, _, _) = url.origin() {
headers.set("Access-Control-Allow-Origin", &origin)?;
headers.set("Vary", "Origin")?;
}

Ok(response)
}

fn handle_options(req: Request, cors_headers: &HashMap<&str, &str>) -> Result<Response> {
let headers: Vec<_> = req.headers().keys().collect();
if [
"access-control-request-method",
"access-control-request-headers",
"origin",
]
.iter()
.all(|i| headers.contains(&i.to_string()))
{
let mut headers = Headers::new();
for (k, v) in cors_headers.iter() {
headers.set(k, v)?;
}
return Ok(Response::empty()?.with_headers(headers));
}
Response::empty()
}
#[event(fetch)]
async fn fetch(req: Request, _env: Env, _ctx: Context) -> Result<Response> {
let cors_headers = HashMap::from([
("Access-Control-Allow-Origin", "*"),
("Access-Control-Allow-Methods", "GET,HEAD,POST,OPTIONS"),
("Access-Control-Max-Age", "86400"),
]);
let api_url = "https://examples.cloudflareworkers.com/demos/demoapi";
let proxy_endpoint = "/corsproxy/";
let demo_page = format!(
r#"
<!DOCTYPE html>
<html>
<body>
<h1>API GET without CORS Proxy</h1>
<a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch#Checking_that_the_fetch_was_successful">Shows TypeError: Failed to fetch since CORS is misconfigured</a>
<p id="noproxy-status"/>
<code id="noproxy">Waiting</code>
<h1>API GET with CORS Proxy</h1>
<p id="proxy-status"/>
<code id="proxy">Waiting</code>
<h1>API POST with CORS Proxy + Preflight</h1>
<p id="proxypreflight-status"/>
<code id="proxypreflight">Waiting</code>
<script>
let reqs = {{}};
reqs.noproxy = () => {{
return fetch("{api_url}").then(r => r.json())
}}
reqs.proxy = async () => {{
let href = "{proxy_endpoint}?apiurl={api_url}"
return fetch(window.location.origin + href).then(r => r.json())
}}
reqs.proxypreflight = async () => {{
let href = "{proxy_endpoint}?apiurl={api_url}"
let response = await fetch(window.location.origin + href, {{
method: "POST",
headers: {{
"Content-Type": "application/json"
}},
body: JSON.stringify({{
msg: "Hello world!"
}})
}})
return response.json()
}}
(async () => {{
for (const [reqName, req] of Object.entries(reqs)) {{
try {{
let data = await req()
document.getElementById(reqName).innerHTML = JSON.stringify(data)
}} catch (e) {{
document.getElementById(reqName).innerHTML = e
}}
}}
}})()
</script>
</body>
</html>
"#
);

if req.url()?.path().starts_with(proxy_endpoint) {
match req.method() {
Method::Options => return handle_options(req, &cors_headers),
Method::Get | Method::Head | Method::Post => return handle_request(req, api_url).await,
_ => return Response::error("Method Not Allowed", 405),
}
}
raw_html_response(&demo_page)
}
```
</TabItem> </Tabs>
13 changes: 12 additions & 1 deletion src/content/docs/workers/examples/logging-headers.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ tags:
- Headers
languages:
- JavaScript
- Rust
- TypeScript
- Python
preview:
Expand Down Expand Up @@ -51,6 +52,16 @@ async def on_fetch(request):
return Response.new('Hello world')
```

</TabItem> <TabItem label="Rust" icon="seti:rust">
```rs
use worker::*;

#[event(fetch)]
async fn fetch(req: HttpRequest, _env: Env, _ctx: Context) -> Result<Response> {
console_log!("{:?}", req.headers());
Response::ok("hello world")
}
```
</TabItem> </Tabs>

---
Expand Down Expand Up @@ -142,4 +153,4 @@ Request headers: {
"cf-ipcountry": "US",
// ...
}"
```
```
74 changes: 73 additions & 1 deletion src/content/docs/workers/examples/security-headers.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ languages:
- JavaScript
- TypeScript
- Python
- Rust
pcx_content_type: example
title: Set security headers
sidebar:
Expand Down Expand Up @@ -250,4 +251,75 @@ async def on_fetch(request):
return Response.new(res.body, status=res.status, statusText=res.statusText, headers=new_headers)
```

</TabItem> </Tabs>
</TabItem> <TabItem label="Rust" icon="seti:rust">
```rs
use std::collections::HashMap;
use worker::*;

#[event(fetch)]
async fn fetch(req: Request, _env: Env, _ctx: Context) -> Result<Response> {
let default_security_headers = HashMap::from([
//Secure your application with Content-Security-Policy headers.
//Enabling these headers will permit content from a trusted domain and all its subdomains.
//@see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy
(
"Content-Security-Policy",
"default-src 'self' example.com *.example.com",
),
//You can also set Strict-Transport-Security headers.
//These are not automatically set because your website might get added to Chrome's HSTS preload list.
//Here's the code if you want to apply it:
(
"Strict-Transport-Security",
"max-age=63072000; includeSubDomains; preload",
),
//Permissions-Policy header provides the ability to allow or deny the use of browser features, such as opting out of FLoC - which you can use below:
("Permissions-Policy", "interest-cohort=()"),
//X-XSS-Protection header prevents a page from loading if an XSS attack is detected.
//@see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-XSS-Protection
("X-XSS-Protection", "0"),
//X-Frame-Options header prevents click-jacking attacks.
//@see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options
("X-Frame-Options", "DENY"),
//X-Content-Type-Options header prevents MIME-sniffing.
//@see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options
("X-Content-Type-Options", "nosniff"),
("Referrer-Policy", "strict-origin-when-cross-origin"),
(
"Cross-Origin-Embedder-Policy",
"require-corp; report-to='default';",
),
(
"Cross-Origin-Opener-Policy",
"same-site; report-to='default';",
),
("Cross-Origin-Resource-Policy", "same-site"),
]);
let blocked_headers = ["Public-Key-Pins", "X-Powered-By", "X-AspNet-Version"];
let tls = req.cf().unwrap().tls_version();
let res = Fetch::Request(req).send().await?;
let mut new_headers = res.headers().clone();

// This sets the headers for HTML responses
if Some(String::from("text/html")) == new_headers.get("Content-Type")? {
return Ok(Response::from_body(res.body().clone())?
.with_headers(new_headers)
.with_status(res.status_code()));
}
for (k, v) in default_security_headers {
new_headers.set(k, v)?;
}

for k in blocked_headers {
new_headers.delete(k)?;
}

if !vec!["TLSv1.2", "TLSv1.3"].contains(&tls.as_str()) {
return Response::error("You need to use TLS version 1.2 or higher.", 400);
}
Ok(Response::from_body(res.body().clone())?
.with_headers(new_headers)
.with_status(res.status_code()))
}
```
</TabItem> </Tabs>
Loading
Loading