You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@knox.apache.org by mo...@apache.org on 2017/09/27 14:08:59 UTC
[4/4] knox git commit: Merge branch 'master' into
KNOX-998-Package_Restructuring
Merge branch 'master' into KNOX-998-Package_Restructuring
Project: http://git-wip-us.apache.org/repos/asf/knox/repo
Commit: http://git-wip-us.apache.org/repos/asf/knox/commit/51348f83
Tree: http://git-wip-us.apache.org/repos/asf/knox/tree/51348f83
Diff: http://git-wip-us.apache.org/repos/asf/knox/diff/51348f83
Branch: refs/heads/KNOX-998-Package_Restructuring
Commit: 51348f838106241efbf34534168c478d77a35d6e
Parents: 7815945 10b3473
Author: Sandeep More <mo...@apache.org>
Authored: Wed Sep 27 10:07:08 2017 -0400
Committer: Sandeep More <mo...@apache.org>
Committed: Wed Sep 27 10:07:08 2017 -0400
----------------------------------------------------------------------
.../knox/gateway/dispatch/AbstractGatewayDispatch.java | 8 ++++++++
.../org/apache/knox/gateway/dispatch/DefaultDispatch.java | 9 +++++++++
.../java/org/apache/knox/gateway/dispatch/Dispatch.java | 6 ++++++
.../apache/knox/gateway/dispatch/GatewayDispatchFilter.java | 8 ++++++++
4 files changed, 31 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/knox/blob/51348f83/gateway-spi/src/main/java/org/apache/knox/gateway/dispatch/AbstractGatewayDispatch.java
----------------------------------------------------------------------
diff --cc gateway-spi/src/main/java/org/apache/knox/gateway/dispatch/AbstractGatewayDispatch.java
index fbc86e7,0000000..aad7d4c
mode 100644,000000..100644
--- a/gateway-spi/src/main/java/org/apache/knox/gateway/dispatch/AbstractGatewayDispatch.java
+++ b/gateway-spi/src/main/java/org/apache/knox/gateway/dispatch/AbstractGatewayDispatch.java
@@@ -1,144 -1,0 +1,152 @@@
+/**
+ * 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.knox.gateway.dispatch;
+
+import org.apache.knox.gateway.filter.GatewayResponse;
+import org.apache.hadoop.io.IOUtils;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.HttpUriRequest;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Set;
+
+public abstract class AbstractGatewayDispatch implements Dispatch {
+
+ private static final int STREAM_COPY_BUFFER_SIZE = 4096;
+ private static final Set<String> REQUEST_EXCLUDE_HEADERS = new HashSet<>();
+
+ static {
+ REQUEST_EXCLUDE_HEADERS.add("Host");
+ REQUEST_EXCLUDE_HEADERS.add("Authorization");
+ REQUEST_EXCLUDE_HEADERS.add("Content-Length");
+ REQUEST_EXCLUDE_HEADERS.add("Transfer-Encoding");
+ }
+
+ protected HttpClient client;
+
+ @Override
+ public void init() {
+ }
+
+ protected void writeResponse(HttpServletRequest request, HttpServletResponse response, InputStream stream )
+ throws IOException {
+// ResponseStreamer streamer =
+// (ResponseStreamer)request.getAttribute( RESPONSE_STREAMER_ATTRIBUTE_NAME );
+// if( streamer != null ) {
+// streamer.streamResponse( stream, response.getOutputStream() );
+// } else {
+ if( response instanceof GatewayResponse ) {
+ ((GatewayResponse)response).streamResponse( stream );
+ } else {
+ OutputStream output = response.getOutputStream();
+ IOUtils.copyBytes( stream, output, STREAM_COPY_BUFFER_SIZE );
+ //KNOX-685: output.flush();
+ output.close();
+ }
+// }
+ }
+
+ @Override
+ public HttpClient getHttpClient() {
+ return client;
+ }
+
+ @Override
+ public void setHttpClient(HttpClient client) {
+ this.client = client;
+ }
+
+ @Override
+ public URI getDispatchUrl(HttpServletRequest request) {
+ StringBuffer str = request.getRequestURL();
+ String query = request.getQueryString();
+ if ( query != null ) {
+ str.append('?');
+ str.append(query);
+ }
+ URI url = URI.create(str.toString());
+ return url;
+ }
+
+ public void doGet( URI url, HttpServletRequest request, HttpServletResponse response )
+ throws IOException, URISyntaxException {
+ response.sendError( HttpServletResponse.SC_METHOD_NOT_ALLOWED );
+ }
+
+ public void doPost( URI url, HttpServletRequest request, HttpServletResponse response )
+ throws IOException, URISyntaxException {
+ response.sendError( HttpServletResponse.SC_METHOD_NOT_ALLOWED );
+ }
+
+ public void doPut( URI url, HttpServletRequest request, HttpServletResponse response )
+ throws IOException, URISyntaxException {
+ response.sendError( HttpServletResponse.SC_METHOD_NOT_ALLOWED );
+ }
+
+ public void doDelete( URI url, HttpServletRequest request, HttpServletResponse response )
+ throws IOException, URISyntaxException {
+ response.sendError( HttpServletResponse.SC_METHOD_NOT_ALLOWED );
+ }
+
+ public void doOptions( URI url, HttpServletRequest request, HttpServletResponse response )
++ throws IOException, URISyntaxException {
++ response.sendError( HttpServletResponse.SC_METHOD_NOT_ALLOWED );
++ }
++
++ /**
++ * @sine 0.14.0
++ */
++ public void doHead( URI url, HttpServletRequest request, HttpServletResponse response )
+ throws IOException, URISyntaxException {
+ response.sendError( HttpServletResponse.SC_METHOD_NOT_ALLOWED );
+ }
+
+ public void copyRequestHeaderFields(HttpUriRequest outboundRequest,
+ HttpServletRequest inboundRequest) {
+ Enumeration<String> headerNames = inboundRequest.getHeaderNames();
+ while( headerNames.hasMoreElements() ) {
+ String name = headerNames.nextElement();
+ if ( !outboundRequest.containsHeader( name )
+ && !getOutboundRequestExcludeHeaders().contains( name ) ) {
+ String value = inboundRequest.getHeader( name );
+ outboundRequest.addHeader( name, value );
+ }
+ }
+ }
+
+ public Set<String> getOutboundRequestExcludeHeaders() {
+ return REQUEST_EXCLUDE_HEADERS;
+ }
+
+ protected void encodeUnwiseCharacters(StringBuffer str) {
+ int pipe = str.indexOf("|");
+ while (pipe > -1) {
+ str.replace(pipe, pipe+1, "%7C");
+ pipe = str.indexOf("|", pipe+1);
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/knox/blob/51348f83/gateway-spi/src/main/java/org/apache/knox/gateway/dispatch/DefaultDispatch.java
----------------------------------------------------------------------
diff --cc gateway-spi/src/main/java/org/apache/knox/gateway/dispatch/DefaultDispatch.java
index 94f64ee,0000000..aeb33b2
mode 100644,000000..100644
--- a/gateway-spi/src/main/java/org/apache/knox/gateway/dispatch/DefaultDispatch.java
+++ b/gateway-spi/src/main/java/org/apache/knox/gateway/dispatch/DefaultDispatch.java
@@@ -1,319 -1,0 +1,328 @@@
+/**
+ * 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.knox.gateway.dispatch;
+
+import org.apache.knox.gateway.SpiGatewayMessages;
+import org.apache.knox.gateway.SpiGatewayResources;
+import org.apache.knox.gateway.audit.api.Action;
+import org.apache.knox.gateway.audit.api.ActionOutcome;
+import org.apache.knox.gateway.audit.api.AuditServiceFactory;
+import org.apache.knox.gateway.audit.api.Auditor;
+import org.apache.knox.gateway.audit.api.ResourceType;
+import org.apache.knox.gateway.audit.log4j.audit.AuditConstants;
+import org.apache.knox.gateway.config.Configure;
+import org.apache.knox.gateway.config.Default;
+import org.apache.knox.gateway.config.GatewayConfig;
+import org.apache.knox.gateway.i18n.messages.MessagesFactory;
+import org.apache.knox.gateway.i18n.resources.ResourcesFactory;
+import org.apache.knox.gateway.util.MimeTypes;
+import org.apache.http.Header;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.methods.HttpDelete;
+import org.apache.http.client.methods.HttpGet;
++import org.apache.http.client.methods.HttpHead;
+import org.apache.http.client.methods.HttpOptions;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.client.methods.HttpPut;
+import org.apache.http.client.methods.HttpUriRequest;
+import org.apache.http.entity.ContentType;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ *
+ */
+public class DefaultDispatch extends AbstractGatewayDispatch {
+
+ protected static final String SET_COOKIE = "SET-COOKIE";
+ protected static final String WWW_AUTHENTICATE = "WWW-AUTHENTICATE";
+
+ protected static SpiGatewayMessages LOG = MessagesFactory.get(SpiGatewayMessages.class);
+ protected static SpiGatewayResources RES = ResourcesFactory.get(SpiGatewayResources.class);
+ protected static Auditor auditor = AuditServiceFactory.getAuditService().getAuditor(AuditConstants.DEFAULT_AUDITOR_NAME,
+ AuditConstants.KNOX_SERVICE_NAME, AuditConstants.KNOX_COMPONENT_NAME);
+
+ private Set<String> outboundResponseExcludeHeaders;
+
+ //Buffer size in bytes
+ private int replayBufferSize = -1;
+
+ @Override
+ public void init() {
+ super.init();
+ outboundResponseExcludeHeaders = new HashSet<>();
+ outboundResponseExcludeHeaders.add(SET_COOKIE);
+ outboundResponseExcludeHeaders.add(WWW_AUTHENTICATE);
+ }
+
+ @Override
+ public void destroy() {
+
+ }
+
+ protected int getReplayBufferSize() {
+ if (replayBufferSize > 0) {
+ return Math.abs(replayBufferSize/1024);
+ }
+ return replayBufferSize;
+ }
+
+ @Configure
+ protected void setReplayBufferSize(@Default("-1")int size) {
+ setReplayBufferSizeInBytes(size);
+ }
+
+ protected int getReplayBufferSizeInBytes() {
+ return replayBufferSize;
+ }
+
+ protected void setReplayBufferSizeInBytes(int size) {
+ if (size > 0) {
+ size *= 1024;
+ }
+ replayBufferSize = size;
+ }
+
+
+ protected void executeRequest(
+ HttpUriRequest outboundRequest,
+ HttpServletRequest inboundRequest,
+ HttpServletResponse outboundResponse)
+ throws IOException {
+ HttpResponse inboundResponse = executeOutboundRequest(outboundRequest);
+ writeOutboundResponse(outboundRequest, inboundRequest, outboundResponse, inboundResponse);
+ }
+
+ protected HttpResponse executeOutboundRequest( HttpUriRequest outboundRequest ) throws IOException {
+ LOG.dispatchRequest( outboundRequest.getMethod(), outboundRequest.getURI() );
+ HttpResponse inboundResponse;
+
+ try {
+ auditor.audit( Action.DISPATCH, outboundRequest.getURI().toString(), ResourceType.URI, ActionOutcome.UNAVAILABLE, RES.requestMethod( outboundRequest.getMethod() ) );
+ if( !"true".equals( System.getProperty( GatewayConfig.HADOOP_KERBEROS_SECURED ) ) ) {
+ // Hadoop cluster not Kerberos enabled
+ addCredentialsToRequest( outboundRequest );
+ }
+ inboundResponse = client.execute( outboundRequest );
+
+ int statusCode = inboundResponse.getStatusLine().getStatusCode();
+ if( statusCode != 201 ) {
+ LOG.dispatchResponseStatusCode( statusCode );
+ } else {
+ Header location = inboundResponse.getFirstHeader( "Location" );
+ if( location == null ) {
+ LOG.dispatchResponseStatusCode( statusCode );
+ } else {
+ LOG.dispatchResponseCreatedStatusCode( statusCode, location.getValue() );
+ }
+ }
+ auditor.audit( Action.DISPATCH, outboundRequest.getURI().toString(), ResourceType.URI, ActionOutcome.SUCCESS, RES.responseStatus( statusCode ) );
+ } catch( Exception e ) {
+ // We do not want to expose back end host. port end points to clients, see JIRA KNOX-58
+ auditor.audit( Action.DISPATCH, outboundRequest.getURI().toString(), ResourceType.URI, ActionOutcome.FAILURE );
+ LOG.dispatchServiceConnectionException( outboundRequest.getURI(), e );
+ throw new IOException( RES.dispatchConnectionError() );
+ }
+ return inboundResponse;
+ }
+
+ protected void writeOutboundResponse(HttpUriRequest outboundRequest, HttpServletRequest inboundRequest, HttpServletResponse outboundResponse, HttpResponse inboundResponse) throws IOException {
+ // Copy the client respond header to the server respond.
+ outboundResponse.setStatus(inboundResponse.getStatusLine().getStatusCode());
+ Header[] headers = inboundResponse.getAllHeaders();
+ Set<String> excludeHeaders = getOutboundResponseExcludeHeaders();
+ boolean hasExcludeHeaders = false;
+ if ((excludeHeaders != null) && !(excludeHeaders.isEmpty())) {
+ hasExcludeHeaders = true;
+ }
+ for ( Header header : headers ) {
+ String name = header.getName();
+ if (hasExcludeHeaders && excludeHeaders.contains(name.toUpperCase())) {
+ continue;
+ }
+ String value = header.getValue();
+ outboundResponse.addHeader(name, value);
+ }
+
+ HttpEntity entity = inboundResponse.getEntity();
+ if( entity != null ) {
+ outboundResponse.setContentType( getInboundResponseContentType( entity ) );
+ //KM[ If this is set here it ends up setting the content length to the content returned from the server.
+ // This length might not match if the the content is rewritten.
+ // long contentLength = entity.getContentLength();
+ // if( contentLength <= Integer.MAX_VALUE ) {
+ // outboundResponse.setContentLength( (int)contentLength );
+ // }
+ //]
+ InputStream stream = entity.getContent();
+ try {
+ writeResponse( inboundRequest, outboundResponse, stream );
+ } finally {
+ closeInboundResponse( inboundResponse, stream );
+ }
+ }
+ }
+
+ private String getInboundResponseContentType( final HttpEntity entity ) {
+ String fullContentType = null;
+ if( entity != null ) {
+ ContentType entityContentType = ContentType.get( entity );
+ if( entityContentType != null ) {
+ if( entityContentType.getCharset() == null ) {
+ final String entityMimeType = entityContentType.getMimeType();
+ final String defaultCharset = MimeTypes.getDefaultCharsetForMimeType( entityMimeType );
+ if( defaultCharset != null ) {
+ LOG.usingDefaultCharsetForEntity( entityMimeType, defaultCharset );
+ entityContentType = entityContentType.withCharset( defaultCharset );
+ }
+ } else {
+ LOG.usingExplicitCharsetForEntity( entityContentType.getMimeType(), entityContentType.getCharset() );
+ }
+ fullContentType = entityContentType.toString();
+ }
+ }
+ if( fullContentType == null ) {
+ LOG.unknownResponseEntityContentType();
+ } else {
+ LOG.inboundResponseEntityContentType( fullContentType );
+ }
+ return fullContentType;
+ }
+
+ protected void closeInboundResponse( HttpResponse response, InputStream stream ) throws IOException {
+ try {
+ stream.close();
+ } finally {
+ if( response instanceof Closeable ) {
+ ( (Closeable)response).close();
+ }
+ }
+ }
+
+ /**
+ * This method provides a hook for specialized credential propagation
+ * in subclasses.
+ *
+ * @param outboundRequest
+ */
+ protected void addCredentialsToRequest(HttpUriRequest outboundRequest) {
+ }
+
+
+ protected HttpEntity createRequestEntity(HttpServletRequest request)
+ throws IOException {
+
+ String contentType = request.getContentType();
+ int contentLength = request.getContentLength();
+ InputStream contentStream = request.getInputStream();
+
+ HttpEntity entity;
+ if (contentType == null) {
+ entity = new InputStreamEntity(contentStream, contentLength);
+ } else {
+ entity = new InputStreamEntity(contentStream, contentLength, ContentType.parse(contentType));
+ }
+ GatewayConfig config =
+ (GatewayConfig)request.getServletContext().getAttribute( GatewayConfig.GATEWAY_CONFIG_ATTRIBUTE );
+ if( config != null && config.isHadoopKerberosSecured() ) {
+ //Check if delegation token is supplied in the request
+ boolean delegationTokenPresent = false;
+ String queryString = request.getQueryString();
+ if (queryString != null) {
+ delegationTokenPresent = queryString.startsWith("delegation=") || queryString.contains("&delegation=");
+ }
+ if (replayBufferSize < 0) {
+ replayBufferSize = config.getHttpServerRequestBuffer();
+ }
+ if (!delegationTokenPresent && replayBufferSize > 0 ) {
+ entity = new PartiallyRepeatableHttpEntity(entity, replayBufferSize);
+ }
+ }
+
+ return entity;
+ }
+
+ @Override
+ public void doGet(URI url, HttpServletRequest request, HttpServletResponse response)
+ throws IOException, URISyntaxException {
+ HttpGet method = new HttpGet(url);
+ // https://issues.apache.org/jira/browse/KNOX-107 - Service URLs not rewritten for WebHDFS GET redirects
+ // This is now taken care of in DefaultHttpClientFactory.createHttpClient
+ // and setting params here causes configuration setup there to be ignored there.
+ // method.getParams().setBooleanParameter("http.protocol.handle-redirects", false);
+ copyRequestHeaderFields(method, request);
+ executeRequest(method, request, response);
+ }
+
+ @Override
+ public void doOptions(URI url, HttpServletRequest request, HttpServletResponse response)
+ throws IOException, URISyntaxException {
+ HttpOptions method = new HttpOptions(url);
+ executeRequest(method, request, response);
+ }
+
+ @Override
+ public void doPut(URI url, HttpServletRequest request, HttpServletResponse response)
+ throws IOException, URISyntaxException {
+ HttpPut method = new HttpPut(url);
+ HttpEntity entity = createRequestEntity(request);
+ method.setEntity(entity);
+ copyRequestHeaderFields(method, request);
+ executeRequest(method, request, response);
+ }
+
+ @Override
+ public void doPost(URI url, HttpServletRequest request, HttpServletResponse response)
+ throws IOException, URISyntaxException {
+ HttpPost method = new HttpPost(url);
+ HttpEntity entity = createRequestEntity(request);
+ method.setEntity(entity);
+ copyRequestHeaderFields(method, request);
+ executeRequest(method, request, response);
+ }
+
+ @Override
+ public void doDelete(URI url, HttpServletRequest request, HttpServletResponse response)
+ throws IOException, URISyntaxException {
+ HttpDelete method = new HttpDelete(url);
+ copyRequestHeaderFields(method, request);
+ executeRequest(method, request, response);
+ }
+
++ @Override
++ public void doHead(URI url, HttpServletRequest request, HttpServletResponse response)
++ throws IOException, URISyntaxException {
++ final HttpHead method = new HttpHead(url);
++ copyRequestHeaderFields(method, request);
++ executeRequest(method, request, response);
++ }
++
+ public Set<String> getOutboundResponseExcludeHeaders() {
+ return outboundResponseExcludeHeaders;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/knox/blob/51348f83/gateway-spi/src/main/java/org/apache/knox/gateway/dispatch/Dispatch.java
----------------------------------------------------------------------
diff --cc gateway-spi/src/main/java/org/apache/knox/gateway/dispatch/Dispatch.java
index ffd472f,0000000..30220b2
mode 100644,000000..100644
--- a/gateway-spi/src/main/java/org/apache/knox/gateway/dispatch/Dispatch.java
+++ b/gateway-spi/src/main/java/org/apache/knox/gateway/dispatch/Dispatch.java
@@@ -1,56 -1,0 +1,62 @@@
+/**
+ * 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.knox.gateway.dispatch;
+
+import org.apache.http.client.HttpClient;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+
+public interface Dispatch {
+
+ void init();
+
+ void destroy();
+
+ HttpClient getHttpClient();
+
+ void setHttpClient(HttpClient httpClient);
+
+ URI getDispatchUrl( HttpServletRequest request );
+
+ void doGet( URI url, HttpServletRequest request, HttpServletResponse response )
+ throws IOException, ServletException, URISyntaxException;
+
+ void doPost( URI url, HttpServletRequest request, HttpServletResponse response )
+ throws IOException, ServletException, URISyntaxException;
+
+ void doPut( URI url, HttpServletRequest request, HttpServletResponse response )
+ throws IOException, ServletException, URISyntaxException;
+
+ void doDelete( URI url, HttpServletRequest request, HttpServletResponse response )
+ throws IOException, ServletException, URISyntaxException;
+
+ void doOptions( URI url, HttpServletRequest request, HttpServletResponse response )
+ throws IOException, ServletException, URISyntaxException;
+
++ /**
++ * @since 0.14.0
++ */
++ void doHead( URI url, HttpServletRequest request, HttpServletResponse response )
++ throws IOException, ServletException, URISyntaxException;
++
+}
http://git-wip-us.apache.org/repos/asf/knox/blob/51348f83/gateway-spi/src/main/java/org/apache/knox/gateway/dispatch/GatewayDispatchFilter.java
----------------------------------------------------------------------
diff --cc gateway-spi/src/main/java/org/apache/knox/gateway/dispatch/GatewayDispatchFilter.java
index eef3718,0000000..7b887d1
mode 100644,000000..100644
--- a/gateway-spi/src/main/java/org/apache/knox/gateway/dispatch/GatewayDispatchFilter.java
+++ b/gateway-spi/src/main/java/org/apache/knox/gateway/dispatch/GatewayDispatchFilter.java
@@@ -1,169 -1,0 +1,177 @@@
+/**
+ * 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.knox.gateway.dispatch;
+
+import org.apache.knox.gateway.SpiGatewayMessages;
+import org.apache.knox.gateway.filter.AbstractGatewayFilter;
+import org.apache.knox.gateway.config.ConfigurationInjectorBuilder;
+import org.apache.knox.gateway.i18n.messages.MessagesFactory;
+import org.apache.http.client.HttpClient;
+import org.apache.http.impl.client.CloseableHttpClient;
+
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+public class GatewayDispatchFilter extends AbstractGatewayFilter {
+
+ private static Map<String, Adapter> METHOD_ADAPTERS = createMethodAdapters();
+
+ protected static SpiGatewayMessages LOG = MessagesFactory.get(SpiGatewayMessages.class);
+
+ private Dispatch dispatch;
+
+ private HttpClient httpClient;
+
+ private static Map<String, Adapter> createMethodAdapters() {
+ Map<String, Adapter> map = new HashMap<>();
+ map.put("GET", new GetAdapter());
+ map.put("POST", new PostAdapter());
+ map.put("PUT", new PutAdapter());
+ map.put("DELETE", new DeleteAdapter());
+ map.put("OPTIONS", new OptionsAdapter());
++ map.put("HEAD", new HeadAdapter());
+ return Collections.unmodifiableMap(map);
+ }
+
+ @Override
+ public void init(FilterConfig filterConfig) throws ServletException {
+ super.init(filterConfig);
+ if (dispatch == null) {
+ String dispatchImpl = filterConfig.getInitParameter("dispatch-impl");
+ dispatch = newInstanceFromName(dispatchImpl);
+ }
+ ConfigurationInjectorBuilder.configuration().target(dispatch).source(filterConfig).inject();
+ HttpClientFactory httpClientFactory;
+ String httpClientFactoryClass = filterConfig.getInitParameter("httpClientFactory");
+ if (httpClientFactoryClass != null) {
+ httpClientFactory = newInstanceFromName(httpClientFactoryClass);
+ } else {
+ httpClientFactory = new DefaultHttpClientFactory();
+ }
+ httpClient = httpClientFactory.createHttpClient(filterConfig);
+ dispatch.setHttpClient(httpClient);
+ dispatch.init();
+ }
+
+ @Override
+ public void destroy() {
+ dispatch.destroy();
+ try {
+ if (httpClient instanceof CloseableHttpClient) {
+ ((CloseableHttpClient) httpClient).close();
+ }
+ } catch ( IOException e ) {
+ LOG.errorClosingHttpClient(e);
+ }
+ }
+
+ public Dispatch getDispatch() {
+ return dispatch;
+ }
+
+ public void setDispatch(Dispatch dispatch) {
+ this.dispatch = dispatch;
+ }
+
+ @Override
+ protected void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
+ String method = request.getMethod().toUpperCase();
+ Adapter adapter = METHOD_ADAPTERS.get(method);
+ if ( adapter != null ) {
+ try {
+ adapter.doMethod(dispatch, request, response);
+ } catch ( URISyntaxException e ) {
+ throw new ServletException(e);
+ }
+ } else {
+ response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
+ }
+ }
+
+ private interface Adapter {
+ public void doMethod(Dispatch dispatch, HttpServletRequest request, HttpServletResponse response)
+ throws IOException, ServletException, URISyntaxException;
+ }
+
+ private static class GetAdapter implements Adapter {
+ public void doMethod(Dispatch dispatch, HttpServletRequest request, HttpServletResponse response)
+ throws IOException, ServletException, URISyntaxException {
+ dispatch.doGet( dispatch.getDispatchUrl(request), request, response);
+ }
+ }
+
+ private static class PostAdapter implements Adapter {
+ public void doMethod(Dispatch dispatch, HttpServletRequest request, HttpServletResponse response)
+ throws IOException, ServletException, URISyntaxException {
+ dispatch.doPost( dispatch.getDispatchUrl(request), request, response);
+ }
+ }
+
+ private static class PutAdapter implements Adapter {
+ public void doMethod(Dispatch dispatch, HttpServletRequest request, HttpServletResponse response)
+ throws IOException, ServletException, URISyntaxException {
+ dispatch.doPut( dispatch.getDispatchUrl(request), request, response);
+ }
+ }
+
+ private static class DeleteAdapter implements Adapter {
+ public void doMethod(Dispatch dispatch, HttpServletRequest request, HttpServletResponse response)
+ throws IOException, ServletException, URISyntaxException {
+ dispatch.doDelete( dispatch.getDispatchUrl(request), request, response);
+ }
+ }
+
+ private static class OptionsAdapter implements Adapter {
+ public void doMethod(Dispatch dispatch, HttpServletRequest request, HttpServletResponse response)
+ throws IOException, ServletException, URISyntaxException {
+ dispatch.doOptions( dispatch.getDispatchUrl(request), request, response);
+ }
+ }
+
++ private static class HeadAdapter implements Adapter {
++ public void doMethod(Dispatch dispatch, HttpServletRequest request, HttpServletResponse response)
++ throws IOException, ServletException, URISyntaxException {
++ dispatch.doHead( dispatch.getDispatchUrl(request), request, response);
++ }
++ }
++
+ private <T> T newInstanceFromName(String dispatchImpl) throws ServletException {
+ try {
+ Class<T> clazz = loadClass(dispatchImpl);
+ return clazz.newInstance();
+ } catch ( Exception e ) {
+ throw new ServletException(e);
+ }
+ }
+
+ private <T> Class<T> loadClass(String className) throws ClassNotFoundException {
+ ClassLoader loader = Thread.currentThread().getContextClassLoader();
+ if ( loader == null ) {
+ loader = this.getClass().getClassLoader();
+ }
+ return (Class<T>) loader.loadClass(className);
+ }
+}