Skip to content

Commit 6eb429b

Browse files
committed
Add initial rewriting framework
1 parent 42ab801 commit 6eb429b

File tree

25 files changed

+866
-73
lines changed

25 files changed

+866
-73
lines changed

__test__/rewriter.spec.mjs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import test from 'ava'
2+
3+
import { Request, Rewriter } from '../index.js'
4+
5+
test('rewrites URLs', (t) => {
6+
const req = new Request({
7+
method: 'GET',
8+
url: 'http://example.com/index.php',
9+
headers: {
10+
TEST: ['foo']
11+
}
12+
})
13+
14+
const rewriter = new Rewriter([
15+
{
16+
operation: 'and',
17+
conditions: [
18+
{
19+
type: 'path',
20+
args: ['^/index.php$']
21+
},
22+
{
23+
type: 'header',
24+
args: ['TEST', '^foo$']
25+
}
26+
],
27+
rewriters: [
28+
{
29+
type: 'path',
30+
args: ['^(/index.php)$', '/foo$1']
31+
}
32+
]
33+
}
34+
])
35+
36+
t.is(rewriter.rewrite(req).url, 'http://example.com/foo/index.php')
37+
})

crates/lang_handler/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,5 @@ cbindgen = "0.28.0"
1717

1818
[dependencies]
1919
bytes = "1.10.1"
20+
regex = "1.11.1"
2021
url = "2.5.4"

crates/lang_handler/src/handler.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::{Request, Response};
1+
use super::{Request, Response};
22

33
/// Enables a type to support handling HTTP requests.
44
///
@@ -50,7 +50,7 @@ pub trait Handler {
5050
/// #
5151
/// let request = Request::builder()
5252
/// .method("GET")
53-
/// .url("http://example.com").expect("invalid url")
53+
/// .url("http://example.com")
5454
/// .build()
5555
/// .expect("should build request");
5656
///

crates/lang_handler/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ mod handler;
44
mod headers;
55
mod request;
66
mod response;
7+
pub mod rewrite;
78

89
#[cfg(feature = "c")]
910
pub use ffi::*;

crates/lang_handler/src/request.rs

Lines changed: 60 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
use std::{
22
fmt::Debug,
3-
net::{AddrParseError, SocketAddr},
3+
net::SocketAddr,
44
};
55

66
use bytes::{Bytes, BytesMut};
7-
use url::{ParseError, Url};
7+
use url::Url;
88

9-
use crate::Headers;
9+
use super::Headers;
1010

1111
/// Represents an HTTP request. Includes the method, URL, headers, and body.
1212
///
@@ -17,7 +17,7 @@ use crate::Headers;
1717
///
1818
/// let request = Request::builder()
1919
/// .method("POST")
20-
/// .url("http://example.com/test.php").expect("invalid url")
20+
/// .url("http://example.com/test.php")
2121
/// .header("Accept", "text/html")
2222
/// .header("Accept", "application/json")
2323
/// .header("Host", "example.com")
@@ -94,7 +94,7 @@ impl Request {
9494
///
9595
/// let request = Request::builder()
9696
/// .method("POST")
97-
/// .url("http://example.com/test.php").expect("invalid url")
97+
/// .url("http://example.com/test.php")
9898
/// .header("Content-Type", "text/html")
9999
/// .header("Content-Length", 13.to_string())
100100
/// .body("Hello, World!")
@@ -120,7 +120,7 @@ impl Request {
120120
///
121121
/// let request = Request::builder()
122122
/// .method("GET")
123-
/// .url("http://example.com/test.php").expect("invalid url")
123+
/// .url("http://example.com/test.php")
124124
/// .header("Content-Type", "text/plain")
125125
/// .build()
126126
/// .expect("should build request");
@@ -282,13 +282,19 @@ impl Request {
282282
#[derive(Debug, PartialEq)]
283283
pub enum RequestBuilderException {
284284
/// Url is required
285-
MissingUrl,
285+
UrlMissing,
286+
/// Url could not be parsed
287+
UrlParseFailed(String),
288+
/// SocketAddr could not be parsed
289+
SocketParseFailed(String)
286290
}
287291

288292
impl std::fmt::Display for RequestBuilderException {
289293
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
290294
match self {
291-
RequestBuilderException::MissingUrl => write!(f, "Expected url to be set"),
295+
RequestBuilderException::UrlMissing => write!(f, "Expected url to be set"),
296+
RequestBuilderException::UrlParseFailed(u) => write!(f, "Failed to parse url: \"{}\"", u),
297+
RequestBuilderException::SocketParseFailed(s) => write!(f, "Failed to parse socket info: \"{}\"", s)
292298
}
293299
}
294300
}
@@ -302,7 +308,7 @@ impl std::fmt::Display for RequestBuilderException {
302308
///
303309
/// let request = Request::builder()
304310
/// .method("POST")
305-
/// .url("http://example.com/test.php").expect("invalid url")
311+
/// .url("http://example.com/test.php")
306312
/// .header("Content-Type", "text/html")
307313
/// .header("Content-Length", 13.to_string())
308314
/// .body("Hello, World!")
@@ -318,11 +324,11 @@ impl std::fmt::Display for RequestBuilderException {
318324
#[derive(Clone)]
319325
pub struct RequestBuilder {
320326
method: Option<String>,
321-
url: Option<Url>,
327+
url: Option<String>,
322328
headers: Headers,
323329
body: BytesMut,
324-
local_socket: Option<SocketAddr>,
325-
remote_socket: Option<SocketAddr>,
330+
local_socket: Option<String>,
331+
remote_socket: Option<String>,
326332
}
327333

328334
impl RequestBuilder {
@@ -377,11 +383,11 @@ impl RequestBuilder {
377383
pub fn extend(request: &Request) -> Self {
378384
Self {
379385
method: Some(request.method().into()),
380-
url: Some(request.url().clone()),
386+
url: Some(request.url().to_string()),
381387
headers: request.headers().clone(),
382388
body: BytesMut::from(request.body()),
383-
local_socket: request.local_socket,
384-
remote_socket: request.remote_socket,
389+
local_socket: request.local_socket.map(|s| s.to_string()),
390+
remote_socket: request.remote_socket.map(|s| s.to_string()),
385391
}
386392
}
387393

@@ -394,7 +400,7 @@ impl RequestBuilder {
394400
///
395401
/// let request = RequestBuilder::new()
396402
/// .method("POST")
397-
/// .url("http://example.com/test.php").expect("invalid url")
403+
/// .url("http://example.com/test.php")
398404
/// .build()
399405
/// .expect("should build request");
400406
///
@@ -413,23 +419,18 @@ impl RequestBuilder {
413419
/// use lang_handler::RequestBuilder;
414420
///
415421
/// let request = RequestBuilder::new()
416-
/// .url("http://example.com/test.php").expect("invalid url")
422+
/// .url("http://example.com/test.php")
417423
/// .build()
418424
/// .expect("should build request");
419425
///
420426
/// assert_eq!(request.url().as_str(), "http://example.com/test.php");
421427
/// ```
422-
pub fn url<T>(mut self, url: T) -> Result<Self, ParseError>
428+
pub fn url<T>(mut self, url: T) -> Self
423429
where
424430
T: Into<String>,
425431
{
426-
match url.into().parse() {
427-
Ok(url) => {
428-
self.url = Some(url);
429-
Ok(self)
430-
}
431-
Err(e) => Err(e),
432-
}
432+
self.url = Some(url.into());
433+
self
433434
}
434435

435436
/// Sets a header of the request.
@@ -440,7 +441,7 @@ impl RequestBuilder {
440441
/// use lang_handler::RequestBuilder;
441442
///
442443
/// let request = RequestBuilder::new()
443-
/// .url("http://example.com/test.php").expect("invalid url")
444+
/// .url("http://example.com/test.php")
444445
/// .header("Accept", "text/html")
445446
/// .build()
446447
/// .expect("should build request");
@@ -464,7 +465,7 @@ impl RequestBuilder {
464465
/// use lang_handler::RequestBuilder;
465466
///
466467
/// let request = RequestBuilder::new()
467-
/// .url("http://example.com/test.php").expect("invalid url")
468+
/// .url("http://example.com/test.php")
468469
/// .body("Hello, World!")
469470
/// .build()
470471
/// .expect("should build request");
@@ -485,8 +486,8 @@ impl RequestBuilder {
485486
/// use lang_handler::RequestBuilder;
486487
///
487488
/// let request = RequestBuilder::new()
488-
/// .url("http://example.com/test.php").expect("invalid url")
489-
/// .local_socket("127.0.0.1:8080").expect("invalid local socket")
489+
/// .url("http://example.com/test.php")
490+
/// .local_socket("127.0.0.1:8080")
490491
/// .build()
491492
/// .expect("should build request");
492493
///
@@ -495,17 +496,12 @@ impl RequestBuilder {
495496
/// .expect("should parse");
496497
/// assert_eq!(request.local_socket(), Some(expected));
497498
/// ```
498-
pub fn local_socket<T>(mut self, local_socket: T) -> Result<Self, AddrParseError>
499+
pub fn local_socket<T>(mut self, local_socket: T) -> Self
499500
where
500501
T: Into<String>,
501502
{
502-
match local_socket.into().parse() {
503-
Err(e) => Err(e),
504-
Ok(local_socket) => {
505-
self.local_socket = Some(local_socket);
506-
Ok(self)
507-
}
508-
}
503+
self.local_socket = Some(local_socket.into());
504+
self
509505
}
510506

511507
/// Sets the remote socket of the request.
@@ -517,8 +513,8 @@ impl RequestBuilder {
517513
/// use lang_handler::RequestBuilder;
518514
///
519515
/// let request = RequestBuilder::new()
520-
/// .url("http://example.com/test.php").expect("invalid url")
521-
/// .remote_socket("127.0.0.1:8080").expect("invalid remote socket")
516+
/// .url("http://example.com/test.php")
517+
/// .remote_socket("127.0.0.1:8080")
522518
/// .build()
523519
/// .expect("should build request");
524520
///
@@ -527,17 +523,12 @@ impl RequestBuilder {
527523
/// .expect("should parse");
528524
/// assert_eq!(request.remote_socket(), Some(expected));
529525
/// ```
530-
pub fn remote_socket<T>(mut self, remote_socket: T) -> Result<Self, AddrParseError>
526+
pub fn remote_socket<T>(mut self, remote_socket: T) -> Self
531527
where
532528
T: Into<String>,
533529
{
534-
match remote_socket.into().parse() {
535-
Err(e) => Err(e),
536-
Ok(remote_socket) => {
537-
self.remote_socket = Some(remote_socket);
538-
Ok(self)
539-
}
540-
}
530+
self.remote_socket = Some(remote_socket.into());
531+
self
541532
}
542533

543534
/// Builds the request.
@@ -548,7 +539,7 @@ impl RequestBuilder {
548539
/// use lang_handler::RequestBuilder;
549540
///
550541
/// let request = RequestBuilder::new()
551-
/// .url("http://example.com/test.php").expect("invalid url")
542+
/// .url("http://example.com/test.php")
552543
/// .build()
553544
/// .expect("should build request");
554545
///
@@ -559,11 +550,11 @@ impl RequestBuilder {
559550
pub fn build(self) -> Result<Request, RequestBuilderException> {
560551
Ok(Request {
561552
method: self.method.unwrap_or_else(|| "GET".to_string()),
562-
url: self.url.ok_or(RequestBuilderException::MissingUrl)?,
553+
url: parse_url(self.url)?,
563554
headers: self.headers,
564555
body: self.body.freeze(),
565-
local_socket: self.local_socket,
566-
remote_socket: self.remote_socket,
556+
local_socket: parse_socket(self.local_socket)?,
557+
remote_socket: parse_socket(self.remote_socket)?
567558
})
568559
}
569560
}
@@ -573,3 +564,21 @@ impl Default for RequestBuilder {
573564
Self::new()
574565
}
575566
}
567+
568+
fn parse_url(url: Option<String>) -> Result<Url, RequestBuilderException> {
569+
url
570+
.ok_or(RequestBuilderException::UrlMissing)
571+
.and_then(|u| {
572+
u.parse()
573+
.map_err(|_| RequestBuilderException::UrlParseFailed(u))
574+
})
575+
}
576+
577+
fn parse_socket(socket: Option<String>) -> Result<Option<SocketAddr>, RequestBuilderException> {
578+
socket.map_or_else(|| Ok(None), |s| {
579+
Ok(Some(
580+
s.parse::<SocketAddr>()
581+
.map_err(|_| RequestBuilderException::SocketParseFailed(s))?
582+
))
583+
})
584+
}

crates/lang_handler/src/response.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use bytes::{Bytes, BytesMut};
22

3-
use crate::Headers;
3+
use super::Headers;
44

55
/// Represents an HTTP response. This includes the status code, headers, body, log, and exception.
66
///

0 commit comments

Comments
 (0)