HDDS-4856. Ruby S3 SDK never get authenticated by Ozone (#2013)

diff --git a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/EmptyContentTypeFilter.java b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/EmptyContentTypeFilter.java
new file mode 100644
index 0000000..d9518e6
--- /dev/null
+++ b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/EmptyContentTypeFilter.java
@@ -0,0 +1,131 @@
+/**
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.hadoop.ozone.s3;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletRequestWrapper;
+import java.io.IOException;
+import java.util.Enumeration;
+import java.util.NoSuchElementException;
+
+/**
+ * Filter to accept queries with empty string content-type (ruby sdk).
+ */
+public class EmptyContentTypeFilter implements Filter {
+
+  @Override
+  public void init(FilterConfig filterConfig) throws ServletException {
+
+  }
+
+  @Override
+  public void doFilter(
+      ServletRequest request, ServletResponse response, FilterChain chain
+  ) throws IOException, ServletException {
+    HttpServletRequest httpRequest = (HttpServletRequest) request;
+    if ("".equals(httpRequest.getContentType())) {
+      chain.doFilter(new HttpServletRequestWrapper(httpRequest) {
+        @Override
+        public String getContentType() {
+          return null;
+        }
+
+        @Override
+        public String getHeader(String name) {
+          if (name.equalsIgnoreCase("Content-Type")) {
+            return null;
+          }
+          return super.getHeader(name);
+        }
+
+        @Override
+        public Enumeration<String> getHeaders(String name) {
+          if ("Content-Type".equalsIgnoreCase(name)) {
+            return null;
+          }
+          return super.getHeaders(name);
+        }
+
+        @Override
+        public Enumeration<String> getHeaderNames() {
+          return new EnumerationWrapper(super.getHeaderNames());
+        }
+      }, response);
+    } else {
+      chain.doFilter(request, response);
+    }
+
+  }
+
+  @Override
+  public void destroy() {
+
+  }
+
+  /**
+   * Enumeration Wrapper which removes Content-Type from the original
+   * enumeration.
+   */
+  public static class EnumerationWrapper implements Enumeration<String> {
+
+    private final Enumeration<String> original;
+
+    private String nextElement;
+
+    public EnumerationWrapper(Enumeration<String> original) {
+      this.original = original;
+      step();
+    }
+
+    private void step() {
+      if (original.hasMoreElements()) {
+        nextElement = original.nextElement();
+      } else {
+        nextElement = null;
+      }
+      if ("Content-Type".equalsIgnoreCase(nextElement)) {
+        if (original.hasMoreElements()) {
+          nextElement = original.nextElement();
+        } else {
+          nextElement = null;
+        }
+      }
+    }
+
+    @Override
+    public boolean hasMoreElements() {
+      return nextElement != null;
+    }
+
+    @Override
+    public String nextElement() {
+      if (nextElement == null) {
+        throw new NoSuchElementException();
+      }
+      String returnValue = nextElement;
+      step();
+      return returnValue;
+    }
+  }
+}
diff --git a/hadoop-ozone/s3gateway/src/main/resources/webapps/s3gateway/WEB-INF/web.xml b/hadoop-ozone/s3gateway/src/main/resources/webapps/s3gateway/WEB-INF/web.xml
index d4e8d02..79bf7b9 100644
--- a/hadoop-ozone/s3gateway/src/main/resources/webapps/s3gateway/WEB-INF/web.xml
+++ b/hadoop-ozone/s3gateway/src/main/resources/webapps/s3gateway/WEB-INF/web.xml
@@ -28,11 +28,20 @@
     <url-pattern>/*</url-pattern>
   </servlet-mapping>
   <filter>
+    <filter-name>optional-content-type</filter-name>
+    <filter-class>org.apache.hadoop.ozone.s3.EmptyContentTypeFilter
+    </filter-class>
+  </filter>
+  <filter>
     <filter-name>info-page-redirect</filter-name>
     <filter-class>org.apache.hadoop.ozone.s3.RootPageDisplayFilter
     </filter-class>
   </filter>
   <filter-mapping>
+    <filter-name>optional-content-type</filter-name>
+    <url-pattern>/*</url-pattern>
+  </filter-mapping>
+  <filter-mapping>
     <filter-name>info-page-redirect</filter-name>
     <url-pattern>/*</url-pattern>
   </filter-mapping>
diff --git a/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/TestEmptyContentTypeFilter.java b/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/TestEmptyContentTypeFilter.java
new file mode 100644
index 0000000..7bac23c
--- /dev/null
+++ b/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/TestEmptyContentTypeFilter.java
@@ -0,0 +1,58 @@
+/**
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.hadoop.ozone.s3;
+
+import java.util.Vector;
+
+import org.apache.hadoop.ozone.s3.EmptyContentTypeFilter.EnumerationWrapper;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class TestEmptyContentTypeFilter {
+
+  @Test
+  public void enumerationWithContentType() {
+    Vector<String> values = new Vector<>();
+    values.add("Content-Type");
+    values.add("1");
+    values.add("2");
+    values.add("Content-Type");
+
+    final EnumerationWrapper enumerationWrapper =
+        new EnumerationWrapper(values.elements());
+
+    Assert.assertTrue(enumerationWrapper.hasMoreElements());
+    Assert.assertEquals("1", enumerationWrapper.nextElement());
+    Assert.assertTrue(enumerationWrapper.hasMoreElements());
+    Assert.assertEquals("2", enumerationWrapper.nextElement());
+    Assert.assertFalse(enumerationWrapper.hasMoreElements());
+  }
+
+  @Test
+  public void enumerationWithOneContentType() {
+    Vector<String> values = new Vector<>();
+    values.add("Content-Type");
+
+    final EnumerationWrapper enumerationWrapper =
+        new EnumerationWrapper(values.elements());
+
+    Assert.assertFalse(enumerationWrapper.hasMoreElements());
+  }
+
+}
\ No newline at end of file