fbi-proxy 1.6.2 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fbi-proxy",
3
- "version": "1.6.2",
3
+ "version": "1.7.0",
4
4
  "description": "FBI-Proxy provides easy HTTPS access to your local services with intelligent domain routing",
5
5
  "keywords": [
6
6
  "development-tools",
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
package/rs/fbi-proxy.rs CHANGED
@@ -1,20 +1,27 @@
1
1
  use clap::{Arg, Command};
2
2
  use futures_util::{SinkExt, StreamExt};
3
+ use http_body_util::{BodyExt, Full};
4
+ use hyper::body::{Bytes, Incoming};
3
5
  use hyper::header::{HeaderValue, HOST};
4
- use hyper::service::{make_service_fn, service_fn};
5
- use hyper::{Body, Client, Request, Response, Server, StatusCode, Uri};
6
+ use hyper::server::conn::http1;
7
+ use hyper::service::service_fn;
8
+ use hyper::{Request, Response, StatusCode, Uri};
6
9
  use hyper_tungstenite::{HyperWebsocket, WebSocketStream};
10
+ use hyper_util::client::legacy::{Client, connect::HttpConnector};
11
+ use hyper_util::rt::TokioIo;
7
12
  use log::{error, info};
8
13
  use regex::Regex;
9
14
  use std::convert::Infallible;
10
15
  use std::net::SocketAddr;
11
16
  use std::sync::Arc;
17
+ use tokio::net::TcpListener;
12
18
  use tokio_tungstenite::connect_async;
13
19
 
14
20
  type BoxError = Box<dyn std::error::Error + Send + Sync>;
21
+ type BoxBody = http_body_util::combinators::BoxBody<Bytes, hyper::Error>;
15
22
 
16
23
  pub struct FBIProxy {
17
- client: Client<hyper::client::HttpConnector>,
24
+ client: Client<HttpConnector, BoxBody>,
18
25
  number_regex: Regex,
19
26
  domain_filter: Option<String>,
20
27
  }
@@ -51,8 +58,12 @@ for subdomains
51
58
  */
52
59
  impl FBIProxy {
53
60
  pub fn new(domain_filter: Option<String>) -> Self {
61
+ let connector = HttpConnector::new();
62
+ let client = Client::builder(hyper_util::rt::TokioExecutor::new())
63
+ .build(connector);
64
+
54
65
  Self {
55
- client: Client::new(),
66
+ client,
56
67
  number_regex: Regex::new(r"^\d+$").unwrap(),
57
68
  domain_filter,
58
69
  }
@@ -131,7 +142,7 @@ impl FBIProxy {
131
142
  Some((format!("{}:80", host), host.to_string()))
132
143
  }
133
144
 
134
- pub async fn handle_request(&self, mut req: Request<Body>) -> Result<Response<Body>, BoxError> {
145
+ pub async fn handle_request(&self, req: Request<Incoming>) -> Result<Response<BoxBody>, BoxError> {
135
146
  // Extract host from headers and process according to rules
136
147
  let host_header = req
137
148
  .headers()
@@ -157,7 +168,7 @@ impl FBIProxy {
157
168
  );
158
169
  return Ok(Response::builder()
159
170
  .status(StatusCode::BAD_GATEWAY)
160
- .body(Body::from("Bad Gateway: Host not allowed"))?);
171
+ .body(Full::new(Bytes::from("Bad Gateway: Host not allowed")).map_err(|e| match e {}).boxed())?);
161
172
  }
162
173
  };
163
174
 
@@ -180,14 +191,20 @@ impl FBIProxy {
180
191
  );
181
192
  let target_uri: Uri = target_url.parse()?;
182
193
 
194
+ // Convert incoming body to a format the client can use
195
+ let (mut parts, incoming_body) = req.into_parts();
196
+ let body = incoming_body.map_err(|e| e).boxed();
197
+
183
198
  // Update request URI and headers
184
- *req.uri_mut() = target_uri;
185
- req.headers_mut()
186
- .insert(HOST, HeaderValue::from_str(&new_host)?);
199
+ parts.uri = target_uri;
200
+ parts.headers.insert(HOST, HeaderValue::from_str(&new_host)?);
187
201
  // Preserve content-encoding header to maintain compression
188
202
 
203
+ // Rebuild the request with the converted body
204
+ let new_req = Request::from_parts(parts, body);
205
+
189
206
  // Forward the request
190
- match self.client.request(req).await {
207
+ match self.client.request(new_req).await {
191
208
  Ok(response) => {
192
209
  // Preserve content-encoding header in response to maintain compression
193
210
  let status = response.status();
@@ -199,7 +216,10 @@ impl FBIProxy {
199
216
  original_uri,
200
217
  status.as_u16()
201
218
  );
202
- Ok(response)
219
+ // Convert the response body back to BoxBody
220
+ let (parts, body) = response.into_parts();
221
+ let boxed_body = body.map_err(|e| e).boxed();
222
+ Ok(Response::from_parts(parts, boxed_body))
203
223
  }
204
224
  Err(e) => {
205
225
  error!(
@@ -212,17 +232,17 @@ impl FBIProxy {
212
232
  );
213
233
  Ok(Response::builder()
214
234
  .status(StatusCode::BAD_GATEWAY)
215
- .body(Body::from("FBIPROXY ERROR"))?)
235
+ .body(Full::new(Bytes::from("FBIPROXY ERROR")).map_err(|e| match e {}).boxed())?)
216
236
  }
217
237
  }
218
238
  }
219
239
 
220
240
  async fn handle_websocket_upgrade(
221
241
  &self,
222
- req: Request<Body>,
242
+ req: Request<Incoming>,
223
243
  target_host: &str,
224
244
  _new_host: &str, // Currently not used for WebSocket connections, but kept for consistency
225
- ) -> Result<Response<Body>, BoxError> {
245
+ ) -> Result<Response<BoxBody>, BoxError> {
226
246
  let uri = req.uri().clone();
227
247
  let ws_url = format!(
228
248
  "ws://{}{}",
@@ -240,7 +260,7 @@ impl FBIProxy {
240
260
  error!("WS :ws:{} => :ws:{}{} 502 ({})", target_host, target_host, uri, e);
241
261
  return Ok(Response::builder()
242
262
  .status(StatusCode::BAD_GATEWAY)
243
- .body(Body::from("WebSocket connection failed"))?);
263
+ .body(Full::new(Bytes::from("WebSocket connection failed")).map_err(|e| match e {}).boxed())?);
244
264
  }
245
265
  };
246
266
 
@@ -253,7 +273,9 @@ impl FBIProxy {
253
273
  });
254
274
 
255
275
  info!("WS :ws:{} => :ws:{}{} 101", target_host, target_host, uri);
256
- Ok(response)
276
+ let (parts, body) = response.into_parts();
277
+ let boxed_body = body.map_err(|_: std::convert::Infallible| unreachable!()).boxed();
278
+ Ok(Response::from_parts(parts, boxed_body))
257
279
  }
258
280
  }
259
281
 
@@ -304,39 +326,27 @@ async fn handle_websocket_forwarding(
304
326
  }
305
327
 
306
328
  async fn handle_connection(
307
- req: Request<Body>,
329
+ req: Request<Incoming>,
308
330
  proxy: Arc<FBIProxy>,
309
- ) -> Result<Response<Body>, Infallible> {
331
+ ) -> Result<Response<BoxBody>, Infallible> {
310
332
  match proxy.handle_request(req).await {
311
333
  Ok(response) => Ok(response),
312
334
  Err(e) => {
313
335
  error!("Request handling error: {}", e);
314
336
  Ok(Response::builder()
315
337
  .status(StatusCode::INTERNAL_SERVER_ERROR)
316
- .body(Body::from("Internal Server Error"))
338
+ .body(Full::new(Bytes::from("Internal Server Error")).map_err(|e| match e {}).boxed())
317
339
  .unwrap())
318
340
  }
319
341
  }
320
342
  }
321
343
 
322
- pub async fn start_proxy_server(port: u16) -> Result<(), BoxError> {
323
- start_proxy_server_with_options("127.0.0.1", port, None).await
324
- }
325
-
326
- pub async fn start_proxy_server_with_host(host: &str, port: u16) -> Result<(), BoxError> {
327
- start_proxy_server_with_options(host, port, None).await
328
- }
329
-
330
- pub async fn start_proxy_server_with_options(host: &str, port: u16, domain_filter: Option<String>) -> Result<(), BoxError> {
344
+ pub async fn start_proxy_server(host: Option<&str>, port: u16, domain_filter: Option<String>) -> Result<(), BoxError> {
345
+ let host = host.unwrap_or("127.0.0.1");
331
346
  let addr: SocketAddr = format!("{}:{}", host, port).parse()?;
332
347
  let proxy = Arc::new(FBIProxy::new(domain_filter.clone()));
333
348
 
334
- let make_svc = make_service_fn(move |_conn: &hyper::server::conn::AddrStream| {
335
- let proxy = proxy.clone();
336
- async move { Ok::<_, Infallible>(service_fn(move |req| handle_connection(req, proxy.clone()))) }
337
- });
338
-
339
- let server = Server::bind(&addr).serve(make_svc);
349
+ let listener = TcpListener::bind(addr).await?;
340
350
 
341
351
  info!("FBI Proxy server running on http://{}", addr);
342
352
  println!("FBI Proxy listening on: http://{}", addr);
@@ -350,11 +360,25 @@ pub async fn start_proxy_server_with_options(host: &str, port: u16, domain_filte
350
360
 
351
361
  info!("Features: HTTP proxying + WebSocket forwarding + Port encoding + Domain filtering");
352
362
 
353
- if let Err(e) = server.await {
354
- error!("Server error: {}", e);
355
- }
363
+ loop {
364
+ let (stream, _) = listener.accept().await?;
365
+ let io = TokioIo::new(stream);
366
+ let proxy = proxy.clone();
356
367
 
357
- Ok(())
368
+ tokio::task::spawn(async move {
369
+ let service = service_fn(move |req| handle_connection(req, proxy.clone()));
370
+
371
+ if let Err(err) = http1::Builder::new()
372
+ .preserve_header_case(true)
373
+ .title_case_headers(true)
374
+ .serve_connection(io, service)
375
+ .with_upgrades()
376
+ .await
377
+ {
378
+ error!("Error serving connection: {:?}", err);
379
+ }
380
+ });
381
+ }
358
382
  }
359
383
 
360
384
  fn main() {
@@ -453,7 +477,7 @@ TRY RUN:
453
477
  "Starting FBI-Proxy on {}:{} with domain filter: {:?}",
454
478
  host, port, domain_filter
455
479
  );
456
- if let Err(e) = start_proxy_server_with_options(host, port, domain_filter).await {
480
+ if let Err(e) = start_proxy_server(Some(host), port, domain_filter).await {
457
481
  error!("Failed to start proxy server: {}", e);
458
482
  }
459
483
  });