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/** Represents an HTTP message, either an HTTP request or a part of a multipart HTTP request. */
025public interface HttpMessage {
026  /**
027   * Returns the value of the {@code Content-Type} header, if any.
028   *
029   * @return the content type, if any.
030   */
031  Optional<String> getContentType();
032
033  /**
034   * Returns the numeric value of the {@code Content-Length} header.
035   *
036   * @return the content length.
037   */
038  long getContentLength();
039
040  /**
041   * Returns the character encoding specified in the {@code Content-Type} header, or {@code
042   * Optional.empty()} if there is no {@code Content-Type} header or it does not have the {@code
043   * charset} parameter.
044   *
045   * @return the character encoding for the content type, if one is specified.
046   */
047  Optional<String> getCharacterEncoding();
048
049  /**
050   * Returns an {@link InputStream} that can be used to read the body of this HTTP request. Every
051   * call to this method on the same {@link HttpMessage} will return the same object. This method is
052   * typically used to read binary data. If the body is text, the {@link #getReader()} method is
053   * more appropriate.
054   *
055   * @return an {@link InputStream} that can be used to read the body of this HTTP request.
056   * @throws IOException if a valid {@link InputStream} cannot be returned for some reason.
057   * @throws IllegalStateException if {@link #getReader()} has already been called on this instance.
058   */
059  InputStream getInputStream() throws IOException;
060
061  /**
062   * Returns a {@link BufferedReader} that can be used to read the text body of this HTTP request.
063   * Every call to this method on the same {@link HttpMessage} will return the same object.
064   *
065   * @return a {@link BufferedReader} that can be used to read the text body of this HTTP request.
066   * @throws IOException if a valid {@link BufferedReader} cannot be returned for some reason.
067   * @throws IllegalStateException if {@link #getInputStream()} has already been called on this
068   *     instance.
069   */
070  BufferedReader getReader() throws IOException;
071
072  /**
073   * Returns a map describing the headers of this HTTP request, or this part of a multipart request.
074   * If the headers look like this...
075   *
076   * <pre>
077   *   Content-Type: text/plain
078   *   Some-Header: some value
079   *   Some-Header: another value
080   * </pre>
081   *
082   * ...then the returned value will map {@code "Content-Type"} to a one-element list containing
083   * {@code "text/plain"}, and {@code "Some-Header"} to a two-element list containing {@code "some
084   * value"} and {@code "another value"}.
085   *
086   * @return a map where each key is an HTTP header and the corresponding {@code List} value has one
087   *     element for each occurrence of that header.
088   */
089  Map<String, List<String>> getHeaders();
090
091  /**
092   * Convenience method that returns the value of the first header with the given name. If the
093   * headers look like this...
094   *
095   * <pre>
096   *   Content-Type: text/plain
097   *   Some-Header: some value
098   *   Some-Header: another value
099   * </pre>
100   *
101   * ...then {@code getFirstHeader("Some-Header")} will return {@code Optional.of("some value")},
102   * and {@code getFirstHeader("Another-Header")} will return {@code Optional.empty()}.
103   *
104   * @param name an HTTP header name.
105   * @return the first value of the given header, if present.
106   */
107  default Optional<String> getFirstHeader(String name) {
108    List<String> headers = getHeaders().get(name);
109    if (headers == null || headers.isEmpty()) {
110      return Optional.empty();
111    }
112    return Optional.of(headers.get(0));
113  }
114}