From 5c67bb0c060896240d1d6acb6a73c1ce2d24b0b8 Mon Sep 17 00:00:00 2001 From: almeidast Date: Tue, 20 Aug 2024 13:44:27 +0100 Subject: [PATCH] #606 - Draft commit to analyze implementation --- .../gateleen/hook/DestinationConfig.java | 150 +++++++ .../swisspush/gateleen/hook/HookHandler.java | 75 ++-- .../org/swisspush/gateleen/hook/HttpHook.java | 26 +- .../org/swisspush/gateleen/hook/Listener.java | 60 ++- .../gateleen/hook/ListenerRepositoryBase.java | 14 +- .../hook/LocalListenerRepository.java | 13 +- .../resources/gateleen_hooking_schema_hook | 386 +++++++++--------- 7 files changed, 458 insertions(+), 266 deletions(-) create mode 100644 gateleen-hook/src/main/java/org/swisspush/gateleen/hook/DestinationConfig.java diff --git a/gateleen-hook/src/main/java/org/swisspush/gateleen/hook/DestinationConfig.java b/gateleen-hook/src/main/java/org/swisspush/gateleen/hook/DestinationConfig.java new file mode 100644 index 000000000..cc45c8d0c --- /dev/null +++ b/gateleen-hook/src/main/java/org/swisspush/gateleen/hook/DestinationConfig.java @@ -0,0 +1,150 @@ +/* + * Copyright 2024 by Swiss Post, Information Technology + */ + +package org.swisspush.gateleen.hook; + +import io.vertx.core.json.JsonObject; +import io.vertx.core.net.ProxyOptions; + +import javax.annotation.Nullable; +import java.util.Optional; + +/** + * Represents the configuration for a destination in the routing or hook system. + * + * @author almeidast + */ +public class DestinationConfig { + private String destinationUri; + private Optional proxyOptions; + private boolean fullUrl; + private Integer connectionPoolSize; + private Integer maxWaitQueueSize; + private Integer timeout; + + /** + * Constructor for creating a DestinationConfig with the given parameters. + * + * @param destinationUri The URI of the destination. + * @param proxyOptions Optional proxy settings for this destination. + * @param fullUrl Indicates whether to use the full URL when forwarding. + * @param connectionPoolSize The size of the connection pool. + * @param maxWaitQueueSize The maximum number of requests in the wait queue. + * @param timeout The timeout for requests in milliseconds. + */ + public DestinationConfig(String destinationUri, @Nullable ProxyOptions proxyOptions, boolean fullUrl, + @Nullable Integer connectionPoolSize, @Nullable Integer maxWaitQueueSize, @Nullable Integer timeout) { + this.destinationUri = destinationUri; + this.proxyOptions = Optional.ofNullable(proxyOptions); + this.fullUrl = fullUrl; + this.connectionPoolSize = connectionPoolSize; + this.maxWaitQueueSize = maxWaitQueueSize; + this.timeout = timeout; + } + + /** + * Gets the destination URI. + * + * @return The destination URI. + */ + public String getDestinationUri() { + return destinationUri; + } + + /** + * Gets the proxy options for this destination, if any. + * + * @return An Optional containing the proxy options if present. + */ + public Optional getProxyOptions() { + return proxyOptions; + } + + /** + * Returns whether the full URL should be used when forwarding. + * + * @return true if the full URL should be used, false otherwise. + */ + public boolean isFullUrl() { + return fullUrl; + } + + /** + * Gets the connection pool size. + * + * @return The connection pool size, or null if not set. + */ + @Nullable + public Integer getConnectionPoolSize() { + return connectionPoolSize; + } + + /** + * Gets the maximum number of requests allowed in the wait queue. + * + * @return The maximum wait queue size, or null if not set. + */ + @Nullable + public Integer getMaxWaitQueueSize() { + return maxWaitQueueSize; + } + + /** + * Gets the timeout for requests in milliseconds. + * + * @return The timeout, or null if not set. + */ + @Nullable + public Integer getTimeout() { + return timeout; + } + + /** + * Creates a DestinationConfig from a JSON object. + * + * @param jsonObject The JSON object containing the configuration. + * @return A DestinationConfig object. + */ + public static DestinationConfig fromJson(JsonObject jsonObject) { + String destinationUri = jsonObject.getString("destinationUri"); + ProxyOptions proxyOptions = jsonObject.containsKey("proxyOptions") + ? new ProxyOptions(jsonObject.getJsonObject("proxyOptions")) + : null; + boolean fullUrl = jsonObject.getBoolean("fullUrl", false); + Integer connectionPoolSize = jsonObject.getInteger("connectionPoolSize"); + Integer maxWaitQueueSize = jsonObject.getInteger("maxWaitQueueSize"); + Integer timeout = jsonObject.getInteger("timeout"); + + return new DestinationConfig(destinationUri, proxyOptions, fullUrl, connectionPoolSize, maxWaitQueueSize, timeout); + } + + /** + * Converts this DestinationConfig to a JSON object. + * + * @return A JSON object representing this configuration. + */ + public JsonObject toJson() { + JsonObject jsonObject = new JsonObject() + .put("destinationUri", destinationUri) + .put("fullUrl", fullUrl); + + if (proxyOptions.isPresent()) { + jsonObject.put("proxyOptions", proxyOptions.get().toJson()); + } + + if (connectionPoolSize != null) { + jsonObject.put("connectionPoolSize", connectionPoolSize); + } + + if (maxWaitQueueSize != null) { + jsonObject.put("maxWaitQueueSize", maxWaitQueueSize); + } + + if (timeout != null) { + jsonObject.put("timeout", timeout); + } + + return jsonObject; + } +} diff --git a/gateleen-hook/src/main/java/org/swisspush/gateleen/hook/HookHandler.java b/gateleen-hook/src/main/java/org/swisspush/gateleen/hook/HookHandler.java index 66b1a59e0..ab4f89379 100755 --- a/gateleen-hook/src/main/java/org/swisspush/gateleen/hook/HookHandler.java +++ b/gateleen-hook/src/main/java/org/swisspush/gateleen/hook/HookHandler.java @@ -790,21 +790,6 @@ private void callListener(RoutingContext ctx, final Buffer buffer, final List
  • internal target: {}", targetUri); - } - // external - else { - targetUri = hookRootUri + LISTENER_HOOK_TARGET_PATH + listener.getListener() + path; - log.debug(" > external target: {}", targetUri); - } - - // Create a new multimap, copied from the original request, - // so that the original request is not overridden with the new values. HeadersMultiMap queueHeaders = new HeadersMultiMap(); queueHeaders.addAll(request.headers()); @@ -831,28 +816,44 @@ private void callListener(RoutingContext ctx, final Buffer buffer, final List
  • internal target: {}", targetUri); + } + // external + else { + targetUri = destinationConfig.getDestinationUri() + path; + log.debug(" > external target: {}", targetUri); } - requestQueue.enqueue(new HttpRequest(request.method(), targetUri, queueHeaders, null), queue, handler); - } else if (queueingStrategy instanceof ReducedPropagationQueueingStrategy) { - if (reducedPropagationManager != null) { - reducedPropagationManager.processIncomingRequest(request.method(), targetUri, queueHeaders, buffer, - queue, ((ReducedPropagationQueueingStrategy) queueingStrategy).getPropagationIntervalMs(), handler); + + if (queueingStrategy instanceof DefaultQueueingStrategy) { + requestQueue.enqueue(new HttpRequest(request.method(), targetUri, queueHeaders, buffer.getBytes()), queue, handler); + } else if (queueingStrategy instanceof DiscardPayloadQueueingStrategy) { + if (HttpRequestHeader.containsHeader(queueHeaders, CONTENT_LENGTH)) { + queueHeaders.set(CONTENT_LENGTH.getName(), "0"); + } + requestQueue.enqueue(new HttpRequest(request.method(), targetUri, queueHeaders, null), queue, handler); + } else if (queueingStrategy instanceof ReducedPropagationQueueingStrategy) { + if (reducedPropagationManager != null) { + reducedPropagationManager.processIncomingRequest(request.method(), targetUri, queueHeaders, buffer, + queue, ((ReducedPropagationQueueingStrategy) queueingStrategy).getPropagationIntervalMs(), handler); + } else { + log.error("ReducedPropagationQueueingStrategy without configured ReducedPropagationManager. " + + "Not going to handle (enqueue) anything!"); + } } else { - log.error("ReducedPropagationQueueingStrategy without configured ReducedPropagationManager. " + - "Not going to handle (enqueue) anything!"); + log.error("QueueingStrategy '{}' is not handled. Could be an error, check the source code!", + queueingStrategy.getClass().getSimpleName()); } - } else { - log.error("QueueingStrategy '{}' is not handled. Could be an error, check the source code!", - queueingStrategy.getClass().getSimpleName()); } } - // if for e.g. the beforListeners are empty, + // if for e.g. the beforeListeners are empty, // we have to ensure, that the original request // is executed. This way the after handler will // also be called properly. @@ -1341,7 +1342,6 @@ private void registerListener(Buffer buffer) { JsonObject jsonHook = storageObject.getJsonObject(HOOK); JsonArray jsonMethods = jsonHook.getJsonArray(METHODS); - HttpHook hook = new HttpHook(jsonHook.getString(DESTINATION)); if (jsonMethods != null) { hook.setMethods(jsonMethods.getList()); @@ -1366,8 +1366,13 @@ private void registerListener(Buffer buffer) { } } - if (jsonHook.containsKey(FILTER)) { - hook.setFilter(jsonHook.getString(FILTER)); + if (jsonHook.containsKey("destinations")) { + JsonArray jsonDestinations = jsonHook.getJsonArray("destinations"); + for (int i = 0; i < jsonDestinations.size(); i++) { + JsonObject jsonDestination = jsonDestinations.getJsonObject(i); + DestinationConfig destinationConfig = DestinationConfig.fromJson(jsonDestination); + hook.getDestinations().add(destinationConfig); + } } if (jsonHook.getInteger(QUEUE_EXPIRE_AFTER) != null) { @@ -1441,7 +1446,6 @@ private void registerListener(Buffer buffer) { * appropriate property in the hook object. *

    * This is the same concept as in gateleen-routing: - * {@link org.swisspush.gateleen.routing.RuleFactory#setProxyOptions(Rule, JsonObject)}} */ private void extractAndAddProxyOptionsToHook(final JsonObject jsonHook, final HttpHook hook) { JsonObject proxyOptions = jsonHook.getJsonObject("proxyOptions"); @@ -1455,7 +1459,6 @@ private void extractAndAddProxyOptionsToHook(final JsonObject jsonHook, final Ht * appropriate list in the hook object. *

    * This is the same concept as in gateleen-routing: - * {@link org.swisspush.gateleen.routing.RuleFactory#setStaticHeaders(Rule, JsonObject)}} */ private void extractAndAddStaticHeadersToHook(final JsonObject jsonHook, final HttpHook hook) { final JsonArray headers = jsonHook.getJsonArray("headers"); diff --git a/gateleen-hook/src/main/java/org/swisspush/gateleen/hook/HttpHook.java b/gateleen-hook/src/main/java/org/swisspush/gateleen/hook/HttpHook.java index 71751f55a..140f07775 100755 --- a/gateleen-hook/src/main/java/org/swisspush/gateleen/hook/HttpHook.java +++ b/gateleen-hook/src/main/java/org/swisspush/gateleen/hook/HttpHook.java @@ -41,6 +41,10 @@ public class HttpHook { private Integer maxWaitQueueSize = null; private ProxyOptions proxyOptions = null; private Integer timeout = null; + + // New field for handling multiple destinations + private List destinations = new ArrayList<>(); + /** * Creates a new hook. * @@ -338,5 +342,25 @@ public void setTimeout(Integer timeout) { * * @param proxyOptions the custom proxy options to set */ - public void setProxyOptions(ProxyOptions proxyOptions) { this.proxyOptions = proxyOptions; } + public void setProxyOptions(ProxyOptions proxyOptions) { + this.proxyOptions = proxyOptions; + } + + /** + * Returns the list of destinations for the hook. + * + * @return a list of DestinationConfig objects + */ + public List getDestinations() { + return destinations; + } + + /** + * Sets the list of destinations for the hook. + * + * @param destinations a list of DestinationConfig objects + */ + public void setDestinations(List destinations) { + this.destinations = destinations; + } } diff --git a/gateleen-hook/src/main/java/org/swisspush/gateleen/hook/Listener.java b/gateleen-hook/src/main/java/org/swisspush/gateleen/hook/Listener.java index 7998af2e0..30adbc3c9 100644 --- a/gateleen-hook/src/main/java/org/swisspush/gateleen/hook/Listener.java +++ b/gateleen-hook/src/main/java/org/swisspush/gateleen/hook/Listener.java @@ -30,69 +30,67 @@ public Listener(String listenerId, String monitoredUrl, String listener, HttpHoo this.listenerId = listenerId; this.monitoredUrl = monitoredUrl; this.listener = listener; - this.setHook(hook); + this.hook = hook; } /** - * Returns the listener segment of the url. - * - * @return String + * Returns the listener ID. + * + * @return The listener ID. */ - public String getListener() { - return listener; + public String getListenerId() { + return listenerId; } /** - * Sets the listener segment of the url. + * Sets the listener id (eg. http/colin/123). * - * @param listener listener + * @param listenerId listenerId */ - public void setListener(String listener) { - this.listener = listener; + public void setListenerId(String listenerId) { + this.listenerId = listenerId; } /** - * Returns the url the listener is hooked up. - * - * @return String + * Returns the monitored URL. + * + * @return The monitored URL. */ public String getMonitoredUrl() { return monitoredUrl; } /** - * Sets the url the listener is hooked up. - * - * @param monitoredUrl monitoredUrl + * Sets the monitored URL. + * + * @param monitoredUrl The monitored URL. */ public void setMonitoredUrl(String monitoredUrl) { this.monitoredUrl = monitoredUrl; } /** - * Returns the listener id (eg. http/colin/123) - * - * @return id of the listener + * Returns the listener URL segment. + * + * @return The listener URL segment. */ - public String getListenerId() { - return listenerId; + public String getListener() { + return listener; } /** - * Sets the listener id (eg. http/colin/123). - * - * @param listenerId listenerId + * Sets the listener URL segment. + * + * @param listener The listener URL segment. */ - public void setListenerId(String listenerId) { - this.listenerId = listenerId; + public void setListener(String listener) { + this.listener = listener; } /** - * Returns the expire after time, for the - * request header. - * Can be null if not set. - * - * @return expire after time + * Returns the expiration time. + * + * @return The expiration time, or null if not set. */ public Integer getExpireAfter() { return expireAfter; diff --git a/gateleen-hook/src/main/java/org/swisspush/gateleen/hook/ListenerRepositoryBase.java b/gateleen-hook/src/main/java/org/swisspush/gateleen/hook/ListenerRepositoryBase.java index cfe9b3102..70850e055 100644 --- a/gateleen-hook/src/main/java/org/swisspush/gateleen/hook/ListenerRepositoryBase.java +++ b/gateleen-hook/src/main/java/org/swisspush/gateleen/hook/ListenerRepositoryBase.java @@ -7,8 +7,8 @@ import java.util.regex.Pattern; /** - * Abstrac base class for all ListenerRepositires. - * + * Abstract base class for all ListenerRepositories. + * * @author https://github.com/ljucam [Mario Ljuca] */ public abstract class ListenerRepositoryBase implements ListenerRepository { @@ -44,12 +44,12 @@ List findListeners(T urlToListenerContainer, String url) { } /** - * Filters the given set of listener, to match the pattern defined in the + * Filters the given set of listeners to match the pattern defined in the * respective hook (if a pattern is defined). - * - * @param set - a set of listeners, matching the first search criteria. - * @param url - the url, which should be checked, if a listener matches - * @return a filtered set of listeners, which matches the given url. + * + * @param set - a set of listeners matching the first search criteria. + * @param url - the url, which should be checked if a listener matches + * @return a filtered set of listeners which match the given url. */ private Set filter(Set listeners, String url) { Set filteredListener = new HashSet<>(); diff --git a/gateleen-hook/src/main/java/org/swisspush/gateleen/hook/LocalListenerRepository.java b/gateleen-hook/src/main/java/org/swisspush/gateleen/hook/LocalListenerRepository.java index a1c824e3d..99292b90a 100644 --- a/gateleen-hook/src/main/java/org/swisspush/gateleen/hook/LocalListenerRepository.java +++ b/gateleen-hook/src/main/java/org/swisspush/gateleen/hook/LocalListenerRepository.java @@ -13,7 +13,7 @@ * * @author https://github.com/ljucam [Mario Ljuca] */ -public class LocalListenerRepository extends ListenerRepositoryBase>>implements ListenerRepository { +public class LocalListenerRepository extends ListenerRepositoryBase>> implements ListenerRepository { private Logger log = LoggerFactory.getLogger(LocalListenerRepository.class); /* @@ -27,7 +27,7 @@ public class LocalListenerRepository extends ListenerRepositoryBase listenerToUrlMap; /** - * Creates a new instance of the local in-memory LocalHookListenerRepository. + * Creates a new instance of the local in-memory LocalListenerRepository. */ public LocalListenerRepository() { urlToListenersMap = new HashMap<>(); @@ -42,7 +42,7 @@ public void addListener(Listener listener) { */ removeListener(listener.getListenerId()); - log.debug("Add listener {} for resource {} with id {}", listener.getListener(), listener.getMonitoredUrl(), + log.debug("Add listener {} for resource {} with id {}", listener.getListenerId(), listener.getMonitoredUrl(), listener.getListenerId()); /* @@ -126,10 +126,7 @@ private boolean doesMethodMatch(Listener listener, String method) { } private boolean doHeadersMatch(Listener listener, MultiMap headers) { - Pattern headersFilterPattern = listener.getHook().getHeadersFilterPattern(); - if (headersFilterPattern != null) { - return HttpHeaderUtil.hasMatchingHeader(headers, headersFilterPattern); - } - return true; + return listener.getHook().getHeadersFilterPattern() == null || + HttpHeaderUtil.hasMatchingHeader(headers, listener.getHook().getHeadersFilterPattern()); } } diff --git a/gateleen-hook/src/main/resources/gateleen_hooking_schema_hook b/gateleen-hook/src/main/resources/gateleen_hooking_schema_hook index aa126b297..a62402b1e 100644 --- a/gateleen-hook/src/main/resources/gateleen_hooking_schema_hook +++ b/gateleen-hook/src/main/resources/gateleen_hooking_schema_hook @@ -1,193 +1,213 @@ { - "$schema": "http://json-schema.org/draft-04/schema#", - "description": "Hook", - "properties": { - "methods": { - "type": "array", - "items": { - "type": "string", - "enum": [ - "OPTIONS", - "GET", - "HEAD", - "POST", - "PUT", - "DELETE", - "PATCH" - ] - } - }, - "filter": { - "type": "string" - }, - "staticHeaders": { - "type": "object", - "additionalProperties": { - "description": "we should only accept string - but for backward compatibility we must also accept other types (which are converted to stings in HookHandler)", - "type": ["string", "boolean", "number", "integer"] - } - }, - "headers": { - "description": "Manipulate (set / remove / replace) request headers - option to reference other header vales", - "type": "array", - "items": { - "$ref": "#/definitions/HeaderFunction" - } - }, + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "Hook", + "properties": { + "methods": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "OPTIONS", + "GET", + "HEAD", + "POST", + "PUT", + "DELETE", + "PATCH" + ] + } + }, + "filter": { + "type": "string" + }, + "staticHeaders": { + "type": "object", + "additionalProperties": { + "description": "we should only accept string - but for backward compatibility we must also accept other types (which are converted to strings in HookHandler)", + "type": ["string", "boolean", "number", "integer"] + } + }, + "headers": { + "description": "Manipulate (set / remove / replace) request headers - option to reference other header values", + "type": "array", + "items": { + "$ref": "#/definitions/HeaderFunction" + } + }, "headersFilter": { "description": "Also filter requests based on header values (besides url and http methods). RegEx can be used to define a filter", "type": "string", "minLength": 1 }, - "destination": { - "type": "string" - }, - "translateStatus": { - "description": "Mapping to transform backend HTTP status.", - "type": "object", - "additionalProperties": { - "type": "integer" + "destinations": { + "description": "List of destinations to forward the request to.", + "type": "array", + "items": { + "type": "object", + "properties": { + "destination": { + "type": "string", + "description": "The destination URL to which the request will be forwarded." + }, + "useOriginalPayload": { + "type": "boolean", + "description": "If true, the original payload will be forwarded to the destination without transformation.", + "default": false + }, + "transform": { + "type": "object", + "description": "Transformation configuration for the payload if useOriginalPayload is false.", + "properties": { + // Add necessary transformation properties here + } + }, + "headers": { + "type": "array", + "description": "Manipulate (set / remove / replace) request headers specifically for this destination.", + "items": { + "$ref": "#/definitions/HeaderFunction" + } + }, + "proxyOptions": { + "description": "Custom proxy options for this specific destination.", + "$ref": "#/definitions/ProxyOptions" + }, + "timeout": { + "description": "The request timeout applied in seconds for this specific destination.", + "type": "integer", + "default": 30 + } + }, + "required": ["destination"] } }, - "expireAfter": { - "description": "DEPRECATED - Hooks don't manipulate or set the x-expire-after header any more. Use HeaderFunctions instead", - "type": "integer", - "minimum": -1 - }, - "queueExpireAfter": { - "type": "integer", - "minimum": -1 - }, - "type": { - "type": "string", - "enum": [ - "before", - "after" - ] - }, - "fullUrl": { - "type": "boolean" - }, - "queueingStrategy": { - "type": "object", - "properties": { - "type": { - "type": "string", - "enum": [ - "discardPayload", - "reducedPropagation" - ] - }, - "intervalMs": { - "type": "integer", - "minimum": 1 - } - }, - "additionalProperties": false - }, - "connectionPoolSize": { - "description": "The maximum number of concurrent connections to the destination.", - "type": "integer", - "default": 50 - }, - "maxWaitQueueSize": { - "description": "The maximum number of requests allowed in the wait queue.", - "type": "integer", - "default": -1 + "expireAfter": { + "description": "DEPRECATED - Hooks don't manipulate or set the x-expire-after header any more. Use HeaderFunctions instead", + "type": "integer", + "minimum": -1 + }, + "queueExpireAfter": { + "type": "integer", + "minimum": -1 + }, + "type": { + "type": "string", + "enum": [ + "before", + "after" + ] + }, + "fullUrl": { + "type": "boolean" + }, + "queueingStrategy": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "discardPayload", + "reducedPropagation" + ] + }, + "intervalMs": { + "type": "integer", + "minimum": 1 + } + }, + "additionalProperties": false + }, + "connectionPoolSize": { + "description": "The maximum number of concurrent connections to the destination.", + "type": "integer", + "default": 50 + }, + "maxWaitQueueSize": { + "description": "The maximum number of requests allowed in the wait queue.", + "type": "integer", + "default": -1 + }, + "timeout": { + "description": "The request timeout applied in seconds.", + "type": "integer", + "default": 30 + }, + "collection": { + "description": "only used for route hook", + "type": "boolean" + }, + "listable": { + "description": "only used for route hook", + "type": "boolean" + }, + "proxyOptions": { + "description": "Custom proxy options for this rule (forwarding)", + "$ref": "#/definitions/ProxyOptions" + } }, - "timeout": { - "description": "The request timeout applied in seconds.", - "type": "integer", - "default": 30 - }, - "collection": { - "description": "only used for route hook", - "type": "boolean" - }, - "listable": { - "description": "only used for route hook", - "type": "boolean" - }, - "proxyOptions": { - "description": "Custom proxy options for this rule (forwarding)", - "$ref": "#/definitions/ProxyOptions" - } - }, - "additionalProperties": false, - "required": [ - "destination" - ], - "not": { - "title": "Disallows use of deprecated 'staticHeaders' and new 'headers' at the same time", - "description": "Disallows use of deprecated 'staticHeaders' and new 'headers' at the same time", - "anyOf": [ - { - "required": [ - "staticHeaders", - "headers" - ] - } - ] - }, - "definitions": { - "HeaderFunction": { - "properties": { - "header": { - "description": "the request-header name to manupulate (set, remove or conditional set)", - "type": "string", - "minLength": 1 - }, - "value": { - "description": "the value to be set. null and empty string means 'remove the header'. value can contain other header names between single angular brackets which resolves to their values accordingly", - "type": [ - "null", - "string" - ] - }, - "mode": { - "description": "define a condition-mode. 'complete' sets the header value only if this header is not yet present. 'overwrite' sets the header value only if the header is already present. Ignored if value is null or empty string", - "type": "string", - "enum": [ - "complete", - "override" - ] - } - }, - "required": [ - "header", - "value" - ] - }, - "ProxyOptions": { - "properties": { - "type": { - "type": "string", - "enum": [ - "HTTP", - "SOCKS4", - "SOCKS5" - ] - }, - "host": { - "type": "string" - }, - "port": { - "type": "integer", - "minimum": 0, - "maximum": 65535 - }, - "username": { - "type": "string" - }, - "password": { - "type": "string" - } - }, - "required": [ - "host", - "port" - ], - "additionalProperties": false - } - } + "additionalProperties": false, + "required": [ + "destinations" + ], + "definitions": { + "HeaderFunction": { + "properties": { + "header": { + "description": "the request-header name to manipulate (set, remove or conditional set)", + "type": "string", + "minLength": 1 + }, + "value": { + "description": "the value to be set. null and empty string means 'remove the header'. value can contain other header names between single angular brackets which resolves to their values accordingly", + "type": [ + "null", + "string" + ] + }, + "mode": { + "description": "define a condition-mode. 'complete' sets the header value only if this header is not yet present. 'overwrite' sets the header value only if the header is already present. Ignored if value is null or empty string", + "type": "string", + "enum": [ + "complete", + "override" + ] + } + }, + "required": [ + "header", + "value" + ] + }, + "ProxyOptions": { + "properties": { + "type": { + "type": "string", + "enum": [ + "HTTP", + "SOCKS4", + "SOCKS5" + ] + }, + "host": { + "type": "string" + }, + "port": { + "type": "integer", + "minimum": 0, + "maximum": 65535 + }, + "username": { + "type": "string" + }, + "password": { + "type": "string" + } + }, + "required": [ + "host", + "port" + ], + "additionalProperties": false + } + } }