001// Copyright 2019 Google LLC
002//
003// Licensed under the Apache License, Version 2.0 (the "License");
004// you may not use this file except in compliance with the License.
005// You may obtain a copy of the License at
006//
007//      http://www.apache.org/licenses/LICENSE-2.0
008//
009// Unless required by applicable law or agreed to in writing, software
010// distributed under the License is distributed on an "AS IS" BASIS,
011// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012// See the License for the specific language governing permissions and
013// limitations under the License.
014
015package com.google.cloud.functions;
016
017import java.io.BufferedReader;
018import java.io.IOException;
019import java.io.InputStream;
020import java.util.List;
021import java.util.Map;
022import java.util.Optional;
023
024/**
025 * Represents an HTTP message, either an HTTP request or a part of a multipart HTTP request.
026 */
027public interface HttpMessage {
028  /** Returns the value of the {@code Content-Type} header, if any. */
029  Optional<String> getContentType();
030
031  /** Returns the numeric value of the {@code Content-Length} header. */
032  long getContentLength();
033
034  /**
035   * Returns the character encoding specified in the {@code Content-Type} header,
036   * or {@code Optional.empty()} if there is no {@code Content-Type} header or it does not have the
037   * {@code charset} parameter.
038   */
039  Optional<String> getCharacterEncoding();
040
041  /**
042   * Returns an {@link InputStream} that can be used to read the body of this HTTP request.
043   * Every call to this method on the same {@link HttpMessage} will return the same object.
044   * This method is typically used to read binary data. If the body is text, the
045   * {@link #getReader()} method is more appropriate.
046   *
047   * @throws IOException if a valid {@link InputStream} cannot be returned for some reason.
048   * @throws IllegalStateException if {@link #getReader()} has already been called on this instance.
049   */
050  InputStream getInputStream() throws IOException;
051
052  /**
053   * Returns a {@link BufferedReader} that can be used to read the text body of this HTTP request.
054   * Every call to this method on the same {@link HttpMessage} will return the same object.
055   *
056   * @throws IOException if a valid {@link BufferedReader} cannot be returned for some reason.
057   * @throws IllegalStateException if {@link #getInputStream()} has already been called on this
058   *     instance.
059   */
060  BufferedReader getReader() throws IOException;
061
062  /**
063   * Returns a map describing the headers of this HTTP request, or this part of a multipart
064   * request. If the headers look like this...
065   *
066   * <pre>
067   *   Content-Type: text/plain
068   *   Some-Header: some value
069   *   Some-Header: another value
070   * </pre>
071   *
072   * ...then the returned value will map {@code Content-Type} to a one-element list containing
073   * {@code text/plain}, and {@code Some-Header} to a two-element list containing {@code some value}
074   * and {@code another value}.
075   */
076  Map<String, List<String>> getHeaders();
077
078  /**
079   * Convenience method that returns the value of the first header with the given name. If the
080   * headers look like this...
081   *
082   * <pre>
083   *   Content-Type: text/plain
084   *   Some-Header: some value
085   *   Some-Header: another value
086   * </pre>
087   *
088   * ...then {@code getFirstHeader("Some-Header")} will return {@code Optional.of("some value")},
089   * and {@code getFirstHeader("Another-Header")} will return {@code Optional.empty()}.
090   */
091  default Optional<String> getFirstHeader(String name) {
092    List<String> headers = getHeaders().get(name);
093    if (headers == null || headers.isEmpty()) {
094      return Optional.empty();
095    }
096    return Optional.of(headers.get(0));
097  }
098}