Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Don't print non-utf8 bodies #125

Merged
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions src/mock_server/bare_server.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crate::mock_server::hyper::run_server;
use crate::mock_set::MockId;
use crate::mock_set::MountedMockSet;
use crate::request::BodyPrintLimit;
use crate::{mock::Mock, verification::VerificationOutcome, Request};
use std::net::{SocketAddr, TcpListener, TcpStream};
use std::pin::pin;
Expand Down Expand Up @@ -29,13 +30,15 @@ pub(crate) struct BareMockServer {
pub(super) struct MockServerState {
mock_set: MountedMockSet,
received_requests: Option<Vec<Request>>,
body_print_limit: BodyPrintLimit,
}

impl MockServerState {
pub(super) async fn handle_request(
&mut self,
request: Request,
mut request: Request,
) -> (http_types::Response, Option<futures_timer::Delay>) {
request.body_print_limit = self.body_print_limit;
// If request recording is enabled, record the incoming request
// by adding it to the `received_requests` stack
if let Some(received_requests) = &mut self.received_requests {
Expand All @@ -48,7 +51,11 @@ impl MockServerState {
impl BareMockServer {
/// Start a new instance of a `BareMockServer` listening on the specified
/// [`TcpListener`](std::net::TcpListener).
pub(super) async fn start(listener: TcpListener, request_recording: RequestRecording) -> Self {
pub(super) async fn start(
listener: TcpListener,
request_recording: RequestRecording,
body_print_limit: BodyPrintLimit,
) -> Self {
let (shutdown_trigger, shutdown_receiver) = tokio::sync::oneshot::channel();
let received_requests = match request_recording {
RequestRecording::Enabled => Some(Vec::new()),
Expand All @@ -57,6 +64,7 @@ impl BareMockServer {
let state = Arc::new(RwLock::new(MockServerState {
mock_set: MountedMockSet::new(),
received_requests,
body_print_limit,
}));
let server_address = listener
.local_addr()
Expand Down
25 changes: 24 additions & 1 deletion src/mock_server/builder.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,31 @@
use crate::mock_server::bare_server::{BareMockServer, RequestRecording};
use crate::mock_server::exposed_server::InnerServer;
use crate::request::{BodyPrintLimit, BODY_PRINT_LIMIT};
use crate::MockServer;
use std::env;
use std::net::TcpListener;

/// A builder providing a fluent API to assemble a [`MockServer`] step-by-step.
/// Use [`MockServer::builder`] to get started.
pub struct MockServerBuilder {
listener: Option<TcpListener>,
record_incoming_requests: bool,
body_print_limit: BodyPrintLimit,
}

impl MockServerBuilder {
pub(super) fn new() -> Self {
let body_print_limit = match env::var("WIREMOCK_BODY_PRINT_LIMIT")
.ok()
.and_then(|x| x.parse::<usize>().ok())
{
Some(limit) => BodyPrintLimit::Limited(limit),
None => BodyPrintLimit::Limited(BODY_PRINT_LIMIT),
};
Self {
listener: None,
record_incoming_requests: true,
body_print_limit,
}
}

Expand Down Expand Up @@ -76,6 +87,18 @@ impl MockServerBuilder {
self
}

/// By default, when printing requests the size of the body that can be
/// printed is limited.
///
/// This may want to be changed if working with services with very large
/// bodies, or when printing wiremock output to a file where size matters
/// less than in a terminal window. You can configure this limit with
/// `MockServerBuilder::body_print_limit`.
LukeMathWalker marked this conversation as resolved.
Show resolved Hide resolved
pub fn body_print_limit(mut self, limit: BodyPrintLimit) -> Self {
self.body_print_limit = limit;
self
}

/// Finalise the builder to get an instance of a [`BareMockServer`].
pub(super) async fn build_bare(self) -> BareMockServer {
let listener = if let Some(listener) = self.listener {
Expand All @@ -88,7 +111,7 @@ impl MockServerBuilder {
} else {
RequestRecording::Disabled
};
BareMockServer::start(listener, recording).await
BareMockServer::start(listener, recording, self.body_print_limit).await
}

/// Finalise the builder and launch the [`MockServer`] instance!
Expand Down
43 changes: 42 additions & 1 deletion src/request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,18 @@ use http_types::convert::DeserializeOwned;
use http_types::headers::{HeaderName, HeaderValue, HeaderValues};
use http_types::{Method, Url};

pub const BODY_PRINT_LIMIT: usize = 10_000;

/// Specifies limitations on printing request bodies when logging requests. For some mock servers
/// the bodies may be too large to reasonably print and it may be desireable to limit them.
#[derive(Debug, Copy, Clone)]
pub enum BodyPrintLimit {
/// Maximum length of a body to print in bytes.
Limited(usize),
/// There is no limit to the size of a body that may be printed.
Unlimited,
}

/// An incoming request to an instance of [`MockServer`].
///
/// Each matcher gets an immutable reference to a `Request` instance in the [`matches`] method
Expand All @@ -31,6 +43,7 @@ pub struct Request {
pub method: Method,
pub headers: HashMap<HeaderName, HeaderValues>,
pub body: Vec<u8>,
pub body_print_limit: BodyPrintLimit,
}

impl fmt::Display for Request {
Expand All @@ -44,7 +57,33 @@ impl fmt::Display for Request {
let values = values.join(",");
writeln!(f, "{}: {}", name, values)?;
}
writeln!(f, "{}", String::from_utf8_lossy(&self.body))

match self.body_print_limit {
BodyPrintLimit::Limited(limit) if self.body.len() > limit => {
// We need to use from_utf8_lossy because the limit may land within a utf8
// character.
xd009642 marked this conversation as resolved.
Show resolved Hide resolved
let truncated = String::from_utf8_lossy(&self.body[..limit]);
xd009642 marked this conversation as resolved.
Show resolved Hide resolved
writeln!(f, "{}", truncated)?;
writeln!(
f,
"We truncated the body because it was too large: {} bytes (limit: {} bytes)",
self.body.len(),
limit
)?;
writeln!(f, "Increase this limit by setting `WIREMOCK_BODY_PRINT_LIMIT`, or calling `MockServerBuilder::body_print_limit` when building your MockServer instance")
}
_ => {
if let Ok(body) = std::str::from_utf8(&self.body) {
writeln!(f, "{}", body)
} else {
writeln!(
f,
"Body is likely binary (invalid utf-8) size is {} bytes",
self.body.len()
)
}
}
}
}
}

Expand Down Expand Up @@ -75,6 +114,7 @@ impl Request {
method,
headers,
body,
body_print_limit: BodyPrintLimit::Limited(BODY_PRINT_LIMIT),
}
}

Expand Down Expand Up @@ -119,6 +159,7 @@ impl Request {
method,
headers,
body,
body_print_limit: BodyPrintLimit::Limited(BODY_PRINT_LIMIT),
}
}
}