fix(server): send 400 responses on parse errors before closing connection
This commit is contained in:
@@ -186,7 +186,8 @@ where I: AsyncRead + AsyncWrite,
|
||||
let was_mid_parse = !self.io.read_buf().is_empty();
|
||||
return if was_mid_parse || must_error {
|
||||
debug!("parse error ({}) with {} bytes", e, self.io.read_buf().len());
|
||||
Err(e)
|
||||
self.on_parse_error(e)
|
||||
.map(|()| Async::NotReady)
|
||||
} else {
|
||||
debug!("read eof");
|
||||
Ok(Async::Ready(None))
|
||||
@@ -213,7 +214,8 @@ where I: AsyncRead + AsyncWrite,
|
||||
Err(e) => {
|
||||
debug!("decoder error = {:?}", e);
|
||||
self.state.close_read();
|
||||
return Err(e);
|
||||
return self.on_parse_error(e)
|
||||
.map(|()| Async::NotReady);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -548,6 +550,27 @@ where I: AsyncRead + AsyncWrite,
|
||||
Ok(AsyncSink::Ready)
|
||||
}
|
||||
|
||||
// When we get a parse error, depending on what side we are, we might be able
|
||||
// to write a response before closing the connection.
|
||||
//
|
||||
// - Client: there is nothing we can do
|
||||
// - Server: if Response hasn't been written yet, we can send a 4xx response
|
||||
fn on_parse_error(&mut self, err: ::Error) -> ::Result<()> {
|
||||
match self.state.writing {
|
||||
Writing::Init => {
|
||||
if let Some(msg) = T::on_error(&err) {
|
||||
self.write_head(msg, false);
|
||||
self.state.error = Some(err);
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
// fallback is pass the error back up
|
||||
Err(err)
|
||||
}
|
||||
|
||||
fn write_queued(&mut self) -> Poll<(), io::Error> {
|
||||
trace!("Conn::write_queued()");
|
||||
let state = match self.state.writing {
|
||||
|
||||
@@ -150,6 +150,26 @@ impl Http1Transaction for ServerTransaction {
|
||||
ret
|
||||
}
|
||||
|
||||
fn on_error(err: &::Error) -> Option<MessageHead<Self::Outgoing>> {
|
||||
let status = match err {
|
||||
&::Error::Method |
|
||||
&::Error::Version |
|
||||
&::Error::Header |
|
||||
&::Error::Uri(_) => {
|
||||
StatusCode::BadRequest
|
||||
},
|
||||
&::Error::TooLarge => {
|
||||
StatusCode::RequestHeaderFieldsTooLarge
|
||||
}
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
debug!("sending automatic response ({}) for parse error", status);
|
||||
let mut msg = MessageHead::default();
|
||||
msg.subject = status;
|
||||
Some(msg)
|
||||
}
|
||||
|
||||
fn should_error_on_parse_eof() -> bool {
|
||||
false
|
||||
}
|
||||
@@ -317,6 +337,11 @@ impl Http1Transaction for ClientTransaction {
|
||||
Ok(body)
|
||||
}
|
||||
|
||||
fn on_error(_err: &::Error) -> Option<MessageHead<Self::Outgoing>> {
|
||||
// we can't tell the server about any errors it creates
|
||||
None
|
||||
}
|
||||
|
||||
fn should_error_on_parse_eof() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
@@ -149,6 +149,7 @@ pub trait Http1Transaction {
|
||||
fn parse(bytes: &mut BytesMut) -> ParseResult<Self::Incoming>;
|
||||
fn decoder(head: &MessageHead<Self::Incoming>, method: &mut Option<::Method>) -> ::Result<Option<h1::Decoder>>;
|
||||
fn encode(head: MessageHead<Self::Outgoing>, has_body: bool, method: &mut Option<Method>, dst: &mut Vec<u8>) -> ::Result<h1::Encoder>;
|
||||
fn on_error(err: &::Error) -> Option<MessageHead<Self::Outgoing>>;
|
||||
|
||||
fn should_error_on_parse_eof() -> bool;
|
||||
fn should_read_first() -> bool;
|
||||
|
||||
Reference in New Issue
Block a user