1use crate::config::Route;
9use crate::vendored::hyper_ext::{empty_body, string_body, ProxyBody};
10use hyper::header::{
11 HeaderName, HeaderValue, CONNECTION, CONTENT_LENGTH, HOST, TRANSFER_ENCODING, UPGRADE,
12};
13use hyper::{Method, Version};
14use hyper::{Request, Response, StatusCode};
15use std::net::SocketAddr;
16
17pub mod headers {
19 use hyper::header::HeaderName;
20
21 pub static X_FORWARDED_FOR: HeaderName = HeaderName::from_static("x-forwarded-for");
22 pub static X_FORWARDED_PROTO: HeaderName = HeaderName::from_static("x-forwarded-proto");
23 pub static X_FORWARDED_HOST: HeaderName = HeaderName::from_static("x-forwarded-host");
24 pub static X_REAL_IP: HeaderName = HeaderName::from_static("x-real-ip");
25}
26
27const HOP_BY_HOP_HEADERS: &[&str] = &[
35 "connection",
36 "keep-alive",
37 "proxy-authenticate",
38 "proxy-authorization",
39 "te",
40 "trailer",
41 "transfer-encoding",
42 "upgrade",
43];
44
45const CONNECTION_TOKEN_EXEMPT: &[&str] = &["host", "content-length"];
51
52pub struct MessageHandler;
54
55impl MessageHandler {
56 pub fn upgrade_protocol(request: &Request<ProxyBody>) -> Option<String> {
64 if request.version() != Version::HTTP_11 {
65 return None;
66 }
67
68 let headers = request.headers();
69 let asks_for_upgrade = headers.get_all(CONNECTION).iter().any(|value| {
70 value
71 .to_str()
72 .map(|v| {
73 v.split(',')
74 .any(|t| t.trim().eq_ignore_ascii_case("upgrade"))
75 })
76 .unwrap_or(false)
77 });
78 if !asks_for_upgrade {
79 return None;
80 }
81
82 headers
83 .get(UPGRADE)
84 .and_then(|v| v.to_str().ok())
85 .map(|v| v.trim().to_ascii_lowercase())
86 .filter(|v| !v.is_empty())
87 }
88
89 pub fn h2_connect_protocol(request: &Request<ProxyBody>) -> Option<String> {
96 if request.version() != Version::HTTP_2 || request.method() != Method::CONNECT {
97 return None;
98 }
99 request
100 .extensions()
101 .get::<hyper::ext::Protocol>()
102 .map(|protocol| protocol.as_str().to_ascii_lowercase())
103 }
104
105 pub fn restore_upgrade_headers(request: &mut Request<ProxyBody>, protocol: &str) {
112 if let Ok(value) = HeaderValue::from_str(protocol) {
113 let headers = request.headers_mut();
114 headers.insert(UPGRADE, value);
115 headers.insert(CONNECTION, HeaderValue::from_static("upgrade"));
116 }
117 }
118
119 pub fn has_ambiguous_framing(request: &Request<ProxyBody>) -> bool {
131 let headers = request.headers();
132 headers.contains_key(CONTENT_LENGTH) && headers.contains_key(TRANSFER_ENCODING)
133 }
134
135 pub fn strip_hop_by_hop_headers(request: &mut Request<ProxyBody>) {
143 let headers = request.headers_mut();
144
145 let mut named: Vec<String> = Vec::new();
148 for value in headers.get_all(CONNECTION).iter() {
149 let Ok(value) = value.to_str() else { continue };
150 for token in value.split(',') {
151 let token = token.trim().to_ascii_lowercase();
152 if !token.is_empty() {
153 named.push(token);
154 }
155 }
156 }
157
158 for name in HOP_BY_HOP_HEADERS {
159 headers.remove(*name);
160 }
161
162 for token in named {
163 if CONNECTION_TOKEN_EXEMPT.contains(&token.as_str()) {
164 continue;
165 }
166 if let Ok(name) = HeaderName::from_bytes(token.as_bytes()) {
167 headers.remove(name);
168 }
169 }
170 }
171
172 pub fn add_forwarding_headers(
174 request: &mut Request<ProxyBody>,
175 client_addr: SocketAddr,
176 proto: &str,
177 ) {
178 let authority = request.uri().authority().map(|a| a.to_string());
179 let headers = request.headers_mut();
180 let client_ip = client_addr.ip().to_string();
181
182 if let Some(existing) = headers.get(&headers::X_FORWARDED_FOR) {
184 let mut new_value = existing.to_str().unwrap_or("").to_string();
185 new_value.push_str(", ");
186 new_value.push_str(&client_ip);
187 if let Ok(value) = HeaderValue::from_str(&new_value) {
188 headers.insert(headers::X_FORWARDED_FOR.clone(), value);
189 }
190 } else if let Ok(value) = HeaderValue::from_str(&client_ip) {
191 headers.insert(headers::X_FORWARDED_FOR.clone(), value);
192 }
193
194 if !headers.contains_key(&headers::X_REAL_IP) {
196 if let Ok(value) = HeaderValue::from_str(&client_ip) {
197 headers.insert(headers::X_REAL_IP.clone(), value);
198 }
199 }
200
201 if let Ok(value) = HeaderValue::from_str(proto) {
203 headers.insert(headers::X_FORWARDED_PROTO.clone(), value);
204 }
205
206 let original_host = headers
210 .get(HOST)
211 .cloned()
212 .or_else(|| authority.and_then(|a| HeaderValue::from_str(&a).ok()));
213 if let Some(host) = original_host {
214 headers.insert(headers::X_FORWARDED_HOST.clone(), host);
215 }
216 }
217
218 pub fn rewrite_host_header(request: &mut Request<ProxyBody>, upstream_host: &str) {
220 if let Ok(value) = HeaderValue::from_str(upstream_host) {
221 request.headers_mut().insert(HOST, value);
222 }
223 }
224
225 pub fn strip_path_prefix(request: &mut Request<ProxyBody>, prefix: &str) {
227 let uri = request.uri();
228 let path = uri.path();
229
230 if let Some(new_path) = path.strip_prefix(prefix) {
231 let new_path = if new_path.is_empty() || !new_path.starts_with('/') {
232 format!("/{}", new_path.trim_start_matches('/'))
233 } else {
234 new_path.to_string()
235 };
236
237 let new_uri = if let Some(query) = uri.query() {
238 format!("{}?{}", new_path, query)
239 } else {
240 new_path
241 };
242
243 if let Ok(new_uri) = new_uri.parse() {
244 *request.uri_mut() = new_uri;
245 }
246 }
247 }
248
249 pub fn apply_route_headers(request: &mut Request<ProxyBody>, route: &Route) {
251 let headers = request.headers_mut();
252
253 for (name, value) in &route.add_headers {
255 if let (Ok(name), Ok(value)) = (
256 HeaderName::from_bytes(name.as_bytes()),
257 HeaderValue::from_str(value),
258 ) {
259 headers.insert(name, value);
260 }
261 }
262
263 for name in &route.remove_headers {
265 if let Ok(name) = HeaderName::from_bytes(name.as_bytes()) {
266 headers.remove(name);
267 }
268 }
269 }
270
271 pub fn error_response(status: StatusCode, message: &str) -> Response<ProxyBody> {
273 Response::builder()
274 .status(status)
275 .header("content-type", "text/plain; charset=utf-8")
276 .body(string_body(message.to_string()))
277 .unwrap_or_else(|_| {
278 Response::builder()
279 .status(StatusCode::INTERNAL_SERVER_ERROR)
280 .body(empty_body())
281 .unwrap()
282 })
283 }
284
285 pub fn bad_request(message: &str) -> Response<ProxyBody> {
287 Self::error_response(StatusCode::BAD_REQUEST, message)
288 }
289
290 pub fn bad_gateway(message: &str) -> Response<ProxyBody> {
292 Self::error_response(StatusCode::BAD_GATEWAY, message)
293 }
294
295 pub fn service_unavailable(message: &str) -> Response<ProxyBody> {
297 Self::error_response(StatusCode::SERVICE_UNAVAILABLE, message)
298 }
299
300 pub fn gateway_timeout() -> Response<ProxyBody> {
302 Self::error_response(StatusCode::GATEWAY_TIMEOUT, "Gateway Timeout")
303 }
304
305 pub fn too_many_requests() -> Response<ProxyBody> {
307 Self::error_response(StatusCode::TOO_MANY_REQUESTS, "Too Many Requests")
308 }
309
310 pub fn not_found() -> Response<ProxyBody> {
312 Self::error_response(StatusCode::NOT_FOUND, "Not Found")
313 }
314}
315
316#[cfg(test)]
317mod tests {
318 use super::*;
319 use crate::vendored::hyper_ext::empty_body;
320
321 #[test]
322 fn test_strip_path_prefix() {
323 let mut request = Request::builder()
324 .uri("/api/v1/users")
325 .body(empty_body())
326 .unwrap();
327
328 MessageHandler::strip_path_prefix(&mut request, "/api/v1");
329
330 assert_eq!(request.uri().path(), "/users");
331 }
332
333 #[test]
334 fn test_strip_path_prefix_root() {
335 let mut request = Request::builder().uri("/api").body(empty_body()).unwrap();
336
337 MessageHandler::strip_path_prefix(&mut request, "/api");
338
339 assert_eq!(request.uri().path(), "/");
340 }
341
342 #[test]
343 fn test_h2_connect_protocol_detected() {
344 let mut request = Request::builder()
347 .method(Method::CONNECT)
348 .version(Version::HTTP_2)
349 .uri("/chat")
350 .body(empty_body())
351 .unwrap();
352 request
353 .extensions_mut()
354 .insert(hyper::ext::Protocol::from_static("websocket"));
355
356 assert_eq!(
357 MessageHandler::h2_connect_protocol(&request).as_deref(),
358 Some("websocket")
359 );
360 assert!(MessageHandler::upgrade_protocol(&request).is_none());
362 }
363
364 #[test]
365 fn test_h2_connect_without_protocol_is_a_plain_connect() {
366 let request = Request::builder()
367 .method(Method::CONNECT)
368 .version(Version::HTTP_2)
369 .uri("/chat")
370 .body(empty_body())
371 .unwrap();
372
373 assert!(MessageHandler::h2_connect_protocol(&request).is_none());
374 }
375
376 #[test]
377 fn test_h1_connect_is_not_extended_connect() {
378 let mut request = Request::builder()
380 .method(Method::CONNECT)
381 .version(Version::HTTP_11)
382 .uri("/chat")
383 .body(empty_body())
384 .unwrap();
385 request
386 .extensions_mut()
387 .insert(hyper::ext::Protocol::from_static("websocket"));
388
389 assert!(MessageHandler::h2_connect_protocol(&request).is_none());
390 }
391
392 #[test]
393 fn test_upgrade_protocol_detected() {
394 let request = Request::builder()
395 .uri("/chat")
396 .header("host", "a")
397 .header("upgrade", "websocket")
398 .header("connection", "Upgrade")
399 .body(empty_body())
400 .unwrap();
401
402 assert_eq!(
403 MessageHandler::upgrade_protocol(&request).as_deref(),
404 Some("websocket")
405 );
406 }
407
408 #[test]
409 fn test_upgrade_needs_connection_token() {
410 let request = Request::builder()
412 .uri("/chat")
413 .header("host", "a")
414 .header("upgrade", "websocket")
415 .body(empty_body())
416 .unwrap();
417
418 assert!(MessageHandler::upgrade_protocol(&request).is_none());
419 }
420
421 #[test]
422 fn test_upgrade_token_found_among_others() {
423 let request = Request::builder()
424 .uri("/chat")
425 .header("host", "a")
426 .header("upgrade", "WebSocket")
427 .header("connection", "keep-alive, UPGRADE")
428 .body(empty_body())
429 .unwrap();
430
431 assert_eq!(
432 MessageHandler::upgrade_protocol(&request).as_deref(),
433 Some("websocket")
434 );
435 }
436
437 #[test]
438 fn test_no_upgrade_without_upgrade_header() {
439 let request = Request::builder()
440 .uri("/chat")
441 .header("host", "a")
442 .header("connection", "upgrade")
443 .body(empty_body())
444 .unwrap();
445
446 assert!(MessageHandler::upgrade_protocol(&request).is_none());
447 }
448
449 #[test]
450 fn test_http2_never_upgrades() {
451 let request = Request::builder()
453 .uri("/chat")
454 .version(Version::HTTP_2)
455 .header("upgrade", "websocket")
456 .header("connection", "Upgrade")
457 .body(empty_body())
458 .unwrap();
459
460 assert!(MessageHandler::upgrade_protocol(&request).is_none());
461 }
462
463 #[test]
464 fn test_restore_upgrade_headers_normalises_connection() {
465 let mut request = Request::builder()
467 .uri("/chat")
468 .header("host", "a")
469 .header("upgrade", "websocket")
470 .header("connection", "Upgrade, X-Sneaky")
471 .header("x-sneaky", "leaked")
472 .header("x-legit", "keep-me")
473 .body(empty_body())
474 .unwrap();
475
476 let protocol = MessageHandler::upgrade_protocol(&request).unwrap();
477 MessageHandler::strip_hop_by_hop_headers(&mut request);
478 MessageHandler::restore_upgrade_headers(&mut request, &protocol);
479
480 assert_eq!(request.headers().get("upgrade").unwrap(), "websocket");
481 assert_eq!(request.headers().get("connection").unwrap(), "upgrade");
482 assert!(!request.headers().contains_key("x-sneaky"));
483 assert_eq!(request.headers().get("x-legit").unwrap(), "keep-me");
484 }
485
486 #[test]
487 fn test_ambiguous_framing_detected() {
488 let request = Request::builder()
492 .uri("/test")
493 .header("host", "a")
494 .header("content-length", "6")
495 .header("transfer-encoding", "chunked")
496 .body(empty_body())
497 .unwrap();
498
499 assert!(MessageHandler::has_ambiguous_framing(&request));
500 }
501
502 #[test]
503 fn test_content_length_alone_is_not_ambiguous() {
504 let request = Request::builder()
505 .uri("/test")
506 .header("host", "a")
507 .header("content-length", "6")
508 .body(empty_body())
509 .unwrap();
510
511 assert!(!MessageHandler::has_ambiguous_framing(&request));
512 }
513
514 #[test]
515 fn test_transfer_encoding_alone_is_not_ambiguous() {
516 let request = Request::builder()
517 .uri("/test")
518 .header("host", "a")
519 .header("transfer-encoding", "chunked")
520 .body(empty_body())
521 .unwrap();
522
523 assert!(!MessageHandler::has_ambiguous_framing(&request));
524 }
525
526 #[test]
527 fn test_strips_each_hop_by_hop_header() {
528 for name in HOP_BY_HOP_HEADERS {
530 let mut request = Request::builder()
531 .uri("/test")
532 .header("host", "example.com")
533 .header(*name, "some-value")
534 .body(empty_body())
535 .unwrap();
536
537 MessageHandler::strip_hop_by_hop_headers(&mut request);
538
539 assert!(
540 !request.headers().contains_key(*name),
541 "hop-by-hop header {} was forwarded",
542 name
543 );
544 assert_eq!(request.headers().get("host").unwrap(), "example.com");
546 }
547 }
548
549 #[test]
550 fn test_strips_headers_named_in_connection() {
551 let mut request = Request::builder()
553 .uri("/test")
554 .header("host", "example.com")
555 .header("connection", "keep-alive, X-Hop")
556 .header("x-hop", "SHOULD-BE-STRIPPED")
557 .body(empty_body())
558 .unwrap();
559
560 MessageHandler::strip_hop_by_hop_headers(&mut request);
561
562 assert!(!request.headers().contains_key("connection"));
563 assert!(!request.headers().contains_key("x-hop"));
564 assert_eq!(request.headers().get("host").unwrap(), "example.com");
565 }
566
567 #[test]
568 fn test_connection_tokens_are_case_insensitive_and_trimmed() {
569 let mut request = Request::builder()
570 .uri("/test")
571 .header("connection", " X-UPPER ,\tx-lower,,")
572 .header("x-upper", "a")
573 .header("x-lower", "b")
574 .body(empty_body())
575 .unwrap();
576
577 MessageHandler::strip_hop_by_hop_headers(&mut request);
578
579 assert!(!request.headers().contains_key("x-upper"));
580 assert!(!request.headers().contains_key("x-lower"));
581 }
582
583 #[test]
584 fn test_connection_token_cannot_strip_host_or_content_length() {
585 let mut request = Request::builder()
588 .uri("/test")
589 .header("host", "example.com")
590 .header("content-length", "0")
591 .header("connection", "host, content-length")
592 .body(empty_body())
593 .unwrap();
594
595 MessageHandler::strip_hop_by_hop_headers(&mut request);
596
597 assert!(!request.headers().contains_key("connection"));
598 assert_eq!(request.headers().get("host").unwrap(), "example.com");
599 assert_eq!(request.headers().get("content-length").unwrap(), "0");
600 }
601
602 #[test]
603 fn test_connection_token_naming_forwarded_header_does_not_suppress_ours() {
604 let mut request = Request::builder()
607 .uri("/test")
608 .header("host", "example.com")
609 .header("connection", "x-forwarded-for")
610 .header("x-forwarded-for", "1.2.3.4")
611 .body(empty_body())
612 .unwrap();
613
614 MessageHandler::strip_hop_by_hop_headers(&mut request);
615 let client_addr: SocketAddr = "192.168.1.100:12345".parse().unwrap();
616 MessageHandler::add_forwarding_headers(&mut request, client_addr, "http");
617
618 assert_eq!(
619 request.headers().get("x-forwarded-for").unwrap(),
620 "192.168.1.100"
621 );
622 }
623
624 #[test]
625 fn test_strip_is_a_no_op_without_hop_by_hop_headers() {
626 let mut request = Request::builder()
627 .uri("/test")
628 .header("host", "example.com")
629 .header("accept", "*/*")
630 .body(empty_body())
631 .unwrap();
632
633 MessageHandler::strip_hop_by_hop_headers(&mut request);
634
635 assert_eq!(request.headers().len(), 2);
636 }
637
638 #[test]
639 fn test_forwarding_headers() {
640 let mut request = Request::builder()
641 .uri("/test")
642 .header("host", "example.com")
643 .body(empty_body())
644 .unwrap();
645
646 let client_addr: SocketAddr = "192.168.1.100:12345".parse().unwrap();
647 MessageHandler::add_forwarding_headers(&mut request, client_addr, "https");
648
649 assert_eq!(
650 request.headers().get("x-forwarded-for").unwrap(),
651 "192.168.1.100"
652 );
653 assert_eq!(request.headers().get("x-forwarded-proto").unwrap(), "https");
654 assert_eq!(
655 request.headers().get("x-forwarded-host").unwrap(),
656 "example.com"
657 );
658 }
659}