h2::Error now knows whether protocol errors happened because the user sent them, because it was received from the remote peer, or because the library itself emitted an error because it detected a protocol violation. It also keeps track of whether it came from a RST_STREAM or GO_AWAY frame, and in the case of the latter, it includes the additional debug data if any. Fixes #530
94 lines
2.5 KiB
Rust
94 lines
2.5 KiB
Rust
use crate::error::Reason;
|
|
use crate::frame::{Pseudo, StreamId};
|
|
use crate::proto::{Error, Open};
|
|
|
|
use http::{HeaderMap, Request, Response};
|
|
|
|
use std::fmt;
|
|
|
|
/// Either a Client or a Server
|
|
pub(crate) trait Peer {
|
|
/// Message type polled from the transport
|
|
type Poll: fmt::Debug;
|
|
const NAME: &'static str;
|
|
|
|
fn r#dyn() -> Dyn;
|
|
|
|
fn is_server() -> bool;
|
|
|
|
fn convert_poll_message(
|
|
pseudo: Pseudo,
|
|
fields: HeaderMap,
|
|
stream_id: StreamId,
|
|
) -> Result<Self::Poll, Error>;
|
|
|
|
fn is_local_init(id: StreamId) -> bool {
|
|
assert!(!id.is_zero());
|
|
Self::is_server() == id.is_server_initiated()
|
|
}
|
|
}
|
|
|
|
/// A dynamic representation of `Peer`.
|
|
///
|
|
/// This is used internally to avoid incurring a generic on all internal types.
|
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
|
pub(crate) enum Dyn {
|
|
Client,
|
|
Server,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub enum PollMessage {
|
|
Client(Response<()>),
|
|
Server(Request<()>),
|
|
}
|
|
|
|
// ===== impl Dyn =====
|
|
|
|
impl Dyn {
|
|
pub fn is_server(&self) -> bool {
|
|
*self == Dyn::Server
|
|
}
|
|
|
|
pub fn is_local_init(&self, id: StreamId) -> bool {
|
|
assert!(!id.is_zero());
|
|
self.is_server() == id.is_server_initiated()
|
|
}
|
|
|
|
pub fn convert_poll_message(
|
|
&self,
|
|
pseudo: Pseudo,
|
|
fields: HeaderMap,
|
|
stream_id: StreamId,
|
|
) -> Result<PollMessage, Error> {
|
|
if self.is_server() {
|
|
crate::server::Peer::convert_poll_message(pseudo, fields, stream_id)
|
|
.map(PollMessage::Server)
|
|
} else {
|
|
crate::client::Peer::convert_poll_message(pseudo, fields, stream_id)
|
|
.map(PollMessage::Client)
|
|
}
|
|
}
|
|
|
|
/// Returns true if the remote peer can initiate a stream with the given ID.
|
|
pub fn ensure_can_open(&self, id: StreamId, mode: Open) -> Result<(), Error> {
|
|
if self.is_server() {
|
|
// Ensure that the ID is a valid client initiated ID
|
|
if mode.is_push_promise() || !id.is_client_initiated() {
|
|
proto_err!(conn: "cannot open stream {:?} - not client initiated", id);
|
|
return Err(Error::library_go_away(Reason::PROTOCOL_ERROR));
|
|
}
|
|
|
|
Ok(())
|
|
} else {
|
|
// Ensure that the ID is a valid server initiated ID
|
|
if !mode.is_push_promise() || !id.is_server_initiated() {
|
|
proto_err!(conn: "cannot open stream {:?} - not server initiated", id);
|
|
return Err(Error::library_go_away(Reason::PROTOCOL_ERROR));
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
}
|