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

com.twosigma.webtau.http.Http Maven / Gradle / Ivy

There is a newer version: 1.20
Show newest version
/*
 * Copyright 2019 TWO SIGMA OPEN SOURCE, LLC
 *
 * 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 com.twosigma.webtau.http;

import com.twosigma.webtau.console.ConsoleOutputs;
import com.twosigma.webtau.console.ansi.Color;
import com.twosigma.webtau.data.traceable.CheckLevel;
import com.twosigma.webtau.data.traceable.TraceableValue;
import com.twosigma.webtau.expectation.ExpectationHandler;
import com.twosigma.webtau.expectation.ExpectationHandlers;
import com.twosigma.webtau.http.binary.BinaryRequestBody;
import com.twosigma.webtau.http.config.HttpConfigurations;
import com.twosigma.webtau.http.datanode.DataNode;
import com.twosigma.webtau.http.datanode.DataNodeBuilder;
import com.twosigma.webtau.http.datanode.DataNodeId;
import com.twosigma.webtau.http.datanode.StructuredDataNode;
import com.twosigma.webtau.http.json.JsonRequestBody;
import com.twosigma.webtau.http.multipart.MultiPartFile;
import com.twosigma.webtau.http.multipart.MultiPartFormData;
import com.twosigma.webtau.http.multipart.MultiPartFormField;
import com.twosigma.webtau.http.render.DataNodeAnsiPrinter;
import com.twosigma.webtau.http.request.*;
import com.twosigma.webtau.http.text.TextRequestBody;
import com.twosigma.webtau.http.validation.*;
import com.twosigma.webtau.reporter.StepReportOptions;
import com.twosigma.webtau.reporter.TestStep;
import com.twosigma.webtau.reporter.stacktrace.StackTraceUtils;
import com.twosigma.webtau.time.Time;
import com.twosigma.webtau.utils.CollectionUtils;
import com.twosigma.webtau.utils.JsonParseException;
import com.twosigma.webtau.utils.JsonUtils;
import org.apache.commons.io.IOUtils;

import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.function.Supplier;
import java.util.zip.GZIPInputStream;

import static com.twosigma.webtau.Ddjt.equal;
import static com.twosigma.webtau.cfg.WebTauConfig.getCfg;
import static com.twosigma.webtau.reporter.IntegrationTestsMessageBuilder.action;
import static com.twosigma.webtau.reporter.IntegrationTestsMessageBuilder.urlValue;
import static com.twosigma.webtau.reporter.TokenizedMessage.tokenizedMessage;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.stream.Collectors.toList;

public class Http {
    private static final HttpResponseValidatorWithReturn EMPTY_RESPONSE_VALIDATOR = (header, body) -> null;

    public static final Http http = new Http();

    public final HttpDocumentation doc = new HttpDocumentation();

    private final ThreadLocal lastValidationResult = new ThreadLocal<>();

    public HttpApplicationMime application = new HttpApplicationMime();
    public HttpTextMime text = new HttpTextMime();

    public  E get(String url, HttpQueryParams queryParams, HttpHeader header, HttpResponseValidatorWithReturn validator) {
        return executeAndValidateHttpCall("GET", queryParams.attachToUrl(url),
                this::getToFullUrl,
                header, null, validator);
    }

    public void get(String url, HttpQueryParams queryParams, HttpHeader header, HttpResponseValidator validator) {
        get(url, queryParams, header, new HttpResponseValidatorIgnoringReturn(validator));
    }

    public  E get(String url, HttpQueryParams queryParams, HttpResponseValidatorWithReturn validator) {
        return get(url, queryParams, HttpHeader.EMPTY, validator);
    }

    public void get(String url, HttpQueryParams queryParams, HttpResponseValidator validator) {
        get(url, queryParams, HttpHeader.EMPTY, new HttpResponseValidatorIgnoringReturn(validator));
    }

    public  E get(String url, HttpHeader header, HttpResponseValidatorWithReturn validator) {
        return get(url, HttpQueryParams.EMPTY, header, validator);
    }

    public void get(String url, HttpHeader header, HttpResponseValidator validator) {
        get(url, HttpQueryParams.EMPTY, header, new HttpResponseValidatorIgnoringReturn(validator));
    }

    public  E get(String url, HttpResponseValidatorWithReturn validator) {
        return get(url, HttpQueryParams.EMPTY, HttpHeader.EMPTY, validator);
    }

    public void get(String url, HttpResponseValidator validator) {
        get(url, HttpQueryParams.EMPTY, HttpHeader.EMPTY, new HttpResponseValidatorIgnoringReturn(validator));
    }

    public  E post(String url, HttpQueryParams queryParams, HttpHeader header, HttpRequestBody requestBody, HttpResponseValidatorWithReturn validator) {
        return executeAndValidateHttpCall("POST", queryParams.attachToUrl(url),
                (fullUrl, fullHeader) -> postToFullUrl(fullUrl, fullHeader, requestBody),
                header,
                requestBody,
                validator);
    }

    public  E post(String url, HttpHeader header, HttpRequestBody requestBody, HttpResponseValidatorWithReturn validator) {
        return post(url, HttpQueryParams.EMPTY, header, requestBody, validator);
    }

    public void post(String url, HttpQueryParams queryParams, HttpHeader header, HttpRequestBody requestBody, HttpResponseValidator validator) {
        post(url, queryParams, header, requestBody, new HttpResponseValidatorIgnoringReturn(validator));
    }

    public void post(String url, HttpHeader header, HttpRequestBody requestBody, HttpResponseValidator validator) {
        post(url, header, requestBody, new HttpResponseValidatorIgnoringReturn(validator));
    }

    public  E post(String url, HttpQueryParams queryParams, HttpHeader header, HttpResponseValidatorWithReturn validator) {
        return post(url, queryParams, header, EmptyRequestBody.INSTANCE, validator);
    }

    public  E post(String url, HttpHeader header, HttpResponseValidatorWithReturn validator) {
        return post(url, header, EmptyRequestBody.INSTANCE, validator);
    }

    public void post(String url, HttpQueryParams queryParams, HttpHeader header, HttpResponseValidator validator) {
        post(url, queryParams, header, EmptyRequestBody.INSTANCE, new HttpResponseValidatorIgnoringReturn(validator));
    }

    public void post(String url, HttpHeader header, HttpResponseValidator validator) {
        post(url, header, EmptyRequestBody.INSTANCE, new HttpResponseValidatorIgnoringReturn(validator));
    }

    public  E post(String url, HttpQueryParams queryParams, HttpRequestBody requestBody, HttpResponseValidatorWithReturn validator) {
        return post(url, queryParams, HttpHeader.EMPTY, requestBody, validator);
    }

    public  E post(String url, HttpRequestBody requestBody, HttpResponseValidatorWithReturn validator) {
        return post(url, HttpHeader.EMPTY, requestBody, validator);
    }

    public void post(String url, HttpQueryParams queryParams, HttpRequestBody requestBody, HttpResponseValidator validator) {
        post(url, queryParams, requestBody, new HttpResponseValidatorIgnoringReturn(validator));
    }

    public void post(String url, HttpRequestBody requestBody, HttpResponseValidator validator) {
        post(url, requestBody, new HttpResponseValidatorIgnoringReturn(validator));
    }

    public  E post(String url, HttpQueryParams queryParams, HttpHeader header, Map requestBody, HttpResponseValidatorWithReturn validator) {
        return post(url, queryParams, header, new JsonRequestBody(requestBody), validator);
    }

    public  E post(String url, HttpHeader header, Map requestBody, HttpResponseValidatorWithReturn validator) {
        return post(url, header, new JsonRequestBody(requestBody), validator);
    }

    public void post(String url, HttpQueryParams queryParams, HttpHeader header, Map requestBody, HttpResponseValidator validator) {
        post(url, queryParams, header, new JsonRequestBody(requestBody), new HttpResponseValidatorIgnoringReturn(validator));
    }

    public void post(String url, HttpHeader header, Map requestBody, HttpResponseValidator validator) {
        post(url, header, new JsonRequestBody(requestBody), new HttpResponseValidatorIgnoringReturn(validator));
    }

    public  E post(String url, HttpQueryParams queryParams, Map requestBody, HttpResponseValidatorWithReturn validator) {
        return post(url, queryParams, HttpHeader.EMPTY, new JsonRequestBody(requestBody), validator);
    }

    public  E post(String url, Map requestBody, HttpResponseValidatorWithReturn validator) {
        return post(url, HttpHeader.EMPTY, new JsonRequestBody(requestBody), validator);
    }

    public void post(String url, HttpQueryParams queryParams, Map requestBody, HttpResponseValidator validator) {
        post(url, queryParams, HttpHeader.EMPTY, new JsonRequestBody(requestBody), new HttpResponseValidatorIgnoringReturn(validator));
    }

    public void post(String url, Map requestBody, HttpResponseValidator validator) {
        post(url, HttpHeader.EMPTY, new JsonRequestBody(requestBody), new HttpResponseValidatorIgnoringReturn(validator));
    }

    public  E post(String url, HttpQueryParams queryParams, HttpResponseValidatorWithReturn validator) {
        return post(url, queryParams, EmptyRequestBody.INSTANCE, validator);
    }

    public  E post(String url, HttpResponseValidatorWithReturn validator) {
        return post(url, EmptyRequestBody.INSTANCE, validator);
    }

    public void post(String url, HttpQueryParams queryParams, HttpResponseValidator validator) {
        post(url, queryParams, EmptyRequestBody.INSTANCE, new HttpResponseValidatorIgnoringReturn(validator));
    }

    public void post(String url, HttpResponseValidator validator) {
        post(url, EmptyRequestBody.INSTANCE, new HttpResponseValidatorIgnoringReturn(validator));
    }

    public void post(String url, HttpQueryParams queryParams, HttpHeader header) {
        post(url, queryParams, header, EMPTY_RESPONSE_VALIDATOR);
    }

    public void post(String url, HttpHeader header) {
        post(url, header, EMPTY_RESPONSE_VALIDATOR);
    }

    public void post(String url, HttpQueryParams queryParams) {
        post(url, queryParams, EMPTY_RESPONSE_VALIDATOR);
    }

    public void post(String url) {
        post(url, EMPTY_RESPONSE_VALIDATOR);
    }

    public void post(String url, HttpQueryParams queryParams, HttpRequestBody requestBody) {
        post(url, queryParams, requestBody, EMPTY_RESPONSE_VALIDATOR);
    }

    public void post(String url, HttpRequestBody requestBody) {
        post(url, requestBody, EMPTY_RESPONSE_VALIDATOR);
    }

    public  E put(String url, HttpHeader header, HttpRequestBody requestBody, HttpResponseValidatorWithReturn validator) {
        return executeAndValidateHttpCall("PUT", url,
                (fullUrl, fullHeader) -> putToFullUrl(fullUrl, fullHeader, requestBody),
                header,
                requestBody,
                validator);
    }

    public void put(String url, HttpHeader header, HttpRequestBody requestBody, HttpResponseValidator validator) {
        put(url, header, requestBody, new HttpResponseValidatorIgnoringReturn(validator));
    }

    public  E put(String url, HttpRequestBody requestBody, HttpResponseValidatorWithReturn validator) {
        return put(url, HttpHeader.EMPTY, requestBody, validator);
    }

    public void put(String url, HttpRequestBody requestBody, HttpResponseValidator validator) {
        put(url, requestBody, new HttpResponseValidatorIgnoringReturn(validator));
    }

    public  E put(String url, HttpResponseValidatorWithReturn validator) {
        return put(url, HttpHeader.EMPTY, EmptyRequestBody.INSTANCE, validator);
    }

    public void put(String url, HttpResponseValidator validator) {
        put(url, HttpHeader.EMPTY, EmptyRequestBody.INSTANCE, new HttpResponseValidatorIgnoringReturn(validator));
    }

    public  E put(String url, HttpHeader header, Map requestBody, HttpResponseValidatorWithReturn validator) {
        return put(url, header, new JsonRequestBody(requestBody), validator);
    }

    public void put(String url, HttpHeader header, Map requestBody, HttpResponseValidator validator) {
        put(url, header, new JsonRequestBody(requestBody), new HttpResponseValidatorIgnoringReturn(validator));
    }

    public  E put(String url, Map requestBody, HttpResponseValidatorWithReturn validator) {
        return put(url, HttpHeader.EMPTY, new JsonRequestBody(requestBody), validator);
    }

    public void put(String url, Map requestBody, HttpResponseValidator validator) {
        put(url, HttpHeader.EMPTY, new JsonRequestBody(requestBody), new HttpResponseValidatorIgnoringReturn(validator));
    }

    public  E put(String url, HttpHeader header, HttpResponseValidatorWithReturn validator) {
        return put(url, header, EmptyRequestBody.INSTANCE, validator);
    }

    public void put(String url, HttpHeader header, HttpResponseValidator validator) {
        put(url, header, EmptyRequestBody.INSTANCE, new HttpResponseValidatorIgnoringReturn(validator));
    }

    public void put(String url, HttpHeader header) {
        put(url, header, EmptyRequestBody.INSTANCE, EMPTY_RESPONSE_VALIDATOR);
    }

    public void put(String url) {
        put(url, HttpHeader.EMPTY, EmptyRequestBody.INSTANCE, EMPTY_RESPONSE_VALIDATOR);
    }

    public  E delete(String url, HttpHeader header, HttpResponseValidatorWithReturn validator) {
        return executeAndValidateHttpCall("DELETE", url,
                this::deleteToFullUrl,
                header,
                null,
                validator);
    }

    public void delete(String url, HttpHeader header, HttpResponseValidator validator) {
        delete(url, header, new HttpResponseValidatorIgnoringReturn(validator));
    }

    public  E delete(String url, HttpResponseValidatorWithReturn validator) {
        return delete(url, HttpHeader.EMPTY, validator);
    }

    public void delete(String url, HttpResponseValidator validator) {
        delete(url, new HttpResponseValidatorIgnoringReturn(validator));
    }

    public void delete(String url, HttpHeader header) {
        delete(url, header, EMPTY_RESPONSE_VALIDATOR);
    }

    public void delete(String url) {
        delete(url, EMPTY_RESPONSE_VALIDATOR);
    }

    public HttpHeader header(String... properties) {
        return new HttpHeader(CollectionUtils.aMapOf((Object[]) properties));
    }

    public HttpHeader header(Map properties) {
        return new HttpHeader(properties);
    }

    public HttpQueryParams query(String... params) {
        return new HttpQueryParams(CollectionUtils.aMapOf((Object[]) params));
    }

    public HttpQueryParams query(Map params) {
        return new HttpQueryParams(params);
    }

    public HttpRequestBody body(String mimeType, String content) {
        return TextRequestBody.withType(mimeType, content);
    }

    public HttpRequestBody body(String mimeType, byte[] content) {
        return BinaryRequestBody.withType(mimeType, content);
    }

    public MultiPartFormData formData(MultiPartFormField... fields) {
        MultiPartFormData formData = new MultiPartFormData();
        Arrays.stream(fields).forEach(formData::addField);

        return formData;
    }

    public MultiPartFormData formData(Map fields) {
        return new MultiPartFormData(fields);
    }

    public MultiPartFormField formField(byte[] fileContent) {
        return formField("file", fileContent, null);
    }

    public MultiPartFormField formField(String fieldName, byte[] fileContent) {
        return formField(fieldName, fileContent, null);
    }

    public MultiPartFormField formField(String fieldName, Path file) {
        return formField(fieldName, file, file.getFileName().toString());
    }

    public MultiPartFormField formField(String fieldName, Path file, String fileName) {
        return MultiPartFormField.fileFormField(fieldName, file, fileName);
    }

    public MultiPartFormField formField(String fieldName, byte[] fileContent, String fileName) {
        return MultiPartFormField.binaryFormField(fieldName, fileContent, fileName);
    }

    public MultiPartFormField formField(String fieldName, String textContent, String fileName) {
        return MultiPartFormField.textFormField(fieldName, textContent, fileName);
    }

    public MultiPartFormField formField(String fieldName, String textContent) {
        return formField(fieldName, textContent, null);
    }

    public MultiPartFile formFile(String fileName, byte[] fileContent) {
        return new MultiPartFile(fileName, fileContent);
    }

    public MultiPartFile formFile(String fileName, Path file) {
        return new MultiPartFile(fileName, file);
    }

    public HttpValidationResult getLastValidationResult() {
        return lastValidationResult.get();
    }

    public HttpResponse getToFullUrl(String fullUrl, HttpHeader requestHeader) {
        return request("GET", fullUrl, requestHeader, EmptyRequestBody.INSTANCE);
    }

    public HttpResponse deleteToFullUrl(String fullUrl, HttpHeader requestHeader) {
        return request("DELETE", fullUrl, requestHeader, EmptyRequestBody.INSTANCE);
    }

    public HttpResponse postToFullUrl(String fullUrl, HttpHeader requestHeader, HttpRequestBody requestBody) {
        return request("POST", fullUrl, requestHeader, requestBody);
    }

    public HttpResponse putToFullUrl(String fullUrl, HttpHeader requestHeader, HttpRequestBody requestBody) {
        return request("PUT", fullUrl, requestHeader, requestBody);
    }

    private  R executeAndValidateHttpCall(String requestMethod, String url, HttpCall httpCall,
                                             HttpHeader requestHeader,
                                             HttpRequestBody requestBody,
                                             HttpResponseValidatorWithReturn validator) {
        String fullUrl = HttpConfigurations.fullUrl(url);
        HttpHeader fullHeader = HttpConfigurations.fullHeader(fullUrl, url, requestHeader);

        HttpValidationResult validationResult = new HttpValidationResult(requestMethod, url, fullUrl, fullHeader, requestBody);

        TestStep step = createHttpStep(validationResult, requestMethod, fullUrl, httpCall, fullHeader, validator);
        try {
            return step.execute(StepReportOptions.REPORT_ALL);
        } finally {
            lastValidationResult.set(validationResult);
            step.addPayload(validationResult);
        }
    }

    private  TestStep createHttpStep(HttpValidationResult validationResult,
                                                 String requestMethod, String fullUrl, HttpCall httpCall,
                                                 HttpHeader fullRequestHeader,
                                                 HttpResponseValidatorWithReturn validator) {
        Supplier httpCallSupplier = () -> {
            try {
                long startTime = Time.currentTimeMillis();
                HttpResponse response = httpCall.execute(fullUrl, fullRequestHeader);
                response = followRedirects(requestMethod, httpCall, fullRequestHeader, response);

                long endTime = Time.currentTimeMillis();

                validationResult.setStartTime(startTime);
                validationResult.setElapsedTime(endTime - startTime);
                validationResult.setResponse(response);

                R validationBlockReturnedValue = validateAndRecord(validationResult, validator);

                if (validationResult.hasMismatches()) {
                    throw new AssertionError("\n" + validationResult.renderMismatches());
                }

                return validationBlockReturnedValue;
            } catch (AssertionError e) {
                throw e;
            } catch (Throwable e) {
                validationResult.setErrorMessage(StackTraceUtils.fullCauseMessage(e));
                throw new HttpException("error during http." + requestMethod.toLowerCase() + "(" + fullUrl + ")", e);
            }
        };

        return TestStep.createStep(null, tokenizedMessage(action("executing HTTP " + requestMethod), urlValue(fullUrl)),
                () -> tokenizedMessage(action("executed HTTP " + requestMethod), urlValue(fullUrl)),
                httpCallSupplier);
    }

    private HttpResponse followRedirects(String requestMethod, HttpCall httpCall, HttpHeader fullRequestHeader, HttpResponse response) {
        int retryCount = 0;
        while (response.isRedirect() && getCfg().shouldFollowRedirects() && retryCount++ < getCfg().maxRedirects()) {
            TestStep httpStep = createRedirectStep(requestMethod, response.locationHeader(), httpCall, fullRequestHeader);
            response = httpStep.execute(StepReportOptions.REPORT_ALL);
        }
        return response;
    }

    private TestStep createRedirectStep(String requestMethod, String fullUrl, HttpCall httpCall,
                                                            HttpHeader fullRequestHeader) {
        Supplier httpCallSupplier = () -> httpCall.execute(fullUrl, fullRequestHeader);

        return TestStep.createStep(null, tokenizedMessage(action("executing HTTP redirect to " + requestMethod), urlValue(fullUrl)),
                () -> tokenizedMessage(action("executed HTTP redirect to " + requestMethod), urlValue(fullUrl)),
                httpCallSupplier);
    }

    @SuppressWarnings("unchecked")
    private  R validateAndRecord(HttpValidationResult validationResult,
                                    HttpResponseValidatorWithReturn validator) {

        HeaderDataNode header = new HeaderDataNode(validationResult.getResponse());
        DataNode body = createBodyDataNode(validationResult.getResponse());

        validationResult.setResponseHeaderNode(header);
        validationResult.setResponseBodyNode(body);

        ExpectationHandler recordAndThrowHandler = (valueMatcher, actualPath, actualValue, message) -> {
            validationResult.addMismatch(message);
            return ExpectationHandler.Flow.PassToNext;
        };

        // 1. validate using user provided validation block
        // 2. validate status code
        // 3. if validation block throws exception,
        //    we still validate status code to make sure user is aware of the status code problem
        try {
            R extracted = ExpectationHandlers.withAdditionalHandler(recordAndThrowHandler, () -> {
                Object returnedValue = validator.validate(header, body);
                return (R) extractOriginalValue(returnedValue);
            });

            ExpectationHandlers.withAdditionalHandler(recordAndThrowHandler, () -> {
                validateStatusCode(validationResult);
                return null;
            });

            HttpValidationHandlers.validate(validationResult);

            return extracted;
        } catch (Throwable e) {
            ExpectationHandlers.withAdditionalHandler((valueMatcher, actualPath, actualValue, message) -> {
                validationResult.addMismatch(message);

                // another assertion happened before status code check
                // we discard it and throw status code instead
                if (e instanceof AssertionError) {
                    throw new AssertionError('\n' + message);
                }

                // originally an exception happened,
                // so we combine it's message with status code failure
                throw new AssertionError('\n' + message +
                        "\n\nadditional exception message:\n" + e.getMessage(), e);
            }, () -> {
                validateErrorsOnlyStatusCode(validationResult);
                return null;
            });

            throw e;
        } finally {
            renderResponse(validationResult);
        }
    }

    @SuppressWarnings("unchecked")
    private DataNode createBodyDataNode(HttpResponse response) {
        try {
            DataNodeId id = new DataNodeId("body");

            if (!response.isBinary() && response.nullOrEmptyTextContent()) {
                return new StructuredDataNode(id, new TraceableValue(null));
            }

            if (response.isText()) {
                return new StructuredDataNode(id, new TraceableValue(response.getTextContent()));
            }

            if (response.isJson()) {
                Object object = JsonUtils.deserialize(response.getTextContent());
                return DataNodeBuilder.fromValue(id, object);
            }

            return new StructuredDataNode(id, new TraceableValue(response.getBinaryContent()));
        } catch (JsonParseException e) {
            throw new RuntimeException("error parsing body: " + response.getTextContent(), e);
        }
    }

    private void validateStatusCode(HttpValidationResult validationResult) {
        DataNode statusCode = validationResult.getHeaderNode().statusCode();
        if (statusCode.getTraceableValue().getCheckLevel() != CheckLevel.None) {
            return;
        }

        statusCode.should(equal(defaultExpectedStatusCodeByRequest(validationResult)));
    }

    private void validateErrorsOnlyStatusCode(HttpValidationResult validationResult) {
        DataNode statusCode = validationResult.getHeaderNode().statusCode();
        if (statusCode.getTraceableValue().getCheckLevel() != CheckLevel.None) {
            return;
        }

        Integer statusCodeValue = (Integer) statusCode.getTraceableValue().getValue();
        if (statusCodeValue >= 200 && statusCodeValue < 300) {
            return;
        }

        statusCode.should(equal(defaultExpectedStatusCodeByRequest(validationResult)));
    }

    private Integer defaultExpectedStatusCodeByRequest(HttpValidationResult validationResult) {
        switch (validationResult.getRequestMethod()) {
            case "GET":
                return 200;
            case "POST":
                return 201;
            case "PUT":
            case "DELETE":
                return validationResult.hasResponseContent() ? 200 : 204;
            default:
                return 200;
        }
    }

    private void renderResponse(HttpValidationResult result) {
        if (getCfg().getVerbosityLevel() <= TestStep.getCurrentStep().getNumberOfParents() + 1) {
            return;
        }

        if (result.getResponse().isBinary()) {
            ConsoleOutputs.out(Color.YELLOW, "[binary content]");
        } else if (!result.hasResponseContent()) {
            ConsoleOutputs.out(Color.YELLOW, "[no content]");
        } else {
            new DataNodeAnsiPrinter().print(result.getBodyNode());
        }
    }

    private HttpResponse request(String method, String fullUrl,
                                 HttpHeader requestHeader,
                                 HttpRequestBody requestBody) {
        if (requestHeader == null) {
            throw new IllegalArgumentException("Request header is null, check your header provider is not returning null");
        }

        try {
            HttpURLConnection connection = (HttpURLConnection) new URL(fullUrl).openConnection();
            connection.setInstanceFollowRedirects(false);
            connection.setRequestMethod(method);
            connection.setRequestProperty("Content-Type", requestBody.type());
            connection.setRequestProperty("Accept", requestBody.type());
            connection.setRequestProperty("User-Agent", getCfg().getUserAgent());
            requestHeader.forEachProperty(connection::setRequestProperty);

            if (! (requestBody instanceof EmptyRequestBody)) {
                connection.setDoOutput(true);

                if (requestBody.isBinary()) {
                    connection.getOutputStream().write(requestBody.asBytes());
                } else {
                    IOUtils.write(requestBody.asString(), connection.getOutputStream(), UTF_8);
                }
            }

            return extractHttpResponse(connection);
        } catch (IOException e) {
            throw new RuntimeException("couldn't " + method + ": " + fullUrl, e);
        }
    }

    private HttpResponse extractHttpResponse(HttpURLConnection connection) throws IOException {
        HttpResponse httpResponse = new HttpResponse();
        populateResponseHeader(httpResponse, connection);

        InputStream inputStream = getInputStream(connection);
        httpResponse.setStatusCode(connection.getResponseCode());
        httpResponse.setContentType(connection.getContentType() != null ? connection.getContentType() : "");

        if (!httpResponse.isBinary()) {
            httpResponse.setTextContent(inputStream != null ? IOUtils.toString(inputStream, StandardCharsets.UTF_8) : "");
        } else {
            httpResponse.setBinaryContent(inputStream != null ? IOUtils.toByteArray(inputStream) : new byte[0]);
        }

        return httpResponse;
    }

    private InputStream getInputStream(HttpURLConnection connection) throws IOException {
        InputStream inputStream = connection.getResponseCode() < 400 ? connection.getInputStream() : connection.getErrorStream();

        if ("gzip".equals(connection.getContentEncoding())) {
            inputStream = new GZIPInputStream(inputStream);
        }

        return inputStream;
    }

    private void populateResponseHeader(HttpResponse httpResponse, HttpURLConnection connection) {
        connection.getHeaderFields().forEach((key, values) -> {
            if (!values.isEmpty()) {
                httpResponse.addHeader(key, values.get(0));
            }
        });
    }

    /**
     * Response consist of DataNode and Traceable values but we need to return back a simple value that can be used for
     * regular calculations and to drive test flow
     *
     * @param v value returned from a validation callback
     * @return extracted regular value
     */
    @SuppressWarnings("unchecked")
    private Object extractOriginalValue(Object v) {
        if (v instanceof DataNode) {
            return ((DataNode) v).get();
        }

        if (v instanceof TraceableValue) {
            return ((TraceableValue) v).getValue();
        }

        if (v instanceof List) {
            return ((List) v).stream().map(this::extractOriginalValue).collect(toList());
        }

        return v;
    }

    private interface HttpCall {
        HttpResponse execute(String fullUrl, HttpHeader fullHeader);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy