You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by ro...@apache.org on 2023/04/25 10:30:32 UTC

[cloudstack] branch 4.18 updated: ssvm: fix post request header case mismatch (#7445)

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

rohit pushed a commit to branch 4.18
in repository https://gitbox.apache.org/repos/asf/cloudstack.git


The following commit(s) were added to refs/heads/4.18 by this push:
     new e166f96094c ssvm: fix post request header case mismatch (#7445)
e166f96094c is described below

commit e166f96094c3619c8cfb7c47f61fa600794340a6
Author: Abhishek Kumar <ab...@gmail.com>
AuthorDate: Tue Apr 25 16:00:21 2023 +0530

    ssvm: fix post request header case mismatch (#7445)
    
    Fixes #7442
    
    With d74f64a2e16 key for the host header was changed to lowercase which is causing host be null while processing upload in SSVM
    
    Signed-off-by: Abhishek Kumar <ab...@gmail.com>
    Co-authored-by: João Jandre <48...@users.noreply.github.com>
---
 .../storage/resource/HttpUploadServerHandler.java  | 85 +++++++++++-----------
 .../resource/HttpUploadServerHandlerTest.java      | 74 +++++++++++++++++++
 2 files changed, 118 insertions(+), 41 deletions(-)

diff --git a/services/secondary-storage/server/src/main/java/org/apache/cloudstack/storage/resource/HttpUploadServerHandler.java b/services/secondary-storage/server/src/main/java/org/apache/cloudstack/storage/resource/HttpUploadServerHandler.java
index 8e3e4908dce..aec1560f411 100644
--- a/services/secondary-storage/server/src/main/java/org/apache/cloudstack/storage/resource/HttpUploadServerHandler.java
+++ b/services/secondary-storage/server/src/main/java/org/apache/cloudstack/storage/resource/HttpUploadServerHandler.java
@@ -18,16 +18,15 @@ package org.apache.cloudstack.storage.resource;
 
 import static io.netty.buffer.Unpooled.copiedBuffer;
 import static io.netty.handler.codec.http.HttpHeaders.Names.CONNECTION;
-import static io.netty.handler.codec.http.HttpHeaders.Names.CONTENT_LENGTH;
 import static io.netty.handler.codec.http.HttpHeaders.Names.CONTENT_TYPE;
 
 import java.io.IOException;
 import java.net.URI;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 
-import io.netty.handler.codec.DecoderException;
 import org.apache.cloudstack.storage.template.UploadEntity;
 import org.apache.cloudstack.utils.imagestore.ImageStoreUtil;
 import org.apache.commons.lang3.StringUtils;
@@ -41,6 +40,7 @@ import io.netty.channel.ChannelFuture;
 import io.netty.channel.ChannelFutureListener;
 import io.netty.channel.ChannelHandlerContext;
 import io.netty.channel.SimpleChannelInboundHandler;
+import io.netty.handler.codec.DecoderException;
 import io.netty.handler.codec.http.DefaultFullHttpResponse;
 import io.netty.handler.codec.http.FullHttpResponse;
 import io.netty.handler.codec.http.HttpContent;
@@ -79,16 +79,46 @@ public class HttpUploadServerHandler extends SimpleChannelInboundHandler<HttpObj
 
     private boolean requestProcessed = false;
 
-    private static final String HEADER_SIGNATURE = "x-signature";
-
-    private static final String HEADER_METADATA = "x-metadata";
+    enum UploadHeader {
+        SIGNATURE("x-signature"),
+        METADATA("x-metadata"),
+        EXPIRES("x-expires"),
+        HOST("x-forwarded-host"),
+        CONTENT_LENGTH("content-length");
 
-    private static final String HEADER_EXPIRES = "x-expires";
-
-    private static final String HEADER_HOST = "x-forwarded-host";
+        private final String name;
+        UploadHeader(String name) {
+            this.name = name;
+        }
+        public String getName() {
+            return this.name;
+        }
+        public static UploadHeader fromName(String name) {
+            for (UploadHeader type : values()) {
+                if (type.getName().equalsIgnoreCase(name)) {
+                    return type;
+                }
+            }
+            return null;
+        }
+    }
 
     private static long processTimeout;
 
+    protected Map<UploadHeader, String> getUploadRequestUsefulHeaders(HttpHeaders headers) {
+        Map<UploadHeader, String> headerMap = new HashMap<>();
+        for (Entry<String, String> entry : headers) {
+            UploadHeader headerType = UploadHeader.fromName(entry.getKey());
+            if (headerType != null) {
+                headerMap.put(headerType, entry.getValue());
+            }
+        }
+        for (UploadHeader type : UploadHeader.values()) {
+            logger.info(String.format("HEADER: %s=%s", type, headerMap.get(type)));
+        }
+        return headerMap;
+    }
+
     public HttpUploadServerHandler(NfsSecondaryStorageResource storageResource) {
         this.storageResource = storageResource;
     }
@@ -123,36 +153,8 @@ public class HttpUploadServerHandler extends SimpleChannelInboundHandler<HttpObj
 
                 URI uri = new URI(request.getUri());
 
-                String signature = null;
-                String expires = null;
-                String metadata = null;
-                String hostname = null;
-                long contentLength = 0;
-
-                for (Entry<String, String> entry : request.headers()) {
-                    switch (entry.getKey()) {
-                        case HEADER_SIGNATURE:
-                            signature = entry.getValue();
-                            break;
-                        case HEADER_METADATA:
-                            metadata = entry.getValue();
-                            break;
-                        case HEADER_EXPIRES:
-                            expires = entry.getValue();
-                            break;
-                        case HEADER_HOST:
-                            hostname = entry.getValue();
-                            break;
-                        case HttpHeaders.Names.CONTENT_LENGTH:
-                            contentLength = Long.parseLong(entry.getValue());
-                            break;
-                    }
-                }
-                logger.info("HEADER: signature=" + signature);
-                logger.info("HEADER: metadata=" + metadata);
-                logger.info("HEADER: expires=" + expires);
-                logger.info("HEADER: hostname=" + hostname);
-                logger.info("HEADER: content-length=" + contentLength);
+                Map<UploadHeader, String> headers = getUploadRequestUsefulHeaders(request.headers());
+                long contentLength = headers.get(UploadHeader.CONTENT_LENGTH) != null ? Long.parseLong(headers.get(UploadHeader.CONTENT_LENGTH)) : 0;
                 QueryStringDecoder decoderQuery = new QueryStringDecoder(uri);
                 Map<String, List<String>> uriAttributes = decoderQuery.parameters();
                 uuid = uriAttributes.get("uuid").get(0);
@@ -160,9 +162,10 @@ public class HttpUploadServerHandler extends SimpleChannelInboundHandler<HttpObj
                 UploadEntity uploadEntity = null;
                 try {
                     // Validate the request here
-                    storageResource.validatePostUploadRequest(signature, metadata, expires, hostname, contentLength, uuid);
+                    storageResource.validatePostUploadRequest(headers.get(UploadHeader.SIGNATURE), headers.get(UploadHeader.METADATA),
+                            headers.get(UploadHeader.EXPIRES), headers.get(UploadHeader.HOST), contentLength, uuid);
                     //create an upload entity. This will fail if entity already exists.
-                    uploadEntity = storageResource.createUploadEntity(uuid, metadata, contentLength);
+                    uploadEntity = storageResource.createUploadEntity(uuid, headers.get(UploadHeader.METADATA), contentLength);
                 } catch (InvalidParameterValueException ex) {
                     logger.error("post request validation failed", ex);
                     responseContent.append(ex.getMessage());
@@ -280,7 +283,7 @@ public class HttpUploadServerHandler extends SimpleChannelInboundHandler<HttpObj
         response.headers().set(CONTENT_TYPE, "text/plain; charset=UTF-8");
         if (!close) {
             // There's no need to add 'content-length' header if this is the last response.
-            response.headers().set(CONTENT_LENGTH, buf.readableBytes());
+            response.headers().set(HttpHeaders.Names.CONTENT_LENGTH, buf.readableBytes());
         }
         // Write the response.
         ChannelFuture future = channel.writeAndFlush(response);
diff --git a/services/secondary-storage/server/src/test/java/org/apache/cloudstack/storage/resource/HttpUploadServerHandlerTest.java b/services/secondary-storage/server/src/test/java/org/apache/cloudstack/storage/resource/HttpUploadServerHandlerTest.java
new file mode 100644
index 00000000000..4f1ae39842e
--- /dev/null
+++ b/services/secondary-storage/server/src/test/java/org/apache/cloudstack/storage/resource/HttpUploadServerHandlerTest.java
@@ -0,0 +1,74 @@
+// 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.cloudstack.storage.resource;
+
+import java.util.Map;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mockito;
+import org.mockito.Spy;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import io.netty.handler.codec.http.DefaultHttpHeaders;
+import io.netty.handler.codec.http.HttpHeaders;
+
+@RunWith(MockitoJUnitRunner.class)
+public class HttpUploadServerHandlerTest {
+
+    @Spy
+    @InjectMocks
+    HttpUploadServerHandler httpUploadServerHandler = new HttpUploadServerHandler(Mockito.mock(NfsSecondaryStorageResource.class));
+
+    private void runGetUploadRequestUsefulHeadersTestForHost(String hostHeaderKey, String hostHeaderValue) {
+        HttpHeaders request = new DefaultHttpHeaders();
+        request.add(hostHeaderKey, hostHeaderValue);
+        Map<HttpUploadServerHandler.UploadHeader, String> headers = httpUploadServerHandler.getUploadRequestUsefulHeaders(request);
+        Assert.assertEquals(hostHeaderValue, headers.get(HttpUploadServerHandler.UploadHeader.HOST));
+    }
+
+    @Test
+    public void testGetUploadRequestUsefulHeadersHeaderKeyDifferentCase() {
+        String host = "SomeHost";
+        runGetUploadRequestUsefulHeadersTestForHost(HttpUploadServerHandler.UploadHeader.HOST.getName(), host);
+        runGetUploadRequestUsefulHeadersTestForHost(HttpUploadServerHandler.UploadHeader.HOST.getName().toUpperCase(), host);
+        runGetUploadRequestUsefulHeadersTestForHost("X-Forwarded-Host", host);
+    }
+
+    @Test
+    public void testGetUploadRequestUsefulHeadersAllKeys() {
+        HttpHeaders request = new DefaultHttpHeaders();
+        String sign = "Sign";
+        String metadata = "met";
+        String expires = "ex";
+        String host = "SomeHost";
+        long contentLength = 100L;
+        request.add("x-Signature", sign);
+        request.add("X-metadata", metadata);
+        request.add("X-EXPIRES", expires);
+        request.add("X-Forwarded-Host", host);
+        request.add(HttpHeaders.Names.CONTENT_LENGTH, String.valueOf(contentLength));
+        Map<HttpUploadServerHandler.UploadHeader, String> headers = httpUploadServerHandler.getUploadRequestUsefulHeaders(request);
+        Assert.assertEquals(sign, headers.get(HttpUploadServerHandler.UploadHeader.SIGNATURE));
+        Assert.assertEquals(metadata, headers.get(HttpUploadServerHandler.UploadHeader.METADATA));
+        Assert.assertEquals(expires, headers.get(HttpUploadServerHandler.UploadHeader.EXPIRES));
+        Assert.assertEquals(host, headers.get(HttpUploadServerHandler.UploadHeader.HOST));
+        Assert.assertEquals(String.valueOf(contentLength), headers.get(HttpUploadServerHandler.UploadHeader.CONTENT_LENGTH));
+    }
+}