Skip to content

Commit

Permalink
Added ability to instruct REST Assured whether or not to URL encode t…
Browse files Browse the repository at this point in the history
…he request URI when it's presented in the request specification log
  • Loading branch information
johanhaleby committed Apr 22, 2016
1 parent d63290d commit ad4c70c
Show file tree
Hide file tree
Showing 10 changed files with 163 additions and 31 deletions.
3 changes: 3 additions & 0 deletions changelog.txt
Expand Up @@ -11,6 +11,9 @@ Change log next release
* It's now possible to pass a File to the body/content method of the RequestSpecification when content-type is set to JSON, XML or TEXT. The contents of this will
be sent to the server (issue 674)
* Fixed an issue where the Jackson and Jackson2 (Faster Jackson) object mappers didn't fully took charset into account when serializing POJO to String (issue 677).
* Added ability to instruct REST Assured whether or not to URL encode the request URI when it's presented in the request specification log.
This is configured using the com.jayway.restassured.config.LogConfig#urlEncodeRequestUri method. By default url encoding of the request uri is enabled to show what the URL targeted by REST Assured actually looks like for real.
But there may be cases where you want to make the URI more readable and this is when you might want to consider setting urlEncodeRequestUri to false (issue 678)

Change log 2.9.0 (2016-03-04)
-----------------------------
Expand Down
Expand Up @@ -41,6 +41,8 @@
import java.io.InputStream;
import java.io.PrintStream;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;

import static com.jayway.restassured.RestAssured.expect;
import static com.jayway.restassured.RestAssured.given;
Expand Down Expand Up @@ -963,4 +965,38 @@ public void logsMultiPartParamsOnLogAll() throws Exception {

assertThat(writer.toString(), equalTo("Request path:\thttp://localhost:8080/greet?firstName=John&lastName=Doe" + LINE_SEPARATOR));
}

@Test public void
shows_request_log_as_url_encoded_when_explicitly_instructing_request_logging_filter_to_do_so() throws UnsupportedEncodingException {
final StringWriter writer = new StringWriter();
final PrintStream captor = new PrintStream(new WriterOutputStream(writer), true);

given().
filter(new RequestLoggingFilter(LogDetail.PATH, true, captor, true)).
queryParam("firstName", "John#€").
queryParam("lastName", "Doe").
when().
get("/greet").
then().
statusCode(200);

assertThat(writer.toString(), equalTo("Request path:\thttp://localhost:8080/greet?firstName=John" + URLEncoder.encode("#€", "UTF-8") + "&lastName=Doe" + LINE_SEPARATOR));
}

@Test public void
shows_request_log_as_without_url_encoding_when_explicitly_instructing_request_logging_filter_to_do_so() throws UnsupportedEncodingException {
final StringWriter writer = new StringWriter();
final PrintStream captor = new PrintStream(new WriterOutputStream(writer), true);

given().
filter(new RequestLoggingFilter(LogDetail.PATH, true, captor, false)).
queryParam("firstName", "John#€").
queryParam("lastName", "Doe").
when().
get("/greet").
then().
statusCode(200);

assertThat(writer.toString(), equalTo("Request path:\thttp://localhost:8080/greet?firstName=John#€&lastName=Doe" + LINE_SEPARATOR));
}
}
Expand Up @@ -100,7 +100,8 @@ private MockMvcRequestSpecification logWith(LogDetail logDetail, boolean prettyP
}

private MockMvcRequestSpecification logWith(LogDetail logDetail, boolean prettyPrintingEnabled, PrintStream printStream) {
requestSpecification.setRequestLoggingFilter(new RequestLoggingFilter(logDetail, prettyPrintingEnabled, printStream));
boolean shouldUrlEncodeRequestUri = requestSpecification.getRestAssuredMockMvcConfig().getLogConfig().shouldUrlEncodeRequestUri();
requestSpecification.setRequestLoggingFilter(new RequestLoggingFilter(logDetail, prettyPrintingEnabled, printStream, shouldUrlEncodeRequestUri));
return requestSpecification;
}

Expand Down
Expand Up @@ -711,7 +711,8 @@ public MockMvcRequestSpecBuilder log(LogDetail logDetail) {
LogConfig logConfig = spec.getRestAssuredMockMvcConfig().getLogConfig();
PrintStream printStream = logConfig.defaultStream();
boolean prettyPrintingEnabled = logConfig.isPrettyPrintingEnabled();
spec.setRequestLoggingFilter(new RequestLoggingFilter(logDetail, prettyPrintingEnabled, printStream));
boolean shouldUrlEncodeRequestUri = logConfig.shouldUrlEncodeRequestUri();
spec.setRequestLoggingFilter(new RequestLoggingFilter(logDetail, prettyPrintingEnabled, printStream, shouldUrlEncodeRequestUri));
return this;
}

Expand Down
Expand Up @@ -15,7 +15,6 @@
*/



package com.jayway.restassured.internal

import com.jayway.restassured.config.LogConfig
Expand All @@ -27,27 +26,31 @@ import com.jayway.restassured.specification.RequestSpecification
*/
class LogSpecificationImpl {

def PrintStream getPrintStream(RequestSpecification requestSpecification) {
def stream = getLogConfig(requestSpecification)?.defaultStream()
if(stream == null) {
stream = System.out
}
stream
def PrintStream getPrintStream(RequestSpecification requestSpecification) {
def stream = getLogConfig(requestSpecification)?.defaultStream()
if (stream == null) {
stream = System.out
}
stream
}

def boolean shouldUrlEncodeRequestUri(RequestSpecification requestSpecification) {
getLogConfig(requestSpecification)?.shouldUrlEncodeRequestUri()
}

def boolean shouldPrettyPrint(RequestSpecification requestSpecification) {
def prettyPrintingEnabled = getLogConfig(requestSpecification)?.isPrettyPrintingEnabled()
if(prettyPrintingEnabled == null) {
return true
}
prettyPrintingEnabled
def boolean shouldPrettyPrint(RequestSpecification requestSpecification) {
def prettyPrintingEnabled = getLogConfig(requestSpecification)?.isPrettyPrintingEnabled()
if (prettyPrintingEnabled == null) {
return true
}
prettyPrintingEnabled
}

private def LogConfig getLogConfig(RequestSpecification requestSpecification) {
if(!requestSpecification) {
throw new IllegalStateException("Cannot configure logging since request specification is not defined. You may be misusing the API.");
}
RestAssuredConfig config = requestSpecification.restAssuredConfig
config?.logConfig
private def LogConfig getLogConfig(RequestSpecification requestSpecification) {
if (!requestSpecification) {
throw new IllegalStateException("Cannot configure logging since request specification is not defined. You may be misusing the API.");
}
RestAssuredConfig config = requestSpecification.restAssuredConfig
config?.logConfig
}
}
Expand Up @@ -100,7 +100,7 @@ class RequestLogSpecificationImpl extends LogSpecificationImpl implements Reques
}

private def logWith(LogDetail logDetail, boolean prettyPrintingEnabled, PrintStream printStream) {
requestSpecification.filter(new RequestLoggingFilter(logDetail, prettyPrintingEnabled, printStream))
requestSpecification.filter(new RequestLoggingFilter(logDetail, prettyPrintingEnabled, printStream, shouldUrlEncodeRequestUri(requestSpecification)))
requestSpecification
}
}
Expand Up @@ -112,7 +112,7 @@ class FormAuthFilter implements AuthFilter {
def logConfig = formAuthConfig.getLogConfig()
def logDetail = formAuthConfig.getLogDetail()
if (logDetail != LogDetail.STATUS) {
loginRequestSpec.filter(new RequestLoggingFilter(logDetail, logConfig.isPrettyPrintingEnabled(), logConfig.defaultStream()));
loginRequestSpec.filter(new RequestLoggingFilter(logDetail, logConfig.isPrettyPrintingEnabled(), logConfig.defaultStream(), logConfig.shouldUrlEncodeRequestUri()));
}

if (logDetail != LogDetail.PARAMS) {
Expand Down
Expand Up @@ -1306,7 +1306,8 @@ public RequestSpecBuilder log(LogDetail logDetail) {
}
PrintStream printStream = logConfig.defaultStream();
boolean prettyPrintingEnabled = logConfig.isPrettyPrintingEnabled();
spec.filter(new RequestLoggingFilter(logDetail, prettyPrintingEnabled, printStream));
boolean shouldUrlEncodeRequestUri = logConfig.shouldUrlEncodeRequestUri();
spec.filter(new RequestLoggingFilter(logDetail, prettyPrintingEnabled, printStream, shouldUrlEncodeRequestUri));
return this;
}

Expand Down
Expand Up @@ -32,13 +32,14 @@ public class LogConfig implements Config {
private final PrintStream defaultPrintStream;
private final boolean prettyPrintingEnabled;
private final LogDetail logDetailIfValidationFails;
private final boolean urlEncodeRequestUri;
private final boolean isUserDefined;

/**
* Configure the default stream to use the System.out stream (default).
*/
public LogConfig() {
this(System.out, true, null, false);
this(System.out, true, null, true, false);
}

/**
Expand All @@ -58,7 +59,7 @@ public LogConfig() {
* @param prettyPrintingEnabled Enable or disable pretty printing when logging. Pretty printing is only possible when content-type is XML, JSON or HTML.
*/
public LogConfig(PrintStream defaultPrintStream, boolean prettyPrintingEnabled) {
this(defaultPrintStream, prettyPrintingEnabled, null, true);
this(defaultPrintStream, prettyPrintingEnabled, null, true, true);
}

/**
Expand All @@ -77,12 +78,14 @@ public LogConfig(PrintStream defaultPrintStream, boolean prettyPrintingEnabled)
* @param defaultPrintStream The default print stream to use for the {@link com.jayway.restassured.specification.LogSpecification}'s.
* @param prettyPrintingEnabled Enable or disable pretty printing when logging. Pretty printing is only possible when content-type is XML, JSON or HTML.
*/
private LogConfig(PrintStream defaultPrintStream, boolean prettyPrintingEnabled, LogDetail logDetailIfValidationFails, boolean isUserDefined) {
private LogConfig(PrintStream defaultPrintStream, boolean prettyPrintingEnabled, LogDetail logDetailIfValidationFails,
boolean urlEncodeRequestUri, boolean isUserDefined) {
Validate.notNull(defaultPrintStream, "Stream to write logs to cannot be null");
this.defaultPrintStream = defaultPrintStream;
this.prettyPrintingEnabled = prettyPrintingEnabled;
this.logDetailIfValidationFails = logDetailIfValidationFails;
this.isUserDefined = isUserDefined;
this.urlEncodeRequestUri = urlEncodeRequestUri;
}

/**
Expand All @@ -99,7 +102,7 @@ public PrintStream defaultStream() {
* @return A new LogConfig instance
*/
public LogConfig defaultStream(PrintStream printStream) {
return new LogConfig(printStream, true, logDetailIfValidationFails, true);
return new LogConfig(printStream, true, logDetailIfValidationFails, urlEncodeRequestUri, true);
}

/**
Expand Down Expand Up @@ -130,7 +133,7 @@ public LogDetail logDetailOfRequestAndResponseIfValidationFails() {
* @return A new LogConfig instance
*/
public LogConfig enablePrettyPrinting(boolean shouldEnable) {
return new LogConfig(defaultPrintStream, shouldEnable, logDetailIfValidationFails, true);
return new LogConfig(defaultPrintStream, shouldEnable, logDetailIfValidationFails, urlEncodeRequestUri, true);
}

/**
Expand All @@ -149,7 +152,27 @@ public LogConfig enableLoggingOfRequestAndResponseIfValidationFails() {
* @return A new LogConfig instance
*/
public LogConfig enableLoggingOfRequestAndResponseIfValidationFails(LogDetail logDetail) {
return new LogConfig(defaultPrintStream, prettyPrintingEnabled, logDetail, true);
return new LogConfig(defaultPrintStream, prettyPrintingEnabled, logDetail, urlEncodeRequestUri, true);
}

/**
* Instruct REST Assured whether or not to URL encode the request URI when it's presented in the request specification log.
* By default url encoding of the request uri is enabled to show what the URL targeted by REST Assured actually looks like for real.
* But there may be cases where you want to make the URI more readable and this is when you might want to consider setting
* <code>urlEncodeRequestUri</code> to <code>false</code>. Note that this only affects logging.
*
* @param urlEncodeRequestUri Whether or not to url encode the request uri when it's presented in the request log
* @return A new LogConfig instance
*/
public LogConfig urlEncodeRequestUri(boolean urlEncodeRequestUri) {
return new LogConfig(defaultPrintStream, prettyPrintingEnabled, logDetailIfValidationFails, urlEncodeRequestUri, true);
}

/**
* @return <code>true</code> is the request URI should be URL encoded in the request log
*/
public boolean shouldUrlEncodeRequestUri() {
return urlEncodeRequestUri;
}

/**
Expand Down
Expand Up @@ -25,6 +25,9 @@
import org.apache.commons.lang3.Validate;

import java.io.PrintStream;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;

import static com.jayway.restassured.filter.log.LogDetail.ALL;
import static com.jayway.restassured.filter.log.LogDetail.STATUS;
Expand All @@ -38,9 +41,11 @@
*/
public class RequestLoggingFilter implements Filter {

private static final boolean SHOW_URL_ENCODED_URI = true;
private final LogDetail logDetail;
private final PrintStream stream;
private final boolean shouldPrettyPrint;
private final boolean showUrlEncodedUri;

/**
* Logs to System.out
Expand Down Expand Up @@ -86,6 +91,18 @@ public RequestLoggingFilter(LogDetail logDetail, PrintStream stream) {
* @param stream The stream to log to.
*/
public RequestLoggingFilter(LogDetail logDetail, boolean shouldPrettyPrint, PrintStream stream) {
this(logDetail, shouldPrettyPrint, stream, SHOW_URL_ENCODED_URI);
}

/**
* Instantiate a logger using a specific print stream and a specific log detail
*
* @param logDetail The log detail
* @param shouldPrettyPrint <code>true</code> if pretty-printing of the body should occur.
* @param stream The stream to log to.
* @param showUrlEncodedUri Whether or not to show the request URI as url encoded
*/
public RequestLoggingFilter(LogDetail logDetail, boolean shouldPrettyPrint, PrintStream stream, boolean showUrlEncodedUri) {
Validate.notNull(stream, "Print stream cannot be null");
Validate.notNull(logDetail, "Log details cannot be null");
if (logDetail == STATUS) {
Expand All @@ -94,10 +111,16 @@ public RequestLoggingFilter(LogDetail logDetail, boolean shouldPrettyPrint, Prin
this.stream = stream;
this.logDetail = logDetail;
this.shouldPrettyPrint = shouldPrettyPrint;
this.showUrlEncodedUri = showUrlEncodedUri;
}

public Response filter(FilterableRequestSpecification requestSpec, FilterableResponseSpecification responseSpec, FilterContext ctx) {
RequestPrinter.print(requestSpec, requestSpec.getMethod().toString(), requestSpec.getURI(), logDetail, stream, shouldPrettyPrint);
String uri = requestSpec.getURI();
if (!showUrlEncodedUri) {
uri = urlDecode(uri, Charset.forName(requestSpec.getConfig().getEncoderConfig().defaultQueryParameterCharset()), true);
}

RequestPrinter.print(requestSpec, requestSpec.getMethod().toString(), uri, logDetail, stream, shouldPrettyPrint);
return ctx.next(requestSpec, responseSpec);
}

Expand All @@ -110,4 +133,45 @@ public Response filter(FilterableRequestSpecification requestSpec, FilterableRes
public static RequestLoggingFilter logRequestTo(PrintStream stream) {
return new RequestLoggingFilter(stream);
}

// Copy of the private method in URLEncodedUtils

/**
* Decode/unescape a portion of a URL, to use with the query part ensure {@code plusAsBlank} is true.
*
* @param content the portion to decode
* @param charset the charset to use
* @param plusAsBlank if {@code true}, then convert '+' to space (e.g. for www-url-form-encoded content), otherwise leave as is.
* @return encoded string
*/
private static String urlDecode(final String content, final Charset charset, final boolean plusAsBlank) {
if (content == null) {
return null;
}
final ByteBuffer bb = ByteBuffer.allocate(content.length());
final CharBuffer cb = CharBuffer.wrap(content);
while (cb.hasRemaining()) {
final char c = cb.get();
if (c == '%' && cb.remaining() >= 2) {
final char uc = cb.get();
final char lc = cb.get();
final int u = Character.digit(uc, 16);
final int l = Character.digit(lc, 16);
if (u != -1 && l != -1) {
bb.put((byte) ((u << 4) + l));
} else {
bb.put((byte) '%');
bb.put((byte) uc);
bb.put((byte) lc);
}
} else if (plusAsBlank && c == '+') {
bb.put((byte) ' ');
} else {
bb.put((byte) c);
}
}
bb.flip();
return charset.decode(bb).toString();
}

}

0 comments on commit ad4c70c

Please sign in to comment.