Skip to content

Commit 2882c91

Browse files
committed
feat(wasm): move webassembly directly to main library
1 parent d7bd51f commit 2882c91

File tree

9 files changed

+254
-16
lines changed

9 files changed

+254
-16
lines changed

Cargo.toml

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ name = "redirectionio"
33
description = "Redirection IO Library to handle matching rule, redirect and filtering headers and body."
44
repository = "https://github.com/redirectionio/libredirectionio"
55
license = "MIT"
6-
version = "2.11.0"
6+
version = "2.11.1"
77
authors = ["Joel Wurtz <[email protected]>"]
88
edition = "2018"
99
build = "src/build.rs"
@@ -27,6 +27,16 @@ compress = ["dep:brotli", "dep:flate2"]
2727
router = []
2828
dot = ["dep:dot_graph"]
2929

30+
[target.'cfg(target_arch = "wasm32")'.dependencies]
31+
chrono = { version = "0.4.19", features = ["serde", "wasmbind"] }
32+
futures-util = { version = "0.3.21", default-features = false }
33+
getrandom = { version = "0.2.8", features = ["js"] }
34+
log = "0.4.21"
35+
serde = { version = "1.0", features = ["derive"] }
36+
serde_json = "1.0.70"
37+
wasm-bindgen = "0.2.84"
38+
wasm-logger = "0.2.0"
39+
3040
[dependencies]
3141
brotli = { version = "3.5.0", optional = true }
3242
cfg-if = "1.0.0"
@@ -53,9 +63,6 @@ url = "2.5.0"
5363
pprof = { version = "0.13", features = ["flamegraph"] }
5464
criterion = { version = "0.5.1", default-features = false }
5565

56-
[target.'cfg(target_arch = "wasm32")'.dependencies]
57-
console_error_panic_hook = { version = "0.1.7" }
58-
5966
[[bench]]
6067
name = "match_rule_benchmark"
6168
harness = false

src/action/ffi.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ pub unsafe extern "C" fn redirectionio_action_json_deserialize(str: *mut c_char)
1818

1919
let action = match json_decode(action_str) {
2020
Err(error) => {
21-
error!("Unable to deserialize \"{}\" to action: {}", action_str, error,);
21+
log::error!("Unable to deserialize \"{}\" to action: {}", action_str, error,);
2222

2323
return null();
2424
}
@@ -40,7 +40,7 @@ pub unsafe extern "C" fn redirectionio_action_json_serialize(_action: *mut Actio
4040
let action = &*_action;
4141
let action_serialized = match json_encode(action) {
4242
Err(error) => {
43-
error!("Unable to serialize to action: {}", error,);
43+
log::error!("Unable to serialize to action: {}", error,);
4444

4545
return null();
4646
}

src/api/rule.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ impl Rule {
6969
let rule_result = json_decode(rule_str);
7070

7171
if rule_result.is_err() {
72-
error!("Unable to create rule from string {}: {}", rule_str, rule_result.err().unwrap());
72+
log::error!("Unable to create rule from string {}: {}", rule_str, rule_result.err().unwrap());
7373

7474
return None;
7575
}

src/ffi_helpers.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ pub unsafe fn c_char_to_str(ptr: *const c_char) -> Option<&'static str> {
99

1010
match CStr::from_ptr(ptr).to_str() {
1111
Err(error) => {
12-
error!(
12+
log::error!(
1313
"Unable to create string for '{}': {}",
1414
String::from_utf8_lossy(CStr::from_ptr(ptr).to_bytes()),
1515
error,
@@ -24,7 +24,7 @@ pub unsafe fn c_char_to_str(ptr: *const c_char) -> Option<&'static str> {
2424
pub unsafe fn string_to_c_char(str: String) -> *const c_char {
2525
let string = match CString::new(str.as_str()) {
2626
Err(error) => {
27-
error!("Cannot create c string {}: {}", str, error,);
27+
log::error!("Cannot create c string {}: {}", str, error,);
2828

2929
return null();
3030
}

src/filter/html_filter_body.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use crate::action::UnitTrace;
22
use crate::filter::error::Result;
33
use crate::filter::html_body_action::HtmlBodyVisitor;
44
use crate::html;
5+
use lazy_static::lazy_static;
56
use std::collections::HashSet;
67

78
#[derive(Debug)]

src/http/ffi.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ pub unsafe extern "C" fn redirectionio_request_json_deserialize(str: *mut c_char
6565

6666
let request = match json_decode(request_str) {
6767
Err(err) => {
68-
error!("cannot deserialize request {} for string {}", err, request_str);
68+
log::error!("cannot deserialize request {} for string {}", err, request_str);
6969

7070
return null();
7171
}
@@ -199,7 +199,7 @@ pub unsafe extern "C" fn redirectionio_request_from_str(_url: *const c_char) ->
199199

200200
match url.parse::<Request>() {
201201
Err(err) => {
202-
error!("cannot create request for url {}: {}", url, err);
202+
log::error!("cannot create request for url {}: {}", url, err);
203203

204204
null()
205205
}

src/lib.rs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,6 @@ This crate provides a library for matching, handling and logging http requests w
33
rule format.
44
*/
55

6-
#[macro_use]
7-
extern crate lazy_static;
8-
#[macro_use]
9-
extern crate log;
10-
116
pub mod action;
127
pub mod api;
138
pub mod filter;
@@ -27,5 +22,7 @@ mod dot;
2722
mod ffi_helpers;
2823
mod regex;
2924
mod router_config;
25+
#[cfg(target_arch = "wasm32")]
26+
mod wasm_api;
3027

3128
pub use router_config::RouterConfig;

src/regex.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ pub struct LazyRegex {
1010
}
1111

1212
impl LazyRegex {
13+
#[cfg(feature = "router")]
1314
pub fn new_node(regex: String, ignore_case: bool) -> LazyRegex {
1415
LazyRegex {
1516
regex: if regex.is_empty() {
@@ -32,6 +33,7 @@ impl LazyRegex {
3233
}
3334
}
3435

36+
#[cfg(feature = "router")]
3537
pub fn is_match(&self, value: &str) -> bool {
3638
match &self.compiled {
3739
Some(regex) => regex.is_match(value),

src/wasm_api.rs

Lines changed: 231 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,231 @@
1+
use crate::action::Action as RedirectionioAction;
2+
use crate::api::Log;
3+
use crate::filter::FilterBodyAction;
4+
use crate::http::{Header, PathAndQueryWithSkipped, Request as RedirectionioRequest, TrustedProxies};
5+
use crate::RouterConfig;
6+
use chrono::Utc;
7+
use serde_json::{from_str as json_decode, to_string as json_encode};
8+
use std::collections::hash_map::DefaultHasher;
9+
use std::hash::{Hash, Hasher};
10+
use wasm_bindgen::prelude::*;
11+
12+
#[wasm_bindgen()]
13+
pub struct Request {
14+
#[wasm_bindgen(skip)]
15+
pub request: RedirectionioRequest,
16+
}
17+
18+
#[wasm_bindgen()]
19+
pub struct HeaderMap {
20+
#[wasm_bindgen(skip)]
21+
pub headers: Vec<Header>,
22+
}
23+
24+
#[wasm_bindgen()]
25+
pub struct Action {
26+
#[wasm_bindgen(skip)]
27+
pub action: Option<RedirectionioAction>,
28+
}
29+
30+
#[wasm_bindgen()]
31+
pub struct BodyFilter {
32+
#[wasm_bindgen(skip)]
33+
pub filter: Option<FilterBodyAction>,
34+
}
35+
36+
#[wasm_bindgen()]
37+
impl Request {
38+
#[wasm_bindgen(constructor)]
39+
pub fn new(uri: String, host: String, scheme: String, method: String) -> Request {
40+
let config = RouterConfig::default();
41+
42+
Request {
43+
request: RedirectionioRequest {
44+
headers: Vec::new(),
45+
host: Some(host),
46+
method: Some(method),
47+
scheme: Some(scheme),
48+
path_and_query_skipped: PathAndQueryWithSkipped::from_config(&config, uri.as_str()),
49+
path_and_query: Some(uri),
50+
remote_addr: None,
51+
created_at: Some(Utc::now()),
52+
sampling_override: None,
53+
},
54+
}
55+
}
56+
57+
pub fn set_remote_ip(&mut self, remote_addr_str: String) {
58+
self.request.set_remote_ip(remote_addr_str, &TrustedProxies::default());
59+
}
60+
61+
pub fn add_header(&mut self, name: String, value: String) {
62+
self.request.add_header(name, value, false)
63+
}
64+
65+
pub fn serialize(&self) -> String {
66+
match json_encode(&self.request) {
67+
Err(_) => "".to_string(),
68+
Ok(request_serialized) => request_serialized,
69+
}
70+
}
71+
72+
pub fn get_hash(&self) -> u64 {
73+
let mut hasher = DefaultHasher::new();
74+
self.request.hash(&mut hasher);
75+
76+
hasher.finish()
77+
}
78+
}
79+
80+
#[wasm_bindgen()]
81+
impl HeaderMap {
82+
#[allow(clippy::new_without_default)]
83+
#[wasm_bindgen(constructor)]
84+
pub fn new() -> HeaderMap {
85+
HeaderMap { headers: Vec::new() }
86+
}
87+
88+
pub fn add_header(&mut self, name: String, value: String) {
89+
self.headers.push(Header { name, value })
90+
}
91+
92+
pub fn remove_header(&mut self, name: String) {
93+
self.headers.retain(|header| header.name != name)
94+
}
95+
96+
pub fn len(&self) -> usize {
97+
self.headers.len()
98+
}
99+
100+
pub fn is_empty(&self) -> bool {
101+
self.headers.is_empty()
102+
}
103+
104+
pub fn get_header_name(&self, index: usize) -> String {
105+
match self.headers.get(index) {
106+
None => "".to_string(),
107+
Some(header) => header.name.clone(),
108+
}
109+
}
110+
111+
pub fn get_header_value(&self, index: usize) -> String {
112+
match self.headers.get(index) {
113+
None => "".to_string(),
114+
Some(header) => header.value.clone(),
115+
}
116+
}
117+
}
118+
119+
#[wasm_bindgen()]
120+
impl Action {
121+
#[wasm_bindgen(constructor)]
122+
pub fn new(action_serialized: String) -> Action {
123+
let action = match json_decode(action_serialized.as_str()) {
124+
Err(error) => {
125+
log::error!("Unable to deserialize \"{}\" to action: {}", action_serialized, error,);
126+
127+
None
128+
}
129+
Ok(action) => Some(action),
130+
};
131+
132+
Action { action }
133+
}
134+
135+
pub fn empty() -> Action {
136+
Action { action: None }
137+
}
138+
139+
pub fn get_status_code(&mut self, response_status_code: u16) -> u16 {
140+
if let Some(action) = self.action.as_mut() {
141+
return action.get_status_code(response_status_code, None);
142+
}
143+
144+
0
145+
}
146+
147+
pub fn filter_headers(&mut self, headers: HeaderMap, response_status_code: u16, add_rule_ids_header: bool) -> HeaderMap {
148+
if self.action.is_none() {
149+
return headers;
150+
}
151+
152+
let action = self.action.as_mut().unwrap();
153+
let new_headers = action.filter_headers(headers.headers, response_status_code, add_rule_ids_header, None);
154+
155+
HeaderMap { headers: new_headers }
156+
}
157+
158+
pub fn create_body_filter(&mut self, response_status_code: u16, headers: &HeaderMap) -> BodyFilter {
159+
if self.action.is_none() {
160+
return BodyFilter { filter: None };
161+
}
162+
163+
let action = self.action.as_mut().unwrap();
164+
let filter = action.create_filter_body(response_status_code, &headers.headers);
165+
166+
BodyFilter { filter }
167+
}
168+
169+
pub fn should_log_request(&mut self, response_status_code: u16) -> bool {
170+
if self.action.is_none() {
171+
return true;
172+
}
173+
174+
let action = self.action.as_mut().unwrap();
175+
176+
action.should_log_request(true, response_status_code, None)
177+
}
178+
}
179+
180+
#[wasm_bindgen()]
181+
impl BodyFilter {
182+
pub fn is_null(&self) -> bool {
183+
self.filter.is_none()
184+
}
185+
186+
pub fn filter(&mut self, data: Vec<u8>) -> Vec<u8> {
187+
match self.filter.as_mut() {
188+
None => data,
189+
Some(filter) => filter.filter(data, None),
190+
}
191+
}
192+
193+
pub fn end(&mut self) -> Vec<u8> {
194+
match self.filter.as_mut() {
195+
None => Vec::new(),
196+
Some(filter) => filter.end(None),
197+
}
198+
}
199+
}
200+
201+
#[wasm_bindgen()]
202+
pub fn init_log() {
203+
wasm_logger::init(wasm_logger::Config::default());
204+
}
205+
206+
#[wasm_bindgen()]
207+
pub fn create_log_in_json(
208+
request: Request,
209+
status_code: u16,
210+
response_headers: HeaderMap,
211+
action: &Action,
212+
proxy: String,
213+
time: u64,
214+
client_ip: String,
215+
) -> String {
216+
let log = Log::from_proxy(
217+
&request.request,
218+
status_code,
219+
&response_headers.headers,
220+
action.action.as_ref(),
221+
proxy.as_str(),
222+
time.into(),
223+
client_ip.as_str(),
224+
None,
225+
);
226+
227+
match json_encode(&log) {
228+
Err(_) => "".to_string(),
229+
Ok(s) => s,
230+
}
231+
}

0 commit comments

Comments
 (0)