/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.nifi.cluster.manager.testutils; import java.io.IOException; import java.io.Reader; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.ws.rs.HttpMethod; import javax.ws.rs.core.MediaType; import org.apache.commons.lang3.StringUtils; /** * Encapsulates an HTTP request. The toString method returns the specification-compliant request. * */ public class HttpRequest { private String method; private String uri; private String rawUri; private String version; private String body; private String rawRequest; private Map<String, String> headers = new HashMap<>(); private Map<String, List<String>> parameters = new HashMap<>(); public static HttpRequestBuilder createFromRequestLine(final String requestLine) { return new HttpRequestBuilder(requestLine); } public String getBody() { return body; } public Map<String, String> getHeaders() { return Collections.unmodifiableMap(headers); } public String getHeaderValue(final String header) { for (final Map.Entry<String, String> entry : getHeaders().entrySet()) { if (entry.getKey().equalsIgnoreCase(header)) { return entry.getValue(); } } return null; } public String getMethod() { return method; } public Map<String, List<String>> getParameters() { final Map<String, List<String>> result = new HashMap<>(); for (final Map.Entry<String, List<String>> entry : parameters.entrySet()) { result.put(entry.getKey(), Collections.unmodifiableList(entry.getValue())); } return Collections.unmodifiableMap(result); } public String getUri() { return uri; } public String getRawUri() { return rawUri; } public String getVersion() { return version; } @Override public String toString() { return rawRequest; } /** * A builder for constructing basic HTTP requests. It handles only enough of the HTTP specification to support basic unit testing, and it should not be used otherwise. */ public static class HttpRequestBuilder { private String method; private String uri; private String rawUri; private String version; private Map<String, String> headers = new HashMap<>(); private Map<String, List<String>> parameters = new HashMap<>(); private int contentLength = 0; private String contentType; private String body = ""; private StringBuilder rawRequest = new StringBuilder(); private HttpRequestBuilder(final String requestLine) { final String[] tokens = requestLine.split(" "); if (tokens.length != 3) { throw new IllegalArgumentException("Invalid HTTP Request Line: " + requestLine); } method = tokens[0]; if (HttpMethod.GET.equalsIgnoreCase(method) || HttpMethod.HEAD.equalsIgnoreCase(method) || HttpMethod.DELETE.equalsIgnoreCase(method) || HttpMethod.OPTIONS.equalsIgnoreCase(method)) { final int queryIndex = tokens[1].indexOf("?"); if (queryIndex > -1) { uri = tokens[1].substring(0, queryIndex); addParameters(tokens[1].substring(queryIndex + 1)); } else { uri = tokens[1]; } } rawUri = tokens[1]; version = tokens[2]; rawRequest.append(requestLine).append("\n"); } private void addHeader(final String key, final String value) { if (key.contains(" ")) { throw new IllegalArgumentException("Header key may not contain spaces."); } else if ("content-length".equalsIgnoreCase(key)) { contentLength = (StringUtils.isBlank(value.trim())) ? 0 : Integer.parseInt(value.trim()); } else if ("content-type".equalsIgnoreCase(key)) { contentType = value.trim(); } headers.put(key, value); } public void addHeader(final String header) { final int firstColonIndex = header.indexOf(":"); if (firstColonIndex < 0) { throw new IllegalArgumentException("Invalid HTTP Header line: " + header); } addHeader(header.substring(0, firstColonIndex), header.substring(firstColonIndex + 1)); rawRequest.append(header).append("\n"); } // final because constructor calls it public final void addParameters(final String queryString) { if (StringUtils.isBlank(queryString)) { return; } final String normQueryString; if (queryString.startsWith("?")) { normQueryString = queryString.substring(1); } else { normQueryString = queryString; } final String[] keyValuePairs = normQueryString.split("&"); for (final String keyValuePair : keyValuePairs) { final String[] keyValueTokens = keyValuePair.split("="); try { addParameter( URLDecoder.decode(keyValueTokens[0], "utf-8"), URLDecoder.decode(keyValueTokens[1], "utf-8") ); } catch (UnsupportedEncodingException use) { throw new RuntimeException(use); } } } public void addParameter(final String key, final String value) { if (key.contains(" ")) { throw new IllegalArgumentException("Parameter key may not contain spaces: " + key); } final List<String> values; if (parameters.containsKey(key)) { values = parameters.get(key); } else { values = new ArrayList<>(); parameters.put(key, values); } values.add(value); } public void addBody(final Reader reader) throws IOException { if (contentLength <= 0) { return; } final char[] buf = new char[contentLength]; int offset = 0; int numRead = 0; while (offset < buf.length && (numRead = reader.read(buf, offset, buf.length - offset)) >= 0) { offset += numRead; } body = new String(buf); rawRequest.append("\n"); rawRequest.append(body); } public HttpRequest build() throws UnsupportedEncodingException { if (HttpMethod.GET.equalsIgnoreCase(method) == false && HttpMethod.HEAD.equalsIgnoreCase(method) == false && contentType.equalsIgnoreCase(MediaType.APPLICATION_FORM_URLENCODED)) { addParameters(body); } final HttpRequest request = new HttpRequest(); request.method = this.method; request.uri = this.uri; request.rawUri = this.rawUri; request.version = this.version; request.headers.putAll(this.headers); request.parameters.putAll(this.parameters); request.body = this.body; request.rawRequest = this.rawRequest.toString(); return request; } } }