All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.vertx.openapi.validation.analyser.MultipartPart Maven / Gradle / Ivy

/*
 * Copyright (c) 2024, SAP SE
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
 * which is available at https://www.apache.org/licenses/LICENSE-2.0.
 *
 * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
 *
 */

package io.vertx.openapi.validation.analyser;

import io.vertx.core.buffer.Buffer;
import io.vertx.openapi.validation.ValidatorException;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import static io.vertx.openapi.validation.ValidatorErrorType.INVALID_VALUE;
import static java.util.regex.Pattern.CASE_INSENSITIVE;

public class MultipartPart {
  private static final Pattern NAME_PATTERN = Pattern.compile("Content-Disposition: form-data; name=\"(.*?)\"",
    CASE_INSENSITIVE);
  private static final Pattern CONTENT_TYPE_PATTERN = Pattern.compile("Content-Type: (.*)", CASE_INSENSITIVE);

  private final String name;
  private final String contentType;
  private final Buffer body;

  // Should only be called by MultipartPartFormTransformer
  static List fromMultipartBody(String body, String boundary) {
    return parseParts(body, boundary).stream().map(MultipartPart::parsePart).collect(Collectors.toList());
  }

  // VisibleForTesting
  public static List parseParts(String body, String boundary) {
    String delimiter = "--" + boundary + "|\r\n--" + boundary;
    String[] rawParts = body.split(delimiter);

    if (rawParts.length < 3 || !"--".equals(rawParts[rawParts.length - 1].strip())) {
      String msg = "The multipart message doesn't contain any parts, or has an invalid structure.";
      throw new ValidatorException(msg, INVALID_VALUE);
    }

    List parts = new ArrayList<>(rawParts.length - 2);

    // Omit first and last part, because first part is everything up to the first delimiter and the last part
    // contains "--";
    for (int i = 1; i < rawParts.length - 1; i++) {
      parts.add(rawParts[i].strip());
    }

    return parts;
  }

  private static Optional parsePattern(Pattern pattern, String rawPart) {
    return pattern.matcher(rawPart).results().findFirst().map(m -> m.group(1));
  }

  // VisibleForTesting
  public static MultipartPart parsePart(String rawPart) {
    String sectionDelimiterPattern = "\r\n\r\n";
    int sectionDelimiter = rawPart.indexOf(sectionDelimiterPattern);

    // if no empty line exists, there are only headers
    String headerSection = sectionDelimiter == -1 ? rawPart : rawPart.substring(0, sectionDelimiter);
    String body = sectionDelimiter == -1 ? null :
      rawPart.substring(sectionDelimiter + sectionDelimiterPattern.length());

    String name = parsePattern(NAME_PATTERN, headerSection).orElseThrow(() -> {
      String msg = "A part of the multipart message doesn't contain a name.";
      return new ValidatorException(msg, INVALID_VALUE);
    });

    // If no header is set, content type defaults to text/plain
    String contentType = parsePattern(CONTENT_TYPE_PATTERN, headerSection).orElse("text/plain");

    return new MultipartPart(name, contentType, body == null ? null : Buffer.buffer(body));
  }

  public MultipartPart(String name, String contentType, Buffer body) {
    this.name = name;
    this.contentType = contentType;
    this.body = body;
  }

  public String getName() {
    return name;
  }

  public String getContentType() {
    return contentType;
  }

  public Buffer getBody() {
    return body;
  }

  @Override
  public boolean equals(Object o) {
    if (this == o) {
      return true;
    }

    if (o == null || getClass() != o.getClass()) {
      return false;
    }

    MultipartPart that = (MultipartPart) o;
    return Objects.equals(name, that.name) && Objects.equals(contentType, that.contentType) && Objects.equals(body,
      that.body);
  }

  @Override
  public int hashCode() {
    return Objects.hash(name, contentType, body);
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy