by.stub.yaml.stubs.StubRequest Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of stubby4j Show documentation
Show all versions of stubby4j Show documentation
HTTP stub server written in Java with embedded Jetty
/*
A Java-based HTTP stub server
Copyright (C) 2012 Alexander Zagniotov, Isa Goksu and Eric Mrak
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
package by.stub.yaml.stubs;
import by.stub.annotations.CoberturaIgnore;
import by.stub.annotations.VisibleForTesting;
import by.stub.common.Common;
import by.stub.utils.CollectionUtils;
import by.stub.utils.FileUtils;
import by.stub.utils.HandlerUtils;
import by.stub.utils.ObjectUtils;
import by.stub.utils.StringUtils;
import by.stub.yaml.YamlProperties;
import org.custommonkey.xmlunit.Diff;
import org.custommonkey.xmlunit.ElementNameAndAttributeQualifier;
import org.json.JSONException;
import org.skyscreamer.jsonassert.JSONCompare;
import org.skyscreamer.jsonassert.JSONCompareMode;
import org.xml.sax.SAXException;
import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import static by.stub.utils.StringUtils.isSet;
import static by.stub.utils.StringUtils.isWithinSquareBrackets;
import static by.stub.yaml.stubs.StubAuthorizationTypes.BASIC;
import static by.stub.yaml.stubs.StubAuthorizationTypes.BEARER;
import static by.stub.yaml.stubs.StubAuthorizationTypes.CUSTOM;
/**
* @author Alexander Zagniotov
* @since 6/14/12, 1:09 AM
*/
public class StubRequest {
public static final String HTTP_HEADER_AUTHORIZATION = "authorization";
private final String url;
private final String post;
private final File file;
private final byte[] fileBytes;
private final List method;
private final Map headers;
private final Map query;
private final Map regexGroups;
public StubRequest(final String url,
final String post,
final File file,
final List method,
final Map headers,
final Map query) {
this.url = url;
this.post = post;
this.file = file;
this.fileBytes = ObjectUtils.isNull(file) ? new byte[]{} : getFileBytes();
this.method = ObjectUtils.isNull(method) ? new ArrayList() : method;
this.headers = ObjectUtils.isNull(headers) ? new LinkedHashMap() : headers;
this.query = ObjectUtils.isNull(query) ? new LinkedHashMap() : query;
this.regexGroups = new TreeMap<>();
}
public final ArrayList getMethod() {
final ArrayList uppercase = new ArrayList<>(method.size());
for (final String string : method) {
uppercase.add(StringUtils.toUpper(string));
}
return uppercase;
}
public void addMethod(final String newMethod) {
if (isSet(newMethod)) {
method.add(newMethod);
}
}
public String getUrl() {
if (getQuery().isEmpty()) {
return url;
}
final String queryString = CollectionUtils.constructQueryString(query);
return String.format("%s?%s", url, queryString);
}
private byte[] getFileBytes() {
try {
return FileUtils.fileToBytes(file);
} catch (Exception e) {
return new byte[]{};
}
}
public String getPostBody() {
if (fileBytes.length == 0) {
return FileUtils.enforceSystemLineSeparator(post);
}
final String utf8FileContent = StringUtils.newStringUtf8(fileBytes);
return FileUtils.enforceSystemLineSeparator(utf8FileContent);
}
//Used by reflection when populating stubby admin page with stubbed information
public String getPost() {
return post;
}
public final Map getHeaders() {
final Map headersCopy = new LinkedHashMap<>(headers);
final Set> entrySet = headersCopy.entrySet();
this.headers.clear();
for (final Map.Entry entry : entrySet) {
this.headers.put(StringUtils.toLower(entry.getKey()), entry.getValue());
}
return headers;
}
public Map getQuery() {
return query;
}
public byte[] getFile() {
return fileBytes;
}
// Just a shallow copy that protects collection from modification, the points themselves are not copied
public Map getRegexGroups() {
return new TreeMap<>(regexGroups);
}
public File getRawFile() {
return file;
}
public boolean hasHeaders() {
return !getHeaders().isEmpty();
}
public boolean hasQuery() {
return !getQuery().isEmpty();
}
public boolean hasPostBody() {
return isSet(getPostBody());
}
public boolean isSecured() {
return getHeaders().containsKey(BASIC.asYamlProp()) ||
getHeaders().containsKey(BEARER.asYamlProp()) ||
getHeaders().containsKey(CUSTOM.asYamlProp());
}
@VisibleForTesting StubAuthorizationTypes getStubbedAuthorizationTypeHeader() {
if (getHeaders().containsKey(BASIC.asYamlProp())) {
return BASIC;
} else if (getHeaders().containsKey(BEARER.asYamlProp())) {
return BEARER;
} else {
return CUSTOM;
}
}
String getStubbedAuthorizationHeaderValue(final StubAuthorizationTypes stubbedAuthorizationHeaderType) {
return getHeaders().get(stubbedAuthorizationHeaderType.asYamlProp());
}
public String getRawAuthorizationHttpHeader() {
return getHeaders().get(HTTP_HEADER_AUTHORIZATION);
}
public static StubRequest newStubRequest() {
return new StubRequest(null, null, null, null, null, null);
}
public static StubRequest newStubRequest(final String url, final String post) {
return new StubRequest(url, post, null, null, null, null);
}
public static StubRequest createFromHttpServletRequest(final HttpServletRequest request) throws IOException {
final StubRequest assertionRequest = StubRequest.newStubRequest(request.getPathInfo(),
HandlerUtils.extractPostRequestBody(request, "stubs"));
assertionRequest.addMethod(request.getMethod());
final Enumeration headerNamesEnumeration = request.getHeaderNames();
final List headerNames = ObjectUtils.isNotNull(headerNamesEnumeration)
? Collections.list(request.getHeaderNames()) : new LinkedList();
for (final String headerName : headerNames) {
final String headerValue = request.getHeader(headerName);
assertionRequest.getHeaders().put(StringUtils.toLower(headerName), headerValue);
}
assertionRequest.getQuery().putAll(CollectionUtils.constructParamMap(request.getQueryString()));
return assertionRequest;
}
@Override
public boolean equals(final Object o) {
if (this == o) {
return true;
} else if (o instanceof StubRequest) {
final StubRequest dataStoreRequest = (StubRequest) o;
return urlsMatch(dataStoreRequest.url, this.url)
&& arraysIntersect(dataStoreRequest.getMethod(), this.getMethod())
&& postBodiesMatch(dataStoreRequest.isPostStubbed(), dataStoreRequest.getPostBody(), this.getPostBody())
&& headersMatch(dataStoreRequest.getHeaders(), this.getHeaders())
&& queriesMatch(dataStoreRequest.getQuery(), this.getQuery());
}
return false;
}
@VisibleForTesting boolean isPostStubbed() {
return isSet(this.getPostBody()) && (getMethod().contains("POST") || getMethod().contains("PUT"));
}
private boolean urlsMatch(final String dataStoreUrl, final String thisAssertingUrl) {
return stringsMatch(dataStoreUrl, thisAssertingUrl, YamlProperties.URL);
}
private boolean postBodiesMatch(final boolean isDataStorePostStubbed, final String dataStorePostBody, final String thisAssertingPostBody) {
if (isDataStorePostStubbed) {
final String assertingContentType = this.getHeaders().get("content-type");
final boolean isAssertingValueSet = isSet(thisAssertingPostBody);
if (!isAssertingValueSet) {
return false;
} else if (isSet(assertingContentType) && assertingContentType.contains(Common.HEADER_APPLICATION_JSON)) {
try {
return JSONCompare.compareJSON(dataStorePostBody, thisAssertingPostBody, JSONCompareMode.NON_EXTENSIBLE).passed();
} catch (JSONException e) {
return false;
}
} else if (isSet(assertingContentType) && assertingContentType.contains(Common.HEADER_APPLICATION_XML)) {
try {
final Diff diff = new Diff(dataStorePostBody, thisAssertingPostBody);
diff.overrideElementQualifier(new ElementNameAndAttributeQualifier());
return (diff.similar() || diff.identical());
} catch (SAXException | IOException e) {
return false;
}
} else {
return stringsMatch(dataStorePostBody, thisAssertingPostBody, YamlProperties.POST);
}
} else {
return true;
}
}
private boolean queriesMatch(final Map dataStoreQuery, final Map thisAssertingQuery) {
return mapsMatch(dataStoreQuery, thisAssertingQuery, YamlProperties.QUERY);
}
private boolean headersMatch(final Map dataStoreHeaders, final Map thisAssertingHeaders) {
final Map dataStoreHeadersCopy = new HashMap<>(dataStoreHeaders);
for (StubAuthorizationTypes authorizationType : StubAuthorizationTypes.values()) {
// auth header is dealt with in StubbedDataManager after request is matched
dataStoreHeadersCopy.remove(authorizationType.asYamlProp());
}
return mapsMatch(dataStoreHeadersCopy, thisAssertingHeaders, YamlProperties.HEADERS);
}
@VisibleForTesting
boolean mapsMatch(final Map dataStoreMap, final Map thisAssertingMap, final String mapName) {
if (dataStoreMap.isEmpty()) {
return true;
} else if (thisAssertingMap.isEmpty()) {
return false;
}
final Map dataStoreMapCopy = new HashMap<>(dataStoreMap);
final Map assertingMapCopy = new HashMap<>(thisAssertingMap);
for (Map.Entry dataStoreParam : dataStoreMapCopy.entrySet()) {
final boolean containsRequiredParam = assertingMapCopy.containsKey(dataStoreParam.getKey());
if (!containsRequiredParam) {
return false;
} else {
final String assertedQueryValue = assertingMapCopy.get(dataStoreParam.getKey());
final String templateTokenName = String.format("%s.%s", mapName, dataStoreParam.getKey());
if (!stringsMatch(dataStoreParam.getValue(), assertedQueryValue, templateTokenName)) {
return false;
}
}
}
return true;
}
@VisibleForTesting
boolean stringsMatch(final String dataStoreValue, final String thisAssertingValue, final String templateTokenName) {
final boolean isDataStoreValueSet = isSet(dataStoreValue);
final boolean isAssertingValueSet = isSet(thisAssertingValue);
if (!isDataStoreValueSet) {
return true;
} else if (!isAssertingValueSet) {
return false;
} else if (isWithinSquareBrackets(dataStoreValue)) {
return dataStoreValue.equals(thisAssertingValue);
} else {
return regexMatch(dataStoreValue, thisAssertingValue, templateTokenName);
}
}
private boolean regexMatch(final String dataStoreValue, final String thisAssertingValue, final String templateTokenName) {
try {
// Pattern.MULTILINE changes the behavior of '^' and '$' characters,
// it does not mean that newline feeds and carriage return will be matched by default
// You need to make sure that you regex pattern covers both \r (carriage return) and \n (linefeed).
// It is achievable by using symbol '\s+' which covers both \r (carriage return) and \n (linefeed).
final Matcher matcher = Pattern.compile(dataStoreValue, Pattern.MULTILINE).matcher(thisAssertingValue);
final boolean isMatch = matcher.matches();
if (isMatch) {
// group(0) holds the full regex match
regexGroups.put(StringUtils.buildToken(templateTokenName, 0), matcher.group(0));
//Matcher.groupCount() returns the number of explicitly defined capturing groups in the pattern regardless
// of whether the capturing groups actually participated in the match. It does not include matcher.group(0)
final int groupCount = matcher.groupCount();
if (groupCount > 0) {
for (int idx = 1; idx <= groupCount; idx++) {
regexGroups.put(StringUtils.buildToken(templateTokenName, idx), matcher.group(idx));
}
}
}
return isMatch;
} catch (PatternSyntaxException e) {
return dataStoreValue.equals(thisAssertingValue);
}
}
@VisibleForTesting
boolean arraysIntersect(final ArrayList dataStoreArray, final ArrayList thisAssertingArray) {
if (dataStoreArray.isEmpty()) {
return true;
} else if (!thisAssertingArray.isEmpty()) {
for (final String entry : thisAssertingArray) {
if (dataStoreArray.contains(entry)) {
return true;
}
}
}
return false;
}
@Override
@CoberturaIgnore
public int hashCode() {
int result = (ObjectUtils.isNotNull(url) ? url.hashCode() : 0);
result = 31 * result + method.hashCode();
result = 31 * result + (ObjectUtils.isNotNull(post) ? post.hashCode() : 0);
result = 31 * result + (ObjectUtils.isNotNull(fileBytes) && fileBytes.length != 0 ? Arrays.hashCode(fileBytes) : 0);
result = 31 * result + headers.hashCode();
result = 31 * result + query.hashCode();
return result;
}
@Override
@CoberturaIgnore
public final String toString() {
final StringBuffer sb = new StringBuffer();
sb.append("StubRequest");
sb.append("{url=").append(url);
sb.append(", method=").append(method);
if (!ObjectUtils.isNull(post)) {
sb.append(", post=").append(post);
}
sb.append(", query=").append(query);
sb.append(", headers=").append(getHeaders());
sb.append('}');
return sb.toString();
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy