axum_cloudflare_adapter/
lib.rs1mod error;
50
51use axum::{
52 body::Body,
53 http::header::HeaderName,
54 http::{Method, Request, Uri},
55 response::Response,
56};
57pub use error::Error;
58use futures::TryStreamExt;
59use std::str::FromStr;
60use std::sync::Arc;
61use worker::{Headers, Request as WorkerRequest, Response as WorkerResponse};
62
63pub async fn to_axum_request(mut worker_request: WorkerRequest) -> Result<Request<Body>, Error> {
64 let method = Method::from_bytes(worker_request.method().to_string().as_bytes())?;
65
66 let uri = Uri::from_str(worker_request.url()?.to_string().as_str())?;
67
68 let body = worker_request.bytes().await?;
69
70 let mut http_request = Request::builder()
71 .method(method)
72 .uri(uri)
73 .body(Body::from(body))?;
74
75 for (header_name, header_value) in worker_request.headers() {
76 http_request.headers_mut().insert(
77 HeaderName::from_str(header_name.as_str())?,
78 header_value.parse()?,
79 );
80 }
81
82 Ok(http_request)
83}
84
85pub async fn to_worker_response(response: Response<Body>) -> Result<WorkerResponse, Error> {
86 let mut bytes: Vec<u8> = Vec::<u8>::new();
87
88 let (parts, body) = response.into_parts();
89
90 let mut stream = body.into_data_stream();
91 while let Some(chunk) = stream.try_next().await? {
92 bytes.extend_from_slice(&chunk);
93 }
94
95 let code = parts.status.as_u16();
96
97 let mut worker_response = WorkerResponse::from_bytes(bytes)?;
98 worker_response = worker_response.with_status(code);
99
100 let mut headers = Headers::new();
101 for (key, value) in parts.headers.iter() {
102 headers.set(key.as_str(), value.to_str()?).unwrap()
103 }
104 worker_response = worker_response.with_headers(headers);
105
106 Ok(worker_response)
107}
108
109pub use axum_wasm_macros::wasm_compat;
110
111#[derive(Clone)]
112pub struct EnvWrapper {
113 pub env: Arc<worker::Env>,
114}
115
116impl EnvWrapper {
117 pub fn new(env: worker::Env) -> Self {
118 Self { env: Arc::new(env) }
119 }
120}
121
122unsafe impl Send for EnvWrapper {}
123
124unsafe impl Sync for EnvWrapper {}
125
126#[cfg(test)]
127mod tests {
128 use super::*;
129 use axum::{response::Html, response::IntoResponse};
130 use wasm_bindgen_test::*;
131 use worker::{Method as WorkerMethod, RequestInit, ResponseBody};
132 wasm_bindgen_test_configure!(run_in_browser);
133
134 #[wasm_bindgen_test]
135 async fn it_should_convert_the_worker_request_to_an_axum_request() {
136 let mut request_init = RequestInit::new();
137 let mut headers = Headers::new();
138 headers.append("Content-Type", "text/html").unwrap();
139 headers.append("Cache-Control", "no-cache").unwrap();
140 request_init.with_headers(headers);
141 request_init.with_method(WorkerMethod::Get);
142 let worker_request =
143 WorkerRequest::new_with_init("https://logankeenan.com", &request_init).unwrap();
144
145 let request = to_axum_request(worker_request).await.unwrap();
146
147 assert_eq!(request.uri(), "https://logankeenan.com");
148 assert_eq!(request.method(), "GET");
149 assert_eq!(request.headers().get("Content-Type").unwrap(), "text/html");
150 assert_eq!(request.headers().get("Cache-Control").unwrap(), "no-cache");
151 }
152
153 #[wasm_bindgen_test]
154 async fn it_should_convert_the_worker_request_to_an_axum_request_with_a_body() {
155 let mut request_init = RequestInit::new();
156 request_init.with_body(Some("hello world!".into()));
157 request_init.with_method(WorkerMethod::Post);
158 let worker_request =
159 WorkerRequest::new_with_init("https://logankeenan.com", &request_init).unwrap();
160
161 let request = to_axum_request(worker_request).await.unwrap();
162
163 let mut bytes: Vec<u8> = Vec::<u8>::new();
164
165 let mut stream = request.into_body().into_data_stream();
166 while let Some(chunk) = stream.try_next().await.unwrap() {
167 bytes.extend_from_slice(&chunk);
168 }
169
170 assert_eq!(bytes.to_vec(), b"hello world!");
171 }
172
173 #[wasm_bindgen_test]
174 async fn it_should_convert_the_axum_response_to_a_worker_response() {
175 let response = Html::from("Hello World!").into_response();
176 let worker_response = to_worker_response(response).await.unwrap();
177
178 assert_eq!(worker_response.status_code(), 200);
179 assert_eq!(
180 worker_response
181 .headers()
182 .get("Content-Type")
183 .unwrap()
184 .unwrap(),
185 "text/html; charset=utf-8"
186 );
187 let body = match worker_response.body() {
188 ResponseBody::Body(body) => body.clone(),
189 _ => vec![],
190 };
191 assert_eq!(body, b"Hello World!");
192 }
193
194 #[wasm_bindgen_test]
195 async fn it_should_convert_the_axum_response_to_a_worker_response_with_an_empty_body() {
196 let body = Body::empty();
197 let response = Response::builder()
198 .status(200)
199 .header("Content-Type", "text/html")
200 .body(body)
201 .unwrap();
202
203 let worker_response = to_worker_response(response).await.unwrap();
204
205 assert_eq!(worker_response.status_code(), 200);
206 assert_eq!(
207 worker_response
208 .headers()
209 .get("Content-Type")
210 .unwrap()
211 .unwrap(),
212 "text/html"
213 );
214 let body = match worker_response.body() {
215 ResponseBody::Body(body) => body.clone(),
216 _ => b"should be empty".to_vec(),
217 };
218 assert_eq!(body.len(), 0);
219 }
220}