Skip to content

Commit a186b4f

Browse files
committed
Add initial rewriting framework
1 parent 4030374 commit a186b4f

File tree

25 files changed

+871
-77
lines changed

25 files changed

+871
-77
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: 64 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,9 @@
1-
use std::{
2-
fmt::Debug,
3-
net::{AddrParseError, SocketAddr},
4-
};
1+
use std::{fmt::Debug, net::SocketAddr};
52

63
use bytes::{Bytes, BytesMut};
7-
use url::{ParseError, Url};
4+
use url::Url;
85

9-
use crate::Headers;
6+
use super::Headers;
107

118
/// Represents an HTTP request. Includes the method, URL, headers, and body.
129
///
@@ -17,7 +14,7 @@ use crate::Headers;
1714
///
1815
/// let request = Request::builder()
1916
/// .method("POST")
20-
/// .url("http://example.com/test.php").expect("invalid url")
17+
/// .url("http://example.com/test.php")
2118
/// .header("Accept", "text/html")
2219
/// .header("Accept", "application/json")
2320
/// .header("Host", "example.com")
@@ -94,7 +91,7 @@ impl Request {
9491
///
9592
/// let request = Request::builder()
9693
/// .method("POST")
97-
/// .url("http://example.com/test.php").expect("invalid url")
94+
/// .url("http://example.com/test.php")
9895
/// .header("Content-Type", "text/html")
9996
/// .header("Content-Length", 13.to_string())
10097
/// .body("Hello, World!")
@@ -120,7 +117,7 @@ impl Request {
120117
///
121118
/// let request = Request::builder()
122119
/// .method("GET")
123-
/// .url("http://example.com/test.php").expect("invalid url")
120+
/// .url("http://example.com/test.php")
124121
/// .header("Content-Type", "text/plain")
125122
/// .build()
126123
/// .expect("should build request");
@@ -282,13 +279,21 @@ impl Request {
282279
#[derive(Debug, PartialEq)]
283280
pub enum RequestBuilderException {
284281
/// Url is required
285-
MissingUrl,
282+
UrlMissing,
283+
/// Url could not be parsed
284+
UrlParseFailed(String),
285+
/// SocketAddr could not be parsed
286+
SocketParseFailed(String),
286287
}
287288

288289
impl std::fmt::Display for RequestBuilderException {
289290
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
290291
match self {
291-
RequestBuilderException::MissingUrl => write!(f, "Expected url to be set"),
292+
RequestBuilderException::UrlMissing => write!(f, "Expected url to be set"),
293+
RequestBuilderException::UrlParseFailed(u) => write!(f, "Failed to parse url: \"{}\"", u),
294+
RequestBuilderException::SocketParseFailed(s) => {
295+
write!(f, "Failed to parse socket info: \"{}\"", s)
296+
}
292297
}
293298
}
294299
}
@@ -302,7 +307,7 @@ impl std::fmt::Display for RequestBuilderException {
302307
///
303308
/// let request = Request::builder()
304309
/// .method("POST")
305-
/// .url("http://example.com/test.php").expect("invalid url")
310+
/// .url("http://example.com/test.php")
306311
/// .header("Content-Type", "text/html")
307312
/// .header("Content-Length", 13.to_string())
308313
/// .body("Hello, World!")
@@ -318,11 +323,11 @@ impl std::fmt::Display for RequestBuilderException {
318323
#[derive(Clone)]
319324
pub struct RequestBuilder {
320325
method: Option<String>,
321-
url: Option<Url>,
326+
url: Option<String>,
322327
headers: Headers,
323328
body: BytesMut,
324-
local_socket: Option<SocketAddr>,
325-
remote_socket: Option<SocketAddr>,
329+
local_socket: Option<String>,
330+
remote_socket: Option<String>,
326331
}
327332

328333
impl RequestBuilder {
@@ -377,11 +382,11 @@ impl RequestBuilder {
377382
pub fn extend(request: &Request) -> Self {
378383
Self {
379384
method: Some(request.method().into()),
380-
url: Some(request.url().clone()),
385+
url: Some(request.url().to_string()),
381386
headers: request.headers().clone(),
382387
body: BytesMut::from(request.body()),
383-
local_socket: request.local_socket,
384-
remote_socket: request.remote_socket,
388+
local_socket: request.local_socket.map(|s| s.to_string()),
389+
remote_socket: request.remote_socket.map(|s| s.to_string()),
385390
}
386391
}
387392

@@ -394,7 +399,7 @@ impl RequestBuilder {
394399
///
395400
/// let request = RequestBuilder::new()
396401
/// .method("POST")
397-
/// .url("http://example.com/test.php").expect("invalid url")
402+
/// .url("http://example.com/test.php")
398403
/// .build()
399404
/// .expect("should build request");
400405
///
@@ -413,23 +418,18 @@ impl RequestBuilder {
413418
/// use lang_handler::RequestBuilder;
414419
///
415420
/// let request = RequestBuilder::new()
416-
/// .url("http://example.com/test.php").expect("invalid url")
421+
/// .url("http://example.com/test.php")
417422
/// .build()
418423
/// .expect("should build request");
419424
///
420425
/// assert_eq!(request.url().as_str(), "http://example.com/test.php");
421426
/// ```
422-
pub fn url<T>(mut self, url: T) -> Result<Self, ParseError>
427+
pub fn url<T>(mut self, url: T) -> Self
423428
where
424429
T: Into<String>,
425430
{
426-
match url.into().parse() {
427-
Ok(url) => {
428-
self.url = Some(url);
429-
Ok(self)
430-
}
431-
Err(e) => Err(e),
432-
}
431+
self.url = Some(url.into());
432+
self
433433
}
434434

435435
/// Sets a header of the request.
@@ -440,7 +440,7 @@ impl RequestBuilder {
440440
/// use lang_handler::RequestBuilder;
441441
///
442442
/// let request = RequestBuilder::new()
443-
/// .url("http://example.com/test.php").expect("invalid url")
443+
/// .url("http://example.com/test.php")
444444
/// .header("Accept", "text/html")
445445
/// .build()
446446
/// .expect("should build request");
@@ -464,7 +464,7 @@ impl RequestBuilder {
464464
/// use lang_handler::RequestBuilder;
465465
///
466466
/// let request = RequestBuilder::new()
467-
/// .url("http://example.com/test.php").expect("invalid url")
467+
/// .url("http://example.com/test.php")
468468
/// .body("Hello, World!")
469469
/// .build()
470470
/// .expect("should build request");
@@ -485,8 +485,8 @@ impl RequestBuilder {
485485
/// use lang_handler::RequestBuilder;
486486
///
487487
/// 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")
488+
/// .url("http://example.com/test.php")
489+
/// .local_socket("127.0.0.1:8080")
490490
/// .build()
491491
/// .expect("should build request");
492492
///
@@ -495,17 +495,12 @@ impl RequestBuilder {
495495
/// .expect("should parse");
496496
/// assert_eq!(request.local_socket(), Some(expected));
497497
/// ```
498-
pub fn local_socket<T>(mut self, local_socket: T) -> Result<Self, AddrParseError>
498+
pub fn local_socket<T>(mut self, local_socket: T) -> Self
499499
where
500500
T: Into<String>,
501501
{
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-
}
502+
self.local_socket = Some(local_socket.into());
503+
self
509504
}
510505

511506
/// Sets the remote socket of the request.
@@ -517,8 +512,8 @@ impl RequestBuilder {
517512
/// use lang_handler::RequestBuilder;
518513
///
519514
/// 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")
515+
/// .url("http://example.com/test.php")
516+
/// .remote_socket("127.0.0.1:8080")
522517
/// .build()
523518
/// .expect("should build request");
524519
///
@@ -527,17 +522,12 @@ impl RequestBuilder {
527522
/// .expect("should parse");
528523
/// assert_eq!(request.remote_socket(), Some(expected));
529524
/// ```
530-
pub fn remote_socket<T>(mut self, remote_socket: T) -> Result<Self, AddrParseError>
525+
pub fn remote_socket<T>(mut self, remote_socket: T) -> Self
531526
where
532527
T: Into<String>,
533528
{
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-
}
529+
self.remote_socket = Some(remote_socket.into());
530+
self
541531
}
542532

543533
/// Builds the request.
@@ -548,7 +538,7 @@ impl RequestBuilder {
548538
/// use lang_handler::RequestBuilder;
549539
///
550540
/// let request = RequestBuilder::new()
551-
/// .url("http://example.com/test.php").expect("invalid url")
541+
/// .url("http://example.com/test.php")
552542
/// .build()
553543
/// .expect("should build request");
554544
///
@@ -559,11 +549,11 @@ impl RequestBuilder {
559549
pub fn build(self) -> Result<Request, RequestBuilderException> {
560550
Ok(Request {
561551
method: self.method.unwrap_or_else(|| "GET".to_string()),
562-
url: self.url.ok_or(RequestBuilderException::MissingUrl)?,
552+
url: parse_url(self.url)?,
563553
headers: self.headers,
564554
body: self.body.freeze(),
565-
local_socket: self.local_socket,
566-
remote_socket: self.remote_socket,
555+
local_socket: parse_socket(self.local_socket)?,
556+
remote_socket: parse_socket(self.remote_socket)?,
567557
})
568558
}
569559
}
@@ -573,3 +563,23 @@ impl Default for RequestBuilder {
573563
Self::new()
574564
}
575565
}
566+
567+
fn parse_url(url: Option<String>) -> Result<Url, RequestBuilderException> {
568+
url
569+
.ok_or(RequestBuilderException::UrlMissing)
570+
.and_then(|u| {
571+
u.parse()
572+
.map_err(|_| RequestBuilderException::UrlParseFailed(u))
573+
})
574+
}
575+
576+
fn parse_socket(socket: Option<String>) -> Result<Option<SocketAddr>, RequestBuilderException> {
577+
socket.map_or_else(
578+
|| Ok(None),
579+
|s| {
580+
Ok(Some(s.parse::<SocketAddr>().map_err(|_| {
581+
RequestBuilderException::SocketParseFailed(s)
582+
})?))
583+
},
584+
)
585+
}

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)