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

nl.hsac.fitnesse.fixture.slim.HttpTest Maven / Gradle / Ivy

There is a newer version: 5.3.20
Show newest version
package nl.hsac.fitnesse.fixture.slim;

import freemarker.template.Template;
import nl.hsac.fitnesse.fixture.util.HttpResponse;
import org.apache.http.client.CookieStore;
import org.apache.http.cookie.Cookie;
import org.apache.http.impl.client.BasicCookieStore;
import org.apache.http.impl.cookie.BasicClientCookie;

import java.io.File;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Fixture to make HTTP requests using Slim scripts and/or scenarios.
 */
public class HttpTest extends SlimFixtureWithMap {
    /** Default content type for posts and puts. */
    public final static String DEFAULT_POST_CONTENT_TYPE = "application/x-www-form-urlencoded; charset=UTF-8";

    private final Map headerValues = new LinkedHashMap<>();
    private boolean storeCookies = false;
    private HttpResponse response = createResponse();
    private String template;
    private boolean explicitContentTypeSet = false;
    private String contentType = DEFAULT_POST_CONTENT_TYPE;
    private String lastUrl = null;
    private String lastMethod = null;

    /**
     * Sets template to use.
     * @param aTemplate name of template file (relative to 'templates' directory on classpath)
     * @return true if template could be found.
     */
    public boolean template(String aTemplate) {
        boolean result = false;
        Template t = getEnvironment().getTemplate(aTemplate);
        if (t != null) {
            template = aTemplate;
            result = true;
        }
        return result;
    }

    /**
     * Stores value to be passed as headers.
     * @param value value to be passed.
     * @param name name to use this value for.
     */
    public void setValueForHeader(Object value, String name) {
        getMapHelper().setValueForIn(value, name, headerValues);
    }

    /**
     * Clears a header value previously set.
     * @param name value to remove.
     * @return true if value was present.
     */
    public boolean clearHeaderValue(String name) {
        String cleanName = cleanupValue(name);
        boolean result = headerValues.containsKey(cleanName);
        headerValues.remove(cleanName);
        return result;
    }

    /**
     * Clears all header values previously set.
     */
    public void clearHeaderValues() {
        headerValues.clear();
    }

    //// methods to support usage in dynamic decision tables

    /**
     * Called before next row is executed. (Clears all current and header values.)
     */
    @Override
    public void reset() {
        clearValues();
        clearHeaderValues();
    }

    private static final Pattern HEADER_KEY_PATTERN = Pattern.compile("header:\\s*(\\.+)");
    /**
     * Sets a value.
     * @param key (possibly nested) key to set value for.
     * @param value value to be stored.
     */
    public void set(String key, Object value) {
        Matcher m = HEADER_KEY_PATTERN.matcher(key);
        if (m.matches()) {
            String headerKey = m.group(1);
            setValueForHeader(value, headerKey);
        } else {
            super.set(key, value);
        }
    }

    //// end: methods to support usage in dynamic decision tables

    /**
     * Sends HTTP POST template with current values to service endpoint.
     * @param serviceUrl service endpoint to send request to.
     * @return true if call could be made and response did not indicate error.
     */
    public boolean postTemplateTo(String serviceUrl) {
        return postTemplateTo(serviceUrl, getContentType());
    }

    /**
     * Sends HTTP POST template with current values to service endpoint.
     * @param serviceUrl service endpoint to send request to.
     * @param aContentType content type to use for post.
     * @return true if call could be made and response did not indicate error.
     */
    public boolean postTemplateTo(String serviceUrl, String aContentType) {
        boolean result;
        resetResponse();
        if (template == null) {
            throw new StopTestException("No template available to use in post");
        } else {
            String url = getUrl(serviceUrl);
            try {
                storeLastCall("POST", serviceUrl);
                getEnvironment().doHttpPost(url, template, getCurrentValues(), response, headerValues, aContentType);
            } catch (Throwable t) {
                throw new StopTestException("Unable to get response from POST to: " + url, t);
            }
            result = postProcessResponse();
        }
        return result;
    }

    /**
     * Sends HTTP POST body to service endpoint.
     * @param body content to post
     * @param serviceUrl service endpoint to send body to.
     * @return true if call could be made and response did not indicate error.
     */
    public boolean postTo(String body, String serviceUrl) {
        String cleanedBody = cleanupBody(body);
        return postToImpl(cleanedBody, serviceUrl);
    }

    /**
     * Sends a file by HTTP POST body to service endpoint.
     * @param fileName fileName to post
     * @param serviceUrl service endpoint to send body to.
     * @return true if call could be made and response did not indicate error.
     */
    public boolean postFileTo(String fileName, String serviceUrl) {
        return postFileToImpl(fileName, serviceUrl);
    }

    /**
     * Sends all values (url encoded) using post.
     * @param serviceUrl service endpoint to send values to.
     * @return true if call could be made and response did not indicate error.
     */
    public boolean postValuesTo(String serviceUrl) {
        String body = urlEncodeCurrentValues();
        return postToImpl(body, serviceUrl);
    }

    protected boolean postToImpl(String body, String serviceUrl) {
        return postToImpl(body, serviceUrl, getContentType());
    }

    protected boolean postToImpl(String body, String serviceUrl, String aContentType) {
        boolean result;
        resetResponse();
        response.setRequest(body);
        String url = getUrl(serviceUrl);
        try {
            storeLastCall("POST", serviceUrl);
            getEnvironment().doHttpPost(url, response, headerValues, aContentType);
        } catch (Throwable t) {
            throw new StopTestException("Unable to get response from POST to: " + url, t);
        }
        result = postProcessResponse();
        return result;
    }

    protected boolean postFileToImpl(String fileName, String serviceUrl) {
        boolean result;
        resetResponse();
        String url = getUrl(serviceUrl);

        String filePath = getFilePathFromWikiUrl(fileName);
        File file = new File(filePath);
        if (!file.exists()) {
            throw new StopTestException(false, "File " + filePath + " not found.");
        }

        try {
            response.setRequest(fileName);
            storeLastCall("POST_FILE", serviceUrl);
            getEnvironment().doHttpFilePost(url, response, headerValues, file);
        } catch (Throwable t) {
            throw new StopTestException("Unable to get response from POST to: " + url, t);
        }
        result = postProcessResponse();
        return result;
    }

    /**
     * Sends HTTP PUT template with current values to service endpoint.
     * @param serviceUrl service endpoint to send request to.
     * @return true if call could be made and response did not indicate error.
     */
    public boolean putTemplateTo(String serviceUrl) {
        return putTemplateTo(serviceUrl, getContentType());
    }

    /**
     * Sends HTTP PUT template with current values to service endpoint.
     * @param serviceUrl service endpoint to send request to.
     * @param aContentType content type to use for post.
     * @return true if call could be made and response did not indicate error.
     */
    public boolean putTemplateTo(String serviceUrl, String aContentType) {
        boolean result;
        resetResponse();
        if (template == null) {
            throw new StopTestException("No template available to use in put");
        } else {
            String url = getUrl(serviceUrl);
            try {
                storeLastCall("PUT", serviceUrl);
                getEnvironment().doHttpPut(url, template, getCurrentValues(), response, headerValues, aContentType);
            } catch (Throwable t) {
                throw new StopTestException("Unable to get response from PUT to: " + url, t);
            }
            result = postProcessResponse();
        }
        return result;
    }

    /**
     * Sends HTTP PUT body to service endpoint.
     * @param body content to put
     * @param serviceUrl service endpoint to send body to.
     * @return true if call could be made and response did not indicate error.
     */
    public boolean putTo(String body, String serviceUrl) {
        String cleanedBody = cleanupBody(body);
        return putToImpl(cleanedBody, serviceUrl);
    }

    /**
     * Sends all values (url encoded) using put.
     * @param serviceUrl service endpoint to send values to.
     * @return true if call could be made and response did not indicate error.
     */
    public boolean putValuesTo(String serviceUrl) {
        String body = urlEncodeCurrentValues();
        return putToImpl(body, serviceUrl);
    }

    protected boolean putToImpl(String body, String serviceUrl) {
        return putToImpl(body, serviceUrl, getContentType());
    }

    protected boolean putToImpl(String body, String serviceUrl, String aContentType) {
        boolean result;
        resetResponse();
        response.setRequest(body);
        String url = getUrl(serviceUrl);
        try {
            storeLastCall("PUT", serviceUrl);
            getEnvironment().doHttpPut(url, response, headerValues, aContentType);
        } catch (Throwable t) {
            throw new StopTestException("Unable to get response from PUT to: " + url, t);
        }
        result = postProcessResponse();
        return result;
    }

    protected String cleanupBody(String body) {
        return getEnvironment().getHtmlCleaner().cleanupPreFormatted(body);
    }

    /**
     * Sends HTTP GET to service endpoint to retrieve content.
     * @param serviceUrl service endpoint to get content from.
     * @return true if call could be made and response did not indicate error.
     */
    public boolean getFrom(String serviceUrl) {
        return getImpl(serviceUrl, true);
    }

    /**
     * Sends HTTP GET to service endpoint to retrieve content, not following a redirect if sent.
     * @param serviceUrl service endpoint to get content from.
     * @return true if call could be made and response did not indicate error.
     */
    public boolean getFromNoRedirect(String serviceUrl) {
        return getImpl(serviceUrl, false);
    }

    protected boolean getImpl(String serviceUrl, boolean followRedirect) {
        boolean result;
        resetResponse();
        String url = createUrlWithParams(serviceUrl);
        try {
            String method;
            if (followRedirect) {
                method = "GET";
            } else {
                method = "GET_NO_REDIRECT";
            }
            storeLastCall(method, serviceUrl);
            getEnvironment().doGet(url, response, headerValues, followRedirect);
        } catch (Throwable t) {
            throw new StopTestException("Unable to GET response from: " + url, t);
        }
        result = postProcessResponse();
        return result;
    }


    /**
     * Sends HTTP DELETE to service endpoint.
     * @param serviceUrl service endpoint to delete.
     * @return true if call could be made and response did not indicate error.
     */
    public boolean delete(String serviceUrl) {
        boolean result;
        resetResponse();
        String url = createUrlWithParams(serviceUrl);
        try {
            storeLastCall("DELETE", serviceUrl);
            getEnvironment().doDelete(url, response, headerValues);
        } catch (Throwable t) {
            throw new StopTestException("Unable to DELETE: " + url, t);
        }
        result = postProcessResponse();
        return result;
    }

    protected void resetResponse() {
        CookieStore cookieStore = null;
        if (storeCookies) {
            cookieStore = getResponse().getCookieStore();
            if (cookieStore == null) {
                cookieStore = new BasicCookieStore();
            }
        }
        response = createResponse();
        if (storeCookies) {
            response.setCookieStore(cookieStore);
        }
    }

    String createUrlWithParams(String serviceUrl) {
        String baseUrl = getUrl(serviceUrl);
        if (!getCurrentValues().isEmpty()) {
            if (baseUrl.contains("?") && !baseUrl.endsWith("?")) {
                baseUrl += "&";
            }
            if (!baseUrl.contains("?")) {
                baseUrl += "?";
            }
            baseUrl += urlEncodeCurrentValues();
        }
        return baseUrl;
    }

    protected String urlEncodeCurrentValues() {
        StringBuilder sb = new StringBuilder();
        addUrlEncodedKeyValues(sb, "", getCurrentValues());
        return sb.toString();
    }

    private void addUrlEncodedKeyValues(StringBuilder sb, String prefix, Map map) {
        for (Map.Entry entry : map.entrySet()) {
            String key = prefix + entry.getKey();
            Object value = entry.getValue();
            if (value instanceof List) {
                List values = (List) value;
                for (Object v : values) {
                    addEncodedKeyValue(sb, key, v);
                }
            } else if (value instanceof Map) {
                addUrlEncodedKeyValues(sb, key + ".", (Map) value);
            } else {
                addEncodedKeyValue(sb, key, value);
            }
        }
    }

    private void addEncodedKeyValue(StringBuilder sb, String key, Object value) {
        if (sb.length() != 0) {
            sb.append("&");
        }
        sb.append(urlEncode(key));
        if (value != null) {
            sb.append("=");
            sb.append(urlEncode(value.toString()));
        }
    }

    protected String urlEncode(String str) {
        try {
            return URLEncoder.encode(str, "utf-8");
        } catch (UnsupportedEncodingException e) {
            throw new Error(e);
        }
    }

    /**
     * Performs any post processing directly after retrieving response.
     * @return true if all is well, false otherwise.
     */
    protected boolean postProcessResponse() {
        return responseIsValid();
    }

    /**
     * @return true if response does not indicate an error.
     */
    public boolean responseIsValid() {
        boolean result;
        try {
            response.validResponse();
            result = true;
        } catch (RuntimeException e) {
            result = false;
        }
        return result;
    }

    /**
     * @return request sent last time.
     */
    public String request() {
        return safeFormatValue(response.getRequest());
    }

    /**
     * @return response received last time postTo(), delete() or getFrom() was called.
     */
    public String response() {
        return safeFormatValue(response.getResponse());
    }

    /**
     * Internal method to format a value, which will just return the 'raw' value if there is a problem formatting.
     * @param value value to format
     * @return formatted value
     */
    protected final String safeFormatValue(String value) {
        String result;
        try {
            result = formatValue(value);
        } catch (Exception e) {
            result = value;
        }
        return result;
    }

    /**
     * Method that will take care of formatting a value, which may be overridden in subclasses.
     * This implementation just returns value.
     * @param value value to format
     * @return formatted value
     */
    protected String formatValue(String value) {
        return value;
    }

    /**
     * @return HTML response received last time postTo() or get() was called.
     */
    public String htmlResponse() {
        String content = response.getResponse();
        content = "
" + content + "
"; return content; } /** * @return response time in ms for call to service. */ public long responseTime() { return getResponse().getResponseTime(); } /** * @return http status received in response to last request. */ public int responseStatus() { return response.getStatusCode(); } /** * @return headers received with response to last request. */ public Map responseHeaders() { return response.getResponseHeaders(); } /** * @param headerName name of response header. * @return value of header in last response (may be a list if the saame header name was sent multiple times * (e.g. Set-Cookie). */ public Object responseHeader(String headerName) { return responseHeaders().get(headerName); } public void setStoreCookies(boolean storeCookies) { this.storeCookies = storeCookies; } /** * Adds all current Selenium cookies to this fixture's cookie store. * This will also ensure this class will store cookies (otherwise copying the cookies has no purpose). */ public void copyBrowserCookies() { setStoreCookies(true); getEnvironment().addSeleniumCookies(getResponse()); } /** * @return name->value of cookies in the cookie store. */ public Map cookieValues() { Map result = null; CookieStore cookies = getResponse().getCookieStore(); if (cookies != null) { result = new LinkedHashMap<>(); for (Cookie cookie : cookies.getCookies()) { result.put(cookie.getName(), cookie.getValue()); } } return result; } /** * @param cookieName name of cookie. * @return value of cookie in the cookie store. */ public String cookieValue(String cookieName) { String result = null; Cookie cookie = getCookie(cookieName); if (cookie != null) { result = cookie.getValue(); } return result; } /** * @param cookieName name of cookie. * @return domain of cookie in the cookie store. */ public String cookieDomain(String cookieName) { String result = null; Cookie cookie = getCookie(cookieName); if (cookie != null) { result = cookie.getDomain(); } return result; } /** * @param cookieName name of cookie. * @return path of cookie in the cookie store. */ public String cookiePath(String cookieName) { String result = null; Cookie cookie = getCookie(cookieName); if (cookie != null) { result = cookie.getPath(); } return result; } /** * @param cookieName name of cookie. * @return whether cookie in the cookie store is persistent. */ public Boolean cookieIsPersistent(String cookieName) { Boolean result = null; Cookie cookie = getCookie(cookieName); if (cookie != null) { result = cookie.isPersistent(); } return result; } /** * @param cookieName name of cookie. * @return whether cookie in the cookie store requires a secure connection. */ public Boolean cookieIsSecure(String cookieName) { Boolean result = null; Cookie cookie = getCookie(cookieName); if (cookie != null) { result = cookie.isSecure(); } return result; } /** * @param cookieName name of cookie. * @return whether cookie in the cookie store is http-only (not accessible to Javascript). */ public Boolean cookieIsHttpOnly(String cookieName) { return cookieAttribute(cookieName, "httponly") != null; } /** * @param cookieName name of cookie. * @param attributeName name of attribute. * @return value of attribute for cookie. */ public String cookieAttribute(String cookieName, String attributeName) { String result = null; Cookie cookie = getCookie(cookieName); if (cookie instanceof BasicClientCookie) { result = ((BasicClientCookie) cookie).getAttribute(attributeName.toLowerCase(Locale.ENGLISH)); } return result; } private Cookie getCookie(String cookieName) { return getResponse().getCookieNamed(cookieName); } /** * Removes all cookies from the cookie store. */ public void clearCookies() { getResponse().getCookieStore().clear(); } protected HttpResponse getResponse() { return response; } protected HttpResponse createResponse() { return new HttpResponse(); } public String getContentType() { return contentType; } public void setContentType(String aContentType) { explicitContentTypeSet = true; contentType = aContentType; } public boolean isExplicitContentTypeSet() { return explicitContentTypeSet; } // Polling public boolean repeatUntilResponseStatusIs(final int expectedStatus) { return repeatUntil( new RepeatLastCall() { @Override public boolean isFinished() { return responseStatus() == expectedStatus; } }); } public boolean repeatUntilResponseIs(final String expectedResponse) { RepeatCompletion completion; if (expectedResponse == null) { completion = new RepeatLastCall() { @Override public boolean isFinished() { return response() == null; } }; } else { completion = new RepeatLastCall() { @Override public boolean isFinished() { Object actual = response(); return compareActualToExpected(expectedResponse, actual); } }; } return repeatUntil(completion); } public boolean repeatUntilHeaderIs(final String header, final Object expectedValue) { RepeatCompletion completion; if (expectedValue == null) { completion = new RepeatLastCall() { @Override public boolean isFinished() { return responseHeader(header) == null; } }; } else { completion = new RepeatLastCall() { @Override public boolean isFinished() { Object actual = responseHeader(header); return compareActualToExpected(expectedValue, actual); } }; } return repeatUntil(completion); } protected void repeatLastCall() { if (lastMethod == null) { throw new SlimFixtureException(false, "First make a call before trying to repeat one."); } switch (lastMethod) { case "GET": getImpl(lastUrl, true); break; case "POST": postToImpl(response.getRequest(), lastUrl); break; case "PUT": putToImpl(response.getRequest(), lastUrl); break; case "DELETE": delete(lastUrl); break; case "GET_NO_REDIRECT": getImpl(lastUrl, false); break; case "POST_FILE": postFileToImpl(response.getRequest(), lastUrl); break; default: throw new SlimFixtureException(false, "Repeat of method: " + lastMethod + " not configured."); } } protected void storeLastCall(String method, String url) { lastMethod = method; lastUrl = url; } protected abstract class RepeatLastCall implements RepeatCompletion { protected boolean compareActualToExpected(Object expected, Object actual) { boolean result; if (actual == null) { result = expected.equals("null"); } else { result = expected.equals(actual) || expected.toString().equals(actual.toString()); } return result; } @Override public void repeat() { repeatLastCall(); } } // Polling }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy