/// Gets the value from the first instance of the field. #[must_use] pubfn get(&self, n: &[u8]) -> Option<&[u8]> { self.get_all(n).next()
}
/// Gets all of the values of the named field. pubfn get_all<'a, 'b>(&'a self, n: &'b [u8]) -> impl Iterator<Item = &'a [u8]> + 'b where 'a: 'b,
{ self.0.iter().filter_map(move |f| { if &f.name[..] == n {
Some(&f.value[..])
} else {
None
}
})
}
#[must_use] pubfn is_chunked(&self) -> bool { // Look at the last symbol in Transfer-Encoding. // This is very primitive decoding; structured field this is not. iflet Some(te) = self.get(TRANSFER_ENCODING) { letmut slc = te; whilelet Some(i) = index_of(COMMA, slc) {
slc = trim_ows(&slc[i + 1..]);
}
slc == CHUNKED
} else { false
}
}
/// As required by the HTTP specification, remove the Connection header /// field, everything it refers to, and a few extra fields. #[cfg(feature = "http")] fn strip_connection_headers(&mutself) { const CONNECTION: &[u8] = b"connection"; const PROXY_CONNECTION: &[u8] = b"proxy-connection"; const SHOULD_REMOVE: &[&[u8]] = &[
CONNECTION,
PROXY_CONNECTION,
b"keep-alive",
b"te",
b"trailer",
b"transfer-encoding",
b"upgrade",
]; letmut listed = Vec::new(); letmut track = |n| { letmut name = Vec::from(trim_ows(n));
downcase(&mut name); if !listed.contains(&name) {
listed.push(name);
}
};
for f inself
.0
.iter()
.filter(|f| f.name() == CONNECTION || f.name == PROXY_CONNECTION)
{ letmut v = f.value(); whilelet Some(i) = index_of(COMMA, v) {
track(&v[..i]);
v = &v[i + 1..];
}
track(v);
}
#[cfg(feature = "http")] fn parse_line(fields: &mut Vec<Field>, line: Vec<u8>) -> Res<()> { // obs-fold is helpful in specs, so support it here too let f = if is_ows(line[0]) { letmut e = fields.pop().ok_or(Error::ObsFold)?;
e.obs_fold(&line);
e
} elseiflet Some((n, v)) = split_at(COLON, line) { letmut name = Vec::from(trim_ows(&n));
downcase(&mut name); let value = Vec::from(trim_ows(&v));
Field::new(name, value)
} else { return Err(Error::Missing(COLON));
};
fields.push(f);
Ok(())
}
/// Control data for an HTTP message, either request or response. pubenum ControlData {
Request {
method: Vec<u8>,
scheme: Vec<u8>,
authority: Vec<u8>,
path: Vec<u8>,
},
Response(StatusCode),
}
#[cfg(feature = "http")] pubfn read_http(line: Vec<u8>) -> Res<Self> { // request-line = method SP request-target SP HTTP-version // status-line = HTTP-version SP status-code SP [reason-phrase] let (a, r) = split_at(SP, line).ok_or(Error::Missing(SP))?; let (b, _) = split_at(SP, r).ok_or(Error::Missing(SP))?; if index_of(SLASH, &a).is_some() { // Probably a response, so treat it as such. let status_str = String::from_utf8(b)?; let code = StatusCode::try_from(status_str.parse::<u64>()?)?;
Ok(Self::Response(code))
} elseif index_of(COLON, &b).is_some() { // Now try to parse the URL. let url_str = String::from_utf8(b)?; let parsed = Url::parse(&url_str)?; let authority = parsed.host_str().map_or_else(String::new, |host| { letmut authority = String::from(host); iflet Some(port) = parsed.port() {
authority.push(':');
authority.push_str(&port.to_string());
}
authority
}); letmut path = String::from(parsed.path()); iflet Some(q) = parsed.query() {
path.push('?');
path.push_str(q);
}
Ok(Self::Request {
method: a,
scheme: Vec::from(parsed.scheme().as_bytes()),
authority: Vec::from(authority.as_bytes()),
path: Vec::from(path.as_bytes()),
})
} else { if a == b"CONNECT" { return Err(Error::ConnectUnsupported);
}
Ok(Self::Request {
method: a,
scheme: Vec::from(&b"https"[..]),
authority: Vec::new(),
path: b,
})
}
}
pubfn read_bhttp<T, R>(request: bool, r: &mut T) -> Res<Self> where
T: BorrowMut<R> + ?Sized,
R: ReadSeek + ?Sized,
{ let v = if request { let method = read_vec(r)?.ok_or(Error::Truncated)?; let scheme = read_vec(r)?.ok_or(Error::Truncated)?; let authority = read_vec(r)?.ok_or(Error::Truncated)?; let path = read_vec(r)?.ok_or(Error::Truncated)?; Self::Request {
method,
scheme,
authority,
path,
}
} else { Self::Response(StatusCode::try_from(
read_varint(r)?.ok_or(Error::Truncated)?,
)?)
};
Ok(v)
}
/// If this is an informational response. #[must_use] fn informational(&self) -> Option<StatusCode> { matchself { Self::Response(v) if v.informational() => Some(*v),
_ => None,
}
}
/// An HTTP message, either request or response, /// including any optional informational responses on a response. pubstruct Message {
informational: Vec<InformationalResponse>,
header: Header,
content: Vec<u8>,
trailer: FieldSection,
}
/// Set a header field value. pubfn put_header(&mutself, name: impl Into<Vec<u8>>, value: impl Into<Vec<u8>>) { self.header.put(name, value);
}
/// Set a trailer field value. pubfn put_trailer(&mutself, name: impl Into<Vec<u8>>, value: impl Into<Vec<u8>>) { self.trailer.put(name, value);
}
/// Extend the content of the message with the given bytes. pubfn write_content(&mutself, d: impl AsRef<[u8]>) { self.content.extend_from_slice(d.as_ref());
}
/// Read an HTTP/1.1 message. #[cfg(feature = "http")] #[allow(clippy::read_zero_byte_vec)] // https://github.com/rust-lang/rust-clippy/issues/9274 pubfn read_http<T, R>(r: &mut T) -> Res<Self> where
T: BorrowMut<R> + ?Sized,
R: ReadSeek + ?Sized,
{ let line = read_line(r)?; letmut control = ControlData::read_http(line)?; letmut informational = Vec::new(); whilelet Some(status) = control.informational() { let fields = FieldSection::read_http(r)?;
informational.push(InformationalResponse::new(status, fields)); let line = read_line(r)?;
control = ControlData::read_http(line)?;
}
letmut hfields = FieldSection::read_http(r)?;
let (content, trailer) = if matches!(control.status().map(StatusCode::code), Some(204 | 304)) { // 204 and 304 have no body, no matter what Content-Length says. // Unfortunately, we can't do the same for responses to HEAD.
(Vec::new(), FieldSection::default())
} elseif hfields.is_chunked() { let content = Self::read_chunked(r)?; let trailer = FieldSection::read_http(r)?;
(content, trailer)
} else { letmut content = Vec::new(); iflet Some(cl) = hfields.get(CONTENT_LENGTH) { let cl_str = String::from_utf8(Vec::from(cl))?; let cl_int = cl_str.parse::<usize>()?; if cl_int > 0 {
content.resize(cl_int, 0);
r.borrow_mut().read_exact(&mut content)?;
}
} else { // Note that for a request, the spec states that the content is // empty, but this just reads all input like for a response.
r.borrow_mut().read_to_end(&mut content)?;
}
(content, FieldSection::default())
};
/// Read a BHTTP message. pubfn read_bhttp<T, R>(r: &mut T) -> Res<Self> where
T: BorrowMut<R> + ?Sized,
R: ReadSeek + ?Sized,
{ let t = read_varint(r)?.ok_or(Error::Truncated)?; let request = t == 0 || t == 2; let mode = Mode::try_from(t)?;
letmut control = ControlData::read_bhttp(request, r)?; letmut informational = Vec::new(); whilelet Some(status) = control.informational() { let fields = FieldSection::read_bhttp(mode, r)?;
informational.push(InformationalResponse::new(status, fields));
control = ControlData::read_bhttp(request, r)?;
} let hfields = FieldSection::read_bhttp(mode, r)?;
letmut content = read_vec(r)?.unwrap_or_default(); if mode == Mode::IndeterminateLength && !content.is_empty() { loop { letmut extra = read_vec(r)?.unwrap_or_default(); if extra.is_empty() { break;
}
content.append(&mut extra);
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.