All Downloads are FREE. Search and download functionalities are using the official Maven repository.

software.xdev.mockserver.model.HttpResponse Maven / Gradle / Ivy

There is a newer version: 1.0.8
Show newest version
/*
 * Copyright © 2024 XDEV Software (https://xdev.software)
 *
 * Licensed 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 software.xdev.mockserver.model;

import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_TYPE;
import static io.netty.handler.codec.http.HttpHeaderNames.SET_COOKIE;
import static software.xdev.mockserver.model.Header.header;
import static software.xdev.mockserver.model.HttpStatusCode.NOT_FOUND_404;
import static software.xdev.mockserver.model.HttpStatusCode.OK_200;
import static software.xdev.mockserver.model.NottableString.string;

import java.nio.charset.Charset;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import com.fasterxml.jackson.annotation.JsonIgnore;

import io.netty.handler.codec.http.cookie.ClientCookieDecoder;


@SuppressWarnings("rawtypes")
public class HttpResponse extends Action implements HttpMessage
{
	private int hashCode;
	private Integer statusCode;
	private String reasonPhrase;
	private BodyWithContentType body;
	private Headers headers;
	private Cookies cookies;
	private ConnectionOptions connectionOptions;
	private Integer streamId;
	
	/**
	 * Static builder to create a response.
	 */
	public static HttpResponse response()
	{
		return new HttpResponse();
	}
	
	/**
	 * Static builder to create a response with a 200 status code and the string response body.
	 *
	 * @param body a string
	 */
	public static HttpResponse response(final String body)
	{
		return new HttpResponse().withStatusCode(OK_200.code()).withReasonPhrase(OK_200.reasonPhrase()).withBody(body);
	}
	
	/**
	 * Static builder to create a not found response.
	 */
	public static HttpResponse notFoundResponse()
	{
		return new HttpResponse().withStatusCode(NOT_FOUND_404.code()).withReasonPhrase(NOT_FOUND_404.reasonPhrase());
	}
	
	/**
	 * The status code to return, such as 200, 404, the status code specified here will result in the default status
	 * message for this status code for example for 200 the status message "OK" is used
	 *
	 * @param statusCode an integer such as 200 or 404
	 */
	public HttpResponse withStatusCode(final Integer statusCode)
	{
		this.statusCode = statusCode;
		this.hashCode = 0;
		return this;
	}
	
	public Integer getStatusCode()
	{
		return this.statusCode;
	}
	
	/**
	 * The reason phrase to return, if no reason code is returned this will be defaulted to the standard reason phrase
	 * for the statusCode, i.e. for a statusCode of 200 the standard reason phrase is "OK"
	 *
	 * @param reasonPhrase an string such as "Not Found" or "OK"
	 */
	public HttpResponse withReasonPhrase(final String reasonPhrase)
	{
		this.reasonPhrase = reasonPhrase;
		this.hashCode = 0;
		return this;
	}
	
	public String getReasonPhrase()
	{
		return this.reasonPhrase;
	}
	
	/**
	 * Set response body to return as a string response body. The character set will be determined by the Content-Type
	 * header on the response. To force the character set, use {@link #withBody(String, Charset)}.
	 *
	 * @param body a string
	 */
	@Override
	public HttpResponse withBody(final String body)
	{
		if(body != null)
		{
			this.body = new StringBody(body);
			this.hashCode = 0;
		}
		return this;
	}
	
	/**
	 * Set response body to return a string response body with the specified encoding. Note: The character
	 * set of
	 * the response will be forced to the specified charset, even if the Content-Type header specifies otherwise.
	 *
	 * @param body    a string
	 * @param charset character set the string will be encoded in
	 */
	@Override
	public HttpResponse withBody(final String body, final Charset charset)
	{
		if(body != null)
		{
			this.body = new StringBody(body, charset);
			this.hashCode = 0;
		}
		return this;
	}
	
	/**
	 * Set response body to return a string response body with the specified encoding. Note: The character
	 * set of
	 * the response will be forced to the specified charset, even if the Content-Type header specifies otherwise.
	 *
	 * @param body        a string
	 * @param contentType media type, if charset is included this will be used for encoding string
	 */
	public HttpResponse withBody(final String body, final MediaType contentType)
	{
		if(body != null)
		{
			this.body = new StringBody(body, contentType);
			this.hashCode = 0;
		}
		return this;
	}
	
	/**
	 * Set response body to return as binary such as a pdf or image
	 *
	 * @param body a byte array
	 */
	@Override
	public HttpResponse withBody(final byte[] body)
	{
		this.body = new BinaryBody(body);
		this.hashCode = 0;
		return this;
	}
	
	/**
	 * Set the body to return for example:
	 * 

* binary body: - binary(IOUtils.readFully(getClass().getClassLoader().getResourceAsStream("example.pdf"), 1024)); *

* or *

* - new BinaryBody(IOUtils.readFully(getClass().getClassLoader().getResourceAsStream("example.pdf"), 1024)); * * @param body an instance of one of the Body subclasses including StringBody or BinaryBody */ @Override public HttpResponse withBody(final BodyWithContentType body) { this.body = body; this.hashCode = 0; return this; } @Override public BodyWithContentType getBody() { return this.body; } @Override @JsonIgnore public byte[] getBodyAsRawBytes() { return this.body != null ? this.body.getRawBytes() : new byte[0]; } @Override @JsonIgnore public String getBodyAsString() { if(this.body != null) { return this.body.toString(); } else { return null; } } @Override public Headers getHeaders() { return this.headers; } private Headers getOrCreateHeaders() { if(this.headers == null) { this.headers = new Headers(); this.hashCode = 0; } return this.headers; } @Override public HttpResponse withHeaders(final Headers headers) { if(headers == null || headers.isEmpty()) { this.headers = null; } else { this.headers = headers; } this.hashCode = 0; return this; } /** * The headers to return as a list of Header objects * * @param headers a list of Header objects */ @Override public HttpResponse withHeaders(final List

headers) { this.getOrCreateHeaders().withEntries(headers); this.hashCode = 0; return this; } /** * The headers to return as a varargs of Header objects * * @param headers varargs of Header objects */ @Override public HttpResponse withHeaders(final Header... headers) { this.getOrCreateHeaders().withEntries(headers); this.hashCode = 0; return this; } /** * Add a header to return as a Header object, if a header with the same name already exists this will NOT be * modified but two headers will exist * * @param header a Header object */ @Override public HttpResponse withHeader(final Header header) { this.getOrCreateHeaders().withEntry(header); this.hashCode = 0; return this; } /** * Add a header to return as a Header object, if a header with the same name already exists this will NOT be * modified but two headers will exist * * @param name the header name * @param values the header values */ @Override public HttpResponse withHeader(final String name, final String... values) { this.getOrCreateHeaders().withEntry(name, values.length == 0 ? new String[]{".*"} : values); this.hashCode = 0; return this; } /** * Add a header to return as a Header object, if a header with the same name already exists this will NOT be * modified but two headers will exist * * @param name the header name as a NottableString * @param values the header values which can be a varags of NottableStrings */ @Override public HttpResponse withHeader(final NottableString name, final NottableString... values) { this.getOrCreateHeaders() .withEntry(header(name, values.length == 0 ? new NottableString[]{string(".*")} : values)); this.hashCode = 0; return this; } @Override public HttpResponse withContentType(final MediaType mediaType) { this.getOrCreateHeaders().withEntry(header(CONTENT_TYPE.toString(), mediaType.toString())); this.hashCode = 0; return this; } /** * Update header to return as a Header object, if a header with the same name already exists it will be modified * * @param header a Header object */ @Override public HttpResponse replaceHeader(final Header header) { this.getOrCreateHeaders().replaceEntry(header); this.hashCode = 0; return this; } /** * Update header to return as a Header object, if a header with the same name already exists it will be modified * * @param name the header name * @param values the header values */ public HttpResponse replaceHeader(final String name, final String... values) { this.getOrCreateHeaders().replaceEntry(name, values.length == 0 ? new String[]{".*"} : values); this.hashCode = 0; return this; } @Override public List
getHeaderList() { if(this.headers != null) { return this.headers.getEntries(); } else { return Collections.emptyList(); } } public Map> getHeaderMultimap() { if(this.headers != null) { return this.headers.getMultimap(); } else { return null; } } @Override public List getHeader(final String name) { if(this.headers != null) { return this.headers.getValues(name); } else { return Collections.emptyList(); } } @Override public String getFirstHeader(final String name) { if(this.headers != null) { return this.headers.getFirstValue(name); } else { return ""; } } /** * Returns true if a header with the specified name has been added * * @param name the header name * @return true if a header has been added with that name otherwise false */ @Override public boolean containsHeader(final String name) { if(this.headers != null) { return this.headers.containsEntry(name); } else { return false; } } @Override public HttpResponse removeHeader(final String name) { if(this.headers != null) { this.headers.remove(name); this.hashCode = 0; } return this; } @Override public HttpResponse removeHeader(final NottableString name) { if(this.headers != null) { this.headers.remove(name); this.hashCode = 0; } return this; } /** * Returns true if a header with the specified name has been added * * @param name the header name * @param value the header value * @return true if a header has been added with that name otherwise false */ public boolean containsHeader(final String name, final String value) { if(this.headers != null) { return this.headers.containsEntry(name, value); } else { return false; } } @Override public Cookies getCookies() { return this.cookies; } private Cookies getOrCreateCookies() { if(this.cookies == null) { this.cookies = new Cookies(); this.hashCode = 0; } return this.cookies; } @Override public HttpResponse withCookies(final Cookies cookies) { if(cookies == null || cookies.isEmpty()) { this.cookies = null; } else { this.cookies = cookies; } this.hashCode = 0; return this; } /** * The cookies to return as Set-Cookie headers as a list of Cookie objects * * @param cookies a list of Cookie objects */ @Override public HttpResponse withCookies(final List cookies) { this.getOrCreateCookies().withEntries(cookies); this.hashCode = 0; return this; } /** * The cookies to return as Set-Cookie headers as a varargs of Cookie objects * * @param cookies a varargs of Cookie objects */ @Override public HttpResponse withCookies(final Cookie... cookies) { this.getOrCreateCookies().withEntries(cookies); this.hashCode = 0; return this; } /** * Add cookie to return as Set-Cookie header * * @param cookie a Cookie object */ @Override public HttpResponse withCookie(final Cookie cookie) { this.getOrCreateCookies().withEntry(cookie); this.hashCode = 0; return this; } /** * Add cookie to return as Set-Cookie header * * @param name the cookies name * @param value the cookies value */ @Override public HttpResponse withCookie(final String name, final String value) { this.getOrCreateCookies().withEntry(name, value); this.hashCode = 0; return this; } /** *

* Adds one cookie to match on or to not match on using the NottableString, each NottableString can either be a * positive matching value, such as string("match"), or a value to not match on, such as not("do not match"), the * string values passed to the NottableString can be a plain string or a regex (for more details of the supported * regex syntax see ...) *

* * @param name the cookies name * @param value the cookies value */ @Override public HttpResponse withCookie(final NottableString name, final NottableString value) { this.getOrCreateCookies().withEntry(name, value); this.hashCode = 0; return this; } @Override public List getCookieList() { if(this.cookies != null) { return this.cookies.getEntries(); } else { return Collections.emptyList(); } } public Map getCookieMap() { if(this.cookies != null) { return this.cookies.getMap(); } else { return null; } } public boolean cookieHeaderDoesNotAlreadyExists(final Cookie cookieValue) { final List setCookieHeaders = this.getHeader(SET_COOKIE.toString()); for(final String setCookieHeader : setCookieHeaders) { final String existingCookieName = ClientCookieDecoder.LAX.decode(setCookieHeader).name(); final String existingCookieValue = ClientCookieDecoder.LAX.decode(setCookieHeader).value(); if(existingCookieName.equalsIgnoreCase(cookieValue.getName().getValue()) && existingCookieValue.equalsIgnoreCase(cookieValue.getValue().getValue())) { return false; } } return true; } public boolean cookieHeaderDoesNotAlreadyExists(final String name, final String value) { final List setCookieHeaders = this.getHeader(SET_COOKIE.toString()); for(final String setCookieHeader : setCookieHeaders) { final String existingCookieName = ClientCookieDecoder.LAX.decode(setCookieHeader).name(); final String existingCookieValue = ClientCookieDecoder.LAX.decode(setCookieHeader).value(); if(existingCookieName.equalsIgnoreCase(name) && existingCookieValue.equalsIgnoreCase(value)) { return false; } } return true; } /** * The connection options for override the default connection behaviour, this allows full control of headers * such as * "Connection" or "Content-Length" or controlling whether the socket is closed after the response has been sent * * @param connectionOptions the connection options for override the default connection behaviour */ public HttpResponse withConnectionOptions(final ConnectionOptions connectionOptions) { this.connectionOptions = connectionOptions; this.hashCode = 0; return this; } public ConnectionOptions getConnectionOptions() { return this.connectionOptions; } public HttpResponse withStreamId(final Integer streamId) { this.streamId = streamId; this.hashCode = 0; return this; } public Integer getStreamId() { return this.streamId; } @Override @JsonIgnore public Type getType() { return Type.RESPONSE; } public HttpResponse shallowClone() { return response() .withStatusCode(this.statusCode) .withReasonPhrase(this.reasonPhrase) .withBody(this.body) .withHeaders(this.headers) .withCookies(this.cookies) .withDelay(this.getDelay()) .withConnectionOptions(this.connectionOptions) .withStreamId(this.streamId); } @Override @SuppressWarnings({"MethodDoesntCallSuperMethod", "checkstyle:NoClone"}) public HttpResponse clone() { return response() .withStatusCode(this.statusCode) .withReasonPhrase(this.reasonPhrase) .withBody(this.body) .withHeaders(this.headers != null ? this.headers.clone() : null) .withCookies(this.cookies != null ? this.cookies.clone() : null) .withDelay(this.getDelay()) .withConnectionOptions(this.connectionOptions) .withStreamId(this.streamId); } public HttpResponse update(final HttpResponse responseOverride, final HttpResponseModifier responseModifier) { if(responseOverride != null) { if(responseOverride.getStatusCode() != null) { this.withStatusCode(responseOverride.getStatusCode()); } if(responseOverride.getReasonPhrase() != null) { this.withReasonPhrase(responseOverride.getReasonPhrase()); } for(final Header header : responseOverride.getHeaderList()) { this.getOrCreateHeaders().replaceEntry(header); } for(final Cookie cookie : responseOverride.getCookieList()) { this.withCookie(cookie); } if(responseOverride.getBody() != null) { this.withBody(responseOverride.getBody()); } if(responseOverride.getConnectionOptions() != null) { this.withConnectionOptions(responseOverride.getConnectionOptions()); } if(responseOverride.getStreamId() != null) { this.withStreamId(responseOverride.getStreamId()); } this.hashCode = 0; } if(responseModifier != null) { if(responseModifier.getHeaders() != null) { this.withHeaders(responseModifier.getHeaders().update(this.getHeaders())); } if(responseModifier.getCookies() != null) { this.withCookies(responseModifier.getCookies().update(this.getCookies())); } } return this; } @Override public boolean equals(final Object o) { if(this == o) { return true; } if(o == null || this.getClass() != o.getClass()) { return false; } if(this.hashCode() != o.hashCode()) { return false; } if(!super.equals(o)) { return false; } final HttpResponse that = (HttpResponse)o; return Objects.equals(this.statusCode, that.statusCode) && Objects.equals(this.reasonPhrase, that.reasonPhrase) && Objects.equals(this.body, that.body) && Objects.equals(this.headers, that.headers) && Objects.equals(this.cookies, that.cookies) && Objects.equals(this.connectionOptions, that.connectionOptions) && Objects.equals(this.streamId, that.streamId); } @Override public int hashCode() { if(this.hashCode == 0) { this.hashCode = Objects.hash( super.hashCode(), this.statusCode, this.reasonPhrase, this.body, this.headers, this.cookies, this.connectionOptions, this.streamId); } return this.hashCode; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy