@Override
    protected HttpResponse handlePOST(HttpRequest request) {
        validateDataAndHeader(request, List.of(APPLICATION_X_GZIP, APPLICATION_ZIP, MULTIPART_FORM_DATA));
        TenantName tenantName = validateTenant(request);

        PrepareParams prepareParams;
        CompressedApplicationInputStream compressedStream;
        boolean multipartRequest = Optional.ofNullable(request.getHeader(HttpHeaders.Names.CONTENT_TYPE))
                .map(ContentType::parse)
                .map(contentType -> contentType.getMimeType().equalsIgnoreCase(MULTIPART_FORM_DATA))
                .orElse(false);
        if (multipartRequest) {
            Map<String, PartItem> parts = Map.of();
            try {
                parts = multiPartFormParser.readParts(request);
                byte[] params;
                try (InputStream part = parts.get(MULTIPART_PARAMS).data()) { params = part.readAllBytes(); }
                log.log(FINE, "Deploy parameters: [{0}]", new String(params, StandardCharsets.UTF_8));
                prepareParams = PrepareParams.fromJson(params, tenantName, zookeeperBarrierTimeout,
                        VERBOSE_DEPLOY_PARAMETER.bindTo(applicationRepository.flagSource()).value());
                PartItem appPackagePart = parts.get(MULTIPART_APPLICATION_PACKAGE);
                compressedStream = createFromCompressedStream(appPackagePart.data(), appPackagePart.contentType(), maxApplicationPackageSize);
            } catch (IOException e) {
                var message = "Deploy request from '" + tenantName.value() + "' contains invalid data: " + e.getMessage();
                if (e instanceof MultiPartFormParser.MultiPartException)
                    log.log(INFO, "Unable to parse multipart in deploy from tenant '" + tenantName.value() + "': " +
                            Exceptions.toMessageString(e) + ". This is usually caused by controller abandoning request " +
                            "while streaming data to config server");
                else
                    log.log(INFO, message + ", parts: " + parts, e);
                throw new BadRequestException(message);
            }
        } else {
            prepareParams = PrepareParams.fromHttpRequest(request, tenantName, zookeeperBarrierTimeout);
            compressedStream = createFromCompressedStream(request.getData(), request.getHeader(contentTypeHeader), maxApplicationPackageSize);
        }

        // Aid debugging by adding full application id to access log (since only tenant name is part of the request URI path)
        request.getAccessLogEntry()
                .ifPresent(e -> e.addKeyValue("app.id", prepareParams.getApplicationId().toFullString()));

        try (compressedStream) {
            PrepareAndActivateResult result = applicationRepository.deploy(compressedStream, prepareParams);
            return new SessionPrepareAndActivateResponse(result, prepareParams.getApplicationId(), request, zone);
        }
        catch (IOException e) {
            throw new UncheckedIOException(e);
        }
    }
* The implementation of the /application/v2 API.


@author hmusum
configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/ApplicationApiHandler.java