Skip to content

Commit e166f96

Browse files
shwstpprJoaoJandre
andauthored
ssvm: fix post request header case mismatch (#7445)
Fixes #7442 With d74f64a 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 <abhishek.mrt22@gmail.com> Co-authored-by: João Jandre <48719461+JoaoJandre@users.noreply.github.com>
1 parent 5885045 commit e166f96

2 files changed

Lines changed: 118 additions & 41 deletions

File tree

services/secondary-storage/server/src/main/java/org/apache/cloudstack/storage/resource/HttpUploadServerHandler.java

Lines changed: 44 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,15 @@
1818

1919
import static io.netty.buffer.Unpooled.copiedBuffer;
2020
import static io.netty.handler.codec.http.HttpHeaders.Names.CONNECTION;
21-
import static io.netty.handler.codec.http.HttpHeaders.Names.CONTENT_LENGTH;
2221
import static io.netty.handler.codec.http.HttpHeaders.Names.CONTENT_TYPE;
2322

2423
import java.io.IOException;
2524
import java.net.URI;
25+
import java.util.HashMap;
2626
import java.util.List;
2727
import java.util.Map;
2828
import java.util.Map.Entry;
2929

30-
import io.netty.handler.codec.DecoderException;
3130
import org.apache.cloudstack.storage.template.UploadEntity;
3231
import org.apache.cloudstack.utils.imagestore.ImageStoreUtil;
3332
import org.apache.commons.lang3.StringUtils;
@@ -41,6 +40,7 @@
4140
import io.netty.channel.ChannelFutureListener;
4241
import io.netty.channel.ChannelHandlerContext;
4342
import io.netty.channel.SimpleChannelInboundHandler;
43+
import io.netty.handler.codec.DecoderException;
4444
import io.netty.handler.codec.http.DefaultFullHttpResponse;
4545
import io.netty.handler.codec.http.FullHttpResponse;
4646
import io.netty.handler.codec.http.HttpContent;
@@ -79,16 +79,46 @@ public class HttpUploadServerHandler extends SimpleChannelInboundHandler<HttpObj
7979

8080
private boolean requestProcessed = false;
8181

82-
private static final String HEADER_SIGNATURE = "x-signature";
83-
84-
private static final String HEADER_METADATA = "x-metadata";
82+
enum UploadHeader {
83+
SIGNATURE("x-signature"),
84+
METADATA("x-metadata"),
85+
EXPIRES("x-expires"),
86+
HOST("x-forwarded-host"),
87+
CONTENT_LENGTH("content-length");
8588

86-
private static final String HEADER_EXPIRES = "x-expires";
87-
88-
private static final String HEADER_HOST = "x-forwarded-host";
89+
private final String name;
90+
UploadHeader(String name) {
91+
this.name = name;
92+
}
93+
public String getName() {
94+
return this.name;
95+
}
96+
public static UploadHeader fromName(String name) {
97+
for (UploadHeader type : values()) {
98+
if (type.getName().equalsIgnoreCase(name)) {
99+
return type;
100+
}
101+
}
102+
return null;
103+
}
104+
}
89105

90106
private static long processTimeout;
91107

108+
protected Map<UploadHeader, String> getUploadRequestUsefulHeaders(HttpHeaders headers) {
109+
Map<UploadHeader, String> headerMap = new HashMap<>();
110+
for (Entry<String, String> entry : headers) {
111+
UploadHeader headerType = UploadHeader.fromName(entry.getKey());
112+
if (headerType != null) {
113+
headerMap.put(headerType, entry.getValue());
114+
}
115+
}
116+
for (UploadHeader type : UploadHeader.values()) {
117+
logger.info(String.format("HEADER: %s=%s", type, headerMap.get(type)));
118+
}
119+
return headerMap;
120+
}
121+
92122
public HttpUploadServerHandler(NfsSecondaryStorageResource storageResource) {
93123
this.storageResource = storageResource;
94124
}
@@ -123,46 +153,19 @@ public void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Excep
123153

124154
URI uri = new URI(request.getUri());
125155

126-
String signature = null;
127-
String expires = null;
128-
String metadata = null;
129-
String hostname = null;
130-
long contentLength = 0;
131-
132-
for (Entry<String, String> entry : request.headers()) {
133-
switch (entry.getKey()) {
134-
case HEADER_SIGNATURE:
135-
signature = entry.getValue();
136-
break;
137-
case HEADER_METADATA:
138-
metadata = entry.getValue();
139-
break;
140-
case HEADER_EXPIRES:
141-
expires = entry.getValue();
142-
break;
143-
case HEADER_HOST:
144-
hostname = entry.getValue();
145-
break;
146-
case HttpHeaders.Names.CONTENT_LENGTH:
147-
contentLength = Long.parseLong(entry.getValue());
148-
break;
149-
}
150-
}
151-
logger.info("HEADER: signature=" + signature);
152-
logger.info("HEADER: metadata=" + metadata);
153-
logger.info("HEADER: expires=" + expires);
154-
logger.info("HEADER: hostname=" + hostname);
155-
logger.info("HEADER: content-length=" + contentLength);
156+
Map<UploadHeader, String> headers = getUploadRequestUsefulHeaders(request.headers());
157+
long contentLength = headers.get(UploadHeader.CONTENT_LENGTH) != null ? Long.parseLong(headers.get(UploadHeader.CONTENT_LENGTH)) : 0;
156158
QueryStringDecoder decoderQuery = new QueryStringDecoder(uri);
157159
Map<String, List<String>> uriAttributes = decoderQuery.parameters();
158160
uuid = uriAttributes.get("uuid").get(0);
159161
logger.info("URI: uuid=" + uuid);
160162
UploadEntity uploadEntity = null;
161163
try {
162164
// Validate the request here
163-
storageResource.validatePostUploadRequest(signature, metadata, expires, hostname, contentLength, uuid);
165+
storageResource.validatePostUploadRequest(headers.get(UploadHeader.SIGNATURE), headers.get(UploadHeader.METADATA),
166+
headers.get(UploadHeader.EXPIRES), headers.get(UploadHeader.HOST), contentLength, uuid);
164167
//create an upload entity. This will fail if entity already exists.
165-
uploadEntity = storageResource.createUploadEntity(uuid, metadata, contentLength);
168+
uploadEntity = storageResource.createUploadEntity(uuid, headers.get(UploadHeader.METADATA), contentLength);
166169
} catch (InvalidParameterValueException ex) {
167170
logger.error("post request validation failed", ex);
168171
responseContent.append(ex.getMessage());
@@ -280,7 +283,7 @@ private void writeResponse(Channel channel, HttpResponseStatus statusCode) {
280283
response.headers().set(CONTENT_TYPE, "text/plain; charset=UTF-8");
281284
if (!close) {
282285
// There's no need to add 'content-length' header if this is the last response.
283-
response.headers().set(CONTENT_LENGTH, buf.readableBytes());
286+
response.headers().set(HttpHeaders.Names.CONTENT_LENGTH, buf.readableBytes());
284287
}
285288
// Write the response.
286289
ChannelFuture future = channel.writeAndFlush(response);
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
// Licensed to the Apache Software Foundation (ASF) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The ASF licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
package org.apache.cloudstack.storage.resource;
18+
19+
import java.util.Map;
20+
21+
import org.junit.Assert;
22+
import org.junit.Test;
23+
import org.junit.runner.RunWith;
24+
import org.mockito.InjectMocks;
25+
import org.mockito.Mockito;
26+
import org.mockito.Spy;
27+
import org.mockito.junit.MockitoJUnitRunner;
28+
29+
import io.netty.handler.codec.http.DefaultHttpHeaders;
30+
import io.netty.handler.codec.http.HttpHeaders;
31+
32+
@RunWith(MockitoJUnitRunner.class)
33+
public class HttpUploadServerHandlerTest {
34+
35+
@Spy
36+
@InjectMocks
37+
HttpUploadServerHandler httpUploadServerHandler = new HttpUploadServerHandler(Mockito.mock(NfsSecondaryStorageResource.class));
38+
39+
private void runGetUploadRequestUsefulHeadersTestForHost(String hostHeaderKey, String hostHeaderValue) {
40+
HttpHeaders request = new DefaultHttpHeaders();
41+
request.add(hostHeaderKey, hostHeaderValue);
42+
Map<HttpUploadServerHandler.UploadHeader, String> headers = httpUploadServerHandler.getUploadRequestUsefulHeaders(request);
43+
Assert.assertEquals(hostHeaderValue, headers.get(HttpUploadServerHandler.UploadHeader.HOST));
44+
}
45+
46+
@Test
47+
public void testGetUploadRequestUsefulHeadersHeaderKeyDifferentCase() {
48+
String host = "SomeHost";
49+
runGetUploadRequestUsefulHeadersTestForHost(HttpUploadServerHandler.UploadHeader.HOST.getName(), host);
50+
runGetUploadRequestUsefulHeadersTestForHost(HttpUploadServerHandler.UploadHeader.HOST.getName().toUpperCase(), host);
51+
runGetUploadRequestUsefulHeadersTestForHost("X-Forwarded-Host", host);
52+
}
53+
54+
@Test
55+
public void testGetUploadRequestUsefulHeadersAllKeys() {
56+
HttpHeaders request = new DefaultHttpHeaders();
57+
String sign = "Sign";
58+
String metadata = "met";
59+
String expires = "ex";
60+
String host = "SomeHost";
61+
long contentLength = 100L;
62+
request.add("x-Signature", sign);
63+
request.add("X-metadata", metadata);
64+
request.add("X-EXPIRES", expires);
65+
request.add("X-Forwarded-Host", host);
66+
request.add(HttpHeaders.Names.CONTENT_LENGTH, String.valueOf(contentLength));
67+
Map<HttpUploadServerHandler.UploadHeader, String> headers = httpUploadServerHandler.getUploadRequestUsefulHeaders(request);
68+
Assert.assertEquals(sign, headers.get(HttpUploadServerHandler.UploadHeader.SIGNATURE));
69+
Assert.assertEquals(metadata, headers.get(HttpUploadServerHandler.UploadHeader.METADATA));
70+
Assert.assertEquals(expires, headers.get(HttpUploadServerHandler.UploadHeader.EXPIRES));
71+
Assert.assertEquals(host, headers.get(HttpUploadServerHandler.UploadHeader.HOST));
72+
Assert.assertEquals(String.valueOf(contentLength), headers.get(HttpUploadServerHandler.UploadHeader.CONTENT_LENGTH));
73+
}
74+
}

0 commit comments

Comments
 (0)