1
0
mirror of https://github.com/fafhrd91/actix-net synced 2025-08-14 11:10:32 +02:00

Compare commits

..

8 Commits

Author SHA1 Message Date
Nikolay Kim
1918c8d4f8 rename .run to .start() 2019-12-29 10:07:46 +06:00
Nikolay Kim
e21c58930b Add impl IntoPattern for &String 2019-12-25 21:34:14 +04:00
Nikolay Kim
59c5e9be6a Use IntoPattern for RouterBuilder::path() 2019-12-25 21:01:07 +04:00
Nikolay Kim
a2a9d9764d introduce IntoPattern trait 2019-12-25 19:54:20 +04:00
Nikolay Kim
bf0a9d2f6e Add IntoPatterns trait 2019-12-25 15:34:21 +04:00
Nikolay Kim
119027f822 fmt 2019-12-25 15:10:13 +04:00
Nikolay Kim
0fe8038d23 allow specify set of resource patters 2019-12-25 15:10:01 +04:00
Nikolay Kim
b599bc4a0c map_config() and unit_config() accepts IntoServiceFactory type 2019-12-22 16:30:49 +04:00
15 changed files with 409 additions and 99 deletions

View File

@@ -1,5 +1,11 @@
# Changes
## [1.0.1] - 2019-12-29
### Changed
* Rename `.start()` method to `.run()`
## [1.0.0] - 2019-12-11
### Changed

View File

@@ -1,6 +1,6 @@
[package]
name = "actix-server"
version = "1.0.0"
version = "1.0.1"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
description = "Actix server - General purpose tcp server"
keywords = ["network", "framework", "async", "futures"]
@@ -21,10 +21,10 @@ path = "src/lib.rs"
default = []
[dependencies]
actix-service = "1.0.0"
actix-service = "1.0.1"
actix-rt = "1.0.0"
actix-codec = "0.2.0"
actix-utils = "1.0.0"
actix-utils = "1.0.4"
log = "0.4"
num_cpus = "1.11"
@@ -38,5 +38,5 @@ mio-uds = { version = "0.6.7" }
[dev-dependencies]
bytes = "0.5"
env_logger = "0.6"
env_logger = "0.7"
actix-testing = "1.0.0"

View File

@@ -31,7 +31,7 @@ pub struct ServerBuilder {
backlog: i32,
workers: Vec<(usize, WorkerClient)>,
services: Vec<Box<dyn InternalServiceFactory>>,
sockets: Vec<(Token, StdListener)>,
sockets: Vec<(Token, String, StdListener)>,
accept: AcceptLoop,
exit: bool,
shutdown_timeout: Duration,
@@ -146,8 +146,8 @@ impl ServerBuilder {
let mut srv = ConfiguredService::new(apply);
for (name, lst) in cfg.services {
let token = self.token.next();
srv.stream(token, name, lst.local_addr()?);
self.sockets.push((token, StdListener::Tcp(lst)));
srv.stream(token, name.clone(), lst.local_addr()?);
self.sockets.push((token, name, StdListener::Tcp(lst)));
}
self.services.push(Box::new(srv));
}
@@ -172,7 +172,8 @@ impl ServerBuilder {
factory.clone(),
lst.local_addr()?,
));
self.sockets.push((token, StdListener::Tcp(lst)));
self.sockets
.push((token, name.as_ref().to_string(), StdListener::Tcp(lst)));
}
Ok(self)
}
@@ -222,7 +223,8 @@ impl ServerBuilder {
factory.clone(),
addr,
));
self.sockets.push((token, StdListener::Uds(lst)));
self.sockets
.push((token, name.as_ref().to_string(), StdListener::Uds(lst)));
Ok(self)
}
@@ -243,36 +245,18 @@ impl ServerBuilder {
factory,
lst.local_addr()?,
));
self.sockets.push((token, StdListener::Tcp(lst)));
self.sockets
.push((token, name.as_ref().to_string(), StdListener::Tcp(lst)));
Ok(self)
}
/// Spawn new thread and start listening for incoming connections.
///
/// This method spawns new thread and starts new actix system. Other than
/// that it is similar to `start()` method. This method blocks.
///
/// This methods panics if no socket addresses get bound.
///
/// ```rust,ignore
/// use actix_web::*;
///
/// fn main() -> std::io::Result<()> {
/// Server::new().
/// .service(
/// HttpServer::new(|| App::new().service(web::service("/").to(|| HttpResponse::Ok())))
/// .bind("127.0.0.1:0")
/// .run()
/// }
/// ```
pub fn run(self) -> io::Result<()> {
let sys = System::new("http-server");
self.start();
sys.run()
#[doc(hidden)]
pub fn start(self) -> Server {
self.run()
}
/// Starts processing incoming connections and return server controller.
pub fn start(mut self) -> Server {
pub fn run(mut self) -> Server {
if self.sockets.is_empty() {
panic!("Server should have at least one bound socket");
} else {
@@ -288,10 +272,15 @@ impl ServerBuilder {
// start accept thread
for sock in &self.sockets {
info!("Starting server on {}", sock.1);
info!("Starting \"{}\" service on {}", sock.1, sock.2);
}
self.accept
.start(mem::replace(&mut self.sockets, Vec::new()), workers);
self.accept.start(
mem::replace(&mut self.sockets, Vec::new())
.into_iter()
.map(|t| (t.0, t.2))
.collect(),
workers,
);
// handle signals
if !self.no_signals {
@@ -391,7 +380,7 @@ impl ServerBuilder {
.await;
System::current().stop();
}
.boxed(),
.boxed(),
);
}
ready(())

View File

@@ -153,7 +153,7 @@ impl InternalServiceFactory for ConfiguredService {
}
return Ok(res);
}
.boxed_local()
.boxed_local()
}
}
@@ -280,6 +280,6 @@ where
}
};
}
.boxed_local()
.boxed_local()
}
}

View File

@@ -217,7 +217,7 @@ impl Worker {
wrk.await
});
}
.boxed(),
.boxed(),
);
WorkerClient::new(idx, tx1, tx2, avail)

View File

@@ -1,5 +1,12 @@
# Changes
## [1.0.1] - 2019-12-22
### Changed
* `map_config()` and `unit_config()` accepts `IntoServiceFactory` type
## [1.0.0] - 2019-12-11
### Added

View File

@@ -1,6 +1,6 @@
[package]
name = "actix-service"
version = "1.0.0"
version = "1.0.1"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
description = "Actix service"
keywords = ["network", "framework", "async", "futures"]

View File

@@ -1,28 +1,30 @@
use std::marker::PhantomData;
use super::ServiceFactory;
use super::{IntoServiceFactory, ServiceFactory};
/// Adapt external config argument to a config for provided service factory
///
/// Note that this function consumes the receiving service factory and returns
/// a wrapped version of it.
pub fn map_config<T, F, C>(factory: T, f: F) -> MapConfig<T, F, C>
pub fn map_config<T, U, F, C>(factory: U, f: F) -> MapConfig<T, F, C>
where
T: ServiceFactory,
U: IntoServiceFactory<T>,
F: Fn(C) -> T::Config,
{
MapConfig::new(factory, f)
MapConfig::new(factory.into_factory(), f)
}
/// Replace config with unit
pub fn unit_config<T, C>(new_service: T) -> UnitConfig<T, C>
pub fn unit_config<T, U, C>(factory: U) -> UnitConfig<T, C>
where
T: ServiceFactory<Config = ()>,
U: IntoServiceFactory<T>,
{
UnitConfig::new(new_service)
UnitConfig::new(factory.into_factory())
}
/// `.map_config()` service combinator
/// `map_config()` adapter service factory
pub struct MapConfig<A, F, C> {
a: A,
f: F,

View File

@@ -88,7 +88,7 @@ impl<T: AsyncRead + AsyncWrite + Unpin + 'static> Service for AcceptorService<T>
let acc = acc;
tokio_openssl::accept(&acc, req).await
}
.boxed_local(),
.boxed_local(),
}
}
}

View File

@@ -1,5 +1,19 @@
# Changes
## [0.2.3] - 2019-12-25
* Add impl `IntoPattern` for `&String`
## [0.2.2] - 2019-12-25
* Use `IntoPattern` for `RouterBuilder::path()`
## [0.2.1] - 2019-12-25
* Add `IntoPattern` trait
* Add multi-pattern resources
## [0.2.0] - 2019-12-07
* Update http to 0.2

View File

@@ -1,6 +1,6 @@
[package]
name = "actix-router"
version = "0.2.0"
version = "0.2.3"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
description = "Path router"
keywords = ["actix"]
@@ -8,9 +8,7 @@ homepage = "https://actix.rs"
repository = "https://github.com/actix/actix-net.git"
documentation = "https://docs.rs/actix-router/"
license = "MIT/Apache-2.0"
exclude = [".gitignore", ".travis.yml", ".cargo/config", "appveyor.yml"]
edition = "2018"
workspace = ".."
[lib]
name = "actix_router"
@@ -21,8 +19,8 @@ default = ["http"]
[dependencies]
regex = "1.3.1"
serde = "1.0.80"
bytestring = "0.1.0"
serde = "1.0.104"
bytestring = "0.1.2"
log = "0.4.8"
http = { version="0.2.0", optional=true }

View File

@@ -492,7 +492,7 @@ mod tests {
#[derive(Deserialize)]
struct Id {
id: String,
_id: String,
}
#[derive(Debug, Deserialize)]

View File

@@ -35,6 +35,100 @@ impl ResourcePath for bytestring::ByteString {
}
}
/// Helper trait for type that could be converted to path pattern
pub trait IntoPattern {
/// Signle patter
fn is_single(&self) -> bool;
fn patterns(&self) -> Vec<String>;
}
impl IntoPattern for String {
fn is_single(&self) -> bool {
true
}
fn patterns(&self) -> Vec<String> {
vec![self.clone()]
}
}
impl<'a> IntoPattern for &'a String {
fn is_single(&self) -> bool {
true
}
fn patterns(&self) -> Vec<String> {
vec![self.as_str().to_string()]
}
}
impl<'a> IntoPattern for &'a str {
fn is_single(&self) -> bool {
true
}
fn patterns(&self) -> Vec<String> {
vec![self.to_string()]
}
}
impl<T: AsRef<str>> IntoPattern for Vec<T> {
fn is_single(&self) -> bool {
self.len() == 1
}
fn patterns(&self) -> Vec<String> {
self.into_iter().map(|v| v.as_ref().to_string()).collect()
}
}
macro_rules! array_patterns (($tp:ty, $num:tt) => {
impl IntoPattern for [$tp; $num] {
fn is_single(&self) -> bool {
$num == 1
}
fn patterns(&self) -> Vec<String> {
self.iter().map(|v| v.to_string()).collect()
}
}
});
array_patterns!(&str, 1);
array_patterns!(&str, 2);
array_patterns!(&str, 3);
array_patterns!(&str, 4);
array_patterns!(&str, 5);
array_patterns!(&str, 6);
array_patterns!(&str, 7);
array_patterns!(&str, 8);
array_patterns!(&str, 9);
array_patterns!(&str, 10);
array_patterns!(&str, 11);
array_patterns!(&str, 12);
array_patterns!(&str, 13);
array_patterns!(&str, 14);
array_patterns!(&str, 15);
array_patterns!(&str, 16);
array_patterns!(String, 1);
array_patterns!(String, 2);
array_patterns!(String, 3);
array_patterns!(String, 4);
array_patterns!(String, 5);
array_patterns!(String, 6);
array_patterns!(String, 7);
array_patterns!(String, 8);
array_patterns!(String, 9);
array_patterns!(String, 10);
array_patterns!(String, 11);
array_patterns!(String, 12);
array_patterns!(String, 13);
array_patterns!(String, 14);
array_patterns!(String, 15);
array_patterns!(String, 16);
#[cfg(feature = "http")]
mod url;

View File

@@ -2,10 +2,10 @@ use std::cmp::min;
use std::hash::{Hash, Hasher};
use std::rc::Rc;
use regex::{escape, Regex};
use regex::{escape, Regex, RegexSet};
use crate::path::{Path, PathItem};
use crate::{Resource, ResourcePath};
use crate::{IntoPattern, Resource, ResourcePath};
const MAX_DYNAMIC_SEGMENTS: usize = 16;
@@ -32,21 +32,53 @@ enum PatternType {
Static(String),
Prefix(String),
Dynamic(Regex, Vec<Rc<String>>, usize),
DynamicSet(RegexSet, Vec<(Regex, Vec<Rc<String>>, usize)>),
}
impl ResourceDef {
/// Parse path pattern and create new `Pattern` instance.
///
/// Panics if path pattern is wrong.
pub fn new(path: &str) -> Self {
ResourceDef::with_prefix(path, false)
/// Panics if path pattern is malformed.
pub fn new<T: IntoPattern>(path: T) -> Self {
if path.is_single() {
let patterns = path.patterns();
ResourceDef::with_prefix(&patterns[0], false)
} else {
let set = path.patterns();
let mut data = Vec::new();
let mut re_set = Vec::new();
for path in set {
let (pattern, _, _, len) = ResourceDef::parse(&path, false);
let re = match Regex::new(&pattern) {
Ok(re) => re,
Err(err) => panic!("Wrong path pattern: \"{}\" {}", path, err),
};
// actix creates one router per thread
let names: Vec<_> = re
.capture_names()
.filter_map(|name| name.map(|name| Rc::new(name.to_owned())))
.collect();
data.push((re, names, len));
re_set.push(pattern);
}
ResourceDef {
id: 0,
tp: PatternType::DynamicSet(RegexSet::new(re_set).unwrap(), data),
elements: Vec::new(),
name: String::new(),
pattern: "".to_owned(),
}
}
}
/// Parse path pattern and create new `Pattern` instance.
///
/// Use `prefix` type instead of `static`.
///
/// Panics if path regex pattern is wrong.
/// Panics if path regex pattern is malformed.
pub fn prefix(path: &str) -> Self {
ResourceDef::with_prefix(path, true)
}
@@ -57,7 +89,7 @@ impl ResourceDef {
///
/// Use `prefix` type instead of `static`.
///
/// Panics if path regex pattern is wrong.
/// Panics if path regex pattern is malformed.
pub fn root_prefix(path: &str) -> Self {
ResourceDef::with_prefix(&insert_slash(path), true)
}
@@ -123,8 +155,9 @@ impl ResourceDef {
pub fn is_match(&self, path: &str) -> bool {
match self.tp {
PatternType::Static(ref s) => s == path,
PatternType::Dynamic(ref re, _, _) => re.is_match(path),
PatternType::Prefix(ref s) => path.starts_with(s),
PatternType::Dynamic(ref re, _, _) => re.is_match(path),
PatternType::DynamicSet(ref re, _) => re.is_match(path),
}
}
@@ -176,6 +209,30 @@ impl ResourceDef {
};
Some(min(plen, len))
}
PatternType::DynamicSet(ref re, ref params) => {
if let Some(idx) = re.matches(path).into_iter().next() {
let (ref pattern, _, len) = params[idx];
if let Some(captures) = pattern.captures(path) {
let mut pos = 0;
let mut passed = false;
for capture in captures.iter() {
if let Some(ref m) = capture {
if !passed {
passed = true;
continue;
}
pos = m.end();
}
}
Some(pos + len)
} else {
None
}
} else {
None
}
}
}
}
@@ -190,6 +247,25 @@ impl ResourceDef {
false
}
}
PatternType::Prefix(ref s) => {
let rpath = path.path();
let len = if s == rpath {
s.len()
} else if rpath.starts_with(s)
&& (s.ends_with('/') || rpath.split_at(s.len()).1.starts_with('/'))
{
if s.ends_with('/') {
s.len() - 1
} else {
s.len()
}
} else {
return false;
};
let rpath_len = rpath.len();
path.skip(min(rpath_len, len) as u16);
true
}
PatternType::Dynamic(ref re, ref names, len) => {
let mut idx = 0;
let mut pos = 0;
@@ -219,24 +295,40 @@ impl ResourceDef {
path.skip((pos + len) as u16);
true
}
PatternType::Prefix(ref s) => {
let rpath = path.path();
let len = if s == rpath {
s.len()
} else if rpath.starts_with(s)
&& (s.ends_with('/') || rpath.split_at(s.len()).1.starts_with('/'))
{
if s.ends_with('/') {
s.len() - 1
PatternType::DynamicSet(ref re, ref params) => {
if let Some(idx) = re.matches(path.path()).into_iter().next() {
let (ref pattern, ref names, len) = params[idx];
let mut idx = 0;
let mut pos = 0;
let mut segments: [PathItem; MAX_DYNAMIC_SEGMENTS] =
[PathItem::Static(""); MAX_DYNAMIC_SEGMENTS];
if let Some(captures) = pattern.captures(path.path()) {
for (no, name) in names.iter().enumerate() {
if let Some(m) = captures.name(&name) {
idx += 1;
pos = m.end();
segments[no] =
PathItem::Segment(m.start() as u16, m.end() as u16);
} else {
log::error!(
"Dynamic path match but not all segments found: {}",
name
);
return false;
}
}
} else {
s.len()
return false;
}
for idx in 0..idx {
path.add(names[idx].clone(), segments[idx]);
}
path.skip((pos + len) as u16);
true
} else {
return false;
};
let rpath_len = rpath.len();
path.skip(min(rpath_len, len) as u16);
true
false
}
}
}
}
@@ -263,6 +355,30 @@ impl ResourceDef {
false
}
}
PatternType::Prefix(ref s) => {
let len = {
let rpath = res.resource_path().path();
if s == rpath {
s.len()
} else if rpath.starts_with(s)
&& (s.ends_with('/') || rpath.split_at(s.len()).1.starts_with('/'))
{
if s.ends_with('/') {
s.len() - 1
} else {
s.len()
}
} else {
return false;
}
};
if !check(res, user_data) {
return false;
}
let path = res.resource_path();
path.skip(min(path.path().len(), len) as u16);
true
}
PatternType::Dynamic(ref re, ref names, len) => {
let mut idx = 0;
let mut pos = 0;
@@ -298,29 +414,47 @@ impl ResourceDef {
path.skip((pos + len) as u16);
true
}
PatternType::Prefix(ref s) => {
let len = {
let rpath = res.resource_path().path();
if s == rpath {
s.len()
} else if rpath.starts_with(s)
&& (s.ends_with('/') || rpath.split_at(s.len()).1.starts_with('/'))
{
if s.ends_with('/') {
s.len() - 1
} else {
s.len()
PatternType::DynamicSet(ref re, ref params) => {
let path = res.resource_path().path();
if let Some(idx) = re.matches(path).into_iter().next() {
let (ref pattern, ref names, len) = params[idx];
let mut idx = 0;
let mut pos = 0;
let mut segments: [PathItem; MAX_DYNAMIC_SEGMENTS] =
[PathItem::Static(""); MAX_DYNAMIC_SEGMENTS];
if let Some(captures) = pattern.captures(path) {
for (no, name) in names.iter().enumerate() {
if let Some(m) = captures.name(&name) {
idx += 1;
pos = m.end();
segments[no] =
PathItem::Segment(m.start() as u16, m.end() as u16);
} else {
log::error!(
"Dynamic path match but not all segments found: {}",
name
);
return false;
}
}
} else {
return false;
}
};
if !check(res, user_data) {
return false;
if !check(res, user_data) {
return false;
}
let path = res.resource_path();
for idx in 0..idx {
path.add(names[idx].clone(), segments[idx]);
}
path.skip((pos + len) as u16);
true
} else {
false
}
let path = res.resource_path();
path.skip(min(path.path().len(), len) as u16);
true
}
}
}
@@ -348,7 +482,10 @@ impl ResourceDef {
}
}
}
};
PatternType::DynamicSet(..) => {
return false;
}
}
true
}
@@ -476,7 +613,7 @@ impl<'a> From<&'a str> for ResourceDef {
impl From<String> for ResourceDef {
fn from(path: String) -> ResourceDef {
ResourceDef::new(&path)
ResourceDef::new(path)
}
}
@@ -558,6 +695,65 @@ mod tests {
assert_eq!(path.get("id").unwrap(), "012345");
}
#[test]
fn test_dynamic_set() {
let re = ResourceDef::new(vec![
"/user/{id}",
"/v{version}/resource/{id}",
"/{id:[[:digit:]]{6}}",
]);
assert!(re.is_match("/user/profile"));
assert!(re.is_match("/user/2345"));
assert!(!re.is_match("/user/2345/"));
assert!(!re.is_match("/user/2345/sdg"));
let mut path = Path::new("/user/profile");
assert!(re.match_path(&mut path));
assert_eq!(path.get("id").unwrap(), "profile");
let mut path = Path::new("/user/1245125");
assert!(re.match_path(&mut path));
assert_eq!(path.get("id").unwrap(), "1245125");
assert!(re.is_match("/v1/resource/320120"));
assert!(!re.is_match("/v/resource/1"));
assert!(!re.is_match("/resource"));
let mut path = Path::new("/v151/resource/adahg32");
assert!(re.match_path(&mut path));
assert_eq!(path.get("version").unwrap(), "151");
assert_eq!(path.get("id").unwrap(), "adahg32");
assert!(re.is_match("/012345"));
assert!(!re.is_match("/012"));
assert!(!re.is_match("/01234567"));
assert!(!re.is_match("/XXXXXX"));
let mut path = Path::new("/012345");
assert!(re.match_path(&mut path));
assert_eq!(path.get("id").unwrap(), "012345");
let re = ResourceDef::new([
"/user/{id}",
"/v{version}/resource/{id}",
"/{id:[[:digit:]]{6}}",
]);
assert!(re.is_match("/user/profile"));
assert!(re.is_match("/user/2345"));
assert!(!re.is_match("/user/2345/"));
assert!(!re.is_match("/user/2345/sdg"));
let re = ResourceDef::new([
"/user/{id}".to_string(),
"/v{version}/resource/{id}".to_string(),
"/{id:[[:digit:]]{6}}".to_string(),
]);
assert!(re.is_match("/user/profile"));
assert!(re.is_match("/user/2345"));
assert!(!re.is_match("/user/2345/"));
assert!(!re.is_match("/user/2345/sdg"));
}
#[test]
fn test_parse_tail() {
let re = ResourceDef::new("/user/-{id}*");

View File

@@ -1,4 +1,4 @@
use crate::{Resource, ResourceDef, ResourcePath};
use crate::{IntoPattern, Resource, ResourceDef, ResourcePath};
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct ResourceId(pub u16);
@@ -70,7 +70,11 @@ pub struct RouterBuilder<T, U = ()> {
impl<T, U> RouterBuilder<T, U> {
/// Register resource for specified path.
pub fn path(&mut self, path: &str, resource: T) -> &mut (ResourceDef, T, Option<U>) {
pub fn path<P: IntoPattern>(
&mut self,
path: P,
resource: T,
) -> &mut (ResourceDef, T, Option<U>) {
self.resources
.push((ResourceDef::new(path), resource, None));
self.resources.last_mut().unwrap()