move common headers from actix-http to actix-web

This commit is contained in:
ibraheemdev 2021-03-19 17:02:34 -04:00
parent ec7cf44371
commit ed33fcc0f7
30 changed files with 89 additions and 78 deletions

View File

@ -97,6 +97,8 @@ either = "1.5.3"
encoding_rs = "0.8" encoding_rs = "0.8"
futures-core = { version = "0.3.7", default-features = false } futures-core = { version = "0.3.7", default-features = false }
futures-util = { version = "0.3.7", default-features = false } futures-util = { version = "0.3.7", default-features = false }
language-tags = "0.2"
once_cell = "1.5"
log = "0.4" log = "0.4"
mime = "0.3" mime = "0.3"
pin-project = "1.0.0" pin-project = "1.0.0"

View File

@ -14,13 +14,10 @@ use crate::HttpMessage;
mod as_name; mod as_name;
mod into_pair; mod into_pair;
mod into_value; mod into_value;
mod utils;
mod common;
pub(crate) mod map; pub(crate) mod map;
mod shared; mod shared;
pub use self::common::*;
#[doc(hidden)] #[doc(hidden)]
pub use self::shared::*; pub use self::shared::*;
@ -30,7 +27,6 @@ pub use self::into_value::IntoHeaderValue;
#[doc(hidden)] #[doc(hidden)]
pub use self::map::GetAll; pub use self::map::GetAll;
pub use self::map::HeaderMap; pub use self::map::HeaderMap;
pub use self::utils::*;
/// A trait for any object that already represents a valid header field and value. /// A trait for any object that already represents a valid header field and value.
pub trait Header: IntoHeaderValue { pub trait Header: IntoHeaderValue {
@ -41,17 +37,18 @@ pub trait Header: IntoHeaderValue {
fn parse<T: HttpMessage>(msg: &T) -> Result<Self, ParseError>; fn parse<T: HttpMessage>(msg: &T) -> Result<Self, ParseError>;
} }
#[doc(hidden)]
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub(crate) struct Writer { pub struct Writer {
buf: BytesMut, buf: BytesMut,
} }
impl Writer { impl Writer {
fn new() -> Writer { pub fn new() -> Writer {
Writer::default() Writer::default()
} }
fn take(&mut self) -> Bytes { pub fn take(&mut self) -> Bytes {
self.buf.split().freeze() self.buf.split().freeze()
} }
} }

View File

@ -4,7 +4,7 @@ use http::header::InvalidHeaderValue;
use crate::{ use crate::{
error::ParseError, error::ParseError,
header::{self, from_one_raw_str, Header, HeaderName, HeaderValue, IntoHeaderValue}, header::{self, Header, HeaderName, HeaderValue, IntoHeaderValue},
HttpMessage, HttpMessage,
}; };
@ -101,6 +101,14 @@ impl Header for ContentEncoding {
} }
fn parse<T: HttpMessage>(msg: &T) -> Result<Self, ParseError> { fn parse<T: HttpMessage>(msg: &T) -> Result<Self, ParseError> {
from_one_raw_str(msg.headers().get(Self::name())) let val = msg.headers().get(Self::name());
if let Some(line) = val {
let line = line.to_str().map_err(|_| ParseError::Header)?;
if !line.is_empty() {
return Self::from_str(line).or(Err(ParseError::Header));
}
}
Err(ParseError::Header)
} }
} }

View File

@ -6,6 +6,7 @@ mod entity;
mod extended; mod extended;
mod httpdate; mod httpdate;
mod quality_item; mod quality_item;
mod content_encoding;
pub use self::charset::Charset; pub use self::charset::Charset;
pub use self::encoding::Encoding; pub use self::encoding::Encoding;
@ -13,4 +14,5 @@ pub use self::entity::EntityTag;
pub use self::extended::{parse_extended_value, ExtendedValue}; pub use self::extended::{parse_extended_value, ExtendedValue};
pub use self::httpdate::HttpDate; pub use self::httpdate::HttpDate;
pub use self::quality_item::{q, qitem, Quality, QualityItem}; pub use self::quality_item::{q, qitem, Quality, QualityItem};
pub use self::content_encoding::ContentEncoding;
pub use language_tags::LanguageTag; pub use language_tags::LanguageTag;

View File

@ -682,7 +682,7 @@ impl ResponseBuilder {
}; };
if !contains { if !contains {
self.insert_header(header::ContentType(mime::APPLICATION_JSON)); self.insert_header((header::CONTENT_TYPE, mime::APPLICATION_JSON));
} }
self.body(Body::from(body)) self.body(Body::from(body))

View File

@ -102,7 +102,7 @@ pub mod web;
#[cfg(feature = "cookies")] #[cfg(feature = "cookies")]
pub use actix_http::cookie; pub use actix_http::cookie;
pub use actix_http::Response as HttpResponse; pub use actix_http::Response as HttpResponse;
pub use actix_http::{body, http, Error, HttpMessage, ResponseError, Result}; pub use actix_http::{body, Error, HttpMessage, ResponseError, Result};
pub use actix_rt as rt; pub use actix_rt as rt;
pub use actix_web_codegen::*; pub use actix_web_codegen::*;
@ -117,6 +117,11 @@ pub use crate::server::HttpServer;
// TODO: is exposing the error directly really needed // TODO: is exposing the error directly really needed
pub use crate::types::{Either, EitherExtractError}; pub use crate::types::{Either, EitherExtractError};
pub mod http {
pub use actix_http::http::*;
pub use crate::types::header;
}
pub mod dev { pub mod dev {
//! The `actix-web` prelude for library developers //! The `actix-web` prelude for library developers
//! //!

View File

@ -8,7 +8,6 @@ use std::{fmt, net, thread, time};
use actix_codec::{AsyncRead, AsyncWrite, Framed}; use actix_codec::{AsyncRead, AsyncWrite, Framed};
#[cfg(feature = "cookies")] #[cfg(feature = "cookies")]
use actix_http::cookie::Cookie; use actix_http::cookie::Cookie;
use actix_http::http::header::{ContentType, IntoHeaderPair};
use actix_http::http::{Method, StatusCode, Uri, Version}; use actix_http::http::{Method, StatusCode, Uri, Version};
use actix_http::test::TestRequest as HttpTestRequest; use actix_http::test::TestRequest as HttpTestRequest;
use actix_http::{ws, Extensions, HttpService, Request}; use actix_http::{ws, Extensions, HttpService, Request};
@ -34,6 +33,7 @@ use crate::dev::{Body, MessageBody, Payload, Server};
use crate::rmap::ResourceMap; use crate::rmap::ResourceMap;
use crate::service::{ServiceRequest, ServiceResponse}; use crate::service::{ServiceRequest, ServiceResponse};
use crate::{Error, HttpRequest, HttpResponse}; use crate::{Error, HttpRequest, HttpResponse};
use crate::http::header::{ContentType, IntoHeaderPair};
/// Create service that always responds with `HttpResponse::Ok()` /// Create service that always responds with `HttpResponse::Ok()`
pub fn ok_service( pub fn ok_service(

View File

@ -2,10 +2,10 @@ use std::cmp::Ordering;
use mime::Mime; use mime::Mime;
use crate::header::{qitem, QualityItem}; use super::{qitem, QualityItem};
use crate::http::header; use crate::http::header;
header! { crate::header! {
/// `Accept` header, defined in [RFC7231](http://tools.ietf.org/html/rfc7231#section-5.3.2) /// `Accept` header, defined in [RFC7231](http://tools.ietf.org/html/rfc7231#section-5.3.2)
/// ///
/// The `Accept` header field can be used by user agents to specify /// The `Accept` header field can be used by user agents to specify
@ -116,8 +116,8 @@ header! {
#[test] #[test]
fn test_fuzzing1() { fn test_fuzzing1() {
use crate::test::TestRequest; use actix_http::test::TestRequest;
let req = TestRequest::default().insert_header((crate::header::ACCEPT, "chunk#;e")).finish(); let req = TestRequest::default().insert_header((crate::http::header::ACCEPT, "chunk#;e")).finish();
let header = Accept::parse(&req); let header = Accept::parse(&req);
assert!(header.is_ok()); assert!(header.is_ok());
} }
@ -213,7 +213,7 @@ impl Accept {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::header::q; use crate::http::header::q;
#[test] #[test]
fn test_mime_precedence() { fn test_mime_precedence() {

View File

@ -1,6 +1,6 @@
use crate::header::{Charset, QualityItem, ACCEPT_CHARSET}; use super::{Charset, QualityItem, ACCEPT_CHARSET};
header! { crate::header! {
/// `Accept-Charset` header, defined in /// `Accept-Charset` header, defined in
/// [RFC7231](http://tools.ietf.org/html/rfc7231#section-5.3.3) /// [RFC7231](http://tools.ietf.org/html/rfc7231#section-5.3.3)
/// ///

View File

@ -1,7 +1,7 @@
use crate::header::{QualityItem, ACCEPT_LANGUAGE}; use super::{QualityItem, ACCEPT_LANGUAGE};
use language_tags::LanguageTag; use language_tags::LanguageTag;
header! { crate::header! {
/// `Accept-Language` header, defined in /// `Accept-Language` header, defined in
/// [RFC7231](http://tools.ietf.org/html/rfc7231#section-5.3.5) /// [RFC7231](http://tools.ietf.org/html/rfc7231#section-5.3.5)
/// ///

View File

@ -1,7 +1,7 @@
use http::header; use actix_http::http::Method;
use http::Method; use crate::http::header;
header! { crate::header! {
/// `Allow` header, defined in [RFC7231](http://tools.ietf.org/html/rfc7231#section-7.4.1) /// `Allow` header, defined in [RFC7231](http://tools.ietf.org/html/rfc7231#section-7.4.1)
/// ///
/// The `Allow` header field lists the set of methods advertised as /// The `Allow` header field lists the set of methods advertised as

View File

@ -1,12 +1,12 @@
use std::fmt::{self, Write}; use std::fmt::{self, Write};
use std::str::FromStr; use std::str::FromStr;
use http::header; use super::{
use crate::header::{
fmt_comma_delimited, from_comma_delimited, Header, IntoHeaderValue, Writer, fmt_comma_delimited, from_comma_delimited, Header, IntoHeaderValue, Writer,
}; };
use crate::http::header;
/// `Cache-Control` header, defined in [RFC7234](https://tools.ietf.org/html/rfc7234#section-5.2) /// `Cache-Control` header, defined in [RFC7234](https://tools.ietf.org/html/rfc7234#section-5.2)
/// ///
/// The `Cache-Control` header field is used to specify directives for /// The `Cache-Control` header field is used to specify directives for
@ -191,8 +191,8 @@ impl FromStr for CacheDirective {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::header::Header; use crate::http::header::Header;
use crate::test::TestRequest; use actix_http::test::TestRequest;
#[test] #[test]
fn test_parse_multiple_headers() { fn test_parse_multiple_headers() {

View File

@ -10,7 +10,8 @@ use once_cell::sync::Lazy;
use regex::Regex; use regex::Regex;
use std::fmt::{self, Write}; use std::fmt::{self, Write};
use crate::header::{self, ExtendedValue, Header, IntoHeaderValue, Writer}; use crate::http::header;
use super::{ExtendedValue, Header, IntoHeaderValue, Writer};
/// Split at the index of the first `needle` if it exists or at the end. /// Split at the index of the first `needle` if it exists or at the end.
fn split_once(haystack: &str, needle: char) -> (&str, &str) { fn split_once(haystack: &str, needle: char) -> (&str, &str) {
@ -554,8 +555,8 @@ impl fmt::Display for ContentDisposition {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::{ContentDisposition, DispositionParam, DispositionType}; use super::{ContentDisposition, DispositionParam, DispositionType};
use crate::header::shared::Charset; use crate::http::header::Charset;
use crate::header::{ExtendedValue, HeaderValue}; use crate::http::header::{ExtendedValue, HeaderValue};
#[test] #[test]
fn test_from_raw_basic() { fn test_from_raw_basic() {

View File

@ -1,7 +1,7 @@
use crate::header::{QualityItem, CONTENT_LANGUAGE}; use super::{QualityItem, CONTENT_LANGUAGE};
use language_tags::LanguageTag; use language_tags::LanguageTag;
header! { crate::header! {
/// `Content-Language` header, defined in /// `Content-Language` header, defined in
/// [RFC7231](https://tools.ietf.org/html/rfc7231#section-3.1.3.2) /// [RFC7231](https://tools.ietf.org/html/rfc7231#section-3.1.3.2)
/// ///

View File

@ -2,11 +2,11 @@ use std::fmt::{self, Display, Write};
use std::str::FromStr; use std::str::FromStr;
use crate::error::ParseError; use crate::error::ParseError;
use crate::header::{ use super::{
HeaderValue, IntoHeaderValue, InvalidHeaderValue, Writer, CONTENT_RANGE, HeaderValue, IntoHeaderValue, InvalidHeaderValue, Writer, CONTENT_RANGE,
}; };
header! { crate::header! {
/// `Content-Range` header, defined in /// `Content-Range` header, defined in
/// [RFC7233](http://tools.ietf.org/html/rfc7233#section-4.2) /// [RFC7233](http://tools.ietf.org/html/rfc7233#section-4.2)
(ContentRange, CONTENT_RANGE) => [ContentRangeSpec] (ContentRange, CONTENT_RANGE) => [ContentRangeSpec]

View File

@ -1,7 +1,7 @@
use crate::header::CONTENT_TYPE; use super::CONTENT_TYPE;
use mime::Mime; use mime::Mime;
header! { crate::header! {
/// `Content-Type` header, defined in /// `Content-Type` header, defined in
/// [RFC7231](http://tools.ietf.org/html/rfc7231#section-3.1.1.5) /// [RFC7231](http://tools.ietf.org/html/rfc7231#section-3.1.1.5)
/// ///

View File

@ -1,7 +1,7 @@
use crate::header::{HttpDate, DATE}; use super::{HttpDate, DATE};
use std::time::SystemTime; use std::time::SystemTime;
header! { crate::header! {
/// `Date` header, defined in [RFC7231](http://tools.ietf.org/html/rfc7231#section-7.1.1.2) /// `Date` header, defined in [RFC7231](http://tools.ietf.org/html/rfc7231#section-7.1.1.2)
/// ///
/// The `Date` header field represents the date and time at which the /// The `Date` header field represents the date and time at which the

View File

@ -1,6 +1,6 @@
use crate::header::{EntityTag, ETAG}; use super::{EntityTag, ETAG};
header! { crate::header! {
/// `ETag` header, defined in [RFC7232](http://tools.ietf.org/html/rfc7232#section-2.3) /// `ETag` header, defined in [RFC7232](http://tools.ietf.org/html/rfc7232#section-2.3)
/// ///
/// The `ETag` header field in a response provides the current entity-tag /// The `ETag` header field in a response provides the current entity-tag

View File

@ -1,6 +1,6 @@
use crate::header::{HttpDate, EXPIRES}; use super::{HttpDate, EXPIRES};
header! { crate::header! {
/// `Expires` header, defined in [RFC7234](http://tools.ietf.org/html/rfc7234#section-5.3) /// `Expires` header, defined in [RFC7234](http://tools.ietf.org/html/rfc7234#section-5.3)
/// ///
/// The `Expires` header field gives the date/time after which the /// The `Expires` header field gives the date/time after which the

View File

@ -1,6 +1,6 @@
use crate::header::{EntityTag, IF_MATCH}; use super::{EntityTag, IF_MATCH};
header! { crate::header! {
/// `If-Match` header, defined in /// `If-Match` header, defined in
/// [RFC7232](https://tools.ietf.org/html/rfc7232#section-3.1) /// [RFC7232](https://tools.ietf.org/html/rfc7232#section-3.1)
/// ///

View File

@ -1,6 +1,6 @@
use crate::header::{HttpDate, IF_MODIFIED_SINCE}; use super::{HttpDate, IF_MODIFIED_SINCE};
header! { crate::header! {
/// `If-Modified-Since` header, defined in /// `If-Modified-Since` header, defined in
/// [RFC7232](http://tools.ietf.org/html/rfc7232#section-3.3) /// [RFC7232](http://tools.ietf.org/html/rfc7232#section-3.3)
/// ///

View File

@ -1,6 +1,6 @@
use crate::header::{EntityTag, IF_NONE_MATCH}; use super::{EntityTag, IF_NONE_MATCH};
header! { crate::header! {
/// `If-None-Match` header, defined in /// `If-None-Match` header, defined in
/// [RFC7232](https://tools.ietf.org/html/rfc7232#section-3.2) /// [RFC7232](https://tools.ietf.org/html/rfc7232#section-3.2)
/// ///
@ -66,8 +66,8 @@ header! {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::IfNoneMatch; use super::IfNoneMatch;
use crate::header::{EntityTag, Header, IF_NONE_MATCH}; use crate::http::header::{EntityTag, Header, IF_NONE_MATCH};
use crate::test::TestRequest; use actix_http::test::TestRequest;
#[test] #[test]
fn test_if_none_match() { fn test_if_none_match() {

View File

@ -1,8 +1,9 @@
use std::fmt::{self, Display, Write}; use std::fmt::{self, Display, Write};
use crate::http::header;
use crate::error::ParseError; use crate::error::ParseError;
use crate::header::{ use super::{
self, from_one_raw_str, EntityTag, Header, HeaderName, HeaderValue, HttpDate, from_one_raw_str, EntityTag, Header, HeaderName, HeaderValue, HttpDate,
IntoHeaderValue, InvalidHeaderValue, Writer, IntoHeaderValue, InvalidHeaderValue, Writer,
}; };
use crate::HttpMessage; use crate::HttpMessage;
@ -111,7 +112,7 @@ impl IntoHeaderValue for IfRange {
#[cfg(test)] #[cfg(test)]
mod test_if_range { mod test_if_range {
use super::IfRange as HeaderField; use super::IfRange as HeaderField;
use crate::header::*; use crate::http::header::*;
use std::str; use std::str;
test_header!(test1, vec![b"Sat, 29 Oct 1994 19:43:31 GMT"]); test_header!(test1, vec![b"Sat, 29 Oct 1994 19:43:31 GMT"]);

View File

@ -1,6 +1,6 @@
use crate::header::{HttpDate, IF_UNMODIFIED_SINCE}; use super::{HttpDate, IF_UNMODIFIED_SINCE};
header! { crate::header! {
/// `If-Unmodified-Since` header, defined in /// `If-Unmodified-Since` header, defined in
/// [RFC7232](http://tools.ietf.org/html/rfc7232#section-3.4) /// [RFC7232](http://tools.ietf.org/html/rfc7232#section-3.4)
/// ///

View File

@ -1,6 +1,6 @@
use crate::header::{HttpDate, LAST_MODIFIED}; use super::{HttpDate, LAST_MODIFIED};
header! { crate::header! {
/// `Last-Modified` header, defined in /// `Last-Modified` header, defined in
/// [RFC7232](http://tools.ietf.org/html/rfc7232#section-2.2) /// [RFC7232](http://tools.ietf.org/html/rfc7232#section-2.2)
/// ///

View File

@ -7,6 +7,7 @@
//! is used, such as `ContentType(pub Mime)`. //! is used, such as `ContentType(pub Mime)`.
#![cfg_attr(rustfmt, rustfmt_skip)] #![cfg_attr(rustfmt, rustfmt_skip)]
pub use actix_http::http::header::*;
pub use self::accept_charset::AcceptCharset; pub use self::accept_charset::AcceptCharset;
//pub use self::accept_encoding::AcceptEncoding; //pub use self::accept_encoding::AcceptEncoding;
pub use self::accept::Accept; pub use self::accept::Accept;
@ -18,7 +19,6 @@ pub use self::content_disposition::{
}; };
pub use self::content_language::ContentLanguage; pub use self::content_language::ContentLanguage;
pub use self::content_range::{ContentRange, ContentRangeSpec}; pub use self::content_range::{ContentRange, ContentRangeSpec};
pub use self::content_encoding::{ContentEncoding};
pub use self::content_type::ContentType; pub use self::content_type::ContentType;
pub use self::date::Date; pub use self::date::Date;
pub use self::etag::ETag; pub use self::etag::ETag;
@ -30,6 +30,7 @@ pub use self::if_range::IfRange;
pub use self::if_unmodified_since::IfUnmodifiedSince; pub use self::if_unmodified_since::IfUnmodifiedSince;
pub use self::last_modified::LastModified; pub use self::last_modified::LastModified;
//pub use self::range::{Range, ByteRangeSpec}; //pub use self::range::{Range, ByteRangeSpec};
pub(crate) use self::utils::{fmt_comma_delimited, from_comma_delimited, from_one_raw_str};
#[doc(hidden)] #[doc(hidden)]
#[macro_export] #[macro_export]
@ -61,9 +62,9 @@ macro_rules! __hyper__tm {
#[cfg(test)] #[cfg(test)]
mod $tm{ mod $tm{
use std::str; use std::str;
use http::Method; use actix_http::http::Method;
use mime::*; use mime::*;
use $crate::header::*; use $crate::http::header::*;
use super::$id as HeaderField; use super::$id as HeaderField;
$($tf)* $($tf)*
} }
@ -77,8 +78,8 @@ macro_rules! test_header {
($id:ident, $raw:expr) => { ($id:ident, $raw:expr) => {
#[test] #[test]
fn $id() { fn $id() {
use super::*; use actix_http::http::header::Header;
use $crate::test; use actix_http::test;
let raw = $raw; let raw = $raw;
let a: Vec<Vec<u8>> = raw.iter().map(|x| x.to_vec()).collect(); let a: Vec<Vec<u8>> = raw.iter().map(|x| x.to_vec()).collect();
@ -106,7 +107,7 @@ macro_rules! test_header {
($id:ident, $raw:expr, $typed:expr) => { ($id:ident, $raw:expr, $typed:expr) => {
#[test] #[test]
fn $id() { fn $id() {
use $crate::test; use actix_http::test;
let a: Vec<Vec<u8>> = $raw.iter().map(|x| x.to_vec()).collect(); let a: Vec<Vec<u8>> = $raw.iter().map(|x| x.to_vec()).collect();
let mut req = test::TestRequest::default(); let mut req = test::TestRequest::default();
@ -134,6 +135,7 @@ macro_rules! test_header {
}; };
} }
#[doc(hidden)]
#[macro_export] #[macro_export]
macro_rules! header { macro_rules! header {
// $a:meta: Attributes associated with the header item (usually docs) // $a:meta: Attributes associated with the header item (usually docs)
@ -341,7 +343,6 @@ mod allow;
mod cache_control; mod cache_control;
mod content_disposition; mod content_disposition;
mod content_language; mod content_language;
mod content_encoding;
mod content_range; mod content_range;
mod content_type; mod content_type;
mod date; mod date;
@ -353,3 +354,4 @@ mod if_none_match;
mod if_range; mod if_range;
mod if_unmodified_since; mod if_unmodified_since;
mod last_modified; mod last_modified;
mod utils;

View File

@ -1,8 +1,8 @@
use std::fmt::{self, Display}; use std::fmt::{self, Display};
use std::str::FromStr; use std::str::FromStr;
use header::parsing::from_one_raw_str; use super::parsing::from_one_raw_str;
use header::{Header, Raw}; use super::{Header, Raw};
/// `Range` header, defined in [RFC7233](https://tools.ietf.org/html/rfc7233#section-3.1) /// `Range` header, defined in [RFC7233](https://tools.ietf.org/html/rfc7233#section-3.1)
/// ///

View File

@ -1,8 +1,7 @@
use std::{fmt, str::FromStr}; use std::{fmt, str::FromStr};
use http::HeaderValue; use super::HeaderValue;
use crate::error::ParseError;
use crate::{error::ParseError, header::HTTP_VALUE};
/// Reads a comma-delimited raw header into a Vec. /// Reads a comma-delimited raw header into a Vec.
#[inline] #[inline]
@ -54,10 +53,3 @@ where
} }
Ok(()) Ok(())
} }
/// Percent encode a sequence of bytes with a character set defined in
/// <https://tools.ietf.org/html/rfc5987#section-3.2>
pub fn http_percent_encode(f: &mut fmt::Formatter<'_>, bytes: &[u8]) -> fmt::Result {
let encoded = percent_encoding::percent_encode(bytes, HTTP_VALUE);
fmt::Display::fmt(&encoded, f)
}

View File

@ -3,6 +3,7 @@
// TODO: review visibility // TODO: review visibility
mod either; mod either;
pub(crate) mod form; pub(crate) mod form;
pub mod header;
pub(crate) mod json; pub(crate) mod json;
mod path; mod path;
pub(crate) mod payload; pub(crate) mod payload;