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

org.swisspush.gateleen.delegate.Delegate Maven / Gradle / Ivy

There is a newer version: 2.1.13
Show newest version
package org.swisspush.gateleen.delegate;

import io.vertx.core.Future;
import io.vertx.core.Handler;
import io.vertx.core.MultiMap;
import io.vertx.core.Promise;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.http.*;
import io.vertx.core.http.impl.headers.HeadersMultiMap;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.swisspush.gateleen.core.http.ClientRequestCreator;
import org.swisspush.gateleen.core.http.HeaderFunctions;
import org.swisspush.gateleen.core.json.transform.JoltTransformer;
import org.swisspush.gateleen.core.util.HttpHeaderUtil;
import org.swisspush.gateleen.core.util.HttpServerRequestUtil;
import org.swisspush.gateleen.core.util.StatusCode;

import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Represents a Delegate.
 *
 * @author https://github.com/ljucam [Mario Ljuca]
 */
public class Delegate {
    private static final Logger LOG = LoggerFactory.getLogger(Delegate.class);
    private static final String HEADERS = "headers";
    private static final String PAYLOAD = "payload";
    private static final String URI = "uri";
    private static final String METHOD = "method";
    private static final int FIRST = 0;
    private static final int STATUS_CODE_2XX = 2;

    private final String name;
    private final ClientRequestCreator clientRequestCreator;
    private final Pattern pattern;
    private final Set methods;
    private final List requests;
    private boolean delegateContainsJoltSpecRequest = false;

    /**
     * Creates a new instance of a Delegate.
     *
     * @param clientRequestCreator selfClient
     * @param name                 name of delegate
     * @param pattern              pattern for the delegate
     * @param methods              methods of the delegate
     * @param requests             requests of the delegate
     */
    public Delegate(final ClientRequestCreator clientRequestCreator, final String name, final Pattern pattern, final Set methods, final List requests) {
        this.clientRequestCreator = clientRequestCreator;
        this.name = name;
        this.pattern = pattern;
        this.methods = methods;
        this.requests = requests;

        this.delegateContainsJoltSpecRequest = doesDelegateContainJoltSpecRequest();
    }

    /**
     * Returns the name of the delegate.
     *
     * @return name
     */
    public String getName() {
        return name;
    }

    List getDelegateRequests() {
        return requests;
    }

    /**
     * Handles the given request.
     *
     * @param request original request
     */
    public void handle(final HttpServerRequest request) {
        // is method handled?
        if (methods.contains(request.method())) {

            // check if Pattern matches the given request url
            Matcher matcher = pattern.matcher(request.uri());
            if (matcher.matches()) {
                extractDelegateExecutionRequestJsonPayload(request).onComplete(payload -> {
                    if (payload.failed()) {
                        String message = "Unable to parse payload of delegate execution request. " +
                                "When a delegate definition with a 'transformation' spec is defined, a valid json payload is required!";
                        LOG.warn(message);
                        request.response().setStatusCode(StatusCode.BAD_REQUEST.getStatusCode());
                        request.response().setStatusMessage(StatusCode.BAD_REQUEST.getStatusMessage());
                        request.response().end(message);
                        return;
                    }
                    final DelegateRequest firstDelegateRequest = requests.get(FIRST);
                    final Handler handler = installDoneHandler(request, payload.result());
                    createRequest(request, payload.result(), firstDelegateRequest, handler);
                });
                return;
            }
        }

        // end response, if nothing matches
        request.response().end();
    }

    /**
     * Prepares and fires a delegate request. 
* The preparation includes the replacement of all groups * matching the given delegate pattern. Also the * request uri is adapted. * * @param originalRequest original request * @param requestContainer the container holding the request object and optionally a json transform spec * @param doneHandler the done handler called as soon as the request is executed */ private void createRequest(final HttpServerRequest originalRequest, final String delegateExecutionRequestJsonPayload, final DelegateRequest requestContainer, final Handler doneHandler) { // matcher to replace wildcards with matching groups final Matcher matcher = pattern.matcher(originalRequest.uri()); generatePayload(delegateExecutionRequestJsonPayload, originalRequest.headers(), requestContainer, matcher).onComplete(payloadBuffer -> { if (payloadBuffer.failed()) { String message = "Unable to generate delegate request payload. Cause: " + payloadBuffer.cause().getClass().getName(); LOG.warn(message); originalRequest.response().setStatusCode(StatusCode.BAD_REQUEST.getStatusCode()); originalRequest.response().setStatusMessage(StatusCode.BAD_REQUEST.getStatusMessage()); originalRequest.response().end(message); return; } final JsonObject requestObject = requestContainer.getRequest(); // adapt the request uri if necessary final String requestUri = matcher.replaceAll(requestObject.getString(URI)); // headers of the delegate HeadersMultiMap headers = createRequestHeaders(requestContainer, originalRequest.headers(), originalRequest.uri()); clientRequestCreator.createClientRequest( HttpMethod.valueOf(requestObject.getString(METHOD)), requestUri, headers, 120000, exception -> LOG.warn("Delegate request {} failed: {}", requestUri, exception.getMessage()) ).onComplete(asyncResult -> { if (asyncResult.failed()) { LOG.warn("Failed request to {}: {}", requestUri, asyncResult.cause()); return; } HttpClientRequest delegateRequest = asyncResult.result(); Buffer buf = payloadBuffer.result(); if (buf != null) { delegateRequest.putHeader(HttpHeaders.CONTENT_LENGTH, String.valueOf(buf.length())); delegateRequest.send(buf, event -> doneHandler.handle(event.result())); } else { delegateRequest.send(event -> doneHandler.handle(event.result())); } }); }); } private HeadersMultiMap createRequestHeaders(DelegateRequest requestContainer, MultiMap originalRequestHeaders, String uri) { HeadersMultiMap headers = new HeadersMultiMap(); JsonArray headersArray = requestContainer.getRequest().getJsonArray(HEADERS); // headers definition? if (headersArray != null) { if (LOG.isTraceEnabled()) { LOG.trace("Request headers:"); } headersArray.forEach(header -> { if (LOG.isTraceEnabled()) { LOG.trace(" > Key [{}], Value [{}]", ((JsonArray) header).getString(0), ((JsonArray) header).getString(1)); } headers.add(((JsonArray) header).getString(0), ((JsonArray) header).getString(1)); }); return headers; } // evaluate dynamicHeaders if (requestContainer.getHeaderFunction() != HeaderFunctions.DO_NOTHING) { HttpHeaderUtil.mergeHeaders(headers, originalRequestHeaders, uri); final HeaderFunctions.EvalScope evalScope = requestContainer.getHeaderFunction().apply(headers); if (evalScope.getErrorMessage() != null) { LOG.warn("problem applying header manipulator chain {} in delegate {}", evalScope.getErrorMessage(), getName()); } } return headers; } /** * Extract the json payload of the original request when a delegate request definition with a transformation spec * is defined. If no transformation is found, the payload will not be parsed and null will be returned. * * @param request the request to get the payload from * @return returns a parsed json payload as string or null */ private Future extractDelegateExecutionRequestJsonPayload(HttpServerRequest request) { Promise promise = Promise.promise(); if (delegateContainsJoltSpecRequest) { request.bodyHandler(bodyHandler -> { try { promise.complete(bodyHandler.toJsonObject().encode()); } catch (Exception ex) { promise.fail(ex); } }); } else { promise.complete(null); } return promise.future(); } private Future generatePayload(String delegateExecutionRequestJsonPayload, MultiMap headers, DelegateRequest requestContainer, final Matcher matcher) { Promise promise = Promise.promise(); if (requestContainer.getJoltSpec() != null) { try { if (delegateExecutionRequestJsonPayload != null) { String transformInput = TransformPayloadInputBuilder.build(requestContainer.getJoltSpec(), delegateExecutionRequestJsonPayload, headers, matcher); LOG.debug("Jolt transformation input: {}", transformInput); JoltTransformer.transform(transformInput, requestContainer.getJoltSpec()).onComplete(transformed -> { if (transformed.failed()) { promise.fail(transformed.cause()); } else { JsonObject transformedJsonObject = transformed.result(); try { String transformedOutput = transformedJsonObject.encode(); LOG.debug("Jolt transformation output: {}", transformedOutput); promise.complete(Buffer.buffer(transformedOutput)); } catch (Exception ex) { promise.fail(ex); } } }); } else { promise.fail("nothing to transform"); } } catch (Exception ex) { promise.fail(ex); } } else { // matcher to replace wildcards with matching groups final JsonObject requestObject = requestContainer.getRequest(); // get the string represantion of the payload object String payloadStr; payloadStr = requestObject.getJsonObject(PAYLOAD).encode(); // replacement of matching groups if (payloadStr != null) { payloadStr = matcher.replaceAll(payloadStr); promise.complete(Buffer.buffer(payloadStr)); } else { promise.complete(null); } } return promise.future(); } /** * Creates a new DoneHandler for the given request. * * @param request the original request * @return a doneHandler */ private Handler installDoneHandler(final HttpServerRequest request, final String delegateExecutionRequestJsonPayload) { return new Handler<>() { private AtomicInteger currentIndex = new AtomicInteger(0); @Override public void handle(HttpClientResponse response) { if (LOG.isTraceEnabled()) { LOG.trace("Done handler - handle"); } // request was fine if ((response.statusCode() / 100) == STATUS_CODE_2XX) { if (LOG.isTraceEnabled()) { LOG.trace("Done handler - OK"); } // is there another request? if (currentIndex.incrementAndGet() < requests.size()) { if (LOG.isTraceEnabled()) { LOG.trace("Done handler - calling next {}", currentIndex.get()); } final DelegateRequest delegateRequest = requests.get(currentIndex.get()); createRequest(request, delegateExecutionRequestJsonPayload, delegateRequest, this); } // if not, send corresponding respond else { if (LOG.isTraceEnabled()) { LOG.trace("Done handler - not 2XX, create response [{}]", response.statusCode()); } createResponse(request, response); } } // request failed else { if (LOG.isTraceEnabled()) { LOG.trace("Done handler - not 200/202, create response [{}]", response.statusCode()); } createResponse(request, response); } } }; } /** * Create a response. * * @param request original request * @param response a response */ private void createResponse(final HttpServerRequest request, final HttpClientResponse response) { HttpServerRequestUtil.prepareResponse(request, response); response.handler(data -> request.response().write(data)); response.endHandler(v -> request.response().end()); } private boolean doesDelegateContainJoltSpecRequest() { if (this.requests == null || this.requests.isEmpty()) { return false; } for (DelegateRequest request : requests) { if (request.getJoltSpec() != null) { return true; } } return false; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy