Unverified Commit e9252ac6 authored by Niklas Adolfsson's avatar Niklas Adolfsson
Browse files

refactor ws server

parents 1dafb1e6 21189759
Pipeline #198737 canceled with stages
in 45 minutes and 35 seconds
......@@ -6,6 +6,29 @@ The format is based on [Keep a Changelog].
## [Unreleased]
## [v0.14.0] - 2022-06-14
v0.14.0 is breaking release which changes the `health and access control APIs` and a bunch of bug fixes.
### [Fixed]
- fix(servers): more descriptive errors when calls fail [#790](https://github.com/paritytech/jsonrpsee/pull/790)
- fix(ws server): support `*` in host and origin filtering [#781](https://github.com/paritytech/jsonrpsee/pull/781)
- fix(rpc module): register failed `unsubscribe calls` in middleware [#792](https://github.com/paritytech/jsonrpsee/pull/792)
- fix(http server): omit jsonrpc details in health API [#785](https://github.com/paritytech/jsonrpsee/pull/785)
- fix(servers): skip leading whitespace in JSON deserialization [#783](https://github.com/paritytech/jsonrpsee/pull/783)
- fix(ws-server): Submit ping regardless of WS messages [#788](https://github.com/paritytech/jsonrpsee/pull/788)
- fix(rpc_module): remove expect in `fn call` [#774](https://github.com/paritytech/jsonrpsee/pull/774)
### [Added]
- feat(ws-client): `ping-pong` for WebSocket clients [#772](https://github.com/paritytech/jsonrpsee/pull/772)
- feat(ws-server): Implement `ping-pong` for WebSocket server [#782](https://github.com/paritytech/jsonrpsee/pull/782)
### [Changed]
- chore(deps): bump Swatinem/rust-cache from 1.3.0 to 1.4.0 [#778](https://github.com/paritytech/jsonrpsee/pull/778)
- chore(deps): bump actions/checkout from 2.4.0 to 3.0.2 [#779](https://github.com/paritytech/jsonrpsee/pull/779)
- chore(ci): bring back daily benchmarks [#777](https://github.com/paritytech/jsonrpsee/pull/777)
- chore(examples): Move examples under dedicated folder to simplify `Cargo.toml` [#769](https://github.com/paritytech/jsonrpsee/pull/769)
## [v0.13.1] - 2022-05-13
v0.13.1 is a release that fixes the documentation for feature-gated items on `docs.rs`.
......@@ -74,7 +97,7 @@ v0.10.1 is a release that fixes a regression in the HTTP server where the backlo
If your usage expects a high rate of new HTTP connections you are encouraged to update or manually configure the socket based on the traffic characteristics.
### [Changed]
- [proc macros]: only generate unsub method if not provided (#702)
- [proc macros]: only generate unsub method if not provided [#702](https://github.com/paritytech/jsonrpsee/pull/702)
- [examples]: update pubsub examples [#705](https://github.com/paritytech/jsonrpsee/pull/705)
- core: remove `Error::Request` variant [#717](https://github.com/paritytech/jsonrpsee/pull/717)
- Replace async-channel [#708](https://github.com/paritytech/jsonrpsee/pull/708)
......
[package]
name = "jsonrpsee-benchmarks"
version = "0.13.1"
version = "0.14.0"
authors = ["Parity Technologies <admin@parity.io>"]
description = "Benchmarks for jsonrpsee"
edition = "2021"
......
[package]
name = "jsonrpsee-http-client"
version = "0.13.1"
version = "0.14.0"
authors = ["Parity Technologies <admin@parity.io>", "Pierre Krieger <pierre.krieger1708@gmail.com>"]
description = "HTTP client for JSON-RPC"
edition = "2021"
......@@ -14,8 +14,8 @@ async-trait = "0.1"
rustc-hash = "1"
hyper = { version = "0.14.10", features = ["client", "http1", "http2", "tcp"] }
hyper-rustls = { version = "0.23", optional = true }
jsonrpsee-types = { path = "../../types", version = "0.13.1" }
jsonrpsee-core = { path = "../../core", version = "0.13.1", features = ["client", "http-helpers"] }
jsonrpsee-types = { path = "../../types", version = "0.14.0" }
jsonrpsee-core = { path = "../../core", version = "0.14.0", features = ["client", "http-helpers"] }
serde = { version = "1.0", default-features = false, features = ["derive"] }
serde_json = "1.0"
thiserror = "1.0"
......
[package]
name = "jsonrpsee-client-transport"
version = "0.13.1"
version = "0.14.0"
authors = ["Parity Technologies <admin@parity.io>", "Pierre Krieger <pierre.krieger1708@gmail.com>"]
description = "WebSocket client for JSON-RPC"
edition = "2021"
......@@ -10,8 +10,8 @@ homepage = "https://github.com/paritytech/jsonrpsee"
documentation = "https://docs.rs/jsonrpsee-ws-client"
[dependencies]
jsonrpsee-types = { path = "../../types", version = "0.13.1", optional = true }
jsonrpsee-core = { path = "../../core", version = "0.13.1", features = ["client"] }
jsonrpsee-types = { path = "../../types", version = "0.14.0", optional = true }
jsonrpsee-core = { path = "../../core", version = "0.14.0", features = ["client"] }
tracing = "0.1"
# optional
......
[package]
name = "jsonrpsee-wasm-client"
version = "0.13.1"
version = "0.14.0"
authors = ["Parity Technologies <admin@parity.io>", "Pierre Krieger <pierre.krieger1708@gmail.com>"]
description = "WASM client for JSON-RPC"
edition = "2021"
......@@ -10,9 +10,9 @@ homepage = "https://github.com/paritytech/jsonrpsee"
documentation = "https://docs.rs/jsonrpsee-ws-client"
[dependencies]
jsonrpsee-types = { path = "../../types", version = "0.13.1" }
jsonrpsee-client-transport = { path = "../transport", version = "0.13.1", features = ["web"] }
jsonrpsee-core = { path = "../../core", version = "0.13.1", features = ["async-wasm-client"] }
jsonrpsee-types = { path = "../../types", version = "0.14.0" }
jsonrpsee-client-transport = { path = "../transport", version = "0.14.0", features = ["web"] }
jsonrpsee-core = { path = "../../core", version = "0.14.0", features = ["async-wasm-client"] }
[dev-dependencies]
env_logger = "0.9"
......
[package]
name = "jsonrpsee-ws-client"
version = "0.13.1"
version = "0.14.0"
authors = ["Parity Technologies <admin@parity.io>", "Pierre Krieger <pierre.krieger1708@gmail.com>"]
description = "WebSocket client for JSON-RPC"
edition = "2021"
......@@ -10,9 +10,9 @@ homepage = "https://github.com/paritytech/jsonrpsee"
documentation = "https://docs.rs/jsonrpsee-ws-client"
[dependencies]
jsonrpsee-types = { path = "../../types", version = "0.13.1" }
jsonrpsee-client-transport = { path = "../transport", version = "0.13.1", features = ["ws"] }
jsonrpsee-core = { path = "../../core", version = "0.13.1", features = ["async-client"] }
jsonrpsee-types = { path = "../../types", version = "0.14.0" }
jsonrpsee-client-transport = { path = "../transport", version = "0.14.0", features = ["ws"] }
jsonrpsee-core = { path = "../../core", version = "0.14.0", features = ["async-client"] }
[dev-dependencies]
env_logger = "0.9"
......
[package]
name = "jsonrpsee-core"
version = "0.13.1"
version = "0.14.0"
authors = ["Parity Technologies <admin@parity.io>"]
description = "Utilities for jsonrpsee"
edition = "2021"
......@@ -11,7 +11,7 @@ anyhow = "1"
async-trait = "0.1"
beef = { version = "0.5.1", features = ["impl_serde"] }
futures-channel = "0.3.14"
jsonrpsee-types = { path = "../types", version = "0.13.1" }
jsonrpsee-types = { path = "../types", version = "0.14.0" }
thiserror = "1"
serde = { version = "1.0", default-features = false, features = ["derive"] }
serde_json = { version = "1", features = ["raw_value"] }
......@@ -31,6 +31,9 @@ parking_lot = { version = "0.12", optional = true }
tokio = { version = "1.16", optional = true }
wasm-bindgen-futures = { version = "0.4.19", optional = true }
futures-timer = { version = "3", optional = true }
globset = { version = "0.4", optional = true }
lazy_static = { version = "1", optional = true }
unicase = { version = "2.6.0", optional = true }
[features]
default = []
......@@ -38,12 +41,15 @@ http-helpers = ["hyper", "futures-util"]
server = [
"arrayvec",
"futures-util/alloc",
"globset",
"rustc-hash/std",
"tracing",
"parking_lot",
"rand",
"tokio/rt",
"tokio/sync",
"lazy_static",
"unicase",
]
client = ["futures-util/sink", "futures-channel/sink", "futures-channel/std"]
async-client = [
......
......@@ -105,9 +105,12 @@ pub enum Error {
/// Attempted to stop server that is already stopped.
#[error("Attempted to stop server that is already stopped")]
AlreadyStopped,
/// List passed into `set_allowed_origins` was empty
/// List passed into access control based on HTTP header verification.
#[error("Must set at least one allowed value for the {0} header")]
EmptyAllowList(&'static str),
/// Access control verification of HTTP headers failed.
#[error("HTTP header: `{0}` value: `{1}` verification failed")]
HttpHeaderRejected(&'static str, String),
/// Failed to execute a method because a resource was already at capacity
#[error("Resource at capacity: {0}")]
ResourceAtCapacity(&'static str),
......
......@@ -101,13 +101,25 @@ pub fn read_header_value<'a>(headers: &'a hyper::header::HeaderMap, header_name:
pub fn read_header_values<'a>(
headers: &'a hyper::header::HeaderMap,
header_name: &str,
) -> hyper::header::ValueIter<'a, hyper::header::HeaderValue> {
headers.get_all(header_name).iter()
) -> hyper::header::GetAll<'a, hyper::header::HeaderValue> {
headers.get_all(header_name)
}
/// Get the header values from the `access-control-request-headers` header.
pub fn get_cors_request_headers<'a>(headers: &'a hyper::header::HeaderMap) -> impl Iterator<Item = &str> {
const ACCESS_CONTROL_REQUEST_HEADERS: &str = "access-control-request-headers";
read_header_values(headers, ACCESS_CONTROL_REQUEST_HEADERS)
.iter()
.filter_map(|val| val.to_str().ok())
.flat_map(|val| val.split(","))
// The strings themselves might contain leading and trailing whitespaces
.map(|s| s.trim())
}
#[cfg(test)]
mod tests {
use super::{read_body, read_header_content_length};
use super::{get_cors_request_headers, read_body, read_header_content_length};
#[tokio::test]
async fn body_to_bytes_size_limit_works() {
......@@ -132,4 +144,23 @@ mod tests {
headers.insert(hyper::header::CONTENT_LENGTH, "18446744073709551616".parse().unwrap());
assert_eq!(read_header_content_length(&headers), None);
}
#[test]
fn get_cors_headers_works() {
let mut headers = hyper::header::HeaderMap::new();
// access-control-request-headers
headers.insert(hyper::header::ACCESS_CONTROL_REQUEST_HEADERS, "Content-Type,x-requested-with".parse().unwrap());
let values: Vec<&str> = get_cors_request_headers(&headers).collect();
assert_eq!(values, vec!["Content-Type", "x-requested-with"]);
headers.insert(
hyper::header::ACCESS_CONTROL_REQUEST_HEADERS,
"Content-Type, x-requested-with ".parse().unwrap(),
);
let values: Vec<&str> = get_cors_request_headers(&headers).collect();
assert_eq!(values, vec!["Content-Type", "x-requested-with"]);
}
}
......@@ -39,7 +39,7 @@ use jsonrpsee_types::Params;
pub trait Middleware: Send + Sync + Clone + 'static {
/// Intended to carry timestamp of a request, for example `std::time::Instant`. How the middleware
/// measures time, if at all, is entirely up to the implementation.
type Instant: Send + Copy;
type Instant: std::fmt::Debug + Send + Sync + Copy;
/// Called when a new client connects (WebSocket only)
fn on_connect(&self) {}
......
......@@ -29,9 +29,9 @@
use std::collections::HashSet;
use std::{fmt, ops};
use crate::access_control::hosts::{Host, Port};
use crate::access_control::matcher::{Matcher, Pattern};
use jsonrpsee_core::Cow;
use crate::server::access_control::host::{Host, Port};
use crate::server::access_control::matcher::{Matcher, Pattern};
use crate::Cow;
use lazy_static::lazy_static;
use unicase::Ascii;
......@@ -128,54 +128,54 @@ impl ops::Deref for Origin {
/// Origins allowed to access
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum AccessControlAllowOrigin {
/// Specific hostname
Value(Origin),
pub enum AllowOrigin {
/// Specific origin.
Origin(Origin),
/// null-origin (file:///, sandboxed iframe)
Null,
/// Any non-null origin
Any,
}
impl fmt::Display for AccessControlAllowOrigin {
impl fmt::Display for AllowOrigin {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"{}",
match *self {
AccessControlAllowOrigin::Any => "*",
AccessControlAllowOrigin::Null => "null",
AccessControlAllowOrigin::Value(ref val) => val,
Self::Any => "*",
Self::Null => "null",
Self::Origin(ref val) => val,
}
)
}
}
impl<T: Into<String>> From<T> for AccessControlAllowOrigin {
fn from(s: T) -> AccessControlAllowOrigin {
impl<T: Into<String>> From<T> for AllowOrigin {
fn from(s: T) -> Self {
match s.into().as_str() {
"all" | "*" | "any" => AccessControlAllowOrigin::Any,
"null" => AccessControlAllowOrigin::Null,
origin => AccessControlAllowOrigin::Value(origin.into()),
"all" | "*" | "any" => Self::Any,
"null" => Self::Null,
origin => Self::Origin(origin.into()),
}
}
}
/// Headers allowed to access
#[derive(Debug, Clone, PartialEq)]
pub enum AccessControlAllowHeaders {
pub enum AllowHeaders {
/// Specific headers
Only(Vec<String>),
/// Any header
Any,
}
impl AccessControlAllowHeaders {
impl AllowHeaders {
/// Return an appropriate value for the CORS header "Access-Control-Allow-Headers".
pub fn to_cors_header_value(&self) -> Cow<'_, str> {
match self {
AccessControlAllowHeaders::Any => "*".into(),
AccessControlAllowHeaders::Only(headers) => headers.join(", ").into(),
AllowHeaders::Any => "*".into(),
AllowHeaders::Only(headers) => headers.join(", ").into(),
}
}
}
......@@ -219,11 +219,11 @@ impl<T> From<AllowCors<T>> for Option<T> {
}
/// Returns correct CORS header (if any) given list of allowed origins and current origin.
pub(crate) fn get_cors_allow_origin(
pub(super) fn get_cors_allow_origin(
origin: Option<&str>,
allowed: &Option<Vec<AllowOrigin>>,
host: Option<&str>,
allowed: &Option<Vec<AccessControlAllowOrigin>>,
) -> AllowCors<AccessControlAllowOrigin> {
) -> AllowCors<AllowOrigin> {
match origin {
None => AllowCors::NotRequired,
Some(ref origin) => {
......@@ -239,22 +239,22 @@ pub(crate) fn get_cors_allow_origin(
}
match allowed.as_ref() {
None if *origin == "null" => AllowCors::Ok(AccessControlAllowOrigin::Null),
None => AllowCors::Ok(AccessControlAllowOrigin::Value(Origin::parse(origin))),
None if *origin == "null" => AllowCors::Ok(AllowOrigin::Null),
None => AllowCors::Ok(AllowOrigin::Origin(Origin::parse(origin))),
Some(allowed) if *origin == "null" => allowed
.iter()
.find(|cors| **cors == AccessControlAllowOrigin::Null)
.find(|cors| **cors == AllowOrigin::Null)
.cloned()
.map(AllowCors::Ok)
.unwrap_or(AllowCors::Invalid),
Some(allowed) => allowed
.iter()
.find(|cors| match **cors {
AccessControlAllowOrigin::Any => true,
AccessControlAllowOrigin::Value(ref val) if val.matches(origin) => true,
AllowOrigin::Any => true,
AllowOrigin::Origin(ref val) if val.matches(origin) => true,
_ => false,
})
.map(|_| AccessControlAllowOrigin::Value(Origin::parse(origin)))
.map(|_| AllowOrigin::Origin(Origin::parse(origin)))
.map(AllowCors::Ok)
.unwrap_or(AllowCors::Invalid),
}
......@@ -262,15 +262,19 @@ pub(crate) fn get_cors_allow_origin(
}
}
/// Validates if the `AccessControlAllowedHeaders` in the request are allowed.
/// Validates if the headers in the request are allowed.
///
/// headers: all the headers in the request.
/// cors_request_headers: `values` in the `access-control-request-headers` header.
/// cors_allow_headers: whitelisted headers by the user.
pub(crate) fn get_cors_allow_headers<T: AsRef<str>, O, F: Fn(T) -> O>(
mut headers: impl Iterator<Item = T>,
requested_headers: impl Iterator<Item = T>,
cors_allow_headers: &AccessControlAllowHeaders,
cors_request_headers: impl Iterator<Item = T>,
cors_allow_headers: &AllowHeaders,
to_result: F,
) -> AllowCors<Vec<O>> {
// Check if the header fields which were sent in the request are allowed
if let AccessControlAllowHeaders::Only(only) = cors_allow_headers {
if let AllowHeaders::Only(only) = cors_allow_headers {
let are_all_allowed = headers.all(|header| {
let name = &Ascii::new(header.as_ref());
only.iter().any(|h| Ascii::new(&*h) == name) || ALWAYS_ALLOWED_HEADERS.contains(name)
......@@ -283,13 +287,13 @@ pub(crate) fn get_cors_allow_headers<T: AsRef<str>, O, F: Fn(T) -> O>(
// Check if `AccessControlRequestHeaders` contains fields which were allowed
let (filtered, headers) = match cors_allow_headers {
AccessControlAllowHeaders::Any => {
let headers = requested_headers.map(to_result).collect();
AllowHeaders::Any => {
let headers = cors_request_headers.map(to_result).collect();
(false, headers)
}
AccessControlAllowHeaders::Only(only) => {
AllowHeaders::Only(only) => {
let mut filtered = false;
let headers: Vec<_> = requested_headers
let headers: Vec<_> = cors_request_headers
.filter(|header| {
let name = &Ascii::new(header.as_ref());
filtered = true;
......@@ -319,7 +323,6 @@ lazy_static! {
let mut hs = HashSet::new();
hs.insert(Ascii::new("Accept"));
hs.insert(Ascii::new("Accept-Language"));
hs.insert(Ascii::new("Access-Control-Allow-Origin"));
hs.insert(Ascii::new("Access-Control-Request-Headers"));
hs.insert(Ascii::new("Content-Language"));
hs.insert(Ascii::new("Content-Type"));
......@@ -337,7 +340,7 @@ mod tests {
use std::iter;
use super::*;
use crate::access_control::hosts::Host;
use crate::server::access_control::host::Host;
#[test]
fn should_parse_origin() {
......@@ -365,8 +368,8 @@ mod tests {
let host = Some(&*host);
// when
let res1 = get_cors_allow_origin(origin1, host, &Some(vec![]));
let res2 = get_cors_allow_origin(origin2, host, &Some(vec![]));
let res1 = get_cors_allow_origin(origin1, &Some(vec![]), host);
let res2 = get_cors_allow_origin(origin2, &Some(vec![]), host);
// then
assert_eq!(res1, AllowCors::Invalid);
......@@ -383,7 +386,7 @@ mod tests {
let host = Some(&*host);
// when
let res = get_cors_allow_origin(origin, host, &None);
let res = get_cors_allow_origin(origin, &None, host);
// then
assert_eq!(res, AllowCors::NotRequired);
......@@ -396,7 +399,7 @@ mod tests {
let host = None;
// when
let res = get_cors_allow_origin(origin, host, &None);
let res = get_cors_allow_origin(origin, &None, host);
// then
assert_eq!(res, AllowCors::NotRequired);
......@@ -409,7 +412,7 @@ mod tests {
let host = None;
// when
let res = get_cors_allow_origin(origin, host, &None);
let res = get_cors_allow_origin(origin, &None, host);
// then
assert_eq!(res, AllowCors::Ok("parity.io".into()));
......@@ -422,11 +425,7 @@ mod tests {
let host = None;
// when
let res = get_cors_allow_origin(
origin,
host,
&Some(vec![AccessControlAllowOrigin::Value("http://ethereum.org".into())]),
);
let res = get_cors_allow_origin(origin, &Some(vec![AllowOrigin::Origin("http://ethereum.org".into())]), host);
// then
assert_eq!(res, AllowCors::NotRequired);
......@@ -439,7 +438,7 @@ mod tests {
let host = None;
// when
let res = get_cors_allow_origin(origin, host, &Some(Vec::new()));
let res = get_cors_allow_origin(origin, &Some(Vec::new()), host);
// then
assert_eq!(res, AllowCors::NotRequired);
......@@ -452,11 +451,7 @@ mod tests {
let host = None;
// when
let res = get_cors_allow_origin(
origin,
host,
&Some(vec![AccessControlAllowOrigin::Value("http://ethereum.org".into())]),
);
let res = get_cors_allow_origin(origin, &Some(vec![AllowOrigin::Origin("http://ethereum.org".into())]), host);
// then
assert_eq!(res, AllowCors::Invalid);
......@@ -469,10 +464,10 @@ mod tests {
let host = None;
// when
let res = get_cors_allow_origin(origin, host, &Some(vec![AccessControlAllowOrigin::Any]));
let res = get_cors_allow_origin(origin, &Some(vec![AllowOrigin::Any]), host);
// then
assert_eq!(res, AllowCors::Ok(AccessControlAllowOrigin::Value("http://parity.io".into())));
assert_eq!(res, AllowCors::Ok(AllowOrigin::Origin("http://parity.io".into())));
}
#[test]
......@@ -482,7 +477,7 @@ mod tests {
let host = None;
// when
let res = get_cors_allow_origin(origin, host, &Some(vec![AccessControlAllowOrigin::Null]));
let res = get_cors_allow_origin(origin, &Some(vec![AllowOrigin::Null]), host);
// then
assert_eq!(res, AllowCors::NotRequired);
......@@ -495,10 +490,10 @@ mod tests {
let host = None;
// when
let res = get_cors_allow_origin(origin, host, &Some(vec![AccessControlAllowOrigin::Null]));
let res = get_cors_allow_origin(origin, &Some(vec![AllowOrigin::Null]), host);
// then
assert_eq!(res, AllowCors::Ok(AccessControlAllowOrigin::Null));
assert_eq!(res, AllowCors::Ok(AllowOrigin::Null));
}
#[test]
......@@ -510,15 +505,15 @@ mod tests {
// when
let res = get_cors_allow_origin(
origin,
host,
&Some(vec![
AccessControlAllowOrigin::Value("http://ethereum.org".into()),
AccessControlAllowOrigin::Value("http://parity.io".into()),
AllowOrigin::Origin("http://ethereum.org".into()),
AllowOrigin::Origin("http://parity.io".into()),
]),
host,
);
// then
assert_eq!(res, AllowCors::Ok(AccessControlAllowOrigin::Value("http://parity.io".into())));
assert_eq!(res, AllowCors::Ok(AllowOrigin::Origin("http://parity.io".into())));
}
#[test]
......@@ -528,26 +523,24 @@ mod tests {
let origin2 = Some("http://parity.iot");
let origin3 = Some("chrome-extension://test");
let host = None;
let allowed = Some(vec![
AccessControlAllowOrigin::Value("http://*.io".into()),
AccessControlAllowOrigin::Value("chrome-extension://*".into()),
]);
let allowed =
Some(vec![AllowOrigin::Origin("http://*.io".into()), AllowOrigin::Origin("chrome-extension://*".into())]);
// when
let res1 = get_cors_allow_origin(origin1, host, &allowed);
let res2 = get_cors_allow_origin(origin2, host, &allowed);
let res3 = get_cors_allow_origin(origin3, host, &allowed);
let res1 = get_cors_allow_origin(origin1, &allowed, host);
let res2 = get_cors_allow_origin(origin2, &allowed, host);
let res3 = get_cors_allow_origin(origin3, &allowed, host);
// then
assert_eq!(res1, AllowCors::Ok(AccessControlAllowOrigin::Value("http://parity.io".into())));
assert_eq!(res1, AllowCors::Ok(AllowOrigin::Origin("http://parity.io".into())));
assert_eq!(res2, AllowCors::Invalid);
assert_eq!(res3, AllowCors::Ok(AccessControlAllowOrigin::Value("chrome-extension://test".into())));
assert_eq!(res3, AllowCors::Ok(AllowOrigin::Origin("chrome-extension://test".into())));
}
#[test]
fn should_return_invalid_if_header_not_allowed() {
// given
let cors_allow_headers = AccessControlAllowHeaders::Only(vec!["x-allowed".to_owned()]);
let cors_allow_headers = AllowHeaders::Only(vec!["x-allowed".to_owned()]);
let headers = vec!["Access-Control-Request-Headers"];
let requested = vec!["x-not-allowed"];
......@@ -562,7 +555,7 @@ mod tests {
fn should_return_valid_if_header_allowed() {
// given
let allowed = vec!["x-allowed".to_owned()];
let cors_allow_headers = AccessControlAllowHeaders::Only(allowed);
let cors_allow_headers = AllowHeaders::Only(allowed);