#[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(())
}
#[cfg(feature = "http")] pub fn write_http(&self, w: &mutimpl io::Write) -> Res<()> {
for f in &self.0 {
f.write_http(w)?;
}
w.write_all(b"\r\n")?;
Ok(())
}
}
#[cfg(test)] impl std::fmt::Debug for FieldSection {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
for fv in self.fields() {
fv.fmt(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")] pub fn 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,
})
}
}
pub fn 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. pub fn put_header(&mutself, name: impl Into<Vec<u8>>, value: impl Into<Vec<u8>>) { self.header.put(name, value);
}
/// Set a trailer field value. pub fn 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. pub fn 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 pub fn 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())
};
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.