This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git


The following commit(s) were added to refs/heads/main by this push:
     new ef4bf6700094 CAMEL-22562_extend_camel-http (#21648)
ef4bf6700094 is described below

commit ef4bf6700094fe890dde078b6bbfbbe64bf97e50
Author: Jono Morris <[email protected]>
AuthorDate: Mon Mar 2 20:50:41 2026 +1300

    CAMEL-22562_extend_camel-http (#21648)
---
 components/camel-graphql/pom.xml                   |  11 +
 .../camel/component/graphql/GraphqlEndpoint.java   |  52 ++--
 .../camel/component/graphql/GraphqlProducer.java   | 269 ++++-----------------
 .../camel/component/graphql/BaseGraphqlTest.java   |  72 ++++++
 .../graphql/GraphqlBasicAuthenticationTest.java    |  91 +++++++
 .../camel/component/graphql/GraphqlClientTest.java | 119 +++++++++
 .../graphql/GraphqlHeaderFilterStrategyTest.java   | 121 +++++++++
 .../graphql/GraphqlProxyNonProxyHostsTest.java     |  88 +++++++
 .../GraphqlThrowExceptionOnFailureTest.java        | 102 ++++++++
 .../graphql/GraphqlTokenAuthenticationTest.java    |  81 +++++++
 .../handler/BearerTokenValidationHandler.java      |  54 +++++
 components/camel-http/pom.xml                      |  16 ++
 12 files changed, 835 insertions(+), 241 deletions(-)

diff --git a/components/camel-graphql/pom.xml b/components/camel-graphql/pom.xml
index 2f84202bc295..0170a0357cc6 100644
--- a/components/camel-graphql/pom.xml
+++ b/components/camel-graphql/pom.xml
@@ -37,6 +37,10 @@
             <groupId>org.apache.camel</groupId>
             <artifactId>camel-support</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-http</artifactId>
+        </dependency>
         <dependency>
             <groupId>org.apache.camel</groupId>
             <artifactId>camel-http-base</artifactId>
@@ -53,6 +57,13 @@
         </dependency>
 
         <!-- for testing -->
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-http</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+            <type>test-jar</type>
+        </dependency>
         <dependency>
             <groupId>org.apache.camel</groupId>
             <artifactId>camel-test-junit6</artifactId>
diff --git 
a/components/camel-graphql/src/main/java/org/apache/camel/component/graphql/GraphqlEndpoint.java
 
b/components/camel-graphql/src/main/java/org/apache/camel/component/graphql/GraphqlEndpoint.java
index ba6ce89b7be6..e3af5fa246a6 100644
--- 
a/components/camel-graphql/src/main/java/org/apache/camel/component/graphql/GraphqlEndpoint.java
+++ 
b/components/camel-graphql/src/main/java/org/apache/camel/component/graphql/GraphqlEndpoint.java
@@ -28,6 +28,7 @@ import org.apache.camel.Consumer;
 import org.apache.camel.Processor;
 import org.apache.camel.Producer;
 import org.apache.camel.RuntimeCamelException;
+import org.apache.camel.component.http.HttpClientConfigurer;
 import org.apache.camel.http.base.HttpHeaderFilterStrategy;
 import org.apache.camel.spi.EndpointServiceLocation;
 import org.apache.camel.spi.HeaderFilterStrategy;
@@ -44,9 +45,7 @@ import org.apache.hc.client5.http.auth.CredentialsStore;
 import org.apache.hc.client5.http.auth.UsernamePasswordCredentials;
 import org.apache.hc.client5.http.classic.HttpClient;
 import org.apache.hc.client5.http.impl.auth.BasicCredentialsProvider;
-import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
 import org.apache.hc.client5.http.impl.classic.HttpClientBuilder;
-import org.apache.hc.client5.http.impl.classic.HttpClients;
 import org.apache.hc.core5.http.HttpHeaders;
 import org.apache.hc.core5.http.HttpHost;
 import org.apache.hc.core5.http.message.BasicHeader;
@@ -135,30 +134,35 @@ public class GraphqlEndpoint extends DefaultEndpoint 
implements EndpointServiceL
         throw new UnsupportedOperationException("You cannot receive messages 
at this endpoint: " + getEndpointUri());
     }
 
-    CloseableHttpClient createHttpClient() {
-        HttpClientBuilder httpClientBuilder = HttpClients.custom();
-        if (proxyHost != null) {
-            String[] parts = proxyHost.split(":");
-            String hostname = parts[0];
-            int port = Integer.parseInt(parts[1]);
-            httpClientBuilder.setProxy(new HttpHost(hostname, port));
-        }
-        if (accessToken != null) {
-            String authType = "Bearer";
-            if (this.jwtAuthorizationType != null) {
-                authType = this.jwtAuthorizationType;
+    HttpClientConfigurer createHttpClientConfigurer() {
+        return new AuthClientConfigurer();
+    }
+
+    private class AuthClientConfigurer implements HttpClientConfigurer {
+        @Override
+        public void configureHttpClient(HttpClientBuilder httpClientBuilder) {
+            if (proxyHost != null) {
+                String[] parts = proxyHost.split(":");
+                String hostname = parts[0];
+                int port = Integer.parseInt(parts[1]);
+                httpClientBuilder.setProxy(new HttpHost(hostname, port));
+            }
+            if (accessToken != null) {
+                String authType = "Bearer";
+                if (jwtAuthorizationType != null) {
+                    authType = jwtAuthorizationType;
+                }
+                httpClientBuilder.setDefaultHeaders(
+                        Arrays.asList(new 
BasicHeader(HttpHeaders.AUTHORIZATION, authType + " " + accessToken)));
+            }
+            if (username != null && password != null) {
+                CredentialsStore credentialsProvider = new 
BasicCredentialsProvider();
+                credentialsProvider.setCredentials(
+                        new AuthScope(null, -1),
+                        new UsernamePasswordCredentials(username, 
password.toCharArray()));
+                
httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
             }
-            httpClientBuilder.setDefaultHeaders(
-                    Arrays.asList(new BasicHeader(HttpHeaders.AUTHORIZATION, 
authType + " " + accessToken)));
-        }
-        if (username != null && password != null) {
-            CredentialsStore credentialsProvider = new 
BasicCredentialsProvider();
-            credentialsProvider.setCredentials(
-                    new AuthScope(null, -1),
-                    new UsernamePasswordCredentials(username, 
password.toCharArray()));
-            
httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
         }
-        return httpClientBuilder.build();
     }
 
     public URI getHttpUri() {
diff --git 
a/components/camel-graphql/src/main/java/org/apache/camel/component/graphql/GraphqlProducer.java
 
b/components/camel-graphql/src/main/java/org/apache/camel/component/graphql/GraphqlProducer.java
index 47b75a5c5374..2783ffab6170 100644
--- 
a/components/camel-graphql/src/main/java/org/apache/camel/component/graphql/GraphqlProducer.java
+++ 
b/components/camel-graphql/src/main/java/org/apache/camel/component/graphql/GraphqlProducer.java
@@ -16,52 +16,23 @@
  */
 package org.apache.camel.component.graphql;
 
-import java.io.IOException;
-import java.net.URI;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-
 import org.apache.camel.AsyncCallback;
+import org.apache.camel.Endpoint;
 import org.apache.camel.Exchange;
-import org.apache.camel.ExchangePropertyKey;
 import org.apache.camel.InvalidPayloadException;
-import org.apache.camel.Message;
-import org.apache.camel.TypeConverter;
-import org.apache.camel.http.base.HttpHelper;
-import org.apache.camel.http.base.HttpOperationFailedException;
+import org.apache.camel.Producer;
+import org.apache.camel.component.http.HttpConstants;
 import org.apache.camel.spi.HeaderFilterStrategy;
+import org.apache.camel.spi.Registry;
 import org.apache.camel.support.DefaultAsyncProducer;
-import org.apache.camel.support.ObjectHelper;
-import org.apache.camel.support.http.HttpUtil;
-import org.apache.camel.util.IOHelper;
+import org.apache.camel.support.MessageHelper;
+import org.apache.camel.support.service.ServiceHelper;
 import org.apache.camel.util.json.JsonObject;
 import org.apache.hc.client5.http.classic.HttpClient;
-import org.apache.hc.client5.http.classic.methods.HttpPost;
-import org.apache.hc.client5.http.classic.methods.HttpUriRequest;
-import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
-import org.apache.hc.core5.http.ClassicHttpResponse;
-import org.apache.hc.core5.http.ContentType;
-import org.apache.hc.core5.http.Header;
-import org.apache.hc.core5.http.HttpEntity;
-import org.apache.hc.core5.http.HttpHeaders;
-import org.apache.hc.core5.http.ParseException;
-import org.apache.hc.core5.http.io.entity.EntityUtils;
-import org.apache.hc.core5.http.io.entity.StringEntity;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 public class GraphqlProducer extends DefaultAsyncProducer {
 
-    private static final Logger LOG = 
LoggerFactory.getLogger(GraphqlProducer.class);
-
-    private static final Integer OK_RESPONSE_CODE = 200;
-    private static final String OK_STATUS_RANGE = "200-299";
-
-    private HttpClient httpClient;
-    private boolean closeHttpClient;
+    private Producer http;
 
     public GraphqlProducer(GraphqlEndpoint endpoint) {
         super(endpoint);
@@ -71,18 +42,39 @@ public class GraphqlProducer extends DefaultAsyncProducer {
     protected void doStart() throws Exception {
         super.doStart();
 
-        httpClient = getEndpoint().getHttpClient();
-        if (httpClient == null) {
-            httpClient = getEndpoint().createHttpClient();
-            closeHttpClient = true;
+        Registry registry = getEndpoint().getCamelContext().getRegistry();
+        String hash = 
Integer.toHexString(getEndpoint().getEndpointUri().hashCode());
+
+        StringBuilder sb = new StringBuilder(getEndpoint().getServiceUrl());
+        sb.append("?httpMethod=POST");
+        
sb.append("&throwExceptionOnFailure=").append(getEndpoint().isThrowExceptionOnFailure());
+
+        String clientConfigurerName = "graphqlHttpClientConfigurer-" + hash;
+        registry.bind(clientConfigurerName, 
getEndpoint().createHttpClientConfigurer());
+        sb.append("&httpClientConfigurer=#").append(clientConfigurerName);
+
+        HeaderFilterStrategy headerFilterStrategy = 
getEndpoint().getHeaderFilterStrategy();
+        if (headerFilterStrategy != null) {
+            String filterStrategyName = "graphqlHeaderFilterStrategy-" + hash;
+            registry.bind(filterStrategyName, headerFilterStrategy);
+            sb.append("&headerFilterStrategy=#").append(filterStrategyName);
+        }
+
+        HttpClient httpClient = getEndpoint().getHttpClient();
+        if (httpClient != null) {
+            String httpClientName = "graphqlHttpClient-" + hash;
+            registry.bind(httpClientName, httpClient);
+            sb.append("&httpClient=#").append(httpClientName);
         }
+
+        Endpoint httpEndpoint = 
getEndpoint().getCamelContext().getEndpoint(sb.toString());
+        http = httpEndpoint.createProducer();
+        ServiceHelper.startService(http);
     }
 
     @Override
     protected void doStop() throws Exception {
-        if (closeHttpClient && httpClient instanceof CloseableHttpClient chc) {
-            IOHelper.close(chc);
-        }
+        ServiceHelper.stopService(http);
     }
 
     @Override
@@ -92,45 +84,26 @@ public class GraphqlProducer extends DefaultAsyncProducer {
 
     @Override
     public boolean process(Exchange exchange, AsyncCallback callback) {
-        try {
-            URI httpUri = getEndpoint().getHttpUri();
-            String requestBody = buildRequestBody(getQuery(exchange), 
getEndpoint().getOperationName(),
-                    getVariables(exchange));
-            try (HttpEntity requestEntity = new StringEntity(requestBody, 
ContentType.APPLICATION_JSON)) {
-                HttpPost httpPost = new HttpPost(httpUri);
-                httpPost.setHeader(HttpHeaders.CONTENT_TYPE, 
"application/json");
-                httpPost.setHeader(HttpHeaders.ACCEPT, "application/json");
-                httpPost.setHeader(HttpHeaders.ACCEPT_ENCODING, "gzip");
-                httpPost.setEntity(requestEntity);
-                populateRequestHeaders(exchange, httpPost);
 
-                httpClient.execute(httpPost, httpResponse -> {
-                    if (LOG.isDebugEnabled()) {
-                        LOG.debug("Finished executing http: {} method: {}", 
httpUri, HttpPost.METHOD_NAME);
-                    }
-                    int responseCode = httpResponse.getCode();
-                    if (LOG.isDebugEnabled()) {
-                        LOG.debug("Http responseCode: {}", responseCode);
-                    }
-                    if (!getEndpoint().isThrowExceptionOnFailure()) {
-                        // if we do not use failed exception then populate 
response for all response codes
-                        populateResponse(exchange, httpResponse, 
getEndpoint().getHeaderFilterStrategy(), responseCode);
-                    } else {
-                        boolean ok = HttpHelper.isStatusCodeOk(responseCode, 
OK_STATUS_RANGE);
-                        if (ok) {
-                            // only populate response for OK response
-                            populateResponse(exchange, httpResponse, 
getEndpoint().getHeaderFilterStrategy(), responseCode);
-                        } else {
-                            // also store response code when throwing exception
-                            populateResponseCode(exchange.getMessage(), 
httpResponse, responseCode);
+        Exchange httpExchange = getEndpoint().createExchange();
+        httpExchange.getIn().setHeader(HttpConstants.HTTP_METHOD, "post");
+        httpExchange.getIn().setHeader("Content-Type", "application/json");
+        httpExchange.getIn().setHeader("Accept", "application/json");
+        httpExchange.getIn().setHeader("Accept-Encoding", "gzip");
+        MessageHelper.copyHeaders(exchange.getMessage(), httpExchange.getIn(), 
true);
 
-                            // operation failed so populate exception to throw
-                            
exchange.setException(populateHttpOperationFailedException(exchange, 
httpResponse, responseCode));
-                        }
-                    }
-                    return null;
-                });
+        try {
+            String requestBody
+                    = buildRequestBody(getQuery(exchange), 
getEndpoint().getOperationName(), getVariables(exchange));
+            httpExchange.getIn().setBody(requestBody);
+            try {
+                http.process(httpExchange);
+                String data = httpExchange.getMessage().getBody(String.class);
+                exchange.getIn().setBody(data);
+            } catch (Exception e) {
+                exchange.setException(e);
             }
+            MessageHelper.copyHeaders(httpExchange.getMessage(), 
exchange.getIn(), true);
         } catch (Exception e) {
             exchange.setException(e);
         }
@@ -139,144 +112,6 @@ public class GraphqlProducer extends DefaultAsyncProducer 
{
         return true;
     }
 
-    private void populateRequestHeaders(Exchange exchange, HttpPost 
httpRequest) {
-        HeaderFilterStrategy strategy = 
getEndpoint().getHeaderFilterStrategy();
-        final TypeConverter tc = exchange.getContext().getTypeConverter();
-        for (Map.Entry<String, Object> entry : 
exchange.getMessage().getHeaders().entrySet()) {
-            String key = entry.getKey();
-            // we should not add known headers
-
-            // skip known headers from graphql
-            boolean skip = getEndpoint().getQueryHeader() != null && 
key.equalsIgnoreCase(getEndpoint().getQueryHeader())
-                    || getEndpoint().getVariablesHeader() != null && 
key.equalsIgnoreCase(getEndpoint().getVariablesHeader());
-            if (skip) {
-                continue;
-            }
-
-            Object headerValue = entry.getValue();
-            if (headerValue != null) {
-                if (headerValue instanceof String || headerValue instanceof 
Integer || headerValue instanceof Long
-                        || headerValue instanceof Boolean || headerValue 
instanceof Date) {
-                    // optimise for common types
-                    String value = headerValue.toString();
-                    if (!strategy.applyFilterToCamelHeaders(key, value, 
exchange)) {
-                        httpRequest.addHeader(key, value);
-                    }
-                    continue;
-                }
-
-                // use an iterator as there can be multiple values. (must not 
use a delimiter, and allow empty values)
-                final Iterator<?> it = 
ObjectHelper.createIterator(headerValue, null, true);
-
-                HttpUtil.applyHeader(strategy, exchange, it, tc, key,
-                        (multiValues, prev) -> applyHeader(httpRequest, key, 
multiValues, prev));
-            }
-        }
-    }
-
-    private static void applyHeader(HttpUriRequest httpRequest, String key, 
List<String> multiValues, String prev) {
-        // add the value(s) as a http request header
-        if (multiValues != null) {
-            // use the default toString of a ArrayList to create in the form 
[xxx, yyy]
-            // if multi valued, for a single value, then just output the value 
as is
-            String s = multiValues.size() > 1 ? multiValues.toString() : 
multiValues.get(0);
-            httpRequest.addHeader(key, s);
-        } else if (prev != null) {
-            httpRequest.addHeader(key, prev);
-        }
-    }
-
-    private static void populateResponseCode(Message message, 
ClassicHttpResponse httpResponse, int responseCode) {
-        // optimize for 200 response code as the boxing is outside the cached 
integers
-        if (responseCode == 200) {
-            message.setHeader(Exchange.HTTP_RESPONSE_CODE, OK_RESPONSE_CODE);
-        } else {
-            message.setHeader(Exchange.HTTP_RESPONSE_CODE, responseCode);
-        }
-        if (httpResponse.getReasonPhrase() != null) {
-            message.setHeader(Exchange.HTTP_RESPONSE_TEXT, 
httpResponse.getReasonPhrase());
-        }
-    }
-
-    protected Exception populateHttpOperationFailedException(
-            Exchange exchange, ClassicHttpResponse httpResponse, int 
responseCode)
-            throws IOException, ParseException {
-        Exception answer;
-
-        String statusText = httpResponse.getReasonPhrase() != null ? 
httpResponse.getReasonPhrase() : null;
-        Map<String, String> headers = 
extractResponseHeaders(httpResponse.getHeaders());
-
-        Object responseBody = EntityUtils.toString(httpResponse.getEntity());
-
-        // make a defensive copy of the response body in the exception so its 
detached from the cache
-        String copy = null;
-        if (responseBody != null) {
-            copy = 
exchange.getContext().getTypeConverter().convertTo(String.class, exchange, 
responseBody);
-        }
-
-        Header locationHeader = httpResponse.getFirstHeader("location");
-        String uri = getEndpoint().getHttpUri().toString();
-        if (locationHeader != null && responseCode >= 300 && responseCode < 
400) {
-            answer = new HttpOperationFailedException(
-                    uri, responseCode, statusText, locationHeader.getValue(), 
headers, copy);
-        } else {
-            answer = new HttpOperationFailedException(uri, responseCode, 
statusText, null, headers, copy);
-        }
-
-        return answer;
-    }
-
-    protected void populateResponse(
-            Exchange exchange, ClassicHttpResponse httpResponse,
-            HeaderFilterStrategy strategy, int responseCode)
-            throws IOException, ParseException {
-
-        Message answer = exchange.getMessage();
-        populateResponseCode(answer, httpResponse, responseCode);
-
-        // We just make the out message is not create when extractResponseBody 
throws exception
-        Object responseBody = EntityUtils.toString(httpResponse.getEntity());
-        answer.setBody(responseBody);
-
-        // optimize to walk headers with an iterator which does not create a 
new array as getAllHeaders does
-        boolean found = false;
-        Iterator<Header> it = httpResponse.headerIterator();
-        while (it.hasNext()) {
-            Header header = it.next();
-            String name = header.getName();
-            String value = header.getValue();
-            if (!found && name.equalsIgnoreCase("content-type")) {
-                name = Exchange.CONTENT_TYPE;
-                exchange.setProperty(ExchangePropertyKey.CHARSET_NAME, 
IOHelper.getCharsetNameFromContentType(value));
-                found = true;
-            }
-            // use http helper to extract parameter value as it may contain 
multiple values
-            Object extracted = HttpHelper.extractHttpParameterValue(value);
-            if (strategy != null && 
!strategy.applyFilterToExternalHeaders(name, extracted, exchange)) {
-                HttpHelper.appendHeader(answer.getHeaders(), name, extracted);
-            }
-        }
-    }
-
-    /**
-     * Extracts the response headers
-     *
-     * @param  responseHeaders the headers
-     * @return                 the extracted headers or an empty map if no 
headers existed
-     */
-    protected static Map<String, String> extractResponseHeaders(Header[] 
responseHeaders) {
-        if (responseHeaders == null || responseHeaders.length == 0) {
-            return Map.of();
-        }
-
-        Map<String, String> answer = new HashMap<>();
-        for (Header header : responseHeaders) {
-            answer.put(header.getName(), header.getValue());
-        }
-
-        return answer;
-    }
-
     protected static String buildRequestBody(String query, String 
operationName, JsonObject variables) {
         JsonObject jsonObject = new JsonObject();
         jsonObject.put("query", query);
diff --git 
a/components/camel-graphql/src/test/java/org/apache/camel/component/graphql/BaseGraphqlTest.java
 
b/components/camel-graphql/src/test/java/org/apache/camel/component/graphql/BaseGraphqlTest.java
new file mode 100644
index 000000000000..b39125bf790e
--- /dev/null
+++ 
b/components/camel-graphql/src/test/java/org/apache/camel/component/graphql/BaseGraphqlTest.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.camel.component.graphql;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.Message;
+import org.apache.camel.component.http.BaseHttpTest;
+import org.apache.camel.component.http.interceptor.RequestBasicAuth;
+import org.apache.camel.component.http.interceptor.ResponseBasicUnauthorized;
+import org.apache.camel.http.base.HttpOperationFailedException;
+import org.apache.hc.core5.http.HttpRequestInterceptor;
+import org.apache.hc.core5.http.HttpResponseInterceptor;
+import org.apache.hc.core5.http.HttpStatus;
+import org.apache.hc.core5.http.protocol.DefaultHttpProcessor;
+import org.apache.hc.core5.http.protocol.HttpProcessor;
+import org.apache.hc.core5.http.protocol.RequestValidateHost;
+import org.apache.hc.core5.http.protocol.ResponseContent;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+public class BaseGraphqlTest extends BaseHttpTest {
+
+    protected void assertUnauthorizedResponse(Exchange exchange) {
+        assertNotNull(exchange);
+
+        Exception ex = exchange.getException();
+        assertNotNull(ex, "Should have thrown an exception");
+
+        HttpOperationFailedException cause = 
assertInstanceOf(HttpOperationFailedException.class, ex);
+        assertEquals(HttpStatus.SC_UNAUTHORIZED, cause.getStatusCode());
+
+        Message in = exchange.getIn();
+        assertNotNull(in);
+
+        Map<String, Object> headers = in.getHeaders();
+        assertEquals(HttpStatus.SC_UNAUTHORIZED, 
headers.get(Exchange.HTTP_RESPONSE_CODE));
+        assertEquals("Unauthorized", headers.get(Exchange.HTTP_RESPONSE_TEXT));
+    }
+
+    @Override
+    protected HttpProcessor getBasicHttpProcessor() {
+        List<HttpRequestInterceptor> requestInterceptors = new ArrayList<>();
+        requestInterceptors.add(new RequestValidateHost());
+        requestInterceptors.add(new RequestBasicAuth());
+
+        List<HttpResponseInterceptor> responseInterceptors = new ArrayList<>();
+        responseInterceptors.add(new ResponseContent());
+        responseInterceptors.add(new ResponseBasicUnauthorized());
+
+        return new DefaultHttpProcessor(requestInterceptors, 
responseInterceptors);
+    }
+}
diff --git 
a/components/camel-graphql/src/test/java/org/apache/camel/component/graphql/GraphqlBasicAuthenticationTest.java
 
b/components/camel-graphql/src/test/java/org/apache/camel/component/graphql/GraphqlBasicAuthenticationTest.java
new file mode 100644
index 000000000000..0dcdebfe72c6
--- /dev/null
+++ 
b/components/camel-graphql/src/test/java/org/apache/camel/component/graphql/GraphqlBasicAuthenticationTest.java
@@ -0,0 +1,91 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.camel.component.graphql;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.http.handler.AuthenticationValidationHandler;
+import org.apache.hc.core5.http.impl.bootstrap.HttpServer;
+import org.apache.hc.core5.http.impl.bootstrap.ServerBootstrap;
+import org.junit.jupiter.api.Test;
+
+import static org.apache.camel.component.http.HttpMethods.POST;
+
+/**
+ * Verify 'AuthClientConfigurer' instances registered with Basic credentials.
+ */
+public class GraphqlBasicAuthenticationTest extends BaseGraphqlTest {
+
+    private final String user = "camel";
+    private final String password = "password";
+    private HttpServer localServer;
+
+    @Override
+    public void setupResources() throws Exception {
+        localServer = ServerBootstrap.bootstrap()
+                
.setCanonicalHostName("localhost").setHttpProcessor(getBasicHttpProcessor())
+                .register("/graphql",
+                        new AuthenticationValidationHandler(
+                                POST.name(), null, null, getExpectedContent(), 
user, password))
+                .create();
+        localServer.start();
+    }
+
+    @Override
+    public void cleanupResources() {
+        if (localServer != null) {
+            localServer.stop();
+        }
+    }
+
+    // verify that an endpoint configured with the correct credentials passes 
authentication
+    @Test
+    public void shouldPassAuthentication() {
+        Exchange exchange = template.request("direct:start1", exchange1 -> {
+        });
+
+        assertExchange(exchange);
+    }
+
+    // verify that an endpoint configured with wrong credentials fails 
authentication
+    @Test
+    public void shouldFailAuthorisation() {
+        Exchange exchange = template.request("direct:start2", exchange1 -> {
+        });
+
+        assertUnauthorizedResponse(exchange);
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() {
+        return new RouteBuilder() {
+            // multiple routes to verify registration of multiple configurers
+            @Override
+            public void configure() {
+                from("direct:start1")
+                        .to("graphql://http://localhost:"; + 
localServer.getLocalPort()
+                            + "/graphql?query={books{id name}}"
+                            + "&username=" + user + "&password=" + password);
+
+                from("direct:start2")
+                        .to("graphql://http://localhost:"; + 
localServer.getLocalPort()
+                            + "/graphql?query={books{id name}}"
+                            + "&username=" + user + "&password=wrongPassword");
+            }
+        };
+    }
+}
diff --git 
a/components/camel-graphql/src/test/java/org/apache/camel/component/graphql/GraphqlClientTest.java
 
b/components/camel-graphql/src/test/java/org/apache/camel/component/graphql/GraphqlClientTest.java
new file mode 100644
index 000000000000..43cf3f9d3eb9
--- /dev/null
+++ 
b/components/camel-graphql/src/test/java/org/apache/camel/component/graphql/GraphqlClientTest.java
@@ -0,0 +1,119 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.camel.component.graphql;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.http.handler.AuthenticationValidationHandler;
+import org.apache.camel.spi.Registry;
+import org.apache.hc.client5.http.auth.AuthScope;
+import org.apache.hc.client5.http.auth.CredentialsStore;
+import org.apache.hc.client5.http.auth.UsernamePasswordCredentials;
+import org.apache.hc.client5.http.impl.auth.BasicCredentialsProvider;
+import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
+import org.apache.hc.client5.http.impl.classic.HttpClientBuilder;
+import org.apache.hc.client5.http.impl.classic.HttpClients;
+import org.apache.hc.core5.http.impl.bootstrap.HttpServer;
+import org.apache.hc.core5.http.impl.bootstrap.ServerBootstrap;
+import org.junit.jupiter.api.Test;
+
+import static org.apache.camel.component.http.HttpMethods.POST;
+
+/**
+ * Verify 'HttpClient' instances registered with Basic credentials.
+ */
+public class GraphqlClientTest extends BaseGraphqlTest {
+
+    private final String user = "camel";
+    private final String password = "password";
+    private HttpServer localServer;
+
+    @Override
+    public void setupResources() throws Exception {
+        localServer = ServerBootstrap.bootstrap()
+                
.setCanonicalHostName("localhost").setHttpProcessor(getBasicHttpProcessor())
+                .register("/graphql",
+                        new AuthenticationValidationHandler(
+                                POST.name(), null, null,
+                                getExpectedContent(), user, password))
+                .create();
+        localServer.start();
+    }
+
+    @Override
+    public void cleanupResources() {
+        if (localServer != null) {
+            localServer.stop();
+        }
+    }
+
+    // verify that a httpClient configured with the correct credentials passes 
authentication
+    @Test
+    public void shouldPassAuthentication() {
+        Exchange exchange = template.request("direct:start1", exchange1 -> {
+        });
+
+        assertExchange(exchange);
+    }
+
+    // verify that a httpClient configured with wrong credentials fails 
authentication
+    @Test
+    public void shouldFailAuthorisation() {
+        Exchange exchange = template.request("direct:start2", exchange1 -> {
+        });
+
+        assertUnauthorizedResponse(exchange);
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() {
+        return new RouteBuilder() {
+            // multiple routes to verify registration of multiple 'httpClient' 
instances
+            @Override
+            public void configure() {
+                from("direct:start1")
+                        .to("graphql://http://localhost:"; + 
localServer.getLocalPort()
+                            + "/graphql?query={books{id name}}"
+                            + "&httpClient=#httpClient");
+
+                from("direct:start2")
+                        .to("graphql://http://localhost:"; + 
localServer.getLocalPort()
+                            + "/graphql?query={books{id name}}"
+                            + "&httpClient=#httpClientWrongPassword");
+            }
+        };
+    }
+
+    @Override
+    protected void bindToRegistry(Registry registry) {
+        registry.bind("httpClient", createHttpClient(user, password));
+        registry.bind("httpClientWrongPassword", createHttpClient(user, 
"wrongPassword"));
+    }
+
+    // create a HttpClient instance with the provided credentials
+    private CloseableHttpClient createHttpClient(String user, String password) 
{
+        CredentialsStore credentialsProvider = new BasicCredentialsProvider();
+        // set credentials on the http-client instead of the endpoint for 
purpose of this test
+        credentialsProvider.setCredentials(
+                new AuthScope(null, -1),
+                new UsernamePasswordCredentials(user, password.toCharArray()));
+        HttpClientBuilder httpClientBuilder = HttpClients.custom();
+        httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
+
+        return httpClientBuilder.build();
+    }
+}
diff --git 
a/components/camel-graphql/src/test/java/org/apache/camel/component/graphql/GraphqlHeaderFilterStrategyTest.java
 
b/components/camel-graphql/src/test/java/org/apache/camel/component/graphql/GraphqlHeaderFilterStrategyTest.java
new file mode 100644
index 000000000000..043d67788d0c
--- /dev/null
+++ 
b/components/camel-graphql/src/test/java/org/apache/camel/component/graphql/GraphqlHeaderFilterStrategyTest.java
@@ -0,0 +1,121 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.camel.component.graphql;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.http.BaseHttpTest;
+import org.apache.camel.component.http.handler.BasicValidationHandler;
+import org.apache.camel.http.base.HttpHeaderFilterStrategy;
+import org.apache.camel.spi.Registry;
+import org.apache.hc.core5.http.HeaderElements;
+import org.apache.hc.core5.http.impl.bootstrap.HttpServer;
+import org.apache.hc.core5.http.impl.bootstrap.ServerBootstrap;
+import org.junit.jupiter.api.Test;
+
+import static org.apache.camel.component.http.HttpMethods.POST;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * Verify that registered 'HttpHeaderFilterStrategy' instances can be used to 
allow specific headers.
+ */
+public class GraphqlHeaderFilterStrategyTest extends BaseHttpTest {
+
+    private HttpServer localServer;
+
+    @Override
+    public void setupResources() throws Exception {
+        localServer = ServerBootstrap.bootstrap()
+                .setCanonicalHostName("localhost")
+                .register("/graphql",
+                        new BasicValidationHandler(POST.name(), null, null, 
getExpectedContent()))
+                .create();
+        localServer.start();
+    }
+
+    @Override
+    public void cleanupResources() {
+        if (localServer != null) {
+            localServer.stop();
+        }
+    }
+
+    @Test
+    public void allowConnectionCloseHeader() {
+        Exchange exchange = template.request(
+                "direct:start1",
+                exchange1 -> {
+                    exchange1.getIn().setHeader("connection", 
HeaderElements.CLOSE);
+                });
+
+        assertEquals(HeaderElements.CLOSE, 
exchange.getMessage().getHeader("connection"));
+        assertExchange(exchange);
+    }
+
+    @Test
+    public void allowWarningHeader() {
+        Exchange exchange2 = template.request(
+                "direct:start2",
+                exchange1 -> {
+                    exchange1.getIn().setHeader("warning", "test warning");
+                });
+
+        assertEquals("test warning", 
exchange2.getMessage().getHeader("warning"));
+        assertExchange(exchange2);
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() {
+        // multiple routes to verify registration of multiple 
'headerFilterStrategy' instances
+        return new RouteBuilder() {
+            @Override
+            public void configure() {
+                from("direct:start1")
+                        .to("graphql://http://localhost:"; + 
localServer.getLocalPort()
+                            + "/graphql?query={books{id name}}"
+                            + 
"&headerFilterStrategy=#allowConnectionCloseFilter");
+
+                from("direct:start2")
+                        .to("graphql://http://localhost:"; + 
localServer.getLocalPort()
+                            + "/graphql?query={books{id name}}"
+                            + "&headerFilterStrategy=#allowWarningFilter");
+            }
+        };
+    }
+
+    @Override
+    protected void bindToRegistry(Registry registry) {
+        registry.bind("allowConnectionCloseFilter", new 
AllowConnectionCloseHeader());
+        registry.bind("allowWarningFilter", new AllowWarningHeader());
+    }
+
+    private static class AllowConnectionCloseHeader extends 
HttpHeaderFilterStrategy {
+        @Override
+        protected void initialize() {
+            super.initialize();
+            getOutFilter().remove("connection");
+        }
+    }
+
+    private static class AllowWarningHeader extends HttpHeaderFilterStrategy {
+        @Override
+        protected void initialize() {
+            super.initialize();
+            getOutFilter().remove("warning");
+        }
+    }
+}
diff --git 
a/components/camel-graphql/src/test/java/org/apache/camel/component/graphql/GraphqlProxyNonProxyHostsTest.java
 
b/components/camel-graphql/src/test/java/org/apache/camel/component/graphql/GraphqlProxyNonProxyHostsTest.java
new file mode 100644
index 000000000000..746d2bafd547
--- /dev/null
+++ 
b/components/camel-graphql/src/test/java/org/apache/camel/component/graphql/GraphqlProxyNonProxyHostsTest.java
@@ -0,0 +1,88 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.camel.component.graphql;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.component.http.BaseHttpTest;
+import org.apache.hc.core5.http.ClassicHttpRequest;
+import org.apache.hc.core5.http.ClassicHttpResponse;
+import org.apache.hc.core5.http.HttpStatus;
+import org.apache.hc.core5.http.impl.bootstrap.HttpServer;
+import org.apache.hc.core5.http.impl.bootstrap.ServerBootstrap;
+import org.apache.hc.core5.http.io.HttpRequestHandler;
+import org.apache.hc.core5.http.io.entity.StringEntity;
+import org.apache.hc.core5.http.protocol.HttpContext;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * Verify the 'AuthClientConfigurer' 'proxyHost' configuration.
+ */
+public class GraphqlProxyNonProxyHostsTest extends BaseHttpTest {
+
+    private final AtomicInteger proxyRequestCount = new AtomicInteger(0);
+    private HttpServer proxy;
+
+    @BeforeEach
+    @Override
+    public void setupResources() throws Exception {
+        proxy = ServerBootstrap.bootstrap()
+                .setHttpProcessor(getBasicHttpProcessor())
+                .setRequestRouter((request, context) -> new 
ProxyServerHandler())
+                .create();
+        proxy.start();
+    }
+
+    @AfterEach
+    @Override
+    public void cleanupResources() {
+        if (proxy != null) {
+            proxy.stop();
+        }
+    }
+
+    @Test
+    public void usesProxyWhenConfigured() {
+        proxyRequestCount.set(0);
+
+        Exchange exchange = template.request(
+                // placeholder localhost port since proxy returns a dummy 
response
+                "graphql://http://localhost:8080";
+                                             + "/graphql?query={books{id 
name}}"
+                                             + "&proxyHost=127.0.0.1:" + 
proxy.getLocalPort(),
+                exchange1 -> {
+                });
+
+        assertExchange(exchange);
+        assertEquals(1, proxyRequestCount.get());
+    }
+
+    private class ProxyServerHandler implements HttpRequestHandler {
+        @Override
+        public void handle(
+                ClassicHttpRequest request, ClassicHttpResponse response, 
HttpContext context) {
+            proxyRequestCount.incrementAndGet();
+            response.setCode(HttpStatus.SC_SUCCESS);
+            response.setEntity(new StringEntity(getExpectedContent()));
+        }
+    }
+}
diff --git 
a/components/camel-graphql/src/test/java/org/apache/camel/component/graphql/GraphqlThrowExceptionOnFailureTest.java
 
b/components/camel-graphql/src/test/java/org/apache/camel/component/graphql/GraphqlThrowExceptionOnFailureTest.java
new file mode 100644
index 000000000000..e288ea87230b
--- /dev/null
+++ 
b/components/camel-graphql/src/test/java/org/apache/camel/component/graphql/GraphqlThrowExceptionOnFailureTest.java
@@ -0,0 +1,102 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.camel.component.graphql;
+
+import java.util.Map;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.Message;
+import org.apache.camel.component.http.BaseHttpTest;
+import org.apache.camel.component.http.handler.BasicValidationHandler;
+import org.apache.camel.http.base.HttpOperationFailedException;
+import org.apache.hc.core5.http.HttpStatus;
+import org.apache.hc.core5.http.impl.bootstrap.HttpServer;
+import org.apache.hc.core5.http.impl.bootstrap.ServerBootstrap;
+import org.junit.jupiter.api.Test;
+
+import static org.apache.camel.component.http.HttpMethods.POST;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+/**
+ * Verify the 'throwExceptionOnFailure' option.
+ */
+public class GraphqlThrowExceptionOnFailureTest extends BaseHttpTest {
+
+    private HttpServer localServer;
+    private String baseUrl;
+
+    @Override
+    public void setupResources() throws Exception {
+        localServer = ServerBootstrap.bootstrap()
+                .setCanonicalHostName("localhost")
+                .register("/",
+                        new BasicValidationHandler(POST.name(), null, null, 
getExpectedContent()))
+                .create();
+        localServer.start();
+        baseUrl = "graphql://http://localhost:"; + localServer.getLocalPort();
+    }
+
+    @Override
+    public void cleanupResources() {
+        if (localServer != null) {
+            localServer.stop();
+        }
+    }
+
+    @Test
+    public void shouldNotThrowExceptionOnFailure() {
+        Exchange exchange
+                = template.request(baseUrl + "/xxx?query={books{id 
name}}&throwExceptionOnFailure=false",
+                        exchange1 -> {
+                        });
+
+        assertNotNull(exchange);
+
+        Message out = exchange.getMessage();
+        assertNotNull(out);
+
+        Map<String, Object> headers = out.getHeaders();
+        assertEquals(HttpStatus.SC_NOT_IMPLEMENTED, 
headers.get(Exchange.HTTP_RESPONSE_CODE));
+        assertEquals("0", headers.get("Content-Length"));
+    }
+
+    @Test
+    public void shouldThrowExceptionOnFailure() {
+        Exchange exchange = template.request(baseUrl
+                                             + "/xxx?query={books{id 
name}}&throwExceptionOnFailure=true",
+                exchange1 -> {
+                    exchange1.getMessage().setHeader("cheese", "gauda");
+                });
+
+        Exception ex = exchange.getException();
+        assertNotNull(ex, "Should have thrown an exception");
+
+        HttpOperationFailedException cause = 
assertInstanceOf(HttpOperationFailedException.class, ex);
+        assertEquals(HttpStatus.SC_NOT_IMPLEMENTED, cause.getStatusCode());
+
+        Message out = exchange.getMessage();
+        assertNotNull(out);
+
+        Map<String, Object> headers = out.getHeaders();
+        assertEquals(HttpStatus.SC_NOT_IMPLEMENTED, 
headers.get(Exchange.HTTP_RESPONSE_CODE));
+        assertEquals("Not Implemented", 
headers.get(Exchange.HTTP_RESPONSE_TEXT));
+        // header should be preserved
+        assertEquals("gauda", out.getHeaders().get("cheese"));
+    }
+}
diff --git 
a/components/camel-graphql/src/test/java/org/apache/camel/component/graphql/GraphqlTokenAuthenticationTest.java
 
b/components/camel-graphql/src/test/java/org/apache/camel/component/graphql/GraphqlTokenAuthenticationTest.java
new file mode 100644
index 000000000000..07e3a1008823
--- /dev/null
+++ 
b/components/camel-graphql/src/test/java/org/apache/camel/component/graphql/GraphqlTokenAuthenticationTest.java
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.camel.component.graphql;
+
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.component.graphql.handler.BearerTokenValidationHandler;
+import org.apache.camel.component.http.BaseHttpTest;
+import org.apache.hc.core5.http.HttpRequestInterceptor;
+import org.apache.hc.core5.http.HttpResponseInterceptor;
+import org.apache.hc.core5.http.impl.bootstrap.HttpServer;
+import org.apache.hc.core5.http.impl.bootstrap.ServerBootstrap;
+import org.apache.hc.core5.http.protocol.DefaultHttpProcessor;
+import org.apache.hc.core5.http.protocol.HttpProcessor;
+import org.apache.hc.core5.http.protocol.RequestValidateHost;
+import org.apache.hc.core5.http.protocol.ResponseContent;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Verify 'AuthClientConfigurer' instances registered with an accessToken.
+ */
+public class GraphqlTokenAuthenticationTest extends BaseHttpTest {
+
+    private final String accessToken = "accessToken";
+    private HttpServer localServer;
+
+    @Override
+    public void setupResources() throws Exception {
+        localServer = ServerBootstrap.bootstrap()
+                .setCanonicalHostName("localhost")
+                .setHttpProcessor(getBasicHttpProcessor())
+                .register("/graphql", new 
BearerTokenValidationHandler(accessToken, getExpectedContent()))
+                .create();
+        localServer.start();
+    }
+
+    @Override
+    public void cleanupResources() {
+        if (localServer != null) {
+            localServer.stop();
+        }
+    }
+
+    @Test
+    public void bearerTokenAuthenticationShouldSucceed() {
+        Exchange exchange = template.request(
+                "graphql://http://localhost:"; + localServer.getLocalPort()
+                                             + "/graphql?query={books{id 
name}}"
+                                             + "&accessToken=" + accessToken,
+                exchange1 -> {
+                });
+
+        assertExchange(exchange);
+    }
+
+    @Override
+    protected HttpProcessor getBasicHttpProcessor() {
+        List<HttpRequestInterceptor> requestInterceptors
+                = Collections.singletonList(new RequestValidateHost());
+        List<HttpResponseInterceptor> responseInterceptors
+                = Collections.singletonList(new ResponseContent());
+
+        return new DefaultHttpProcessor(requestInterceptors, 
responseInterceptors);
+    }
+}
diff --git 
a/components/camel-graphql/src/test/java/org/apache/camel/component/graphql/handler/BearerTokenValidationHandler.java
 
b/components/camel-graphql/src/test/java/org/apache/camel/component/graphql/handler/BearerTokenValidationHandler.java
new file mode 100644
index 000000000000..6625407ffc3b
--- /dev/null
+++ 
b/components/camel-graphql/src/test/java/org/apache/camel/component/graphql/handler/BearerTokenValidationHandler.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.camel.component.graphql.handler;
+
+import java.nio.charset.StandardCharsets;
+
+import org.apache.hc.core5.http.ClassicHttpRequest;
+import org.apache.hc.core5.http.ClassicHttpResponse;
+import org.apache.hc.core5.http.Header;
+import org.apache.hc.core5.http.HttpException;
+import org.apache.hc.core5.http.HttpHeaders;
+import org.apache.hc.core5.http.HttpStatus;
+import org.apache.hc.core5.http.io.HttpRequestHandler;
+import org.apache.hc.core5.http.io.entity.StringEntity;
+import org.apache.hc.core5.http.protocol.HttpContext;
+
+public class BearerTokenValidationHandler implements HttpRequestHandler {
+
+    private final String expectedToken;
+    private final String content;
+
+    public BearerTokenValidationHandler(String expectedToken, String content) {
+        this.expectedToken = expectedToken;
+        this.content = content;
+    }
+
+    @Override
+    public void handle(ClassicHttpRequest request, ClassicHttpResponse 
response, HttpContext context)
+            throws HttpException {
+
+        Header header = request.getHeader(HttpHeaders.AUTHORIZATION);
+        String auth = header != null ? header.getValue() : null;
+        if (auth == null || !auth.equals("Bearer " + expectedToken)) {
+            response.setCode(HttpStatus.SC_UNAUTHORIZED);
+            return;
+        }
+        response.setCode(HttpStatus.SC_OK);
+        response.setEntity(new StringEntity(content, 
StandardCharsets.US_ASCII));
+    }
+}
diff --git a/components/camel-http/pom.xml b/components/camel-http/pom.xml
index 022fa5c3a11a..a5374236ae4f 100644
--- a/components/camel-http/pom.xml
+++ b/components/camel-http/pom.xml
@@ -164,6 +164,22 @@
                     </execution>
                 </executions>
             </plugin>
+            <!-- generate the attached tests jar -->
+            <plugin>
+                <artifactId>maven-jar-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>test-jar</goal>
+                        </goals>
+                    </execution>
+                </executions>
+                <configuration>
+                    <excludes>
+                        <exclude>log4j2.properties</exclude>
+                    </excludes>
+                </configuration>
+            </plugin>
         </plugins>
     </build>
 

Reply via email to