/*
 * Copyright 2019 the original author or authors.
 *
 * 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 io.restassured;

import io.restassured.authentication.*;
import io.restassured.builder.RequestSpecBuilder;
import io.restassured.builder.ResponseSpecBuilder;
import io.restassured.config.*;
import io.restassured.filter.Filter;
import io.restassured.filter.log.LogDetail;
import io.restassured.http.Method;
import io.restassured.internal.*;
import io.restassured.internal.common.assertion.AssertParameter;
import io.restassured.internal.log.LogRepository;
import io.restassured.mapper.ObjectMapper;
import io.restassured.matcher.RestAssuredMatchers;
import io.restassured.parsing.Parser;
import io.restassured.path.json.JsonPath;
import io.restassured.path.xml.XmlPath;
import io.restassured.response.Response;
import io.restassured.specification.*;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;

import java.io.File;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.security.KeyStore;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import static io.restassured.specification.ProxySpecification.host;

/**
 * REST Assured is a Java DSL for simplifying testing of REST based services built on top of
 * HTTP Builder.
 * It supports POST, GET, PUT, DELETE, HEAD, PATCH  and OPTIONS
 * requests and to verify the response of these requests. Usage examples:
 *

 *

 * Assume that the GET request (to <tt>http://localhost:8080/lotto</tt>) returns JSON as:
 *

 * {
 * "lotto":{
 *   "lottoId":5,
 *   "winning-numbers":[2,45,34,23,7,5,3],
 *   "winners":[{
 *     "winnerId":23,
 *     "numbers":[2,45,34,23,3,5]
 *   },{
 *     "winnerId":54,
 *     "numbers":[52,3,12,11,18,22]
 *   }]
 *  }
 * }
 *

 *

 * REST assured can then help you to easily make the GET request and verify the response. E.g. if you want to verify
 * that <tt>lottoId</tt> is equal to 5 you can do like this:
 *

 *

 * get("/lotto").then().assertThat().body("lotto.lottoId", equalTo(5));
 *

 *

 * or perhaps you want to check that the winnerId's are 23 and 54:
 *

 * get("/lotto").then().assertThat().body("lotto.winners.winnerId", hasItems(23, 54));
 *

 *

 *

 * XML can be verified in a similar way. Imagine that a POST request to <tt>http://localhost:8080/greetXML<tt>  returns:
 *

 * <greeting>
 *     <firstName>{params("firstName")}</firstName>
 *     <lastName>{params("lastName")}</lastName>
 *   </greeting>
 *

 *

 * i.e. it sends back a greeting based on the <tt>firstName</tt> and <tt>lastName</tt> parameter sent in the request.
 * You can easily perform and verify e.g. the <tt>firstName</tt> with REST assured:
 *

 * with().parameters("firstName", "John", "lastName", "Doe").when().post("/greetXML").then().assertThat().body("greeting.firstName", equalTo("John"));
 *

 *

 * If you want to verify both <tt>firstName</tt> and <tt>lastName</tt> you may do like this:
 *

 * with().parameters("firstName", "John", "lastName", "Doe").when().post("/greetXML").then().assertThat().body("greeting.firstName", equalTo("John")).and().body("greeting.lastName", equalTo("Doe"));
 *

 *

 * or a little shorter:
 *

 * with().parameters("firstName", "John", "lastName", "Doe").when().post("/greetXML").then().assertThat().body("greeting.firstName", equalTo("John"), "greeting.lastName", equalTo("Doe"));
 *

 *

 *

 * You can also verify XML responses using x-path. For example:
 *

 * given().parameters("firstName", "John", "lastName", "Doe").when().post("/greetXML").then().assertThat().body(hasXPath("/greeting/firstName", containsString("Jo")));
 *

 * or
 *

 * with().parameters("firstName", "John", "lastName", "Doe").post("/greetXML").then().body(hasXPath("/greeting/firstName[text()='John']"));
 *

 *

 *

 * XML response bodies can also be verified against an XML Schema (XSD) or DTD.
XSD example:
 *

 * get("/carRecords").then().assertThat().body(matchesXsd(xsd));
 *

 * DTD example:
 *

 * get("/videos").then().assertThat().body(matchesDtd(dtd));
 *

 * matchesXsd and matchesDtd are Hamcrest matchers which you can import from {@link RestAssuredMatchers}.
 *

 *

 * Besides specifying request parameters you can also specify headers, cookies, body and content type.

 *

 *

 * Cookie:
 *

 * given().cookie("username", "John").when().get("/cookie").then().assertThat().body(equalTo("username"));
 *

 *

 *

 * Headers:
 *

 * given().header("MyHeader", "Something").and(). ..
 * given().headers("MyHeader", "Something", "MyOtherHeader", "SomethingElse").and(). ..
 *

 *

 *

 * Content Type:
 *

 * given().contentType(ContentType.TEXT). ..
 *

 *

 *

 * Body:
 *

 * given().request().body("some body"). .. // Works for POST and PUT requests
 * given().request().body(new byte[]{42}). .. // Works for POST
 *

 *

 *

 *

 *

 * You can also verify status code, status line, cookies, headers, content type and body.
 *

 *

 * Cookie:
 *

 * expect().cookie("cookieName", "cookieValue"). ..
 * expect().cookies("cookieName1", "cookieValue1", "cookieName2", "cookieValue2"). ..
 * expect().cookies("cookieName1", "cookieValue1", "cookieName2", containsString("Value2")). ..
 *

 *

 *

 * Status:
 *

 * get("/x").then().assertThat().statusCode(200). ..
 * get("/x").then().assertThat().statusLine("something"). ..
 * get("/x").then().assertThat().statusLine(containsString("some")). ..
 *

 *

 *

 * Headers:
 *

 * get("/x").then().assertThat().header("headerName", "headerValue"). ..
 * get("/x").then().assertThat().headers("headerName1", "headerValue1", "headerName2", "headerValue2"). ..
 * get("/x").then().assertThat().headers("headerName1", "headerValue1", "headerName2", containsString("Value2")). ..
 *

 *

 *

 * Content-Type:
 *

 * get("/x").then().assertThat().contentType(ContentType.JSON). ..
 *

 *

 *

 * REST Assured also supports mapping a request body and response body to and from a Java object using Jackson, Gson or JAXB. Usage example:
 *

 * Greeting greeting = get("/greeting").as(Greeting.class);
 *

 *

 * Greeting greeting = new Greeting();
 * greeting.setFirstName("John");
 * greeting.setLastName("Doe");
 *
 * given().body(greeting).when().post("/greeting");
 *

 * See the javadoc for the body method for more details.
 *

 *

 * Full body/content matching:
 *

 * get("/x").then().assertThat().body(equalsTo("something")). ..
 * get("/x").then().assertThat().content(equalsTo("something")). .. // Same as above
 *

 *

 *

 *

 *

 * REST assured also supports some authentication schemes, for example basic authentication:
 *

 * given().auth().basic("username", "password").when().get("/secured/hello").then().statusCode(200);
 *

 * Other supported schemes are OAuth and certificate authentication.
 *

 *

 * By default REST assured assumes host localhost and port 8080 when doing a request. If you want a different port you can do:
 *

 * given().port(80). ..
 *

 * or simply:
 *

 * .. when().get("http://myhost.org:80/doSomething");
 *

 *

 *

 * Parameters can also be set directly on the url:
 *

 * ..when().get("/name?firstName=John&lastName=Doe");
 *

 *

 *

 * You can use the {@link XmlPath} or {@link JsonPath} to
 * easily parse XML or JSON data from a response.
 *

 *
XML example:
 *

 *            String xml = post("/greetXML?firstName=John&lastName=Doe").andReturn().asString();
 *            // Now use XmlPath to get the first and last name
 *            String firstName = with(xml).get("greeting.firstName");
 *            String lastName = with(xml).get("greeting.lastName");
 *
 *            // or a bit more efficiently:
 *            XmlPath xmlPath = new XmlPath(xml).setRootPath("greeting");
 *            String firstName = xmlPath.get("firstName");
 *            String lastName = xmlPath.get("lastName");
 *

 *

 *
JSON example:
 *

 *            String json = get("/lotto").asString();
 *            // Now use JsonPath to get data out of the JSON body
 *            int lottoId = with(json).getInt("lotto.lottoId);
 *            List<Integer> winnerIds = with(json).get("lotto.winners.winnerId");
 *
 *            // or a bit more efficiently:
 *            JsonPath jsonPath = new JsonPath(json).setRootPath("lotto");
 *            int lottoId = jsonPath.getInt("lottoId");
 *            List<Integer> winnderIds = jsonPath.get("winnders.winnderId");
 *

 *

 *

 *

 *

 * REST Assured providers predefined parsers for e.g. HTML, XML and JSON. But you can parse other kinds of content by registering a predefined parser for unsupported content-types by using:
 *

 * RestAssured.registerParser(<content-type>, <parser>);
 *

 * E.g. to register that content-type 'application/custom' should be parsed using the XML parser do:
 *

 * RestAssured.registerParser("application/custom", Parser.XML);
 *

 * You can also unregister a parser using:
 *

 * RestAssured.unregisterParser("application/custom");
 *

 * If can also specify a default parser for all content-types that do not match a pre-defined or registered parser. This is also useful if the response doesn't contain a content-type at all:
 *

 * RestAssured.defaultParser = Parser.JSON;
 *

 *

 *
If you need to re-use a specification in multiple tests or multiple requests you can use the {@link ResponseSpecBuilder}
 * and {@link RequestSpecBuilder} like this:
 *

 * RequestSpecification requestSpec = new RequestSpecBuilder().addParameter("parameter1", "value1").build();
 * ResponseSpecification responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build();
 *
 * given().
 *         spec(requestSpec).
 * when().
 *        get("/something");
 * then().
 *         spec(responseSpec).
 *         body("x.y.z", equalTo("something"));
 *

 *

 *
You can also create filters and add to the request specification. A filter allows you to inspect and alter a request before it's actually committed and also inspect and alter the
 * response before it's returned to the expectations. You can regard it as an "around advice" in AOP terms. Filters can be used to implement custom authentication schemes, session management, logging etc. E.g.
 *

 * given().filter(ResponseLoggingFilter.logResponseIfStatusCodeIs(302)). ..
 *

 * will log/print the response body to after each request.
 *

 *

 * You can also change the default base URI, base path, port, authentication scheme, root path and filters for all subsequent requests:
 *

 * RestAssured.baseURI = "http://myhost.org";
 * RestAssured.port = 80;
 * RestAssured.basePath = "/resource";
 * RestAssured.authentication = basic("username", "password");
 * RestAssured.rootPath = "store.book";
 *

 * This means that a request like e.g. get("/hello") goes to: <tt>http://myhost.org:8080/resource/hello</tt>
 * which basic authentication credentials "username" and "password". See {@link #rootPath} for more info about setting the root paths, {@link #filters(java.util.List)} for setting
 * default filters

 * You can reset to the standard baseURI (localhost), basePath (empty), standard port (8080), default authentication scheme (none), default parser (none) and default root path (empty string) using:
 *

 * RestAssured.reset();
 *

 *

 *

 *

 * In order to use REST assured effectively it's recommended to statically import
 * methods from the following classes:
 *

 *
io.restassured.RestAssured.*

 *
io.restassured.matcher.RestAssuredMatchers.*

 *
org.hamcrest.Matchers.*

 *

 *

 */
public class RestAssured {

    private static final String SSL = "SSL";
    private static ResponseParserRegistrar RESPONSE_PARSER_REGISTRAR = new ResponseParserRegistrar();

    public static final String DEFAULT_URI = "http://localhost";
    public static final String DEFAULT_BODY_ROOT_PATH = "";
    public static final int DEFAULT_PORT = 8080;
    public static final int UNDEFINED_PORT = -1;
    public static final String DEFAULT_PATH = "";
    public static final AuthenticationScheme DEFAULT_AUTH = new NoAuthScheme();
    public static final boolean DEFAULT_URL_ENCODING_ENABLED = true;
    public static final String DEFAULT_SESSION_ID_VALUE = null;

    /**
     * The base URI that's used by REST assured when making requests if a non-fully qualified URI is used in the request.
     * Default value is {@value #DEFAULT_URI}.
     */
    public static String baseURI = DEFAULT_URI;

    /**
     * The port that's used by REST assured when it's left out of the specified URI when making a request.
     * Default port will evaluate to {@value #DEFAULT_PORT}.
     */
    public static int port = UNDEFINED_PORT;

    /**
     * A base path that's added to the {@link #baseURI} by REST assured when making requests. E.g. let's say that
     * the {@link #baseURI} is http://localhost and basePath is /resource
     * then
     *

     *

     * ..when().get("/something");
     *

     *

     * will make a request to http://localhost/resource.
     * Default basePath value is empty.
     */
    public static String basePath = DEFAULT_PATH;

    /**
     * Specifies if Rest Assured should url encode the URL automatically. Usually this is a recommended but in some cases
     * e.g. the query parameters are already be encoded before you provide them to Rest Assured then it's useful to disable
     * URL encoding. For example:
     *

     * RestAssured.baseURI = "https://jira.atlassian.com";
     * RestAssured.port = 443;
     * RestAssured.urlEncodingEnabled = false; // Because "query" is already url encoded
     * String query = "project%20=%20BAM%20AND%20issuetype%20=%20Bug";
     * String response = get("/rest/api/2.0.alpha1/search?jql={q}",query).andReturn().asString();
     * ...
     *

     * The query is already url encoded so you need to disable Rest Assureds url encoding to prevent double encoding.
     */
    public static boolean urlEncodingEnabled = DEFAULT_URL_ENCODING_ENABLED;

    /**
     * Set an authentication scheme that should be used for each request. By default no authentication is used.
     * If you have specified an authentication scheme and wish to override it for a single request then
     * you can do this using:
     *

     *

     *     given().auth().none()..
     *

     */
    public static AuthenticationScheme authentication = DEFAULT_AUTH;

    /**
     * Define a configuration for e.g. redirection settings and http client parameters (default is new RestAssuredConfig()). E.g.
     *

     * RestAssured.config = config().redirect(redirectConfig().followRedirects(true).and().maxRedirects(0));
     *

     *

     * config() can be statically imported from {@link RestAssuredConfig}.
     *

     *

     */
    public static RestAssuredConfig config = new RestAssuredConfig();

    /**
     * Set the default root path of the response body so that you don't need to write the entire path for each expectation.
     * E.g. instead of writing:
     *

     *

     * get(..).then().assertThat().
     *          body("x.y.firstName", is(..)).
     *          body("x.y.lastName", is(..)).
     *          body("x.y.age", is(..)).
     *          body("x.y.gender", is(..));
     *

     *

     * you can use a root and do:
     *

     * RestAssured.rootPath = "x.y";
     * get(..).then().assertThat().
     *          body("firstName", is(..)).
     *          body("lastName", is(..)).
     *          body("age", is(..)).
     *          body("gender", is(..)).
     *

     */
    public static String rootPath = DEFAULT_BODY_ROOT_PATH;

    /**
     * Specify a default request specification that will be sent with each request. E,g.
     *

     * RestAssured.requestSpecification = new RequestSpecBuilder().addParameter("parameter1", "value1").build();
     *

     *

     * means that for each request by Rest Assured "parameter1" will be equal to "value1".
     */
    public static RequestSpecification requestSpecification = null;

    /**
     * Specify a default parser. This parser will be used if the response content-type
     * doesn't match any pre-registered or custom registered parsers. Also useful if the response
     * doesn't contain a content-type at all.
     */
    public static Parser defaultParser = null;

    /**
     * Specify a default response specification that will be sent with each request. E,g.
     *

     * RestAssured.responseSpecification = new ResponseSpecBuilder().expectStatusCode(200).build();
     *

     *

     * means that for each response Rest Assured will assert that the status code is equal to 200.
     */
    public static ResponseSpecification responseSpecification = null;

    /**
     * Set the default session id value that'll be used for each request. This value will be set in the {@link SessionConfig} so it'll
     * override the session id value previously defined there (if any). If you need to change the sessionId cookie name you need to configure and supply the {@link SessionConfig} to
     * RestAssured.config.
     */
    public static String sessionId = DEFAULT_SESSION_ID_VALUE;

    /**
     * Specify a default proxy that REST Assured will use for all requests (unless overridden by individual tests). For example:
     *

     *

     * RestAssured.proxy = host("127.0.0.1").withPort(8888);
     *

     * where host is statically imported from {@link ProxySpecification#host(String)}.
     *
     * @see #proxy(String)
     * @see #proxy(String, int)
     * @see #proxy(String, int, String)
     * @see #proxy(java.net.URI)
     * @see #proxy(ProxySpecification)
     */
    public static ProxySpecification proxy = null;

    private static List<Filter> filters = new LinkedList<Filter>();

    /**
     * Add default filters that will be applied to each request.
     *
     * @param filters The filter list
     */
    public static void filters(List<Filter> filters) {
        Validate.notNull(filters, "Filter list cannot be null");
        RestAssured.filters.addAll(filters);
    }

    /**
     * Add default filters to apply to each request.
     *
     * @param filter            The filter to add
     * @param additionalFilters An optional array of additional filters to add
     */
    public static void filters(Filter filter, Filter... additionalFilters) {
        Validate.notNull(filter, "Filter cannot be null");
        RestAssured.filters.add(filter);
        if (additionalFilters != null) {
            Collections.addAll(RestAssured.filters, additionalFilters);
        }
    }

    /**
     * Sets the default filters to apply to each request.
     *
     * @param filters The filter list
     */
    public static void replaceFiltersWith(List<Filter> filters) {
        Validate.notNull(filters, "Filter list cannot be null");
        RestAssured.filters.clear();
        filters(filters);
    }

    /**
     * Sets the default filters to apply to each request.
     *
     * @param filter            The filter to set
     * @param additionalFilters An optional array of additional filters to set
     */
    public static void replaceFiltersWith(Filter filter, Filter... additionalFilters) {
        Validate.notNull(filter, "Filter cannot be null");
        RestAssured.filters.clear();
        filters(filter, additionalFilters);
    }

    /**
     * @return The current default filters
     */
    public static List<Filter> filters() {
        return Collections.unmodifiableList(filters);
    }

    /**
     * Set a object mapper that'll be used when serializing and deserializing Java objects to and from it's
     * document representation (XML, JSON etc).
     *
     * @param objectMapper The object mapper to use.
     */
    public static void objectMapper(ObjectMapper objectMapper) {
        Validate.notNull(objectMapper, "Default object mapper cannot be null");
        config = config().objectMapperConfig(ObjectMapperConfig.objectMapperConfig().defaultObjectMapper(objectMapper));
    }

    /**
     * Start building the response part of the test io.restassured.specification. E.g.
     *

     *

     * expect().body("lotto.lottoId", equalTo(5)).when().get("/lotto");
     *

     *

     * will expect that the response body for the GET request to "/lotto" should
     * contain JSON or XML which has a lottoId equal to 5.
     *
     * @return A response io.restassured.specification.
     */
    public static ResponseSpecification expect() {
        return createTestSpecification().getResponseSpecification();
    }

    /**
     * Start building the request part of the test io.restassured.specification. E.g.
     *

     *

     * with().parameters("firstName", "John", "lastName", "Doe").when().post("/greetXML").then().assertThat().body("greeting.firstName", equalTo("John"));
     *

     *

     * will send a POST request to "/greetXML" with request parameters <tt>firstName=John</tt> and <tt>lastName=Doe</tt> and
     * expect that the response body containing JSON or XML firstName equal to John.
     *

     * The only difference between {@link #with()} and {@link #given()} is syntactical.
     *
     * @return A request specification.
     */
    public static RequestSpecification with() {
        return given();
    }

    /**
     * Create a list of arguments that can be used to create parts of the path in a body/content expression.
     * This is useful in situations where you have e.g. pre-defined variables that constitutes the key. For example:
     *

     * String someSubPath = "else";
     * int index = 1;
     * when().get().then().body("something.%s[%d]", withArgs(someSubPath, index), equalTo("some value")). ..
     *

     *

     * or if you have complex root paths and don't wish to duplicate the path for small variations:
     *

     * get("/x").then().assertThat().
     *          root("filters.filterConfig[%d].filterConfigGroups.find { it.name == 'Gold' }.includes").
     *          body(withArgs(0), hasItem("first")).
     *          body(withArgs(1), hasItem("second")).
     *          ..
     *

     *

     * The key and arguments follows the standard formatting syntax of Java.
     *
     * @return A list of arguments.
     */
    public static List<Argument> withArgs(Object firstArgument, Object... additionalArguments) {
        Validate.notNull(firstArgument, "You need to supply at least one argument");
        final List<Argument> arguments = new LinkedList<Argument>();
        arguments.add(Argument.arg(firstArgument));
        if (additionalArguments != null && additionalArguments.length > 0) {
            for (Object additionalArgument : additionalArguments) {
                arguments.add(Argument.arg(additionalArgument));
            }
        }
        return Collections.unmodifiableList(arguments);
    }

    /**
     * Create a list of no arguments that can be used to create parts of the path in a response specification for JSON, XML or HTML validation.
     * This is useful in situations where you have e.g. pre-defined variables that constitutes the key. For example:
     *

     * get("/jsonStore").then().
     *          root("store.%s", withArgs("book")).
     *          body("category.size()", equalTo(4)).
     *          appendRoot("%s.%s", withArgs("author", "size()")).
     *          body(withNoArgs(), equalTo(4));
     *
     * @return A list of no arguments.
     */
    public static List<Argument> withNoArgs() {
        return Collections.unmodifiableList(Collections.<Argument>emptyList());
    }

    /**
     * Start building the request part of the test io.restassured.specification. E.g.
     *

     *

     * given().parameters("firstName", "John", "lastName", "Doe").when().post("/greetXML").then().body("greeting.firstName", equalTo("John"));
     *

     *

     * will send a POST request to "/greetXML" with request parameters <tt>firstName=John</tt> and <tt>lastName=Doe</tt> and
     * expect that the response body containing JSON or XML firstName equal to John.
     *

     * The only difference between {@link #with()} and {@link #given()} is syntactical.
     *
     * @return A request specification.
     */
    public static RequestSpecification given() {
        return createTestSpecification().getRequestSpecification();
    }

    /**
     * Start building the DSL expression by sending a request without any parameters or headers etc. E.g.
     *

     *

     * when().
     *        get("/x").
     * then().
     *        body("x.y.z1", equalTo("Z1")).
     *        body("x.y.z2", equalTo("Z2"));
     *

     *

     * Note that if you need to add parameters, headers, cookies or other request properties use the {@link #given()} method.
     *

     *
     * @return A request sender interface that let's you call resources on the server
     */
    public static RequestSender when() {
        return createTestSpecification().getRequestSpecification();
    }

    /**
     * When you have long specifications it can be better to split up the definition of response and request specifications in multiple lines.
     * You can then pass the response and request specifications to this method. E.g.
     *

     *

     * RequestSpecification requestSpecification = with().parameters("firstName", "John", "lastName", "Doe");
     * ResponseSpecification responseSpecification = expect().body("greeting", equalTo("Greetings John Doe"));
     * given(requestSpecification, responseSpecification).get("/greet");
     *

     *

     * This will perform a GET request to "/greet" and verify it according to the responseSpecification.
     *
     * @return A test io.restassured.specification.
     */
    public static RequestSender given(RequestSpecification requestSpecification, ResponseSpecification responseSpecification) {
        return new TestSpecificationImpl(requestSpecification, responseSpecification);
    }

    /**
     * When you're only interested in supplying a predefined request specification without a response specification then you can use this method.
     * For example:
     *

     *

     * RequestSpecification requestSpecification = with().parameters("firstName", "John", "lastName", "Doe");
     * given(requestSpecification).get("/greet"). ..;
     *

     *

     * This will perform a GET request to "/greet" and without any validation (only a static response specification has been configured).
     *
     * @return A RequestSender
     */
    public static RequestSpecification given(RequestSpecification requestSpecification) {
        return given().spec(requestSpecification);
    }

    /**
     * Perform a GET request to a path. Normally the path doesn't have to be fully-qualified e.g. you don't need to
     * specify the path as <tt>http://localhost:8080/path</tt>. In this case it's enough to use <tt>/path</tt>.
     *
     * @param path       The path to send the request to.
     * @param pathParams The path parameters. E.g. if path is "/book/{hotelId}/{roomNumber}" you can do get("/book/{hotelName}/{roomNumber}", "Hotels R Us", 22);.
     * @return The response of the GET request.
     */
    public static Response get(String path, Object... pathParams) {
        return given().get(path, pathParams);
    }

    /**
     * Perform a GET request to a path. Normally the path doesn't have to be fully-qualified e.g. you don't need to
     * specify the path as <tt>http://localhost:8080/path</tt>. In this case it's enough to use <tt>/path</tt>.
     *
     * @param path       The path to send the request to.
     * @param pathParams The path parameters.
     * @return The response of the GET request.
     */
    public static Response get(String path, Map<String, ?> pathParams) {
        return given().get(path, pathParams);
    }

    /**
     * Perform a POST request to a path. Normally the path doesn't have to be fully-qualified e.g. you don't need to
     * specify the path as <tt>http://localhost:8080/path</tt>. In this case it's enough to use <tt>/path</tt>.
     *
     * @param path       The path to send the request to.
     * @param pathParams The path parameters. E.g. if path is "/book/{hotelId}/{roomNumber}" you can do post("/book/{hotelName}/{roomNumber}", "Hotels R Us", 22);.
     * @return The response of the request.
     */
    public static Response post(String path, Object... pathParams) {
        return given().post(path, pathParams);
    }

    /**
     * Perform a POST request to a path. Normally the path doesn't have to be fully-qualified e.g. you don't need to
     * specify the path as <tt>http://localhost:8080/path</tt>. In this case it's enough to use <tt>/path</tt>.
     *
     * @param path       The path to send the request to.
     * @param pathParams The path parameters.
     * @return The response of the request.
     */
    public static Response post(String path, Map<String, ?> pathParams) {
        return given().post(path, pathParams);
    }

    /**
     * Perform a PUT request to a path. Normally the path doesn't have to be fully-qualified e.g. you don't need to
     * specify the path as <tt>http://localhost:8080/path</tt>. In this case it's enough to use <tt>/path</tt>.
     *
     * @param path       The path to send the request to.
     * @param pathParams The path parameters. E.g. if path is "/book/{hotelId}/{roomNumber}" you can do put("/book/{hotelName}/{roomNumber}", "Hotels R Us", 22);.
     * @return The response of the request.
     */
    public static Response put(String path, Object... pathParams) {
        return given().put(path, pathParams);
    }

    /**
     * Perform a DELETE request to a path. Normally the path doesn't have to be fully-qualified e.g. you don't need to
     * specify the path as <tt>http://localhost:8080/path</tt>. In this case it's enough to use <tt>/path</tt>.
     *
     * @param path       The path to send the request to.
     * @param pathParams The path parameters. E.g. if path is "/book/{hotelId}/{roomNumber}" you can do delete("/book/{hotelName}/{roomNumber}", "Hotels R Us", 22);.
     * @return The response of the request.
     */
    public static Response delete(String path, Object... pathParams) {
        return given().delete(path, pathParams);
    }

    /**
     * Perform a DELETE request to a path. Normally the path doesn't have to be fully-qualified e.g. you don't need to
     * specify the path as <tt>http://localhost:8080/path</tt>. In this case it's enough to use <tt>/path</tt>.
     *
     * @param path       The path to send the request to.
     * @param pathParams The path parameters.
     * @return The response of the request.
     */
    public static Response delete(String path, Map<String, ?> pathParams) {
        return given().delete(path, pathParams);
    }

    /**
     * Perform a HEAD request to a path. Normally the path doesn't have to be fully-qualified e.g. you don't need to
     * specify the path as <tt>http://localhost:8080/path</tt>. In this case it's enough to use <tt>/path</tt>.
     *
     * @param path       The path to send the request to.
     * @param pathParams The path parameters. E.g. if path is "/book/{hotelId}/{roomNumber}" you can do head("/book/{hotelName}/{roomNumber}", "Hotels R Us", 22);.
     * @return The response of the request.
     */
    public static Response head(String path, Object... pathParams) {
        return given().head(path, pathParams);
    }

    /**
     * Perform a HEAD request to a path. Normally the path doesn't have to be fully-qualified e.g. you don't need to
     * specify the path as <tt>http://localhost:8080/path</tt>. In this case it's enough to use <tt>/path</tt>.
     *
     * @param path       The path to send the request to.
     * @param pathParams The path parameters.
     * @return The response of the request.
     */
    public static Response head(String path, Map<String, ?> pathParams) {
        return given().head(path, pathParams);
    }

    /**
     * Perform a PATCH request to a path. Normally the path doesn't have to be fully-qualified e.g. you don't need to
     * specify the path as <tt>http://localhost:8080/path</tt>. In this case it's enough to use <tt>/path</tt>.
     *
     * @param path       The path to send the request to.
     * @param pathParams The path parameters. E.g. if path is "/book/{hotelId}/{roomNumber}" you can do head("/book/{hotelName}/{roomNumber}", "Hotels R Us", 22);.
     * @return The response of the request.
     */
    public static Response patch(String path, Object... pathParams) {
        return given().patch(path, pathParams);
    }

    /**
     * Perform a PATCH request to a path. Normally the path doesn't have to be fully-qualified e.g. you don't need to
     * specify the path as <tt>http://localhost:8080/path</tt>. In this case it's enough to use <tt>/path</tt>.
     *
     * @param path       The path to send the request to.
     * @param pathParams The path parameters.
     * @return The response of the request.
     */
    public static Response patch(String path, Map<String, ?> pathParams) {
        return given().patch(path, pathParams);
    }

    /**
     * Perform a OPTIONS request to a path. Normally the path doesn't have to be fully-qualified e.g. you don't need to
     * specify the path as <tt>http://localhost:8080/path</tt>. In this case it's enough to use <tt>/path</tt>.
     *
     * @param path       The path to send the request to.
     * @param pathParams The path parameters. E.g. if path is "/book/{hotelId}/{roomNumber}" you can do head("/book/{hotelName}/{roomNumber}", "Hotels R Us", 22);.
     * @return The response of the request.
     */
    public static Response options(String path, Object... pathParams) {
        return given().options(path, pathParams);
    }

    /**
     * Perform a OPTIONS request to a path. Normally the path doesn't have to be fully-qualified e.g. you don't need to
     * specify the path as <tt>http://localhost:8080/path</tt>. In this case it's enough to use <tt>/path</tt>.
     *
     * @param path       The path to send the request to.
     * @param pathParams The path parameters.
     * @return The response of the request.
     */
    public static Response options(String path, Map<String, ?> pathParams) {
        return given().options(path, pathParams);
    }

    /**
     * Perform a GET request to a uri.
     *
     * @param uri The uri to send the request to.
     * @return The response of the GET request.
     */
    public static Response get(URI uri) {
        return given().get(uri);
    }

    /**
     * Perform a POST request to a uri.
     *
     * @param uri The uri to send the request to.
     * @return The response of the request.
     */
    public static Response post(URI uri) {
        return given().post(uri);
    }

    /**
     * Perform a PUT request to a uri.
     *
     * @param uri The uri to send the request to.
     * @return The response of the request.
     */
    public static Response put(URI uri) {
        return given().put(uri);
    }

    /**
     * Perform a DELETE request to a uri.
     *
     * @param uri The uri to send the request to.
     * @return The response of the request.
     */
    public static Response delete(URI uri) {
        return given().delete(uri);
    }

    /**
     * Perform a HEAD request to a uri.
     *
     * @param uri The uri to send the request to.
     * @return The response of the request.
     */
    public static Response head(URI uri) {
        return given().head(uri);
    }

    /**
     * Perform a PATCH request to a uri.
     *
     * @param uri The uri to send the request to.
     * @return The response of the request.
     */
    public static Response patch(URI uri) {
        return given().patch(uri);
    }

    /**
     * Perform a OPTIONS request to a uri.
     *
     * @param uri The uri to send the request to.
     * @return The response of the request.
     */
    public static Response options(URI uri) {
        return given().options(uri);
    }

    /**
     * Perform a GET request to a url.
     *
     * @param url The url to send the request to.
     * @return The response of the GET request.
     */
    public static Response get(URL url) {
        return given().get(url);
    }

    /**
     * Perform a POST request to a url.
     *
     * @param url The url to send the request to.
     * @return The response of the request.
     */
    public static Response post(URL url) {
        return given().post(url);
    }

    /**
     * Perform a PUT request to a url.
     *
     * @param url The url to send the request to.
     * @return The response of the request.
     */
    public static Response put(URL url) {
        return given().put(url);
    }

    /**
     * Perform a DELETE request to a url.
     *
     * @param url The url to send the request to.
     * @return The response of the request.
     */
    public static Response delete(URL url) {
        return given().delete(url);
    }

    /**
     * Perform a HEAD request to a url.
     *
     * @param url The url to send the request to.
     * @return The response of the request.
     */
    public static Response head(URL url) {
        return given().head(url);
    }

    /**
     * Perform a PATCH request to a url.
     *
     * @param url The url to send the request to.
     * @return The response of the request.
     */
    public static Response patch(URL url) {
        return given().patch(url);
    }

    /**
     * Perform a OPTIONS request to a url.
     *
     * @param url The url to send the request to.
     * @return The response of the request.
     */
    public static Response options(URL url) {
        return given().options(url);
    }

    /**
     * Perform a GET request to the statically configured path (by default http://localhost:8080).
     *
     * @return The response of the GET request.
     */
    public static Response get() {
        return given().get();
    }

    /**
     * Perform a POST request to the statically configured path (by default http://localhost:8080).
     *
     * @return The response of the request.
     */
    public static Response post() {
        return given().post();
    }

    /**
     * Perform a PUT request to the statically configured path (by default http://localhost:8080).
     *
     * @return The response of the request.
     */
    public static Response put() {
        return given().put();
    }

    /**
     * Perform a DELETE request to the statically configured path (by default http://localhost:8080).
     *
     * @return The response of the request.
     */
    public static Response delete() {
        return given().delete();
    }

    /**
     * Perform a HEAD request to the statically configured path (by default http://localhost:8080).
     *
     * @return The response of the request.
     */
    public static Response head() {
        return given().head();
    }

    /**
     * Perform a PATCH request to the statically configured path (by default http://localhost:8080).
     *
     * @return The response of the request.
     */
    public static Response patch() {
        return given().patch();
    }

    /**
     * Perform a OPTIONS request to the statically configured path (by default http://localhost:8080).
     *
     * @return The response of the request.
     */
    public static Response options() {
        return given().options();
    }

    /**
     * Perform a request to the pre-configured path (by default http://localhost:8080).
     *
     * @param method The HTTP method to use
     * @return The response of the request.
     */
    public static Response request(Method method) {
        return given().request(method);
    }

    /**
     * Perform a custom HTTP request to the pre-configured path (by default http://localhost:8080).
     *
     * @param method The HTTP method to use
     * @return The response of the request.
     */
    public static Response request(String method) {
        return given().request(method);
    }

    /**
     * Perform a HTTP request to a path. Normally the path doesn't have to be fully-qualified e.g. you don't need to
     * specify the path as <tt>http://localhost:8080/path</tt>. In this case it's enough to use <tt>/path</tt>.
     *
     * @param method     The HTTP method to use
     * @param path       The path to send the request to.
     * @param pathParams The path parameters. E.g. if path is "/book/{hotelId}/{roomNumber}" you can do request(Method.TRACE,"/book/{hotelName}/{roomNumber}", "Hotels R Us", 22);.
     * @return The response of the request.
     */
    public static Response request(Method method, String path, Object... pathParams) {
        return given().request(method, path, pathParams);
    }

    /**
     * Perform a custom HTTP request to a path. Normally the path doesn't have to be fully-qualified e.g. you don't need to
     * specify the path as <tt>http://localhost:8080/path</tt>. In this case it's enough to use <tt>/path</tt>.
     *
     * @param method     The HTTP method to use
     * @param path       The path to send the request to.
     * @param pathParams The path parameters. E.g. if path is "/book/{hotelId}/{roomNumber}" you can do request("method","/book/{hotelName}/{roomNumber}", "Hotels R Us", 22);.
     * @return The response of the request.
     */
    public static Response request(String method, String path, Object... pathParams) {
        return given().request(method, path, pathParams);
    }

    /**
     * Perform a request to a uri.
     *
     * @param method The HTTP method to use
     * @param uri    The uri to send the request to.
     * @return The response of the GET request.
     */
    public static Response request(Method method, URI uri) {
        return given().request(method, uri);
    }

    /**
     * Perform a request to a url.
     *
     * @param method The HTTP method to use
     * @param url    The url to send the request to.
     * @return The response of the GET request.
     */
    public static Response request(Method method, URL url) {
        return given().request(method, url);
    }

    /**
     * Perform a custom HTTP request to a uri.
     *
     * @param method The HTTP method to use
     * @param uri    The uri to send the request to.
     * @return The response of the GET request.
     */
    public static Response request(String method, URI uri) {
        return given().request(method, uri);
    }

    /**
     * Perform a custom HTTP request to a url.
     *
     * @param method The HTTP method to use
     * @param url    The url to send the request to.
     * @return The response of the GET request.
     */
    public static Response request(String method, URL url) {
        return given().request(method, url);
    }

    /**
     * Create a http basic authentication scheme.
     *
     * @param userName The user name.
     * @param password The password.
     * @return The authentication scheme
     */
    public static AuthenticationScheme basic(String userName, String password) {
        final BasicAuthScheme scheme = new BasicAuthScheme();
        scheme.setUserName(userName);
        scheme.setPassword(password);
        return scheme;
    }

    /**
     * Create a NTLM authentication scheme.
     *
     * @param userName The user name.
     * @param password The password.
     * @param workstation The NTLM workstation.
     * @param domain The NTLM workstation.
     * @return The authentication scheme
     */
    public static AuthenticationScheme ntlm(String userName, String password, String workstation, String domain) {
        final NTLMAuthScheme scheme = new NTLMAuthScheme();
        scheme.setUserName(userName);
        scheme.setPassword(password);
        scheme.setWorkstation(workstation);
        scheme.setDomain(domain);
        return scheme;
    }

    /**
     * Use form authentication. Rest Assured will try to parse the response
     * login page and determine and try find the action, username and password input
     * field automatically.
     *

     * Note that the request will be much faster if you also supply a form auth configuration.
     *

     *
     * @param userName The user name.
     * @param password The password.
     * @return The authentication scheme
     * @see #form(String, String, FormAuthConfig)
     */
    public static AuthenticationScheme form(String userName, String password) {
        return form(userName, password, null);
    }

    /**
     * Use form authentication with the supplied configuration.
     *
     * @param userName The user name.
     * @param password The password.
     * @param config   The form authentication config
     * @return The authentication scheme
     */
    public static AuthenticationScheme form(String userName, String password, FormAuthConfig config) {
        if (userName == null) {
            throw new IllegalArgumentException("Username cannot be null");
        }
        if (password == null) {
            throw new IllegalArgumentException("Password cannot be null");
        }
        final FormAuthScheme scheme = new FormAuthScheme();
        scheme.setUserName(userName);
        scheme.setPassword(password);
        scheme.setConfig(config);
        return scheme;
    }

    /**
     * Return the http preemptive authentication specification for setting up preemptive authentication requests.
     * This means that the authentication details are sent in the request header regardless if the server challenged
     * for authentication or not.
     *
     * @return The authentication scheme
     */
    public static PreemptiveAuthProvider preemptive() {
        return new PreemptiveAuthProvider();
    }

    /**
     * Sets a certificate to be used for SSL authentication. See {@link java.lang.Class#getResource(String)}
     * for how to get a URL from a resource on the classpath.
     *

     * Uses SSL settings defined in {@link SSLConfig}.
     *

     *
     * @param certURL  URL to a JKS keystore where the certificate is stored.
     * @param password The password for the keystore
     * @return The request io.restassured.specification
     */
    public static AuthenticationScheme certificate(String certURL, String password) {
        SSLConfig sslConfig = config().getSSLConfig();
        return certificate(certURL, password, CertificateAuthSettings.certAuthSettings().keyStoreType(sslConfig.getKeyStoreType()).trustStore(sslConfig.getTrustStore()).
                keyStore(sslConfig.getKeyStore()).trustStoreType(sslConfig.getTrustStoreType()).x509HostnameVerifier(sslConfig.getX509HostnameVerifier()).
                port(sslConfig.getPort()).sslSocketFactory(sslConfig.getSSLSocketFactory()));
    }

    /**
     * Sets a certificate to be used for SSL authentication. See {@link Class#getResource(String)} for how to get a URL from a resource
     * on the classpath.
     *

     *
     * @param certURL                 URL to a JKS keystore where the certificate is stored.
     * @param password                The password for the keystore
     * @param certificateAuthSettings More advanced settings for the certificate authentication
     */
    public static AuthenticationScheme certificate(String certURL, String password, CertificateAuthSettings certificateAuthSettings) {
        return certificate(certURL, password, "", "", certificateAuthSettings);
    }

    /**
     * Sets a certificate to be used for SSL authentication. See {@link Class#getResource(String)} for how to get a URL from a resource
     * on the classpath.
     *

     *
     * @param trustStorePath          URL to a JKS trust store where the certificate is stored.
     * @param trustStorePassword      The password for the trust store
     * @param keyStorePath            URL to a JKS keystore where the certificate is stored.
     * @param keyStorePassword        The password for the keystore
     * @param certificateAuthSettings More advanced settings for the certificate authentication
     */
    public static AuthenticationScheme certificate(String trustStorePath, String trustStorePassword,
                                                   String keyStorePath, String keyStorePassword,
                                                   CertificateAuthSettings certificateAuthSettings) {
        AssertParameter.notNull(keyStorePath, "Keystore path");
        AssertParameter.notNull(keyStorePassword, "Keystore password");
        AssertParameter.notNull(trustStorePath, "Trust store path");
        AssertParameter.notNull(trustStorePassword, "Keystore password");
        AssertParameter.notNull(certificateAuthSettings, CertificateAuthSettings.class);
        final CertAuthScheme scheme = new CertAuthScheme();
        scheme.setPathToKeyStore(keyStorePath);
        scheme.setKeyStorePassword(keyStorePassword);
        scheme.setKeystoreType(certificateAuthSettings.getKeyStoreType());
        scheme.setKeyStore(certificateAuthSettings.getKeyStore());
        scheme.setPort(certificateAuthSettings.getPort());
        scheme.setTrustStore(certificateAuthSettings.getTrustStore());
        scheme.setTrustStoreType(certificateAuthSettings.getTrustStoreType());
        scheme.setPathToTrustStore(trustStorePath);
        scheme.setTrustStorePassword(trustStorePassword);
        scheme.setX509HostnameVerifier(certificateAuthSettings.getX509HostnameVerifier());
        scheme.setSslSocketFactory(certificateAuthSettings.getSSLSocketFactory());
        return scheme;
    }

    /**
     * Use http digest authentication. Note that you need to encode the password yourself.
     *
     * @param userName The user name.
     * @param password The password.
     * @return The authentication scheme
     */
    public static AuthenticationScheme digest(String userName, String password) {
        return basic(userName, password);
    }

    /**
     * Excerpt from the HttpBuilder docs:

     * OAuth sign the request. Note that this currently does not wait for a WWW-Authenticate challenge before sending the the OAuth header.
     * All requests to all domains will be signed for this instance.
     * This assumes you've already generated an accessToken and secretToken for the site you're targeting.
     * For More information on how to achieve this, see the Signpost documentation.
     *
     * @param consumerKey
     * @param consumerSecret
     * @param accessToken
     * @param secretToken
     * @return The authentication scheme
     */
    public static AuthenticationScheme oauth(String consumerKey, String consumerSecret, String accessToken, String secretToken) {
        OAuthScheme scheme = new OAuthScheme();
        scheme.setConsumerKey(consumerKey);
        scheme.setConsumerSecret(consumerSecret);
        scheme.setAccessToken(accessToken);
        scheme.setSecretToken(secretToken);
        return scheme;
    }

    /**
     * Excerpt from the HttpBuilder docs:

     * OAuth sign the request. Note that this currently does not wait for a WWW-Authenticate challenge before sending the the OAuth header.
     * All requests to all domains will be signed for this instance.
     * This assumes you've already generated an accessToken and secretToken for the site you're targeting.
     * For More information on how to achieve this, see the Signpost documentation.
     *
     * @param consumerKey
     * @param consumerSecret
     * @param accessToken
     * @param secretToken
     * @param signature
     * @return The authentication scheme
     */
    public static AuthenticationScheme oauth(String consumerKey, String consumerSecret, String accessToken, String secretToken, OAuthSignature signature) {
        OAuthScheme scheme = new OAuthScheme();
        scheme.setConsumerKey(consumerKey);
        scheme.setConsumerSecret(consumerSecret);
        scheme.setAccessToken(accessToken);
        scheme.setSecretToken(secretToken);
        scheme.setSignature(signature);
        return scheme;
    }

    /**
     * OAuth sign the request. Note that this currently does not wait for a WWW-Authenticate challenge before sending the the OAuth header.
     * All requests to all domains will be signed for this instance.
     *
     * @param accessToken The access token to use
     * @return The authentication scheme
     */
    public static AuthenticationScheme oauth2(String accessToken) {
        PreemptiveOAuth2HeaderScheme myScheme = new PreemptiveOAuth2HeaderScheme();
        myScheme.setAccessToken(accessToken);
        return myScheme;
    }

    /**
     * OAuth sign the request. Note that this currently does not wait for a WWW-Authenticate challenge before sending the the OAuth header.
     * All requests to all domains will be signed for this instance.
     *
     * @param accessToken
     * @param signature
     * @return The authentication scheme
     */
    public static AuthenticationScheme oauth2(String accessToken, OAuthSignature signature) {
        OAuth2Scheme scheme = new OAuth2Scheme();
        scheme.setAccessToken(accessToken);
        scheme.setSignature(signature);
        return scheme;
    }

    /**
     * Register a custom content-type to be parsed using a predefined parser. E.g. let's say you want parse
     * content-type <tt>application/custom</tt> with the XML parser to be able to verify the response using the XML dot notations:
     *

     * get("/x").then().assertThat().body("document.child", equalsTo("something"))..
     *

     * Since <tt>application/custom</tt> is not registered to be processed by the XML parser by default you need to explicitly
     * tell REST Assured to use this parser before making the request:
     *

     * RestAssured.registerParser("application/custom, Parser.XML");
     *

     *
     * @param contentType The content-type to register
     * @param parser      The parser to use when verifying the response.
     */
    public static void registerParser(String contentType, Parser parser) {
        RESPONSE_PARSER_REGISTRAR.registerParser(contentType, parser);
    }

    /**
     * Unregister the parser associated with the provided content-type
     *
     * @param contentType The content-type associated with the parser to unregister.
     */
    public static void unregisterParser(String contentType) {
        RESPONSE_PARSER_REGISTRAR.unregisterParser(contentType);
    }

    /**
     * Resets the {@link #baseURI}, {@link #basePath}, {@link #port}, {@link #authentication} and {@link #rootPath},
     * {@link #filters(java.util.List)}, {@link #requestSpecification}, {@link #responseSpecification},
     * {@link #urlEncodingEnabled}, {@link #config}, {@link #sessionId} and {@link #proxy} to their default values of {@value #DEFAULT_URI}, {@value #DEFAULT_PATH}, {@value #UNDEFINED_PORT},
     * no authentication, <empty string>, null, null,
     * <empty list>, null, null, none, true, new RestAssuredConfig(), null and null.
     */
    public static void reset() {
        baseURI = DEFAULT_URI;
        port = UNDEFINED_PORT;
        basePath = DEFAULT_PATH;
        authentication = DEFAULT_AUTH;
        rootPath = DEFAULT_BODY_ROOT_PATH;
        filters = new LinkedList<Filter>();
        requestSpecification = null;
        responseSpecification = null;
        urlEncodingEnabled = DEFAULT_URL_ENCODING_ENABLED;
        RESPONSE_PARSER_REGISTRAR = new ResponseParserRegistrar();
        defaultParser = null;
        config = new RestAssuredConfig();
        sessionId = DEFAULT_SESSION_ID_VALUE;
        proxy = null;
    }

    private static TestSpecificationImpl createTestSpecification() {
        if (defaultParser != null) {
            RESPONSE_PARSER_REGISTRAR.registerDefaultParser(defaultParser);
        }
        final ResponseParserRegistrar responseParserRegistrar = new ResponseParserRegistrar(RESPONSE_PARSER_REGISTRAR);
        applySessionIdIfApplicable();
        LogRepository logRepository = new LogRepository();
        RestAssuredConfig restAssuredConfig = config();
        return new TestSpecificationImpl(
                new RequestSpecificationImpl(baseURI, port, basePath, authentication, filters,
                        requestSpecification, urlEncodingEnabled, restAssuredConfig, logRepository, proxy),
                new ResponseSpecificationImpl(rootPath, responseSpecification, responseParserRegistrar, restAssuredConfig, logRepository)
        );
    }

    private static void applySessionIdIfApplicable() {
        if (!StringUtils.equals(sessionId, DEFAULT_SESSION_ID_VALUE)) {
            final RestAssuredConfig configToUse;
            if (config == null) {
                configToUse = new RestAssuredConfig();
            } else {
                configToUse = config;
            }
            config = configToUse.sessionConfig(configToUse.getSessionConfig().sessionIdValue(sessionId));
        }
    }

    /**
     * Use relaxed HTTP validation with protocol {@value #SSL}. This means that you'll trust all hosts regardless if the SSL certificate is invalid. By using this
     * method you don't need to specify a keystore (see {@link #keyStore(String, String)} or trust store (see {@link #trustStore(java.security.KeyStore)}.
     *

     * This is just a shortcut for:
     *

     *

     * RestAssured.config = RestAssured.config().sslConfig(sslConfig().relaxedHTTPSValidation());
     *

     */
    public static void useRelaxedHTTPSValidation() {
        useRelaxedHTTPSValidation(SSL);
    }

    /**
     * Use relaxed HTTP validation with a specific protocol. This means that you'll trust all hosts regardless if the SSL certificate is invalid. By using this
     * method you don't need to specify a keystore (see {@link #keyStore(String, String)} or trust store (see {@link #trustStore(java.security.KeyStore)}.
     *

     * This is just a shortcut for:
     *

     *

     * RestAssured.config = RestAssured.config().sslConfig(sslConfig().relaxedHTTPSValidation(<protocol>));
     *

     *
     * @param protocol The standard name of the requested protocol. See the SSLContext section in the Java Cryptography Architecture Standard Algorithm Name Documentation for information about standard protocol names.
     */
    public static void useRelaxedHTTPSValidation(String protocol) {
        config = RestAssured.config().sslConfig(SSLConfig.sslConfig().relaxedHTTPSValidation(protocol));
    }

    /**
     * Enable logging of both the request and the response if REST Assureds test validation fails with log detail equal to {@link LogDetail#ALL}.
     *

     *

     * This is just a shortcut for:
     *

     *

     * RestAssured.config = RestAssured.config().logConfig(logConfig().enableLoggingOfRequestAndResponseIfValidationFails());
     *

     */
    public static void enableLoggingOfRequestAndResponseIfValidationFails() {
        enableLoggingOfRequestAndResponseIfValidationFails(LogDetail.ALL);
    }

    /**
     * Enable logging of both the request and the response if REST Assureds test validation fails with the specified log detail.
     *

     *

     * This is just a shortcut for:
     *

     *

     * RestAssured.config = RestAssured.config().logConfig(logConfig().enableLoggingOfRequestAndResponseIfValidationFails(logDetail));
     *

     *
     * @param logDetail The log detail to show in the log
     */
    public static void enableLoggingOfRequestAndResponseIfValidationFails(LogDetail logDetail) {
        LogConfig logConfig = LogConfig.logConfig().enableLoggingOfRequestAndResponseIfValidationFails(logDetail);
        config = RestAssured.config().logConfig(logConfig);

        // Update request specification if already defined otherwise it'll override the configs.
        // Note that request spec also influence response spec when it comes to logging if validation fails due to the way filters work
        if (requestSpecification != null && requestSpecification instanceof RequestSpecificationImpl) {
            RestAssuredConfig restAssuredConfig = ((RequestSpecificationImpl) requestSpecification).getConfig();
            if (restAssuredConfig == null) {
                restAssuredConfig = config;
            } else {
                LogConfig logConfigForRequestSpec = restAssuredConfig.getLogConfig().enableLoggingOfRequestAndResponseIfValidationFails(logDetail);
                restAssuredConfig = restAssuredConfig.logConfig(logConfigForRequestSpec);
            }
            requestSpecification.config(restAssuredConfig);
        }
    }

    /**
     * Apply a keystore for all requests
     *

     * given().keyStore("/truststore_javanet.jks", "test1234"). ..
     *

     *

     *

     * Note that this is just a shortcut for:
     *

     *

     * RestAssured.config = RestAssured.config().sslConfig(sslConfig().keyStore(pathToJks, password));
     *

     *
     * @param pathToJks The path to the JKS. REST Assured will first look in the classpath and if not found it will look for the JKS in the local file-system
     * @param password  The store pass
     */
    public static void keyStore(String pathToJks, String password) {
        Validate.notEmpty(password, "Password cannot be empty");
        applyKeyStore(pathToJks, password);
    }

    /**
     * The following documentation is taken from https://github.com/jgritman/httpbuilder/wiki/SSL:
     *

     *
SSL Configuration

     *

     * SSL should, for the most part, "just work." There are a few situations where it is not completely intuitive. You can follow the example below, or see HttpClient's SSLSocketFactory documentation for more information.
     *

     *
SSLPeerUnverifiedException

     *

     * If you can't connect to an SSL website, it is likely because the certificate chain is not trusted. This is an Apache HttpClient issue, but explained here for convenience. To correct the untrusted certificate, you need to import a certificate into an SSL truststore.
     *

     * First, export a certificate from the website using your browser. For example, if you go to https://dev.java.net in Firefox, you will probably get a warning in your browser. Choose "Add Exception," "Get Certificate," "View," "Details tab." Choose a certificate in the chain and export it as a PEM file. You can view the details of the exported certificate like so:
     *

     * $ keytool -printcert -file EquifaxSecureGlobaleBusinessCA-1.crt
     * Owner: CN=Equifax Secure Global eBusiness CA-1, O=Equifax Secure Inc., C=US
     * Issuer: CN=Equifax Secure Global eBusiness CA-1, O=Equifax Secure Inc., C=US
     * Serial number: 1
     * Valid from: Mon Jun 21 00:00:00 EDT 1999 until: Sun Jun 21 00:00:00 EDT 2020
     * Certificate fingerprints:
     * MD5:  8F:5D:77:06:27:C4:98:3C:5B:93:78:E7:D7:7D:9B:CC
     * SHA1: 7E:78:4A:10:1C:82:65:CC:2D:E1:F1:6D:47:B4:40:CA:D9:0A:19:45
     * Signature algorithm name: MD5withRSA
     * Version: 3
     * ....
     *

     * Now, import that into a Java keystore file:
     *

     * $ keytool -importcert -alias "equifax-ca" -file EquifaxSecureGlobaleBusinessCA-1.crt -keystore truststore_javanet.jks -storepass test1234
     * Owner: CN=Equifax Secure Global eBusiness CA-1, O=Equifax Secure Inc., C=US
     * Issuer: CN=Equifax Secure Global eBusiness CA-1, O=Equifax Secure Inc., C=US
     * Serial number: 1
     * Valid from: Mon Jun 21 00:00:00 EDT 1999 until: Sun Jun 21 00:00:00 EDT 2020
     * Certificate fingerprints:
     * MD5:  8F:5D:77:06:27:C4:98:3C:5B:93:78:E7:D7:7D:9B:CC
     * SHA1: 7E:78:4A:10:1C:82:65:CC:2D:E1:F1:6D:47:B4:40:CA:D9:0A:19:45
     * Signature algorithm name: MD5withRSA
     * Version: 3
     * ...
     * Trust this certificate? [no]:  yes
     * Certificate was added to keystore
     *

     * Now you want to use this truststore in your client:
     *

     * RestAssured.trustSture("/truststore_javanet.jks", "test1234");
     *

     * or
     *

     * given().trustStore("/truststore_javanet.jks", "test1234"). ..
     *

     *

     *

     * Note that this is just a shortcut for:
     *

     *

     * RestAssured.config = RestAssured.config().sslConfig(sslConfig().trustStore(pathToJks, password));
     *

     *
     * @param pathToJks The path to the JKS. REST Assured will first look in the classpath and if not found it will look for the JKS in the local file-system
     * @param password  The store pass
     */
    public static void trustStore(String pathToJks, String password) {
        Validate.notEmpty(password, "Password cannot be empty");
        applyTrustStore(pathToJks, password);
    }

    /**
     * Specify a trust store that'll be used for HTTPS requests. A trust store is a {@link java.security.KeyStore} that has been loaded with the password.
     * If you wish that REST Assured loads the KeyStore store and applies the password (thus making it a trust store) please see some of the
     * keystore methods such as {@link #keyStore(java.io.File, String)}.
     *
     * @param truststore A pre-loaded {@link java.security.KeyStore}.
     * @see #keyStore(String, String)
     */
    public static void trustStore(KeyStore truststore) {
        Validate.notNull(truststore, "Truststore cannot be null");
        config = config().sslConfig(SSLConfig.sslConfig().trustStore(truststore));
    }

    /**
     * Use a keystore located on the file-system. See {@link #keyStore(String, String)} for more details.
     * *

     * Note that this is just a shortcut for:
     *

     *

     * RestAssured.config = RestAssured.config().sslConfig(sslConfig().keyStore(pathToJks, password));
     *

     *
     * @param pathToJks The path to JKS file on the file-system
     * @param password  The password for the keystore
     * @see #keyStore(String, String)
     */
    public static void keyStore(File pathToJks, String password) {
        Validate.notNull(pathToJks, "Path to JKS on the file system cannot be null");
        applyKeyStore(pathToJks, password);
    }

    /**
     * Use a trust store located on the file-system. See {@link #trustStore(String, String)} for more details.
     * *

     * Note that this is just a shortcut for:
     *

     *

     * RestAssured.config = RestAssured.config().sslConfig(sslConfig().trustStore(pathToJks, password));
     *

     *
     * @param pathToJks The path to JKS file on the file-system
     * @param password  The password for the keystore
     * @see #keyStore(String, String)
     */
    public static void trustStore(File pathToJks, String password) {
        Validate.notNull(pathToJks, "Path to JKS on the file system cannot be null");
        applyTrustStore(pathToJks, password);
    }

    /**
     * Uses the user default keystore stored in @{user.home}/.keystore
     * *

     * Note that this is just a shortcut for:
     *

     *

     * RestAssured.config = RestAssured.config().sslConfig(sslConfig().keyStore(password));
     *

     *
     * @param password - Use null for no password
     */
    public static void keyStore(String password) {
        applyKeyStore(null, password);
    }

    /**
     * Instruct REST Assured to connect to a proxy on the specified host and port.
     *
     * @param host The hostname of the proxy to connect to (for example 127.0.0.1)
     * @param port The port of the proxy to connect to (for example 8888)
     */
    public static void proxy(String host, int port) {
        proxy(host(host).withPort(port));
    }

    /**
     * Instruct REST Assured to connect to a proxy on the specified host on port 8888.
     *
     * @param host The hostname of the proxy to connect to (for example 127.0.0.1). Can also be a URI represented as a String.
     * @see #proxy(String, int)
     */
    public static void proxy(String host) {
        if (UriValidator.isUri(host)) {
            try {
                proxy(new URI(host));
            } catch (URISyntaxException e) {
                throw new RuntimeException("Internal error in REST Assured when constructing URI for Proxy.", e);
            }
        } else {
            proxy(host(host));
        }
    }

    /**
     * Instruct REST Assured to connect to a proxy on the specified port on localhost.
     *
     * @param port The port of the proxy to connect to (for example 8888)
     * @see #proxy(String, int)
     */
    public static void proxy(int port) {
        proxy(ProxySpecification.port(port));
    }

    /**
     * Instruct REST Assured to connect to a proxy on the specified port on localhost with a specific scheme.
     *
     * @param host   The hostname of the proxy to connect to (for example 127.0.0.1)
     * @param port   The port of the proxy to connect to (for example 8888)
     * @param scheme The http scheme (http or https)
     */
    public static void proxy(String host, int port, String scheme) {
        proxy(new ProxySpecification(host, port, scheme));
    }

    /**
     * Instruct REST Assured to connect to a proxy using a URI.
     *
     * @param uri The URI of the proxy
     */
    public static void proxy(URI uri) {
        if (uri == null) {
            throw new IllegalArgumentException("Proxy URI cannot be null");
        }
        proxy(new ProxySpecification(uri.getHost(), uri.getPort(), uri.getScheme()));
    }

    /**
     * Instruct REST Assured to connect to a proxy using a {@link ProxySpecification}.
     *
     * @param proxySpecification The proxy specification to use.
     * @see RequestSpecification#proxy(ProxySpecification)
     */
    public static void proxy(ProxySpecification proxySpecification) {
        RestAssured.proxy = proxySpecification;
    }

    private static void applyKeyStore(Object pathToJks, String password) {
        RestAssuredConfig restAssuredConfig = config();
        final SSLConfig updatedSSLConfig;
        if (pathToJks instanceof File) {
            updatedSSLConfig = restAssuredConfig.getSSLConfig().keyStore((File) pathToJks, password);
        } else {
            updatedSSLConfig = restAssuredConfig.getSSLConfig().keyStore((String) pathToJks, password);
        }
        config = config().sslConfig(updatedSSLConfig.allowAllHostnames()); // Allow all host names to be backward-compatible
    }

    private static void applyTrustStore(Object pathToJks, String password) {
        RestAssuredConfig restAssuredConfig = config();
        final SSLConfig updatedSSLConfig;
        if (pathToJks instanceof File) {
            updatedSSLConfig = restAssuredConfig.getSSLConfig().trustStore((File) pathToJks, password);
        } else {
            updatedSSLConfig = restAssuredConfig.getSSLConfig().trustStore((String) pathToJks, password);
        }
        config = config().sslConfig(updatedSSLConfig.allowAllHostnames()); // Allow all host names to be backward-compatible
    }

    /**
     * @return The assigned config or a new config is no config is assigned
     */
    public static RestAssuredConfig config() {
        return config == null ? new RestAssuredConfig() : config;
    }
}


/*
 * Copyright 2019 the original author or authors.
 *
 * 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 io.restassured.internal.print;

import io.restassured.filter.log.LogDetail;
import io.restassured.http.Cookies;
import io.restassured.http.Header;
import io.restassured.http.Headers;
import io.restassured.internal.support.Prettifier;
import io.restassured.response.ResponseBody;
import io.restassured.response.ResponseOptions;
import org.apache.commons.lang3.SystemUtils;

import java.io.PrintStream;

import static io.restassured.filter.log.LogDetail.*;
import static org.apache.commons.lang3.StringUtils.isBlank;

/**
 * A response printer can be used to print a response.
 */
public class ResponsePrinter {

    private static final String HEADER_NAME_AND_VALUE_SEPARATOR = ": ";

    /**
     * Prints the response to the print stream
     *
     * @return A string of representing the response
     */
    public static String print(ResponseOptions responseOptions, ResponseBody responseBody, PrintStream stream, LogDetail logDetail, boolean shouldPrettyPrint) {
        final StringBuilder builder = new StringBuilder();
        if (logDetail == ALL || logDetail == STATUS) {
            builder.append(responseOptions.statusLine());
        }
        if (logDetail == ALL || logDetail == HEADERS) {
            final Headers headers = responseOptions.headers();
            if (headers.exist()) {
                appendNewLineIfAll(logDetail, builder).append(toString(headers));
            }
        } else if (logDetail == COOKIES) {
            final Cookies cookies = responseOptions.detailedCookies();
            if (cookies.exist()) {
                appendNewLineIfAll(logDetail, builder).append(cookies.toString());
            }
        }
        if (logDetail == ALL || logDetail == BODY) {
            String responseBodyToAppend;
            if (shouldPrettyPrint) {
                responseBodyToAppend = new Prettifier().getPrettifiedBodyIfPossible(responseOptions, responseBody);
            } else {
                responseBodyToAppend = responseBody.asString();
            }
            if (logDetail == ALL && !isBlank(responseBodyToAppend)) {
                builder.append(SystemUtils.LINE_SEPARATOR).append(SystemUtils.LINE_SEPARATOR);
            }

            builder.append(responseBodyToAppend);
        }
        String response = builder.toString();
        stream.println(response);
        return response;
    }

    private static String toString(Headers headers) {
        if (!headers.exist()) {
            return "";
        }

        final StringBuilder builder = new StringBuilder();
        for (Header header : headers) {
            builder.append(header.getName())
                    .append(HEADER_NAME_AND_VALUE_SEPARATOR)
                    .append(header.getValue())
                    .append(SystemUtils.LINE_SEPARATOR);
        }
        builder.delete(builder.length() - SystemUtils.LINE_SEPARATOR.length(), builder.length());
        return builder.toString();
    }

    private static StringBuilder appendNewLineIfAll(LogDetail logDetail, StringBuilder builder) {
        if (logDetail == ALL) {
            builder.append(SystemUtils.LINE_SEPARATOR);
        }
        return builder;
    }

}


/*
 * Copyright 2019 the original author or authors.
 *
 * 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 io.restassured.config;

import java.util.HashMap;
import java.util.Map;

import static io.restassured.internal.common.assertion.AssertParameter.notNull;

/**
 * Main configuration for REST Assured that allows you to configure advanced settings such as redirections and HTTP Client parameters.
 *

 * Usage example:
 *

 *  RestAssured.config = RestAssured.config().redirect(redirectConfig().followRedirects(false));
 *

 *

 */
public class RestAssuredConfig implements Config {

    final Map<Class<? extends Config>, Config> configs = new HashMap<Class<? extends Config>, Config>();

    /**
     * Create a new RestAssuredConfiguration with the default configurations.
     */
    public RestAssuredConfig() {
        this(new RedirectConfig(), new HttpClientConfig(), new LogConfig(), new EncoderConfig(), new DecoderConfig(),
                new SessionConfig(), new ObjectMapperConfig(), new ConnectionConfig(), new JsonConfig(), new XmlConfig(), new SSLConfig(),
                new MatcherConfig(), new HeaderConfig(), new MultiPartConfig(), new ParamConfig(), new OAuthConfig(), new FailureConfig());
    }

    /**
     * Create a new RestAssuredConfiguration with the supplied {@link RedirectConfig}, {@link HttpClientConfig}, {@link LogConfig},
     * {@link EncoderConfig}, {@link DecoderConfig}, {@link SessionConfig}, {@link ObjectMapperConfig}, {@link ConnectionConfig},
     * {@link JsonConfig}, {@link XmlConfig}, {@link SSLConfig},
     * {@link MatcherConfig}, {@link HeaderConfig}, {@link MultiPartConfig}
     */
    public RestAssuredConfig(RedirectConfig redirectConfig,
                             HttpClientConfig httpClientConfig,
                             LogConfig logConfig,
                             EncoderConfig encoderConfig,
                             DecoderConfig decoderConfig,
                             SessionConfig sessionConfig,
                             ObjectMapperConfig objectMapperConfig,
                             ConnectionConfig connectionConfig,
                             JsonConfig jsonConfig,
                             XmlConfig xmlConfig,
                             SSLConfig sslConfig,
                             MatcherConfig matcherConfig,
                             HeaderConfig headerConfig,
                             MultiPartConfig multiPartConfig,
                             ParamConfig paramConfig,
                             OAuthConfig oAuthConfig,
                             FailureConfig failureConfig) {
        notNull(redirectConfig, "Redirect Config");
        notNull(httpClientConfig, "HTTP Client Config");
        notNull(logConfig, "Log config");
        notNull(encoderConfig, "Encoder config");
        notNull(decoderConfig, "Decoder config");
        notNull(sessionConfig, "Session config");
        notNull(objectMapperConfig, "Object mapper config");
        notNull(connectionConfig, "Connection config");
        notNull(jsonConfig, "Json config");
        notNull(xmlConfig, "Xml config");
        notNull(sslConfig, "SSL config");
        notNull(matcherConfig, "Matcher config");
        notNull(headerConfig, "Header config");
        notNull(multiPartConfig, "Multipart config");
        notNull(paramConfig, "Param config");
        notNull(oAuthConfig, "OAuth config");
        notNull(failureConfig, "Failre config");
        configs.put(HttpClientConfig.class, httpClientConfig);
        configs.put(RedirectConfig.class, redirectConfig);
        configs.put(LogConfig.class, logConfig);
        configs.put(EncoderConfig.class, encoderConfig);
        configs.put(DecoderConfig.class, decoderConfig);
        configs.put(SessionConfig.class, sessionConfig);
        configs.put(ObjectMapperConfig.class, objectMapperConfig);
        configs.put(ConnectionConfig.class, connectionConfig);
        configs.put(JsonConfig.class, jsonConfig);
        configs.put(XmlConfig.class, xmlConfig);
        configs.put(SSLConfig.class, sslConfig);
        configs.put(MatcherConfig.class, matcherConfig);
        configs.put(HeaderConfig.class, headerConfig);
        configs.put(MultiPartConfig.class, multiPartConfig);
        configs.put(ParamConfig.class, paramConfig);
        configs.put(OAuthConfig.class, oAuthConfig);
        configs.put(FailureConfig.class, failureConfig);
    }

    /**
     * Set the redirect config.
     *
     * @param redirectConfig The {@link RedirectConfig} to set
     * @return An updated RestAssuredConfiguration
     */
    public RestAssuredConfig redirect(RedirectConfig redirectConfig) {
        notNull(redirectConfig, "Redirect config");
        return new RestAssuredConfig(redirectConfig, conf(HttpClientConfig.class), conf(LogConfig.class), conf(EncoderConfig.class),
                conf(DecoderConfig.class), conf(SessionConfig.class), conf(ObjectMapperConfig.class), conf(ConnectionConfig.class),
                conf(JsonConfig.class), conf(XmlConfig.class), conf(SSLConfig.class), conf(MatcherConfig.class),
                conf(HeaderConfig.class), conf(MultiPartConfig.class), conf(ParamConfig.class), conf(OAuthConfig.class), conf(FailureConfig.class));
    }

    /**
     * Set the HTTP Client config.
     *
     * @param httpClientConfig The {@link HttpClientConfig} to set
     * @return An updated RestAssuredConfiguration
     */
    public RestAssuredConfig httpClient(HttpClientConfig httpClientConfig) {
        notNull(httpClientConfig, "HTTP Client Config");
        return new RestAssuredConfig(conf(RedirectConfig.class), httpClientConfig, conf(LogConfig.class), conf(EncoderConfig.class),
                conf(DecoderConfig.class), conf(SessionConfig.class), conf(ObjectMapperConfig.class), conf(ConnectionConfig.class),
                conf(JsonConfig.class), conf(XmlConfig.class), conf(SSLConfig.class), conf(MatcherConfig.class),
                conf(HeaderConfig.class), conf(MultiPartConfig.class), conf(ParamConfig.class), conf(OAuthConfig.class), conf(FailureConfig.class));
    }

    /**
     * Set the Log config.
     *
     * @param logConfig The {@link LogConfig} to set
     * @return An updated RestAssuredConfiguration
     */
    public RestAssuredConfig logConfig(LogConfig logConfig) {
        notNull(logConfig, "Log config");
        return new RestAssuredConfig(conf(RedirectConfig.class), conf(HttpClientConfig.class), logConfig, conf(EncoderConfig.class),
                conf(DecoderConfig.class), conf(SessionConfig.class), conf(ObjectMapperConfig.class), conf(ConnectionConfig.class),
                conf(JsonConfig.class), conf(XmlConfig.class), conf(SSLConfig.class), conf(MatcherConfig.class),
                conf(HeaderConfig.class), conf(MultiPartConfig.class), conf(ParamConfig.class), conf(OAuthConfig.class), conf(FailureConfig.class));
    }

    /**
     * Set the Encoder config.
     *
     * @param encoderConfig The {@link EncoderConfig} to set
     * @return An updated RestAssuredConfiguration
     */
    public RestAssuredConfig encoderConfig(EncoderConfig encoderConfig) {
        notNull(encoderConfig, "Encoder config");
        return new RestAssuredConfig(conf(RedirectConfig.class), conf(HttpClientConfig.class), conf(LogConfig.class), encoderConfig,
                conf(DecoderConfig.class), conf(SessionConfig.class), conf(ObjectMapperConfig.class), conf(ConnectionConfig.class),
                conf(JsonConfig.class), conf(XmlConfig.class), conf(SSLConfig.class), conf(MatcherConfig.class),
                conf(HeaderConfig.class), conf(MultiPartConfig.class), conf(ParamConfig.class), conf(OAuthConfig.class), conf(FailureConfig.class));
    }

    /**
     * Set the Decoder config.
     *
     * @param decoderConfig The {@link DecoderConfig} to set
     * @return An updated RestAssuredConfiguration
     */
    public RestAssuredConfig decoderConfig(DecoderConfig decoderConfig) {
        notNull(decoderConfig, "Decoder config");
        return new RestAssuredConfig(conf(RedirectConfig.class), conf(HttpClientConfig.class), conf(LogConfig.class), conf(EncoderConfig.class),
                decoderConfig, conf(SessionConfig.class), conf(ObjectMapperConfig.class), conf(ConnectionConfig.class),
                conf(JsonConfig.class), conf(XmlConfig.class), conf(SSLConfig.class), conf(MatcherConfig.class),
                conf(HeaderConfig.class), conf(MultiPartConfig.class), conf(ParamConfig.class), conf(OAuthConfig.class), conf(FailureConfig.class));
    }

    /**
     * Set the session config.
     *
     * @param sessionConfig The {@link SessionConfig} to set
     * @return An updated RestAssuredConfiguration
     */
    public RestAssuredConfig sessionConfig(SessionConfig sessionConfig) {
        notNull(sessionConfig, "Session config");
        return new RestAssuredConfig(conf(RedirectConfig.class), conf(HttpClientConfig.class), conf(LogConfig.class), conf(EncoderConfig.class),
                conf(DecoderConfig.class), sessionConfig, conf(ObjectMapperConfig.class), conf(ConnectionConfig.class),
                conf(JsonConfig.class), conf(XmlConfig.class), conf(SSLConfig.class), conf(MatcherConfig.class),
                conf(HeaderConfig.class), conf(MultiPartConfig.class), conf(ParamConfig.class), conf(OAuthConfig.class), conf(FailureConfig.class));
    }

    /**
     * Set the object mapper config.
     *
     * @param objectMapperConfig The {@link ObjectMapperConfig} to set
     * @return An updated RestAssuredConfiguration
     */
    public RestAssuredConfig objectMapperConfig(ObjectMapperConfig objectMapperConfig) {
        notNull(objectMapperConfig, "Object mapper config");
        return new RestAssuredConfig(conf(RedirectConfig.class), conf(HttpClientConfig.class), conf(LogConfig.class), conf(EncoderConfig.class),
                conf(DecoderConfig.class), conf(SessionConfig.class), objectMapperConfig, conf(ConnectionConfig.class),
                conf(JsonConfig.class), conf(XmlConfig.class), conf(SSLConfig.class), conf(MatcherConfig.class),
                conf(HeaderConfig.class), conf(MultiPartConfig.class), conf(ParamConfig.class), conf(OAuthConfig.class), conf(FailureConfig.class));
    }

    /**
     * Set the connection config.
     *
     * @param connectionConfig The {@link ConnectionConfig} to set
     * @return An updated RestAssuredConfiguration
     */
    public RestAssuredConfig connectionConfig(ConnectionConfig connectionConfig) {
        notNull(connectionConfig, "Connection config");
        return new RestAssuredConfig(conf(RedirectConfig.class), conf(HttpClientConfig.class), conf(LogConfig.class), conf(EncoderConfig.class),
                conf(DecoderConfig.class), conf(SessionConfig.class), conf(ObjectMapperConfig.class), connectionConfig,
                conf(JsonConfig.class), conf(XmlConfig.class), conf(SSLConfig.class), conf(MatcherConfig.class),
                conf(HeaderConfig.class), conf(MultiPartConfig.class), conf(ParamConfig.class), conf(OAuthConfig.class), conf(FailureConfig.class));
    }

    /**
     * Set the Json config.
     *
     * @param jsonConfig The {@link JsonConfig} to set
     * @return An updated RestAssuredConfiguration
     */
    public RestAssuredConfig jsonConfig(JsonConfig jsonConfig) {
        notNull(jsonConfig, "JsonConfig");
        return new RestAssuredConfig(conf(RedirectConfig.class), conf(HttpClientConfig.class), conf(LogConfig.class), conf(EncoderConfig.class),
                conf(DecoderConfig.class), conf(SessionConfig.class), conf(ObjectMapperConfig.class), conf(ConnectionConfig.class),
                jsonConfig, conf(XmlConfig.class), conf(SSLConfig.class), conf(MatcherConfig.class),
                conf(HeaderConfig.class), conf(MultiPartConfig.class), conf(ParamConfig.class), conf(OAuthConfig.class),conf(FailureConfig.class));
    }

    /**
     * Set the Xml config.
     *
     * @param xmlConfig The {@link XmlConfig} to set
     * @return An updated RestAssuredConfiguration
     */
    public RestAssuredConfig xmlConfig(XmlConfig xmlConfig) {
        notNull(xmlConfig, "XmlConfig");
        return new RestAssuredConfig(conf(RedirectConfig.class), conf(HttpClientConfig.class), conf(LogConfig.class), conf(EncoderConfig.class),
                conf(DecoderConfig.class), conf(SessionConfig.class), conf(ObjectMapperConfig.class), conf(ConnectionConfig.class),
                conf(JsonConfig.class), xmlConfig, conf(SSLConfig.class), conf(MatcherConfig.class), conf(HeaderConfig.class),
                conf(MultiPartConfig.class), conf(ParamConfig.class), conf(OAuthConfig.class), conf(FailureConfig.class));
    }

    /**
     * Set the SSL config.
     *
     * @param sslConfig The {@link SSLConfig} to set
     * @return An updated RestAssuredConfiguration
     */
    public RestAssuredConfig sslConfig(SSLConfig sslConfig) {
        notNull(sslConfig, "SSLConfig");
        return new RestAssuredConfig(conf(RedirectConfig.class), conf(HttpClientConfig.class), conf(LogConfig.class), conf(EncoderConfig.class),
                conf(DecoderConfig.class), conf(SessionConfig.class), conf(ObjectMapperConfig.class), conf(ConnectionConfig.class),
                conf(JsonConfig.class), conf(XmlConfig.class), sslConfig, conf(MatcherConfig.class), conf(HeaderConfig.class),
                conf(MultiPartConfig.class), conf(ParamConfig.class), conf(OAuthConfig.class), conf(FailureConfig.class));
    }

    /**
     * Set the Matcher config.
     *
     * @param matcherConfig The {@link MatcherConfig} to set
     * @return An updated RestAssuredConfiguration
     */
    public RestAssuredConfig matcherConfig(MatcherConfig matcherConfig) {
        notNull(matcherConfig, "MatcherConfig");
        return new RestAssuredConfig(conf(RedirectConfig.class), conf(HttpClientConfig.class), conf(LogConfig.class), conf(EncoderConfig.class),
                conf(DecoderConfig.class), conf(SessionConfig.class), conf(ObjectMapperConfig.class), conf(ConnectionConfig.class),
                conf(JsonConfig.class), conf(XmlConfig.class), conf(SSLConfig.class), matcherConfig, conf(HeaderConfig.class),
                conf(MultiPartConfig.class), conf(ParamConfig.class), conf(OAuthConfig.class), conf(FailureConfig.class));
    }

    /**
     * Set the Header config.
     *
     * @param headerConfig The {@link HeaderConfig} to set
     * @return An updated RestAssuredConfiguration
     */
    public RestAssuredConfig headerConfig(HeaderConfig headerConfig) {
        notNull(headerConfig, "HeaderConfig");
        return new RestAssuredConfig(conf(RedirectConfig.class), conf(HttpClientConfig.class), conf(LogConfig.class), conf(EncoderConfig.class),
                conf(DecoderConfig.class), conf(SessionConfig.class), conf(ObjectMapperConfig.class), conf(ConnectionConfig.class),
                conf(JsonConfig.class), conf(XmlConfig.class), conf(SSLConfig.class), conf(MatcherConfig.class), headerConfig,
                conf(MultiPartConfig.class), conf(ParamConfig.class), conf(OAuthConfig.class), conf(FailureConfig.class));
    }

    /**
     * Set the MultiPart config.
     *
     * @param multiPartConfig The {@link MultiPartConfig} to set
     * @return An updated RestAssuredConfiguration
     */
    public RestAssuredConfig multiPartConfig(MultiPartConfig multiPartConfig) {
        notNull(multiPartConfig, "MultiPartConfig");
        return new RestAssuredConfig(conf(RedirectConfig.class), conf(HttpClientConfig.class), conf(LogConfig.class), conf(EncoderConfig.class),
                conf(DecoderConfig.class), conf(SessionConfig.class), conf(ObjectMapperConfig.class), conf(ConnectionConfig.class),
                conf(JsonConfig.class), conf(XmlConfig.class), conf(SSLConfig.class), conf(MatcherConfig.class), conf(HeaderConfig.class),
                multiPartConfig, conf(ParamConfig.class), conf(OAuthConfig.class), conf(FailureConfig.class));
    }

    /**
     * Set the parameter config.
     *
     * @param paramConfig The {@link ParamConfig} to set
     * @return An updated RestAssuredConfiguration
     */
    public RestAssuredConfig paramConfig(ParamConfig paramConfig) {
        notNull(paramConfig, ParamConfig.class);
        return new RestAssuredConfig(conf(RedirectConfig.class), conf(HttpClientConfig.class), conf(LogConfig.class), conf(EncoderConfig.class),
                conf(DecoderConfig.class), conf(SessionConfig.class), conf(ObjectMapperConfig.class), conf(ConnectionConfig.class),
                conf(JsonConfig.class), conf(XmlConfig.class), conf(SSLConfig.class), conf(MatcherConfig.class), conf(HeaderConfig.class),
                conf(MultiPartConfig.class), paramConfig, conf(OAuthConfig.class), conf(FailureConfig.class));
    }

    /**
     * Set the oauth config.
     *
     * @param oauthConfig The {@link OAuthConfig} to set
     * @return An updated RestAssuredConfiguration
     */
    public RestAssuredConfig oauthConfig(OAuthConfig oauthConfig) {
        notNull(oauthConfig, OAuthConfig.class);
        return new RestAssuredConfig(conf(RedirectConfig.class), conf(HttpClientConfig.class), conf(LogConfig.class), conf(EncoderConfig.class),
                conf(DecoderConfig.class), conf(SessionConfig.class), conf(ObjectMapperConfig.class), conf(ConnectionConfig.class),
                conf(JsonConfig.class), conf(XmlConfig.class), conf(SSLConfig.class), conf(MatcherConfig.class), conf(HeaderConfig.class),
                conf(MultiPartConfig.class), conf(ParamConfig.class), oauthConfig, conf(FailureConfig.class));
    }

    /**
     * Set the failure config.
     *
     * @param failureConfig The {@link FailureConfig} to set
     * @return An updated RestAssuredConfiguration
     */
    public RestAssuredConfig failureConfig(FailureConfig failureConfig) {
        notNull(failureConfig, FailureConfig.class);
        return new RestAssuredConfig(conf(RedirectConfig.class), conf(HttpClientConfig.class), conf(LogConfig.class), conf(EncoderConfig.class),
                conf(DecoderConfig.class), conf(SessionConfig.class), conf(ObjectMapperConfig.class), conf(ConnectionConfig.class),
                conf(JsonConfig.class), conf(XmlConfig.class), conf(SSLConfig.class), conf(MatcherConfig.class), conf(HeaderConfig.class),
                conf(MultiPartConfig.class), conf(ParamConfig.class), conf(OAuthConfig.class), failureConfig);
    }
    /**
     * Syntactic sugar.
     *
     * @return The same RestAssuredConfiguration instance.
     */
    public RestAssuredConfig and() {
        return this;
    }

    /**
     * Syntactic sugar.
     *
     * @return The same RestAssuredConfiguration instance.
     */
    public RestAssuredConfig set() {
        return this;
    }

    /**
     * Syntactic sugar.
     *
     * @return The same RestAssuredConfiguration instance.
     */
    public RestAssuredConfig with() {
        return this;
    }

    /**
     * @return The RedirectConfig
     */
    public RedirectConfig getRedirectConfig() {
        return conf(RedirectConfig.class);
    }

    /**
     * @return The LogConfig
     */
    public LogConfig getLogConfig() {
        return conf(LogConfig.class);
    }

    /**
     * @return The HttpClientConfig
     */
    public HttpClientConfig getHttpClientConfig() {
        return conf(HttpClientConfig.class);
    }

    /**
     * @return The EncoderConfig
     */
    public EncoderConfig getEncoderConfig() {
        return conf(EncoderConfig.class);
    }

    /**
     * @return The DecoderConfig
     */
    public DecoderConfig getDecoderConfig() {
        return conf(DecoderConfig.class);
    }

    /**
     * @return The SessionConfig
     */
    public SessionConfig getSessionConfig() {
        return conf(SessionConfig.class);
    }

    /**
     * @return The ObjectMapperConfig
     */
    public ObjectMapperConfig getObjectMapperConfig() {
        return conf(ObjectMapperConfig.class);
    }

    /**
     * @return The ConnectionConfig
     */
    public ConnectionConfig getConnectionConfig() {
        return conf(ConnectionConfig.class);
    }

    /**
     * @return The JsonPath Config
     */
    public JsonConfig getJsonConfig() {
        return conf(JsonConfig.class);
    }

    /**
     * @return The Xml Config
     */
    public XmlConfig getXmlConfig() {
        return conf(XmlConfig.class);
    }

    /**
     * @return The SSL Config
     */
    public SSLConfig getSSLConfig() {
        return conf(SSLConfig.class);
    }

    /**
     * @return The matcher config
     */
    public MatcherConfig getMatcherConfig() {
        return conf(MatcherConfig.class);
    }

    /**
     * @return The header config
     */
    public HeaderConfig getHeaderConfig() {
        return conf(HeaderConfig.class);
    }

    /**
     * @return The MultiPart Config
     */
    public MultiPartConfig getMultiPartConfig() {
        return conf(MultiPartConfig.class);
    }

    /**
     * @return The Param Config
     */
    public ParamConfig getParamConfig() {
        return conf(ParamConfig.class);
    }

    /**
     * @return The Param Config
     */
    public OAuthConfig getOAuthConfig() {
        return conf(OAuthConfig.class);
    }

    /**
     * @return The Failure Config
     */
    public FailureConfig getFailureConfig() {
        return conf(FailureConfig.class);
    }

    /**
     * @return A static way to create a new RestAssuredConfiguration instance without calling "new" explicitly. Mainly for syntactic sugar.
     */
    public static RestAssuredConfig newConfig() {
        return new RestAssuredConfig();
    }

    /**
     * @return A static way to create a new RestAssuredConfiguration instance without calling "new" explicitly. Mainly for syntactic sugar.
     */
    public static RestAssuredConfig config() {
        return new RestAssuredConfig();
    }

    /**
     * @return true if this instance is carrying any config that is user configured.
     */
    public boolean isUserConfigured() {
        for (Config cfg : configs.values()) {
            if (cfg.isUserConfigured()) {
                return true;
            }
        }
        return false;
    }

    @SuppressWarnings("unchecked")
    private <T extends Config> T conf(Class<T> type) {
        return (T) configs.get(type);
    }
}