http-client: add success and failure event listeners for requests

Summary:
Previously, it was only possible to register event listeners for request completion on the `HttpClient` itself, rather than on individual `Request`s. This diff adds similar event listeners to `Request`s themselves, so that its possible to register a callback to fire when any request completes, regardless of whether it was sent via an `HttpClient` or as a one-off.

This is similar to `RequestCreationEventListeners`, which run for the creation of every request, whether or not the request is associated with a client.

Notably, to avoid circular references the new event listeners take a `RequestInfo` argument instead of a `RequestContext` (since the listeners are themselves stored inside the `RequestContext`). In practice, the `RequestInfo` should contain all of the information one might want to access about the request.

Reviewed By: quark-zju

Differential Revision: D30831840

fbshipit-source-id: 77ca9dc5fd9f8fc5ee60319baabd77171af70d45
This commit is contained in:
Arun Kulshreshtha 2021-09-09 15:15:18 -07:00 committed by Facebook GitHub Bot
parent af83efe403
commit 4c174dcda0
3 changed files with 38 additions and 11 deletions

View File

@ -111,13 +111,17 @@ impl HttpClient {
let stats = driver.perform(|res| {
let res = res
.map_err(|(easy, e)| {
let ctx = easy.get_ref().request_context();
.map_err(|(mut easy, e)| {
let ctx = easy.get_mut().request_context_mut();
let info = ctx.info().clone();
ctx.event_listeners().trigger_failure(&info);
self.event_listeners.trigger_failed_request(ctx);
e.into()
})
.and_then(|mut easy| {
let ctx = easy.get_ref().request_context();
let ctx = easy.get_mut().request_context_mut();
let info = ctx.info().clone();
ctx.event_listeners().trigger_success(&info);
self.event_listeners.trigger_succeeded_request(ctx);
Response::try_from(easy.get_mut())
});
@ -259,14 +263,18 @@ impl HttpClient {
// need to pass the result to the handler contained
// therein.
let (mut easy, res) = match res {
Ok(easy) => {
self.event_listeners
.trigger_succeeded_request(easy.get_ref().request_context());
Ok(mut easy) => {
let ctx = easy.get_mut().request_context_mut();
let info = ctx.info().clone();
ctx.event_listeners().trigger_success(&info);
self.event_listeners.trigger_succeeded_request(ctx);
(easy, Ok(()))
}
Err((easy, e)) => {
self.event_listeners
.trigger_failed_request(easy.get_ref().request_context());
Err((mut easy, e)) => {
let ctx = easy.get_mut().request_context_mut();
let info = ctx.info().clone();
ctx.event_listeners().trigger_failure(&info);
self.event_listeners.trigger_failed_request(ctx);
(easy, Err(e.into()))
}
};

View File

@ -8,7 +8,7 @@
use std::sync::Arc;
use crate::progress::Progress;
use crate::request::{Request, RequestContext};
use crate::request::{Request, RequestContext, RequestInfo};
use crate::stats::Stats;
/// Generate a struct for holding event listeners (callbacks).
@ -98,6 +98,12 @@ gen_event_listeners! {
/// On progress update. Note: this is called periodically even if there is no progress.
progress(req: &RequestContext, progress: Progress),
/// The request completed successfully.
success(req: &RequestInfo),
/// The request completed unsuccessfully.
failure(req: &RequestInfo),
}
}

View File

@ -569,7 +569,20 @@ impl Request {
/// progress reporting.
pub fn send(self) -> Result<Response, HttpClientError> {
let mut easy: Easy2<Buffered> = self.try_into()?;
easy.perform()?;
let res = easy.perform();
let ctx = easy.get_mut().request_context_mut();
let info = ctx.info().clone();
match res {
Ok(()) => {
ctx.event_listeners().trigger_success(&info);
}
Err(e) => {
ctx.event_listeners().trigger_failure(&info);
return Err(e.into());
}
}
Response::try_from(easy.get_mut())
}