kfaraz commented on code in PR #18424:
URL: https://github.com/apache/druid/pull/18424#discussion_r2357937876


##########
server/src/main/java/org/apache/druid/initialization/ServerInjectorBuilder.java:
##########
@@ -73,6 +73,22 @@ public static Injector makeServerInjector(
         .build();
   }
 
+  /**
+   * Needed for Hadoop indexing that needs server-like Injector but can't run 
jetty 12
+   */
+  @VisibleForTesting

Review Comment:
   I think this method is used by `HadoopDruidIndexerConfig` too. If that is 
the case, let's remove this annotation.



##########
server/src/main/java/org/apache/druid/initialization/ServerInjectorBuilder.java:
##########
@@ -91,6 +107,33 @@ public ServerInjectorBuilder serviceModules(final 
Iterable<? extends Module> mod
   }
 
   public Injector build()
+  {
+    return this.build(true);
+  }
+
+  /**
+   * Needed for Hadoop indexing that needs server-like Injector but can't run 
jetty 12
+   */
+  public Injector buildWithoutJettyModules()
+  {
+    return this.build(false);
+  }
+
+  public static Module registerNodeRoleModule(Set<NodeRole> nodeRoles)

Review Comment:
   It seems that this method is unchanged. Can we move it back to its original 
position so that we can avoid it from showing up in the diff?



##########
integration-tests/stop_cluster.sh:
##########
@@ -14,6 +14,15 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+# Determine if docker compose is available. If not, assume Docker supports

Review Comment:
   Does this change need to be included in this PR? Or can it be done 
separately too?



##########
server/src/test/java/org/apache/druid/initialization/ServerConfigTest.java:
##########
@@ -79,6 +81,7 @@ public void testSerde() throws Exception
     Assert.assertEquals("my-cool-policy", 
modifiedConfig.getContentSecurityPolicy());
     Assert.assertEquals("my-cool-policy", 
modifiedConfig2.getContentSecurityPolicy());
     Assert.assertTrue(modifiedConfig2.isEnableHSTS());
+    Assert.assertEquals(UriCompliance.RFC3986.getName(), 
modifiedConfig2.getUriCompliance());

Review Comment:
   Please update one of the tests to verify the default value of this config 
too.



##########
pom.xml:
##########
@@ -754,26 +754,44 @@
                 <groupId>org.eclipse.jetty</groupId>
                 <artifactId>jetty-server</artifactId>
                 <version>${jetty.version}</version>
+                <exclusions>
+                    <exclusion>
+                        <groupId>org.eclipse.jetty.toolchain</groupId>
+                        <artifactId>jetty-servlet-api</artifactId>
+                    </exclusion>
+                </exclusions>
             </dependency>
             <dependency>
-                <groupId>org.eclipse.jetty</groupId>
-                <artifactId>jetty-servlet</artifactId>
+                <groupId>org.eclipse.jetty.ee8</groupId>
+                <artifactId>jetty-ee8-servlet</artifactId>
                 <version>${jetty.version}</version>
+              <exclusions>

Review Comment:
   Nit: indentation seems off here.



##########
server/src/main/java/org/apache/druid/server/initialization/jetty/StandardResponseHeaderFilterHolder.java:
##########
@@ -66,16 +66,17 @@ public StandardResponseHeaderFilterHolder(final 
ServerConfig serverConfig)
    * Remove any standard headers in proxyResponse if they were also set in the 
origin response, serverResponse.
    * This prevents duplicates headers from appearing in proxy responses.
    *
-   * Used by implementations of {@link 
org.eclipse.jetty.proxy.AsyncProxyServlet}.
+   * Used by implementations of {@link 
org.eclipse.jetty.ee8.proxy.AsyncProxyServlet}.
    */
   public static void deduplicateHeadersInProxyServlet(
       final HttpServletResponse proxyResponse,
       final Response serverResponse
   )
   {
     for (final String headerName : 
StandardResponseHeaderFilterHolder.STANDARD_HEADERS) {
-      if (serverResponse.getHeaders().containsKey(headerName) && 
proxyResponse.containsHeader(headerName)) {
-        ((org.eclipse.jetty.server.Response) 
proxyResponse).getHttpFields().remove(headerName);
+      if (serverResponse.getHeaders().contains(headerName) && 
proxyResponse.containsHeader(headerName)) {
+        // In Jetty 12 EE8, use the standard servlet API method to remove 
headers

Review Comment:
   By "standard servlet API method", are you referring to 
`proxyResponse.setHeader()` or some other method that we would like to use but 
are not using here?



##########
server/src/test/java/org/apache/druid/server/initialization/jetty/StandardResponseHeaderFilterHolderTest.java:
##########
@@ -130,6 +139,40 @@ public void test_get_invalidContentSecurityPolicy()
     );
   }
 
+  @Test
+  public void test_deduplicateHeadersInProxyServlet_duplicatesExist()
+  {
+    
EasyMock.expect(proxyResponse.containsHeader("Cache-Control")).andReturn(true).once();
+    proxyResponse.setHeader("Cache-Control", null);
+    EasyMock.expectLastCall().once();
+    
EasyMock.expect(proxyResponse.containsHeader("Strict-Transport-Security")).andReturn(false).once();
+
+    EasyMock.expect(clientResponse.getHeaders()).andReturn(HttpFields.from(new 
HttpField("Cache-Control", "true"), new HttpField("Strict-Transport-Security", 
"true"))).times(3);

Review Comment:
   Please break this up into multiple lines in any way you see fit.



##########
server/src/test/java/org/apache/druid/server/initialization/jetty/StandardResponseHeaderFilterHolderTest.java:
##########
@@ -130,6 +139,40 @@ public void test_get_invalidContentSecurityPolicy()
     );
   }
 
+  @Test
+  public void test_deduplicateHeadersInProxyServlet_duplicatesExist()
+  {
+    
EasyMock.expect(proxyResponse.containsHeader("Cache-Control")).andReturn(true).once();
+    proxyResponse.setHeader("Cache-Control", null);
+    EasyMock.expectLastCall().once();
+    
EasyMock.expect(proxyResponse.containsHeader("Strict-Transport-Security")).andReturn(false).once();
+
+    EasyMock.expect(clientResponse.getHeaders()).andReturn(HttpFields.from(new 
HttpField("Cache-Control", "true"), new HttpField("Strict-Transport-Security", 
"true"))).times(3);
+
+

Review Comment:
   Nit: extra newline



##########
sql/src/main/java/org/apache/druid/sql/avatica/DruidAvaticaProtobufHandler.java:
##########
@@ -19,46 +19,100 @@
 
 package org.apache.druid.sql.avatica;
 
-import com.google.inject.Inject;
-import org.apache.calcite.avatica.remote.LocalService;
+import org.apache.calcite.avatica.AvaticaUtils;
+import org.apache.calcite.avatica.metrics.Timer;
+import org.apache.calcite.avatica.remote.ProtobufHandler;
+import org.apache.calcite.avatica.remote.ProtobufTranslation;
+import org.apache.calcite.avatica.remote.ProtobufTranslationImpl;
 import org.apache.calcite.avatica.remote.Service;
 import org.apache.calcite.avatica.server.AvaticaProtobufHandler;
+import org.apache.calcite.avatica.util.UnsynchronizedBuffer;
 import org.apache.druid.guice.annotations.Self;
 import org.apache.druid.java.util.common.StringUtils;
+import org.apache.druid.java.util.common.logger.Logger;
 import org.apache.druid.server.DruidNode;
+import org.eclipse.jetty.io.Content;
 import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.Response;
+import org.eclipse.jetty.util.Callback;
 
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import java.io.IOException;
+import javax.inject.Inject;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
 
-public class DruidAvaticaProtobufHandler extends AvaticaProtobufHandler
+public class DruidAvaticaProtobufHandler extends DruidAvaticaHandler
 {
+
+  private static final Logger LOG = new 
Logger(DruidAvaticaProtobufHandler.class);
+
   public static final String AVATICA_PATH_NO_TRAILING_SLASH = 
"/druid/v2/sql/avatica-protobuf";
   public static final String AVATICA_PATH = AVATICA_PATH_NO_TRAILING_SLASH + 
"/";
 
+  private final ProtobufHandler pbHandler;

Review Comment:
   ```suggestion
     private final ProtobufHandler protobufHandler;
   ```



##########
sql/src/test/java/org/apache/druid/sql/avatica/DruidAvaticaProtobufHandlerTest.java:
##########
@@ -35,12 +44,49 @@ protected String getJdbcUrlTail()
   }
 
   @Override
-  protected AbstractAvaticaHandler getAvaticaHandler(final DruidMeta druidMeta)
+  protected Handler.Abstract getAvaticaHandler(final DruidMeta druidMeta)

Review Comment:
   ```suggestion
     protected DruidAvaticaHandler getAvaticaHandler(final DruidMeta druidMeta)
   ```



##########
server/src/test/java/org/apache/druid/server/mocks/MockHttpServletResponse.java:
##########
@@ -337,4 +345,17 @@ public Locale getLocale()
   {
     throw new UnsupportedOperationException();
   }
+
+

Review Comment:
   Nit: extra newline.



##########
server/src/test/java/org/apache/druid/server/initialization/jetty/StandardResponseHeaderFilterHolderTest.java:
##########
@@ -130,6 +139,40 @@ public void test_get_invalidContentSecurityPolicy()
     );
   }
 
+  @Test
+  public void test_deduplicateHeadersInProxyServlet_duplicatesExist()

Review Comment:
   ```suggestion
     public void test_deduplicateHeadersInProxyServlet_withDuplicates()
   ```



##########
indexing-hadoop/src/main/java/org/apache/druid/indexer/JobHelper.java:
##########
@@ -155,7 +155,8 @@ public static void setupClasspath(
     for (String jarFilePath : jarFiles) {
 
       final File jarFile = new File(jarFilePath);
-      if (jarFile.getName().endsWith(".jar")) {
+      // Keep Druid jetty jars out of the classpath. They are not runnable in 
the < 17 hadoop java runtime
+      if (jarFile.getName().endsWith(".jar") && 
!jarFile.getName().contains("jetty")) {

Review Comment:
   Could we use `jetty-ee8-*` instead?



##########
server/src/main/java/org/apache/druid/server/initialization/ServerConfig.java:
##########
@@ -185,6 +188,9 @@ public ServerConfig(boolean enableQueryRequestsQueuing)
   @JsonProperty
   private boolean enableHSTS = false;
 
+  @JsonProperty
+  private String uriCompliance = UriCompliance.LEGACY.getName();

Review Comment:
   Would it make sense to have this be of type `UriCompliance` rather than 
`String`?



##########
sql/src/main/java/org/apache/druid/sql/avatica/DruidAvaticaHandler.java:
##########
@@ -0,0 +1,63 @@
+/*
+ * 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.druid.sql.avatica;
+
+import org.apache.calcite.avatica.metrics.MetricsSystem;
+import org.apache.calcite.avatica.metrics.Timer;
+import org.apache.calcite.avatica.remote.LocalService;
+import org.apache.calcite.avatica.remote.MetricsHelper;
+import org.apache.calcite.avatica.remote.Service;
+import org.apache.calcite.avatica.server.MetricsAwareAvaticaHandler;
+import org.apache.calcite.avatica.util.UnsynchronizedBuffer;
+import org.eclipse.jetty.server.Handler;
+
+public abstract class DruidAvaticaHandler extends Handler.Abstract implements 
MetricsAwareAvaticaHandler

Review Comment:
   Please add a short javadoc, if possible.



##########
server/src/main/java/org/apache/druid/server/initialization/jetty/JettyServerModule.java:
##########
@@ -467,25 +468,21 @@ public void stop()
       server.setErrorHandler(new ErrorHandler()
       {
         @Override
-        public boolean isShowServlet()
-        {
-          return false;
-        }
-
-        @Override
-        public void handle(
-            String target,
+        public boolean handle(
             Request baseRequest,
-            HttpServletRequest request,
-            HttpServletResponse response
-        ) throws IOException, ServletException
+            Response response,
+            Callback callback
+        ) throws Exception
         {
-          request.setAttribute(RequestDispatcher.ERROR_EXCEPTION, null);
-          super.handle(target, baseRequest, request, response);
+          baseRequest.setAttribute(RequestDispatcher.ERROR_EXCEPTION, null);
+          return super.handle(baseRequest, response, callback);
         }
       });
     }
 
+    log.info("Configuraing Jetty Request log for server");

Review Comment:
   Is this needed?



##########
server/src/main/java/org/apache/druid/server/initialization/ServerConfig.java:
##########
@@ -306,6 +312,11 @@ public boolean isEnableQueryRequestsQueuing()
     return enableQueryRequestsQueuing;
   }
 
+  public String getUriCompliance()

Review Comment:
   This should probably return a `UriCompliance` object rather than a String.



##########
sql/src/main/java/org/apache/druid/sql/avatica/DruidAvaticaProtobufHandler.java:
##########
@@ -19,46 +19,100 @@
 
 package org.apache.druid.sql.avatica;
 
-import com.google.inject.Inject;
-import org.apache.calcite.avatica.remote.LocalService;
+import org.apache.calcite.avatica.AvaticaUtils;
+import org.apache.calcite.avatica.metrics.Timer;
+import org.apache.calcite.avatica.remote.ProtobufHandler;
+import org.apache.calcite.avatica.remote.ProtobufTranslation;
+import org.apache.calcite.avatica.remote.ProtobufTranslationImpl;
 import org.apache.calcite.avatica.remote.Service;
 import org.apache.calcite.avatica.server.AvaticaProtobufHandler;
+import org.apache.calcite.avatica.util.UnsynchronizedBuffer;
 import org.apache.druid.guice.annotations.Self;
 import org.apache.druid.java.util.common.StringUtils;
+import org.apache.druid.java.util.common.logger.Logger;
 import org.apache.druid.server.DruidNode;
+import org.eclipse.jetty.io.Content;
 import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.Response;
+import org.eclipse.jetty.util.Callback;
 
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import java.io.IOException;
+import javax.inject.Inject;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
 
-public class DruidAvaticaProtobufHandler extends AvaticaProtobufHandler
+public class DruidAvaticaProtobufHandler extends DruidAvaticaHandler
 {
+
+  private static final Logger LOG = new 
Logger(DruidAvaticaProtobufHandler.class);
+
   public static final String AVATICA_PATH_NO_TRAILING_SLASH = 
"/druid/v2/sql/avatica-protobuf";
   public static final String AVATICA_PATH = AVATICA_PATH_NO_TRAILING_SLASH + 
"/";
 
+  private final ProtobufHandler pbHandler;
+
   @Inject
   public DruidAvaticaProtobufHandler(
       final DruidMeta druidMeta,
       @Self final DruidNode druidNode,
-      final AvaticaMonitor avaticaMonitor
+      final AvaticaMonitor metrics
   )
   {
-    super(new LocalService(druidMeta), avaticaMonitor);
+    super(druidMeta, metrics, AvaticaProtobufHandler.class);
+    ProtobufTranslation protobufTranslation = new ProtobufTranslationImpl();
+    this.pbHandler = new ProtobufHandler(service, protobufTranslation, 
this.metrics);
     setServerRpcMetadata(new 
Service.RpcMetadataResponse(druidNode.getHostAndPortToUse()));
   }
 
   @Override
-  public void handle(
-      final String target,
-      final Request baseRequest,
-      final HttpServletRequest request,
-      final HttpServletResponse response
-  ) throws IOException, ServletException
+  public boolean handle(Request request, Response response, Callback callback) 
throws Exception
+  {
+    String requestURI = request.getHttpURI().getPath();
+    if 
(AVATICA_PATH_NO_TRAILING_SLASH.equals(StringUtils.maybeRemoveTrailingSlash(requestURI)))
 {
+      try (Timer.Context ctx = this.requestTimer.start()) {
+        if (!"POST".equals(request.getMethod())) {
+          response.setStatus(405);
+          response.write(
+              true,
+              ByteBuffer.wrap("This server expects only POST 
calls.".getBytes(StandardCharsets.UTF_8)), callback
+          );
+          return true;
+        }
+        final byte[] requestBytes;
+        // Avoid a new buffer creation for every HTTP request
+        final UnsynchronizedBuffer buffer = threadLocalBuffer.get();
+        try (InputStream inputStream = Content.Source.asInputStream(request)) {
+          requestBytes = AvaticaUtils.readFullyToBytes(inputStream, buffer);
+        }
+        finally {
+          buffer.reset();
+        }
+
+        response.getHeaders().put("Content-Type", 
"application/octet-stream;charset=utf-8");
+
+        org.apache.calcite.avatica.remote.Handler.HandlerResponse<byte[]> 
handlerResponse;
+        try {
+          handlerResponse = pbHandler.apply(requestBytes);
+        }
+        catch (Exception e) {
+          LOG.debug(e, "Error invoking request");
+          handlerResponse = pbHandler.convertToErrorResponse(e);
+        }
+
+        response.setStatus(handlerResponse.getStatusCode());
+        response.write(true, ByteBuffer.wrap(handlerResponse.getResponse()), 
callback);
+        return true;

Review Comment:
   I wonder if parts of this logic can also live in the super class 
`DruidAvaticaHandler`. Not a blocker for this PR though.



##########
server/src/test/java/org/apache/druid/server/AsyncManagementForwardingServletTest.java:
##########
@@ -534,12 +532,11 @@ public String getCurrentLeader()
       AuthenticationUtils.addAuthenticationFilterChain(root, 
ImmutableList.of(new AllowAllAuthenticator()));
       JettyServerInitUtils.addExtensionFilters(root, injector);
 
-      final HandlerList handlerList = new HandlerList();
-      handlerList.setHandlers(
-          new Handler[]{JettyServerInitUtils.wrapWithDefaultGzipHandler(
+      final Handler.Sequence handlerList = new Handler.Sequence(
+          JettyServerInitUtils.wrapWithDefaultGzipHandler(
               root,
               ServerConfig.DEFAULT_GZIP_INFLATE_BUFFER_SIZE,
-              Deflater.DEFAULT_COMPRESSION)}
+              Deflater.DEFAULT_COMPRESSION)

Review Comment:
   ```suggestion
                 Deflater.DEFAULT_COMPRESSION
             )
   ```



##########
server/src/test/java/org/apache/druid/server/mocks/MockHttpServletResponse.java:
##########
@@ -145,9 +149,13 @@ public void addDateHeader(String name, long date)
     throw new UnsupportedOperationException();
   }
 
+  /**
+   * HttpServletResponse 4.0.1 spec dictates that setHeader  overwrites 
existing values.

Review Comment:
   Nit: Since this must already be specified in the super javadoc, we can just 
make this a regular (non-javadoc) comment inside the method body.



##########
server/src/test/java/org/apache/druid/server/initialization/jetty/StandardResponseHeaderFilterHolderTest.java:
##########
@@ -130,6 +139,40 @@ public void test_get_invalidContentSecurityPolicy()
     );
   }
 
+  @Test
+  public void test_deduplicateHeadersInProxyServlet_duplicatesExist()
+  {
+    
EasyMock.expect(proxyResponse.containsHeader("Cache-Control")).andReturn(true).once();
+    proxyResponse.setHeader("Cache-Control", null);
+    EasyMock.expectLastCall().once();
+    
EasyMock.expect(proxyResponse.containsHeader("Strict-Transport-Security")).andReturn(false).once();
+
+    EasyMock.expect(clientResponse.getHeaders()).andReturn(HttpFields.from(new 
HttpField("Cache-Control", "true"), new HttpField("Strict-Transport-Security", 
"true"))).times(3);
+
+
+    replayAllMocks();
+
+    
StandardResponseHeaderFilterHolder.deduplicateHeadersInProxyServlet(proxyResponse,
 clientResponse);
+  }
+
+  @Test
+  public void test_duplicateHeadersInProxyServlet_noDuplicates()

Review Comment:
   ```suggestion
     public void test_deduplicateHeadersInProxyServlet_withNoDuplicates()
   ```



##########
sql/src/test/java/org/apache/druid/sql/avatica/DruidAvaticaHandlerTest.java:
##########
@@ -255,7 +255,7 @@ protected String getJdbcUrlTail()
   }
 
   // Default implementation is for JSON to allow debugging of tests.
-  protected AbstractAvaticaHandler getAvaticaHandler(final DruidMeta druidMeta)
+  protected Handler.Abstract getAvaticaHandler(final DruidMeta druidMeta)

Review Comment:
   ```suggestion
     protected DruidAvaticaHandler getAvaticaHandler(final DruidMeta druidMeta)
   ```



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to