diff options
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 |