use crate::handle::BodySource;
use crate::types::{OwnedHead, Version};
use std::io::Read;
use std::net::SocketAddr;
use std::sync::Arc;
#[derive(Clone, Copy)]
pub(crate) enum HeaderView<'a> {
#[cfg(feature = "http1")]
H1(&'a [simoom_http1::Header<'a>]),
Owned(&'a OwnedHead),
}
pub(crate) type Param = (Arc<str>, u32, u32);
#[derive(Clone, Debug, Default)]
pub(crate) struct Meta {
pub(crate) local: Addr,
pub(crate) listener: u32,
#[cfg(feature = "tls")]
pub(crate) tls: Option<Arc<TlsInfo>>,
}
impl Meta {
pub(crate) fn new(local: Option<SocketAddr>, listener: u32) -> Meta {
Meta {
local: Addr::new(local),
listener,
#[cfg(feature = "tls")]
tls: None,
}
}
fn tls(&self) -> Option<&TlsInfo> {
#[cfg(feature = "tls")]
{
self.tls.as_deref()
}
#[cfg(not(feature = "tls"))]
{
None
}
}
}
#[derive(Clone, Debug, Default)]
pub(crate) enum Addr {
#[default]
None,
V4(std::net::Ipv4Addr, u16),
V6(Box<std::net::SocketAddrV6>),
}
impl Addr {
pub(crate) fn new(a: Option<SocketAddr>) -> Addr {
match a {
None => Addr::None,
Some(SocketAddr::V4(a)) => Addr::V4(*a.ip(), a.port()),
Some(SocketAddr::V6(a)) => Addr::V6(Box::new(a)),
}
}
#[inline]
pub(crate) fn get(&self) -> Option<SocketAddr> {
match self {
Addr::None => None,
Addr::V4(ip, port) => Some(SocketAddr::from((*ip, *port))),
Addr::V6(a) => Some(SocketAddr::V6(**a)),
}
}
}
#[derive(Clone, Debug, Default)]
pub struct TlsInfo {
pub(crate) cipher: u16,
pub(crate) sni: Option<Box<str>>,
pub(crate) alpn: Option<Box<[u8]>>,
pub(crate) peer_certs: Vec<Vec<u8>>,
}
impl TlsInfo {
pub fn version(&self) -> u16 {
0x0304
}
pub fn version_name(&self) -> &'static str {
"TLSv1.3"
}
pub fn cipher_suite(&self) -> u16 {
self.cipher
}
pub fn cipher_suite_name(&self) -> &'static str {
match self.cipher {
0x1301 => "TLS_AES_128_GCM_SHA256",
0x1302 => "TLS_AES_256_GCM_SHA384",
0x1303 => "TLS_CHACHA20_POLY1305_SHA256",
_ => "",
}
}
pub fn server_name(&self) -> Option<&str> {
self.sni.as_deref()
}
pub fn alpn(&self) -> Option<&[u8]> {
self.alpn.as_deref()
}
pub fn peer_certificates(&self) -> &[Vec<u8>] {
&self.peer_certs
}
#[cfg(feature = "tls")]
pub(crate) fn of(c: &simoom_tls::Connection) -> TlsInfo {
TlsInfo {
cipher: c.negotiated_cipher_suite().map_or(0, |s| s.id()),
sni: c.server_name().map(Into::into),
alpn: c.alpn_protocol().map(Into::into),
peer_certs: c.peer_certificates().to_vec(),
}
}
}
pub struct Request<'a> {
pub(crate) method: &'a [u8],
pub(crate) target: &'a [u8],
pub(crate) version: Version,
pub(crate) authority: Option<&'a [u8]>,
pub(crate) headers: HeaderView<'a>,
pub(crate) body: &'a [u8],
pub(crate) trailers: Option<&'a OwnedHead>,
pub(crate) source: Option<BodySource>,
pub(crate) streamed: bool,
pub(crate) peer: Option<SocketAddr>,
pub(crate) secure: bool,
pub(crate) params: Vec<Param>,
pub(crate) route: u32,
pub(crate) sample: Option<crate::watchdog::Sample>,
pub(crate) websocket: bool,
pub(crate) loop_index: usize,
pub(crate) meta: Option<&'a Meta>,
}
impl std::fmt::Debug for Request<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Request")
.field("method", &String::from_utf8_lossy(self.method))
.field("target", &String::from_utf8_lossy(self.target))
.field("version", &self.version)
.finish()
}
}
fn path_of(target: &[u8]) -> &[u8] {
match target.iter().position(|&b| b == b'?') {
Some(i) => &target[..i],
None => target,
}
}
fn query_of(target: &[u8]) -> Option<&[u8]> {
target.iter().position(|&b| b == b'?').map(|i| &target[i + 1..])
}
fn query_param<'q>(query: &'q [u8], name: &str) -> Option<&'q [u8]> {
query.split(|&b| b == b'&').find_map(|kv| {
let (k, v) = match kv.iter().position(|&b| b == b'=') {
Some(i) => (&kv[..i], &kv[i + 1..]),
None => (kv, &kv[kv.len()..]),
};
(k == name.as_bytes()).then_some(v)
})
}
impl<'a> Request<'a> {
pub(crate) fn new(method: &'a [u8], target: &'a [u8], version: Version, headers: HeaderView<'a>) -> Request<'a> {
Request {
method,
target,
version,
authority: None,
headers,
body: b"",
trailers: None,
source: None,
streamed: false,
peer: None,
secure: false,
params: Vec::new(),
route: 0,
sample: None,
websocket: false,
loop_index: 0,
meta: None,
}
}
pub fn method(&self) -> &'a [u8] {
self.method
}
pub fn target(&self) -> &'a [u8] {
self.target
}
pub fn path(&self) -> &'a [u8] {
path_of(self.target)
}
pub fn path_str(&self) -> &'a str {
std::str::from_utf8(self.path()).unwrap_or("")
}
pub fn query(&self) -> Option<&'a [u8]> {
query_of(self.target)
}
pub fn query_param(&self, name: &str) -> Option<&'a [u8]> {
query_param(self.query()?, name)
}
pub fn version(&self) -> Version {
self.version
}
pub fn host(&self) -> Option<&'a [u8]> {
self.authority.or_else(|| self.header("host"))
}
pub fn header(&self, name: &str) -> Option<&'a [u8]> {
let name = name.as_bytes();
match self.headers {
#[cfg(feature = "http1")]
HeaderView::H1(h) => h.iter().find(|h| h.name.eq_ignore_ascii_case(name)).map(|h| h.value),
HeaderView::Owned(o) => (0..o.fields.len()).map(|i| o.field(i)).find(|(n, _)| n.eq_ignore_ascii_case(name)).map(|(_, v)| v),
}
}
pub fn headers(&self) -> impl Iterator<Item = (&'a [u8], &'a [u8])> + 'a {
let view = self.headers;
let n = match view {
#[cfg(feature = "http1")]
HeaderView::H1(h) => h.len(),
HeaderView::Owned(o) => o.fields.len(),
};
(0..n).map(move |i| match view {
#[cfg(feature = "http1")]
HeaderView::H1(h) => (h[i].name, h[i].value),
HeaderView::Owned(o) => o.field(i),
})
}
pub fn body(&self) -> &'a [u8] {
self.body
}
pub fn is_streamed(&self) -> bool {
self.streamed
}
pub fn trailers(&self) -> impl Iterator<Item = (&'a [u8], &'a [u8])> + 'a {
let t = self.trailers;
(0..t.map_or(0, |t| t.fields.len())).map(move |i| t.expect("counted").field(i))
}
pub fn trailer(&self, name: &str) -> Option<&'a [u8]> {
self.trailers().find(|(n, _)| n.eq_ignore_ascii_case(name.as_bytes())).map(|(_, v)| v)
}
pub fn take_body(&mut self) -> Option<BodySource> {
self.source.take()
}
pub fn param(&self, name: &str) -> Option<&'a [u8]> {
let t = self.target;
self.params.iter().find(|p| &*p.0 == name).map(|p| &t[p.1 as usize..p.2 as usize])
}
pub fn param_str(&self, name: &str) -> Option<&'a str> {
self.param(name).and_then(|p| std::str::from_utf8(p).ok())
}
pub fn peer_addr(&self) -> Option<SocketAddr> {
self.peer
}
pub fn is_secure(&self) -> bool {
self.secure
}
pub fn is_websocket_upgrade(&self) -> bool {
self.websocket
}
pub fn loop_index(&self) -> usize {
self.loop_index
}
pub fn local_addr(&self) -> Option<SocketAddr> {
self.meta.and_then(|m| m.local.get())
}
pub fn listener(&self) -> usize {
self.meta.map_or(0, |m| m.listener as usize)
}
pub fn tls(&self) -> Option<&'a TlsInfo> {
self.meta.and_then(|m| m.tls())
}
pub fn to_owned_request(&mut self) -> OwnedRequest {
let mut head = OwnedHead::default();
head.buf.reserve(self.target.len() + 64);
head.set_method(self.method);
head.set_target(self.target);
if let Some(a) = self.authority {
head.set_authority(a);
}
for (n, v) in self.headers() {
head.add_field(n, v);
}
let trailers = self.trailers.cloned();
OwnedRequest {
head,
version: self.version,
body: self.body.to_vec(),
trailers,
source: self.source.take(),
streamed: self.streamed,
peer: self.peer,
secure: self.secure,
params: self.params.clone(),
route: self.route,
websocket: false,
loop_index: self.loop_index,
meta: self.meta.cloned().unwrap_or_default(),
}
}
}
#[derive(Debug)]
pub struct OwnedRequest {
pub(crate) head: OwnedHead,
pub(crate) version: Version,
pub(crate) body: Vec<u8>,
pub(crate) trailers: Option<OwnedHead>,
pub(crate) source: Option<BodySource>,
pub(crate) streamed: bool,
pub(crate) peer: Option<SocketAddr>,
pub(crate) secure: bool,
pub(crate) params: Vec<Param>,
pub(crate) route: u32,
pub(crate) websocket: bool,
pub(crate) loop_index: usize,
pub(crate) meta: Meta,
}
impl OwnedRequest {
pub fn method(&self) -> &[u8] {
self.head.method()
}
pub fn target(&self) -> &[u8] {
self.head.target()
}
pub fn path(&self) -> &[u8] {
path_of(self.head.target())
}
pub fn path_str(&self) -> &str {
std::str::from_utf8(self.path()).unwrap_or("")
}
pub fn query(&self) -> Option<&[u8]> {
query_of(self.head.target())
}
pub fn query_param(&self, name: &str) -> Option<&[u8]> {
query_param(self.query()?, name)
}
pub fn version(&self) -> Version {
self.version
}
pub fn header(&self, name: &str) -> Option<&[u8]> {
(0..self.head.fields.len()).map(|i| self.head.field(i)).find(|(n, _)| n.eq_ignore_ascii_case(name.as_bytes())).map(|(_, v)| v)
}
pub fn headers(&self) -> impl Iterator<Item = (&[u8], &[u8])> + '_ {
(0..self.head.fields.len()).map(|i| self.head.field(i))
}
pub fn host(&self) -> Option<&[u8]> {
self.head.authority().or_else(|| self.header("host"))
}
pub fn param(&self, name: &str) -> Option<&[u8]> {
let t = self.head.target();
self.params.iter().find(|p| &*p.0 == name).map(|p| &t[p.1 as usize..p.2 as usize])
}
pub fn peer_addr(&self) -> Option<SocketAddr> {
self.peer
}
pub fn is_secure(&self) -> bool {
self.secure
}
pub fn local_addr(&self) -> Option<SocketAddr> {
self.meta.local.get()
}
pub fn listener(&self) -> usize {
self.meta.listener as usize
}
pub fn tls(&self) -> Option<&TlsInfo> {
self.meta.tls()
}
pub fn body(&self) -> &[u8] {
&self.body
}
pub fn is_streamed(&self) -> bool {
self.streamed
}
pub fn take_body(&mut self) -> Option<BodySource> {
self.source.take()
}
pub fn trailers(&self) -> impl Iterator<Item = (&[u8], &[u8])> + '_ {
let t = self.trailers.as_ref();
(0..t.map_or(0, |t| t.fields.len())).map(move |i| t.expect("counted").field(i))
}
pub fn read_body(&mut self) -> std::io::Result<Vec<u8>> {
match self.source.take() {
None => Ok(std::mem::take(&mut self.body)),
Some(mut s) => {
let mut v = std::mem::take(&mut self.body);
s.read_to_end(&mut v)?;
Ok(v)
}
}
}
pub(crate) fn as_request(&mut self) -> Request<'_> {
Request {
method: self.head.method(),
target: self.head.target(),
version: self.version,
authority: self.head.authority(),
headers: HeaderView::Owned(&self.head),
body: &self.body,
trailers: self.trailers.as_ref(),
source: self.source.take(),
streamed: self.streamed,
peer: self.peer,
secure: self.secure,
params: std::mem::take(&mut self.params),
route: self.route,
sample: None,
websocket: self.websocket,
loop_index: self.loop_index,
meta: Some(&self.meta),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn path_query_and_params() {
let mut h = OwnedHead::default();
h.set_method(b"GET");
h.set_target(b"/a/b?x=1&y=two&z");
h.add_field(b"Host", b"example.com");
let mut r = Request::new(b"GET", b"/a/b?x=1&y=two&z", Version::Http11, HeaderView::Owned(&h));
assert_eq!(r.path(), b"/a/b");
assert_eq!(r.query_param("y"), Some(&b"two"[..]));
assert_eq!(r.query_param("z"), Some(&b""[..]));
assert_eq!(r.query_param("w"), None);
assert_eq!(r.header("HOST"), Some(&b"example.com"[..]));
assert_eq!(r.host(), Some(&b"example.com"[..]));
r.params.push((Arc::from("p"), 3, 4));
assert_eq!(r.param("p"), Some(&b"b"[..]));
let o = r.to_owned_request();
assert_eq!(o.path_str(), "/a/b");
assert_eq!(o.param("p"), Some(&b"b"[..]));
assert_eq!(o.headers().count(), 1);
}
}