/**
* Copyright (C) 2012-2014 Gist Labs, LLC. (http://gistlabs.com)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package com.gistlabs.mechanize;
import static org.junit.Assert.assertEquals;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.util.Arrays;
import org.apache.http.HttpEntity;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.ProtocolVersion;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.entity.BasicHttpEntity;
import org.apache.http.entity.BufferedHttpEntity;
import org.apache.http.entity.mime.MultipartEntity;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicHttpResponse;
import org.junit.Assert;
import org.junit.internal.ArrayComparisonFailure;
import com.gistlabs.mechanize.exceptions.MechanizeExceptionFactory;
import com.gistlabs.mechanize.headers.Header;
import com.gistlabs.mechanize.headers.Headers;
import com.gistlabs.mechanize.parameters.Parameters;
import com.gistlabs.mechanize.util.apache.ContentType;
import com.gistlabs.mechanize.util.apache.URLEncodedUtils;
public class PageRequest {
public final String httpMethod;
public final String uri;
public Parameters parameters;
public Headers headers = new Headers();
public final InputStream body;
public boolean wasExecuted = false;
public HttpClient client = null;
public HttpRequest request = null;
public String contentType = ContentType.TEXT_HTML.getMimeType();
public String charset = "utf-8";
private String contentLocation = null;
public PageRequest(final String uri, final String body) {
this("GET", uri, body);
}
public PageRequest(final String method, final String uri, final String body) {
this(method, uri, new Parameters(), body);
}
public PageRequest(final String method, final String uri, final InputStream body) {
this(method, uri, new Parameters(), body);
}
public PageRequest(final String method, final String uri, final Parameters parameters, final String body) {
this(method, uri, parameters, new ByteArrayInputStream(body.getBytes()));
}
public PageRequest(final String method, final String uri, final Parameters parameters, final InputStream body) {
this.httpMethod = method;
this.uri = uri;
this.parameters = parameters;
this.body = body;
}
public PageRequest setParameters(Parameters parameters) {
this.parameters = parameters;
return this;
}
public Parameters getParameters() {
return parameters;
}
public PageRequest addHeader(final String header, final String... values) {
this.headers.add(header, values);
return this;
}
public PageRequest setContentType(String contentType) {
this.contentType = contentType;
return this;
}
public boolean wasExecuted() {
return wasExecuted;
}
public void setEncoding(final String charset) {
this.charset = charset;
}
public PageRequest setContentLocation(final String contentLocation) {
this.contentLocation = contentLocation;
return this;
}
public HttpResponse consume(final HttpClient client, final HttpRequestBase request) throws Exception {
if(!wasExecuted) {
this.client = client;
this.request = request;
if(!request.getMethod().equalsIgnoreCase(httpMethod))
throw new IllegalArgumentException(String.format("Expected %s, but was %s", httpMethod, request.getMethod()));
if(request.getURI().toString().equals(uri)) {
HttpResponse response = new BasicHttpResponse(new ProtocolVersion("HTTP", 1, 1), 200, "OK");
BasicHttpEntity entity = new BasicHttpEntity();
if(contentLocation != null)
response.addHeader(new BasicHeader("Content-Location", contentLocation));
entity.setContentEncoding(charset);
entity.setContentType(this.contentType);
entity.setContent(this.body);
response.setEntity(new BufferedHttpEntity(entity));
assertParameters(request);
assertHeaders(request);
this.wasExecuted = true;
return response;
}
else {
assertEquals("URI of the next PageRequest does not match", uri, request.getURI().toString());
return null;
}
}
else
throw new UnsupportedOperationException("Request already executed");
}
private void assertHeaders(final HttpRequestBase request) throws ArrayComparisonFailure {
for(Header header: headers) {
org.apache.http.Header[] requestHeaders = request.getHeaders(header.getName());
boolean foundMatch = false;
for(org.apache.http.Header requestHeader : requestHeaders)
if (header.getValues().contains(requestHeader.getValue()))
foundMatch=true;
if (!foundMatch)
Assert.fail(String.format("Could not find request header matching: %s", header));
}
if(request instanceof HttpPost) {
HttpPost post = (HttpPost)request;
HttpEntity entity = post.getEntity();
Parameters actualParameters = extractParameters(entity);
String [] expectedNames = parameters.getNames();
String [] actualNames = actualParameters.getNames();
Arrays.sort(expectedNames);
Arrays.sort(actualNames);
Assert.assertArrayEquals("Expected and actual parameters should equal by available parameter names",
expectedNames, actualNames);
for(String name : expectedNames) {
String [] expectedValue = parameters.get(name);
String [] actualValue = actualParameters.get(name);
Assert.assertArrayEquals("Expected parameter of next PageRequest '" + uri + "' must match", expectedValue, actualValue);
}
}
}
private void assertParameters(final HttpRequestBase request) throws ArrayComparisonFailure {
if(request instanceof HttpPost) {
HttpPost post = (HttpPost)request;
HttpEntity entity = post.getEntity();
Parameters actualParameters = extractParameters(entity);
String [] expectedNames = parameters.getNames();
String [] actualNames = actualParameters.getNames();
Arrays.sort(expectedNames);
Arrays.sort(actualNames);
Assert.assertArrayEquals("Expected and actual parameters should equal by available parameter names",
expectedNames, actualNames);
for(String name : expectedNames) {
String [] expectedValue = parameters.get(name);
String [] actualValue = actualParameters.get(name);
Assert.assertArrayEquals("Expected parameter of next PageRequest '" + uri + "' must match", expectedValue, actualValue);
}
}
}
private Parameters extractParameters(final HttpEntity entity) {
if (entity instanceof UrlEncodedFormEntity)
return extractParameters((UrlEncodedFormEntity)entity);
if (entity instanceof MultipartEntity)
return extractParameters((MultipartEntity)entity);
throw new ClassCastException(String.format("Can't convert %s to either UrlEncodedFormEntity or MultipartEntity",entity.getClass()));
}
private Parameters extractParameters(final MultipartEntity entity) {
Parameters parameters = new Parameters();
// appears to be impossible to get parts...?
return parameters;
}
private Parameters extractParameters(final UrlEncodedFormEntity entity) {
try {
InputStream stream = entity.getContent();
BufferedReader reader = new BufferedReader(new InputStreamReader(stream));
StringBuilder content = new StringBuilder();
while(true) {
String line = reader.readLine();
if(line != null) {
if(content.length() > 0)
content.append("\n");
content.append(line);
}
else
break;
}
Parameters parameters = new Parameters();
for(NameValuePair param : URLEncodedUtils.parse(content.toString(), Charset.forName("UTF-8")))
parameters.add(param.getName(), param.getValue());
return parameters;
}
catch(IOException e) {
throw MechanizeExceptionFactory.newException(e);
}
}
@Override
public String toString() {
return "<" + httpMethod + ":" + uri + "{" + parameters + "}" + ">";
}
}