summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBjørn Christian Seime <bjorncs@verizonmedia.com>2022-06-03 13:29:35 +0200
committergjoranv <gv@verizonmedia.com>2022-06-08 11:45:28 +0200
commit31c396f3664f35d9639cfb3ad4de83a7199f98e9 (patch)
treece7194ed5e52457f47333ef8c583cad9ad327346
parent19f9c783c2f1ca136a6ed874656e0a2c93b4adca (diff)
Create wrapper for Jetty's MultiPartFormInputStream
-rw-r--r--configserver/pom.xml4
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/ApplicationApiHandler.java19
-rw-r--r--container-core/src/main/java/com/yahoo/container/jdisc/utils/MultiPartFormParser.java64
-rw-r--r--container-core/src/main/java/com/yahoo/container/jdisc/utils/package-info.java10
4 files changed, 83 insertions, 14 deletions
diff --git a/configserver/pom.xml b/configserver/pom.xml
index 9de371aba39..268bfbdfc00 100644
--- a/configserver/pom.xml
+++ b/configserver/pom.xml
@@ -280,11 +280,11 @@
<version>${project.version}</version>
</dependency>
<dependency>
- <!-- TODO Remove dependency on Jetty once no longer required for multi-part response parsing -->
+ <!-- TODO Remove when Jetty is embedded inside container-core -->
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-http</artifactId>
<version>${jetty.version}</version>
- <scope>provided</scope>
+ <scope>test</scope>
</dependency>
</dependencies>
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/ApplicationApiHandler.java b/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/ApplicationApiHandler.java
index 6ffd26478c3..b489eb70927 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/ApplicationApiHandler.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/ApplicationApiHandler.java
@@ -1,12 +1,14 @@
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.config.server.http.v2;
-import com.yahoo.component.annotation.Inject;
import com.yahoo.cloud.config.ConfigserverConfig;
+import com.yahoo.component.annotation.Inject;
import com.yahoo.config.provision.TenantName;
import com.yahoo.config.provision.Zone;
import com.yahoo.container.jdisc.HttpRequest;
import com.yahoo.container.jdisc.HttpResponse;
+import com.yahoo.container.jdisc.utils.MultiPartFormParser;
+import com.yahoo.container.jdisc.utils.MultiPartFormParser.PartItem;
import com.yahoo.jdisc.application.BindingMatch;
import com.yahoo.jdisc.http.HttpHeaders;
import com.yahoo.vespa.config.server.ApplicationRepository;
@@ -18,9 +20,7 @@ import com.yahoo.vespa.config.server.http.v2.response.SessionPrepareAndActivateR
import com.yahoo.vespa.config.server.session.PrepareParams;
import com.yahoo.vespa.config.server.tenant.TenantRepository;
import org.apache.hc.core5.http.ContentType;
-import org.eclipse.jetty.http.MultiPartFormInputStream;
-import javax.servlet.http.Part;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
@@ -28,7 +28,6 @@ import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.logging.Level;
-import java.util.stream.Collectors;
import static com.yahoo.vespa.config.server.application.CompressedApplicationInputStream.createFromCompressedStream;
import static com.yahoo.vespa.config.server.http.Utils.checkThatTenantExists;
@@ -80,16 +79,12 @@ public class ApplicationApiHandler extends SessionHandler {
.orElse(false);
if (multipartRequest) {
try {
- // TODO Remove direct dependency on Jetty for parsing multi-part response (add helper in container-core)
- MultiPartFormInputStream multiPartFormInputStream = new MultiPartFormInputStream(request.getData(), request.getHeader(CONTENT_TYPE), /* config */null, /* contextTmpDir */null);
- Map<String, Part> parts = multiPartFormInputStream.getParts().stream()
- .collect(Collectors.toMap(Part::getName, p -> p));
-
- byte[] params = parts.get(MULTIPART_PARAMS).getInputStream().readAllBytes();
+ Map<String, PartItem> parts = new MultiPartFormParser(request).readParts();
+ byte[] params = parts.get(MULTIPART_PARAMS).data().readAllBytes();
log.log(Level.FINE, "Deploy parameters: [{0}]", new String(params, StandardCharsets.UTF_8));
prepareParams = PrepareParams.fromJson(params, tenantName, zookeeperBarrierTimeout);
- Part appPackagePart = parts.get(MULTIPART_APPLICATION_PACKAGE);
- compressedStream = createFromCompressedStream(appPackagePart.getInputStream(), appPackagePart.getContentType(), maxApplicationPackageSize);
+ PartItem appPackagePart = parts.get(MULTIPART_APPLICATION_PACKAGE);
+ compressedStream = createFromCompressedStream(appPackagePart.data(), appPackagePart.contentType(), maxApplicationPackageSize);
} catch (IOException e) {
log.log(Level.WARNING, "Unable to parse multipart in deploy", e);
throw new BadRequestException("Request contains invalid data");
diff --git a/container-core/src/main/java/com/yahoo/container/jdisc/utils/MultiPartFormParser.java b/container-core/src/main/java/com/yahoo/container/jdisc/utils/MultiPartFormParser.java
new file mode 100644
index 00000000000..104d2f8ae4a
--- /dev/null
+++ b/container-core/src/main/java/com/yahoo/container/jdisc/utils/MultiPartFormParser.java
@@ -0,0 +1,64 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.container.jdisc.utils;
+
+import com.yahoo.container.jdisc.HttpRequest;
+import org.eclipse.jetty.http.MultiPartFormInputStream;
+
+import javax.servlet.http.Part;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Map;
+import java.util.TreeMap;
+
+/**
+ * Wrapper around Jetty's {@link MultiPartFormInputStream}.
+ *
+ * @author bjorncs
+ */
+public class MultiPartFormParser {
+
+ private final MultiPartFormInputStream multipart;
+
+ public MultiPartFormParser(InputStream in, String contentType) {
+ this.multipart = new MultiPartFormInputStream(in, contentType, /*config*/null, /*contextTmpDir*/null);
+ }
+
+ public MultiPartFormParser(HttpRequest request) { this(request.getData(), request.getHeader("Content-Type")); }
+
+ public Map<String, PartItem> readParts() throws MultiPartException {
+ try {
+ Map<String, PartItem> result = new TreeMap<>();
+ for (Part servletPart : multipart.getParts()) {
+ result.put(servletPart.getName(), new PartItem(servletPart));
+ }
+ return result;
+ } catch (Exception e) {
+ throw new MultiPartException(e.getCause());
+ }
+ }
+
+ public static class PartItem {
+ private final String name;
+ private final InputStream data;
+ private final String contentType;
+
+ private PartItem(Part servletPart) throws IOException {
+ this(servletPart.getName(), servletPart.getInputStream(), servletPart.getContentType());
+ }
+
+ public PartItem(String name, InputStream data, String contentType) {
+ this.name = name;
+ this.data = data;
+ this.contentType = contentType;
+ }
+
+ public String name() { return name; }
+ public InputStream data() { return data; }
+ public String contentType() { return contentType; }
+ }
+
+ public static class MultiPartException extends IOException {
+ public MultiPartException(Throwable cause) { super(cause.getMessage(), cause); }
+ }
+
+}
diff --git a/container-core/src/main/java/com/yahoo/container/jdisc/utils/package-info.java b/container-core/src/main/java/com/yahoo/container/jdisc/utils/package-info.java
new file mode 100644
index 00000000000..221843749a5
--- /dev/null
+++ b/container-core/src/main/java/com/yahoo/container/jdisc/utils/package-info.java
@@ -0,0 +1,10 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+/**
+ * Internal utils that are not public api
+ *
+ * @author bjorncs
+ */
+@ExportPackage
+package com.yahoo.container.jdisc.utils;
+
+import com.yahoo.osgi.annotation.ExportPackage; \ No newline at end of file