Skip to content

Commit

Permalink
JENKINS-37627 - Improved saveToFile to avoid convertion of string. Ad…
Browse files Browse the repository at this point in the history
…ded ResponseHandle to better control how reponse should be handled.
  • Loading branch information
janario committed Mar 28, 2017
1 parent 434ce55 commit 038edbe
Show file tree
Hide file tree
Showing 7 changed files with 229 additions and 96 deletions.
151 changes: 87 additions & 64 deletions src/main/java/jenkins/plugins/http_request/HttpRequestExecution.java
@@ -1,8 +1,8 @@
package jenkins.plugins.http_request;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintStream;
import java.io.UnsupportedEncodingException;
import java.net.ConnectException;
Expand Down Expand Up @@ -30,6 +30,7 @@
import org.apache.http.ssl.SSLContextBuilder;

import com.google.common.collect.Range;
import com.google.common.io.ByteStreams;

import hudson.AbortException;
import hudson.CloseProofOutputStream;
Expand Down Expand Up @@ -65,6 +66,7 @@ public class HttpRequestExecution extends MasterToSlaveCallable<ResponseContentS
private final FilePath outputFile;
private final int timeout;
private final boolean consoleLogResponseBody;
private final ResponseHandle responseHandle;

private final Authenticator authenticator;

Expand All @@ -87,6 +89,7 @@ static HttpRequestExecution from(HttpRequest http,

http.getValidResponseCodes(), http.getValidResponseContent(),
http.getConsoleLogResponseBody(), outputFile,
ResponseHandle.NONE,

taskListener.getLogger());
} catch (IOException e) {
Expand All @@ -105,7 +108,7 @@ static HttpRequestExecution from(HttpRequestStep step, TaskListener taskListener

step.getValidResponseCodes(), step.getValidResponseContent(),
step.getConsoleLogResponseBody(), outputFile,

step.getResponseHandle(),
taskListener.getLogger());
}

Expand All @@ -116,6 +119,7 @@ private HttpRequestExecution(

String validResponseCodes, String validResponseContent,
Boolean consoleLogResponseBody, FilePath outputFile,
ResponseHandle responseHandle,
PrintStream logger
) {
this.url = url;
Expand All @@ -137,6 +141,8 @@ private HttpRequestExecution(
this.validResponseCodes = validResponseCodes;
this.validResponseContent = validResponseContent != null ? validResponseContent : "";
this.consoleLogResponseBody = Boolean.TRUE.equals(consoleLogResponseBody);
this.responseHandle = this.consoleLogResponseBody || !this.validResponseContent.isEmpty() ?
ResponseHandle.STRING : responseHandle;
this.outputFile = outputFile;

this.localLogger = logger;
Expand All @@ -145,20 +151,15 @@ private HttpRequestExecution(

@Override
public ResponseContentSupplier call() throws RuntimeException {
try {
logger().println("HttpMethod: " + httpMode);
logger().println("URL: " + url);
for (HttpRequestNameValuePair header : headers) {
logger().print(header.getName() + ": ");
logger().println(header.getMaskValue() ? "*****" : header.getValue());
}

ResponseContentSupplier response = authAndRequest();
responseCodeIsValid(response);
contentIsValid(response);
logResponseToFile(response);
logger().println("HttpMethod: " + httpMode);
logger().println("URL: " + url);
for (HttpRequestNameValuePair header : headers) {
logger().print(header.getName() + ": ");
logger().println(header.getMaskValue() ? "*****" : header.getValue());
}

return response;
try {
return authAndRequest();
} catch (IOException | InterruptedException |
KeyStoreException | NoSuchAlgorithmException | KeyManagementException e) {
throw new IllegalStateException(e);
Expand All @@ -178,45 +179,61 @@ private PrintStream logger() {

private ResponseContentSupplier authAndRequest()
throws IOException, InterruptedException, KeyStoreException, NoSuchAlgorithmException, KeyManagementException {
//only leave open if no error happen
ResponseHandle responseHandle = ResponseHandle.NONE;
CloseableHttpClient httpclient = null;
try {
HttpClientBuilder clientBuilder = HttpClientBuilder.create().useSystemProperties();
//timeout
if (timeout > 0) {
int t = timeout * 1000;
RequestConfig config = RequestConfig.custom()
.setSocketTimeout(t)
.setConnectTimeout(t)
.setConnectionRequestTimeout(t)
.build();
clientBuilder.setDefaultRequestConfig(config);
}
//ssl
if (ignoreSslErrors) {
SSLContextBuilder builder = SSLContextBuilder.create();
builder.loadTrustMaterial(null, new TrustStrategy() {
@Override
public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
return true;
}
});
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(builder.build(), NoopHostnameVerifier.INSTANCE);
clientBuilder.setSSLSocketFactory(sslsf);
}
configureTimeoutAndSsl(clientBuilder);

HttpClientUtil clientUtil = new HttpClientUtil();
HttpRequestBase httpRequestBase = clientUtil.createRequestBase(new RequestAction(new URL(url), httpMode, body, null, headers));
HttpContext context = new BasicHttpContext();

httpclient = auth(clientBuilder, httpRequestBase, context);
return executeRequest(httpclient, clientUtil, httpRequestBase, context);

ResponseContentSupplier response = executeRequest(httpclient, clientUtil, httpRequestBase, context);
processResponse(response);

responseHandle = this.responseHandle;
if (responseHandle == ResponseHandle.LEAVE_OPEN) {
response.setHttpClient(httpclient);
}
return response;
} finally {
if (httpclient != null) {
httpclient.close();
if (responseHandle != ResponseHandle.LEAVE_OPEN) {
if (httpclient != null) {
httpclient.close();
}
}
}
}

private void configureTimeoutAndSsl(HttpClientBuilder clientBuilder) throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException {
//timeout
if (timeout > 0) {
int t = timeout * 1000;
RequestConfig config = RequestConfig.custom()
.setSocketTimeout(t)
.setConnectTimeout(t)
.setConnectionRequestTimeout(t)
.build();
clientBuilder.setDefaultRequestConfig(config);
}
//ssl
if (ignoreSslErrors) {
SSLContextBuilder builder = SSLContextBuilder.create();
builder.loadTrustMaterial(null, new TrustStrategy() {
@Override
public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
return true;
}
});
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(builder.build(), NoopHostnameVerifier.INSTANCE);
clientBuilder.setSSLSocketFactory(sslsf);
}
}

private CloseableHttpClient auth(
HttpClientBuilder clientBuilder, HttpRequestBase httpRequestBase,
HttpContext context) throws IOException, InterruptedException {
Expand All @@ -235,7 +252,7 @@ private ResponseContentSupplier executeRequest(
try {
final HttpResponse response = clientUtil.execute(httpclient, context, httpRequestBase, logger());
// The HttpEntity is consumed by the ResponseContentSupplier
responseContentSupplier = new ResponseContentSupplier(response);
responseContentSupplier = new ResponseContentSupplier(responseHandle, response);
} catch (UnknownHostException uhe) {
logger().println("Treating UnknownHostException(" + uhe.getMessage() + ") as 404 Not Found");
responseContentSupplier = new ResponseContentSupplier("UnknownHostException as 404 Not Found", 404);
Expand All @@ -244,23 +261,9 @@ private ResponseContentSupplier executeRequest(
responseContentSupplier = new ResponseContentSupplier(ce.getClass() + "(" + ce.getMessage() + ") as 408 Request Timeout", 408);
}

if (consoleLogResponseBody) {
logger().println("Response: \n" + responseContentSupplier.getContent());
}
return responseContentSupplier;
}

private void contentIsValid(ResponseContentSupplier response) throws AbortException {
if (validResponseContent.isEmpty()) {
return;
}

String content = response.getContent();
if (!content.contains(validResponseContent)) {
throw new AbortException("Fail: Response doesn't contain expected content '" + validResponseContent + "'");
}
}

private void responseCodeIsValid(ResponseContentSupplier response) throws AbortException {
List<Range<Integer>> ranges = DescriptorImpl.parseToRange(validResponseCodes);
for (Range<Integer> range : ranges) {
Expand All @@ -272,21 +275,41 @@ private void responseCodeIsValid(ResponseContentSupplier response) throws AbortE
throw new AbortException("Fail: the returned code " + response.getStatus() + " is not in the accepted range: " + ranges);
}

private void logResponseToFile(ResponseContentSupplier response)
throws IOException, InterruptedException {
if (outputFile == null || response.getContent() == null) {
return;
private void processResponse(ResponseContentSupplier response) throws IOException, InterruptedException {
//logs
if (consoleLogResponseBody) {
logger().println("Response: \n" + response.getContent());
}

//validate status code
responseCodeIsValid(response);

//validate content
if (!validResponseContent.isEmpty()) {
if (!response.getContent().contains(validResponseContent)) {
throw new AbortException("Fail: Response doesn't contain expected content '" + validResponseContent + "'");
}
}

//save file
if (outputFile == null) {
return;
}
logger().println("Saving response body to " + outputFile);
OutputStreamWriter write = null;

InputStream in = response.getContentStream();
if (in == null) {
return;
}
OutputStream out = null;
try {
write = new OutputStreamWriter(outputFile.write(), StandardCharsets.UTF_8);
write.write(response.getContent());
out = outputFile.write();
ByteStreams.copy(in, out);
} finally {
if (write != null) {
write.close();
if (out != null) {
out.close();
}
in.close();
}
}
}
20 changes: 20 additions & 0 deletions src/main/java/jenkins/plugins/http_request/HttpRequestStep.java
Expand Up @@ -43,6 +43,7 @@ public final class HttpRequestStep extends AbstractStepImpl {
private String requestBody = DescriptorImpl.requestBody;
private List<HttpRequestNameValuePair> customHeaders = DescriptorImpl.customHeaders;
private String outputFile = DescriptorImpl.outputFile;
private ResponseHandle responseHandle = ResponseHandle.STRING;

@DataBoundConstructor
public HttpRequestStep(String url) {
Expand Down Expand Up @@ -161,6 +162,16 @@ public void setOutputFile(String outputFile) {
this.outputFile = outputFile;
}

public ResponseHandle getResponseHandle() {
return responseHandle;
}


@DataBoundSetter
public void setResponseHandle(ResponseHandle responseHandle) {
this.responseHandle = responseHandle;
}

@Override
public DescriptorImpl getDescriptor() {
return (DescriptorImpl) super.getDescriptor();
Expand Down Expand Up @@ -199,6 +210,7 @@ public static final class DescriptorImpl extends AbstractStepDescriptorImpl {
public static final String requestBody = HttpRequest.DescriptorImpl.requestBody;
public static final List <HttpRequestNameValuePair> customHeaders = Collections.<HttpRequestNameValuePair>emptyList();
public static final String outputFile = "";
public static final ResponseHandle responseHandle = ResponseHandle.STRING;

public DescriptorImpl() {
super(Execution.class);
Expand Down Expand Up @@ -226,6 +238,14 @@ public ListBoxModel doFillContentTypeItems() {
return MimeType.getContentTypeFillItems();
}

public ListBoxModel doFillResponseHandleItems() {
ListBoxModel items = new ListBoxModel();
for (ResponseHandle responseHandle : ResponseHandle.values()) {
items.add(responseHandle.name());
}
return items;
}

public ListBoxModel doFillAuthenticationItems() {
return HttpRequest.DescriptorImpl.fillAuthenticationItems();
}
Expand Down

0 comments on commit 038edbe

Please sign in to comment.