// Copyright (C) 2003-2009 by Object Mentor, Inc. All rights reserved.
// Released under the terms of the CPL Common Public License version 1.0.
package fitnesse.http;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.security.SecureRandom;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
import util.StreamReader;
import fitnesse.util.Base64;
public class RequestBuilder {
private static final byte[] ENDL = "\r\n".getBytes();
private static final Random RANDOM_GENERATOR = new SecureRandom();
private String resource;
private String method = "GET";
private List<InputStream> bodyParts = new LinkedList<InputStream>();
private HashMap<String, String> headers = new HashMap<String, String>();
private HashMap<String, Object> inputs = new HashMap<String, Object>();
private String host;
private int port;
private String boundary;
private boolean isMultipart = false;
private int bodyLength = 0;
public RequestBuilder(String resource) {
this.resource = resource;
}
public void setMethod(String method) {
this.method = method;
}
public void addHeader(String key, String value) {
headers.put(key, value);
}
public String getText() throws Exception {
ByteArrayOutputStream output = new ByteArrayOutputStream();
send(output);
return output.toString();
}
private String buildRequestLine() throws UnsupportedEncodingException {
StringBuffer text = new StringBuffer();
text.append(method).append(" ").append(resource);
if (isGet()) {
String inputString = inputString();
if (inputString.length() > 0)
text.append("?").append(inputString);
}
text.append(" HTTP/1.1");
return text.toString();
}
private boolean isGet() {
return method.equals("GET");
}
public void send(OutputStream output) throws IOException {
output.write(buildRequestLine().getBytes("UTF-8"));
output.write(ENDL);
buildBody();
sendHeaders(output);
output.write(ENDL);
sendBody(output);
}
private void sendHeaders(OutputStream output) throws IOException {
addHostHeader();
for (Iterator<String> iterator = headers.keySet().iterator(); iterator.hasNext();) {
String key = iterator.next();
output.write((key + ": " + headers.get(key)).getBytes("UTF-8"));
output.write(ENDL);
}
}
private void buildBody() throws IOException {
if (!isMultipart) {
byte[] bytes = inputString().getBytes("UTF-8");
bodyParts.add(new ByteArrayInputStream(bytes));
bodyLength += bytes.length;
} else {
for (Iterator<String> iterator = inputs.keySet().iterator(); iterator.hasNext();) {
String name = iterator.next();
Object value = inputs.get(name);
StringBuffer partBuffer = new StringBuffer();
partBuffer.append("--").append(getBoundary()).append("\r\n");
partBuffer.append("Content-Disposition: form-data; name=\"").append(name).append("\"").append("\r\n");
if (value instanceof InputStreamPart) {
InputStreamPart part = (InputStreamPart) value;
partBuffer.append("Content-Type: ").append(part.contentType).append("\r\n");
partBuffer.append("\r\n");
addBodyPart(partBuffer.toString());
bodyParts.add(part.input);
bodyLength += part.size;
addBodyPart("\r\n");
} else {
partBuffer.append("Content-Type: text/plain").append("\r\n");
partBuffer.append("\r\n");
partBuffer.append(value);
partBuffer.append("\r\n");
addBodyPart(partBuffer.toString());
}
}
StringBuffer tail = new StringBuffer();
tail.append("--").append(getBoundary()).append("--").append("\r\n");
addBodyPart(tail.toString());
}
addHeader("Content-Length", bodyLength + "");
}
private void addBodyPart(String input) throws UnsupportedEncodingException {
byte[] bytes = input.toString().getBytes("UTF-8");
bodyParts.add(new ByteArrayInputStream(bytes));
bodyLength += bytes.length;
}
private void sendBody(OutputStream output) throws IOException {
for (Iterator<InputStream> iterator = bodyParts.iterator(); iterator.hasNext();) {
InputStream input = iterator.next();
StreamReader reader = new StreamReader(input);
while (!reader.isEof()) {
byte[] bytes = reader.readBytes(1000);
output.write(bytes);
}
}
}
private void addHostHeader() {
if (host != null)
addHeader("Host", host + ":" + port);
else
addHeader("Host", "");
}
public void addInput(String key, Object value) {
inputs.put(key, value);
}
public String inputString() throws UnsupportedEncodingException {
StringBuffer buffer = new StringBuffer();
boolean first = true;
for (Iterator<String> iterator = inputs.keySet().iterator(); iterator.hasNext();) {
String key = iterator.next();
String value = (String) inputs.get(key);
if (!first)
buffer.append("&");
buffer.append(key).append("=").append(URLEncoder.encode(value, "UTF-8"));
first = false;
}
return buffer.toString();
}
public void addCredentials(String username, String password) {
String rawUserpass = username + ":" + password;
String userpass = Base64.encode(rawUserpass);
addHeader("Authorization", "Basic " + userpass);
}
public void setHostAndPort(String host, int port) {
this.host = host;
this.port = port;
}
public String getBoundary() {
if (boundary == null) {
boundary = "----------" + RANDOM_GENERATOR.nextInt() + "BoUnDaRy";
}
return boundary;
}
public void addInputAsPart(String name, Object content) {
multipart();
addInput(name, content);
}
public void addInputAsPart(String name, InputStream input, int size, String contentType) {
addInputAsPart(name, new InputStreamPart(input, size, contentType));
}
private void multipart() {
if (!isMultipart) {
isMultipart = true;
setMethod("POST");
addHeader("Content-Type", "multipart/form-data; boundary=" + getBoundary());
}
}
private static class InputStreamPart {
public InputStream input;
public int size;
public String contentType;
public InputStreamPart(InputStream input, int size, String contentType) {
this.input = input;
this.size = size;
this.contentType = contentType;
}
}
}