// 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.LinkedList; import java.util.List; import java.util.Map; import java.util.Random; import util.StreamReader; import fitnesse.util.Base64; import static util.FileUtil.CHARENCODING; 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<>(); private HashMap<String, String> headers = new HashMap<>(); private HashMap<String, Object> inputs = new HashMap<>(); 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 { StringBuilder text = new StringBuilder(); text.append(method).append(" ").append(resource); if (isGet()) { String inputString = inputString(); if (!inputString.isEmpty()) 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(CHARENCODING)); output.write(ENDL); buildBody(); sendHeaders(output); output.write(ENDL); sendBody(output); } private void sendHeaders(OutputStream output) throws IOException { addHostHeader(); for (Map.Entry<String, String> entry : headers.entrySet()) { output.write((entry.getKey() + ": " + entry.getValue()).getBytes(CHARENCODING)); output.write(ENDL); } } private void buildBody() throws IOException { if (!isMultipart) { byte[] bytes = inputString().getBytes(CHARENCODING); bodyParts.add(new ByteArrayInputStream(bytes)); bodyLength += bytes.length; } else { for (Map.Entry<String, Object> entry : inputs.entrySet()) { String name = entry.getKey(); Object value = entry.getValue(); StringBuilder partBuffer = new StringBuilder(); 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()); } } String tail = "--" + getBoundary() + "--" + "\r\n"; addBodyPart(tail); } addHeader("Content-Length", bodyLength + ""); } private void addBodyPart(String input) throws UnsupportedEncodingException { byte[] bytes = input.getBytes(CHARENCODING); bodyParts.add(new ByteArrayInputStream(bytes)); bodyLength += bytes.length; } private void sendBody(OutputStream output) throws IOException { for (InputStream input : bodyParts) { 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 { StringBuilder buffer = new StringBuilder(); boolean first = true; for (Map.Entry<String, Object> entry : inputs.entrySet()) { String value = (String) entry.getValue(); if (!first) buffer.append("&"); String key = entry.getKey(); buffer.append(key).append("=").append(URLEncoder.encode(value, CHARENCODING)); 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; } } }