Package com.google.api.client.http

Source Code of com.google.api.client.http.HttpHeaders$HeaderParsingFakeLevelHttpRequest

/*
* Copyright (c) 2010 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/

package com.google.api.client.http;

import com.google.api.client.util.ArrayValueMap;
import com.google.api.client.util.Base64;
import com.google.api.client.util.ClassInfo;
import com.google.api.client.util.Data;
import com.google.api.client.util.FieldInfo;
import com.google.api.client.util.GenericData;
import com.google.api.client.util.Key;
import com.google.api.client.util.StringUtils;
import com.google.api.client.util.Types;
import com.google.common.base.Preconditions;

import java.io.IOException;
import java.io.Writer;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
* Stores HTTP headers used in an HTTP request or response, as defined in <a
* href="http://tools.ietf.org/html/rfc2616#section-14">Header Field Definitions</a>.
*
* <p>
* {@code null} is not allowed as a name or value of a header. Names are case-insensitive.
* </p>
*
* <p>
* Implementation is not thread-safe.
* </p>
*
* @since 1.0
* @author Yaniv Inbar
*/
public class HttpHeaders extends GenericData {

  public HttpHeaders() {
    super(EnumSet.of(Flags.IGNORE_CASE));
  }

  /** {@code "Accept"} header. */
  @Key("Accept")
  private String accept;

  /** {@code "Accept-Encoding"} header. */
  @Key("Accept-Encoding")
  private String acceptEncoding = "gzip";

  /** {@code "Authorization"} header. */
  @Key("Authorization")
  private String authorization;

  /** {@code "Cache-Control"} header. */
  @Key("Cache-Control")
  private String cacheControl;

  /** {@code "Content-Encoding"} header. */
  @Key("Content-Encoding")
  private String contentEncoding;

  /** {@code "Content-Length"} header. */
  @Key("Content-Length")
  private Long contentLength;

  /** {@code "Content-MD5"} header. */
  @Key("Content-MD5")
  private String contentMD5;

  /** {@code "Content-Range"} header. */
  @Key("Content-Range")
  private String contentRange;

  /** {@code "Content-Type"} header. */
  @Key("Content-Type")
  private String contentType;

  /** {@code "Cookie"} header. */
  @Key("Cookie")
  private String cookie;

  /** {@code "Date"} header. */
  @Key("Date")
  private String date;

  /** {@code "ETag"} header. */
  @Key("ETag")
  private String etag;

  /** {@code "Expires"} header. */
  @Key("Expires")
  private String expires;

  /** {@code "If-Modified-Since"} header. */
  @Key("If-Modified-Since")
  private String ifModifiedSince;

  /** {@code "If-Match"} header. */
  @Key("If-Match")
  private String ifMatch;

  /** {@code "If-None-Match"} header. */
  @Key("If-None-Match")
  private String ifNoneMatch;

  /** {@code "If-Unmodified-Since"} header. */
  @Key("If-Unmodified-Since")
  private String ifUnmodifiedSince;

  /** {@code "Last-Modified"} header. */
  @Key("Last-Modified")
  private String lastModified;

  /** {@code "Location"} header. */
  @Key("Location")
  private String location;

  /** {@code "MIME-Version"} header. */
  @Key("MIME-Version")
  private String mimeVersion;

  /** {@code "Range"} header. */
  @Key("Range")
  private String range;

  /** {@code "Retry-After"} header. */
  @Key("Retry-After")
  private String retryAfter;

  /** {@code "User-Agent"} header. */
  @Key("User-Agent")
  private String userAgent;

  /** {@code "WWW-Authenticate"} header. */
  @Key("WWW-Authenticate")
  private String authenticate;

  @Override
  public HttpHeaders clone() {
    return (HttpHeaders) super.clone();
  }

  /**
   * Returns the {@code "Accept"} header or {@code null} for none.
   *
   * @since 1.5
   */
  public final String getAccept() {
    return accept;
  }

  /**
   * Sets the {@code "Accept"} header or {@code null} for none.
   *
   * @since 1.5
   */
  public final void setAccept(String accept) {
    this.accept = accept;
  }

  /**
   * Returns the {@code "Accept-Encoding"} header or {@code null} for none.
   *
   * @since 1.5
   */
  public final String getAcceptEncoding() {
    return acceptEncoding;
  }

  /**
   * Sets the {@code "Accept-Encoding"} header or {@code null} for none.
   *
   * <p>
   * By default, this is {@code "gzip"}.
   * </p>
   *
   * @since 1.5
   */
  public final void setAcceptEncoding(String acceptEncoding) {
    this.acceptEncoding = acceptEncoding;
  }

  /**
   * Returns the {@code "Authorization"} header or {@code null} for none.
   *
   * @since 1.5
   */
  public final String getAuthorization() {
    return authorization;
  }

  /**
   * Sets the {@code "Authorization"} header or {@code null} for none.
   *
   * @since 1.5
   */
  public final void setAuthorization(String authorization) {
    this.authorization = authorization;
  }

  /**
   * Returns the {@code "Cache-Control"} header or {@code null} for none.
   *
   * @since 1.5
   */
  public final String getCacheControl() {
    return cacheControl;
  }

  /**
   * Sets the {@code "Cache-Control"} header or {@code null} for none.
   *
   * @since 1.5
   */
  public final void setCacheControl(String cacheControl) {
    this.cacheControl = cacheControl;
  }

  /**
   * Returns the {@code "Content-Encoding"} header or {@code null} for none.
   *
   * @since 1.5
   */
  public final String getContentEncoding() {
    return contentEncoding;
  }

  /**
   * Sets the {@code "Content-Encoding"} header or {@code null} for none.
   *
   * @since 1.5
   */
  public final void setContentEncoding(String contentEncoding) {
    this.contentEncoding = contentEncoding;
  }

  /**
   * Returns the {@code "Content-Length"} header or {@code null} for none.
   *
   * @since 1.5
   */
  public final Long getContentLength() {
    return contentLength;
  }

  /**
   * Sets the {@code "Content-Length"} header or {@code null} for none.
   *
   * @since 1.5
   */
  public final void setContentLength(Long contentLength) {
    this.contentLength = contentLength;
  }

  /**
   * Returns the {@code "Content-MD5"} header or {@code null} for none.
   *
   * @since 1.5
   */
  public final String getContentMD5() {
    return contentMD5;
  }

  /**
   * Sets the {@code "Content-MD5"} header or {@code null} for none.
   *
   * @since 1.5
   */
  public final void setContentMD5(String contentMD5) {
    this.contentMD5 = contentMD5;
  }

  /**
   * Returns the {@code "Content-Range"} header or {@code null} for none.
   *
   * @since 1.5
   */
  public final String getContentRange() {
    return contentRange;
  }

  /**
   * Sets the {@code "Content-Range"} header or {@code null} for none.
   *
   * @since 1.5
   */
  public final void setContentRange(String contentRange) {
    this.contentRange = contentRange;
  }

  /**
   * Returns the {@code "Content-Type"} header or {@code null} for none.
   *
   * @since 1.5
   */
  public final String getContentType() {
    return contentType;
  }

  /**
   * Sets the {@code "Content-Type"} header or {@code null} for none.
   *
   * @since 1.5
   */
  public final void setContentType(String contentType) {
    this.contentType = contentType;
  }

  /**
   * Returns the {@code "Cookie"} header or {@code null} for none.
   * <a href='http://tools.ietf.org/html/rfc6265'>See Cookie Specification.</a>
   * @since 1.6
   */
  public final String getCookie() {
    return cookie;
  }

  /**
   * Sets the {@code "Cookie"} header or {@code null} for none.
   *
   * @since 1.6
   */
  public final void setCookie(String cookie) {
    this.cookie = cookie;
  }

  /**
   * Returns the {@code "Date"} header or {@code null} for none.
   *
   * @since 1.5
   */
  public final String getDate() {
    return date;
  }

  /**
   * Sets the {@code "Date"} header or {@code null} for none.
   *
   * @since 1.5
   */
  public final void setDate(String date) {
    this.date = date;
  }

  /**
   * Returns the {@code "ETag"} header or {@code null} for none.
   *
   * @since 1.5
   */
  public final String getETag() {
    return etag;
  }

  /**
   * Sets the {@code "ETag"} header or {@code null} for none.
   *
   * @since 1.5
   */
  public final void setETag(String etag) {
    this.etag = etag;
  }

  /**
   * Returns the {@code "Expires"} header or {@code null} for none.
   *
   * @since 1.5
   */
  public final String getExpires() {
    return expires;
  }

  /**
   * Sets the {@code "Expires"} header or {@code null} for none.
   *
   * @since 1.5
   */
  public final void setExpires(String expires) {
    this.expires = expires;
  }

  /**
   * Returns the {@code "If-Modified-Since"} header or {@code null} for none.
   *
   * @since 1.5
   */
  public final String getIfModifiedSince() {
    return ifModifiedSince;
  }

  /**
   * Sets the {@code "If-Modified-Since"} header or {@code null} for none.
   *
   * @since 1.5
   */
  public final void setIfModifiedSince(String ifModifiedSince) {
    this.ifModifiedSince = ifModifiedSince;
  }

  /**
   * Returns the {@code "If-Match"} header or {@code null} for none.
   *
   * @since 1.5
   */
  public final String getIfMatch() {
    return ifMatch;
  }

  /**
   * Sets the {@code "If-Match"} header or {@code null} for none.
   *
   * @since 1.5
   */
  public final void setIfMatch(String ifMatch) {
    this.ifMatch = ifMatch;
  }

  /**
   * Returns the {@code "If-None-Match"} header or {@code null} for none.
   *
   * @since 1.5
   */
  public final String getIfNoneMatch() {
    return ifNoneMatch;
  }

  /**
   * Sets the {@code "If-None-Match"} header or {@code null} for none.
   *
   * @since 1.5
   */
  public final void setIfNoneMatch(String ifNoneMatch) {
    this.ifNoneMatch = ifNoneMatch;
  }

  /**
   * Returns the {@code "If-Unmodified-Since"} header or {@code null} for none.
   *
   * @since 1.5
   */
  public final String getIfUnmodifiedSince() {
    return ifUnmodifiedSince;
  }

  /**
   * Sets the {@code "If-Unmodified-Since"} header or {@code null} for none.
   *
   * @since 1.5
   */
  public final void setIfUnmodifiedSince(String ifUnmodifiedSince) {
    this.ifUnmodifiedSince = ifUnmodifiedSince;
  }

  /**
   * Returns the {@code "Last-Modified"} header or {@code null} for none.
   *
   * @since 1.5
   */
  public final String getLastModified() {
    return lastModified;
  }

  /**
   * Sets the {@code "Last-Modified"} header or {@code null} for none.
   *
   * @since 1.5
   */
  public final void setLastModified(String lastModified) {
    this.lastModified = lastModified;
  }

  /**
   * Returns the {@code "Location"} header or {@code null} for none.
   *
   * @since 1.5
   */
  public final String getLocation() {
    return location;
  }

  /**
   * Sets the {@code "Location"} header or {@code null} for none.
   *
   * @since 1.5
   */
  public final void setLocation(String location) {
    this.location = location;
  }

  /**
   * Returns the {@code "MIME-Version"} header or {@code null} for none.
   *
   * @since 1.5
   */
  public final String getMimeVersion() {
    return mimeVersion;
  }

  /**
   * Sets the {@code "MIME-Version"} header or {@code null} for none.
   *
   * @since 1.5
   */
  public final void setMimeVersion(String mimeVersion) {
    this.mimeVersion = mimeVersion;
  }

  /**
   * Returns the {@code "Range"} header or {@code null} for none.
   *
   * @since 1.5
   */
  public final String getRange() {
    return range;
  }

  /**
   * Sets the {@code "Range"} header or {@code null} for none.
   *
   * @since 1.5
   */
  public final void setRange(String range) {
    this.range = range;
  }

  /**
   * Returns the {@code "Retry-After"} header or {@code null} for none.
   *
   * @since 1.5
   */
  public final String getRetryAfter() {
    return retryAfter;
  }

  /**
   * Sets the {@code "Retry-After"} header or {@code null} for none.
   *
   * @since 1.5
   */
  public final void setRetryAfter(String retryAfter) {
    this.retryAfter = retryAfter;
  }

  /**
   * Returns the {@code "User-Agent"} header or {@code null} for none.
   *
   * @since 1.5
   */
  public final String getUserAgent() {
    return userAgent;
  }

  /**
   * Sets the {@code "User-Agent"} header or {@code null} for none.
   *
   * @since 1.5
   */
  public final void setUserAgent(String userAgent) {
    this.userAgent = userAgent;
  }

  /**
   * Returns the {@code "WWW-Authenticate"} header or {@code null} for none.
   *
   * @since 1.5
   */
  public final String getAuthenticate() {
    return authenticate;
  }

  /**
   * Sets the {@code "WWW-Authenticate"} header or {@code null} for none.
   *
   * @since 1.5
   */
  public final void setAuthenticate(String authenticate) {
    this.authenticate = authenticate;
  }

  /**
   * Sets the {@link #authorization} header as specified in <a
   * href="http://tools.ietf.org/html/rfc2617#section-2">Basic Authentication Scheme</a>.
   *
   * @since 1.2
   */
  public final void setBasicAuthentication(String username, String password) {
    String userPass =
        Preconditions.checkNotNull(username) + ":" + Preconditions.checkNotNull(password);
    String encoded = Base64.encodeBase64String(StringUtils.getBytesUtf8(userPass));
    authorization = "Basic " + encoded;
  }

  private static void addHeader(Logger logger,
      StringBuilder logbuf,
      StringBuilder curlbuf,
      LowLevelHttpRequest lowLevelHttpRequest,
      String name,
      Object value,
      Writer writer) throws IOException {
    // ignore nulls
    if (value == null || Data.isNull(value)) {
      return;
    }
    // compute value
    String stringValue =
        value instanceof Enum<?> ? FieldInfo.of((Enum<?>) value).getName() : value.toString();
    // log header
    String loggedStringValue = stringValue;
    if (("Authorization".equalsIgnoreCase(name) || "Cookie".equalsIgnoreCase(name))
        && (logger == null || !logger.isLoggable(Level.ALL))) {
      loggedStringValue = "<Not Logged>";
    }
    if (logbuf != null) {
      logbuf.append(name).append(": ");
      logbuf.append(loggedStringValue);
      logbuf.append(StringUtils.LINE_SEPARATOR);
    }
    if (curlbuf != null) {
      curlbuf.append(" -H '").append(name).append(": ").append(loggedStringValue).append("'");
    }
    // add header to lowLevelHttpRequest
    if (lowLevelHttpRequest != null) {
      lowLevelHttpRequest.addHeader(name, stringValue);
    }
    // add header to the writer
    if (writer != null) {
      writer.write(name);
      writer.write(": ");
      writer.write(stringValue);
      writer.write("\r\n");
    }
  }

  /**
   * Serializes headers to an {@link LowLevelHttpRequest}.
   *
   * @param headers HTTP headers
   * @param logbuf log buffer or {@code null} for none
   * @param logger logger or {@code null} for none. Logger must be specified if log buffer is
   *        specified
   * @param lowLevelHttpRequest low level HTTP request where HTTP headers will be serialized to or
   *        {@code null} for none
   *
   * @since 1.9
   * @deprecated (scheduled to be removed in 1.12)
   */
  @Deprecated
  public static void serializeHeaders(HttpHeaders headers, StringBuilder logbuf, Logger logger,
      LowLevelHttpRequest lowLevelHttpRequest) throws IOException {
    serializeHeaders(headers, logbuf, null, logger, lowLevelHttpRequest, null);
  }

  /**
   * Serializes headers to an {@link LowLevelHttpRequest}.
   *
   * @param headers HTTP headers
   * @param logbuf log buffer or {@code null} for none
   * @param curlbuf log buffer for logging curl requests or {@code null} for none
   * @param logger logger or {@code null} for none. Logger must be specified if log buffer is
   *        specified
   * @param lowLevelHttpRequest low level HTTP request where HTTP headers will be serialized to or
   *        {@code null} for none
   */
  static void serializeHeaders(HttpHeaders headers, StringBuilder logbuf,
      StringBuilder curlbuf, Logger logger, LowLevelHttpRequest lowLevelHttpRequest)
      throws IOException {
    serializeHeaders(headers, logbuf, curlbuf, logger, lowLevelHttpRequest, null);
  }

  /**
   * Serializes headers to an {@link Writer} for Multi-part requests.
   *
   * @param headers HTTP headers
   * @param logbuf log buffer or {@code null} for none
   * @param logger logger or {@code null} for none. Logger must be specified if log buffer is
   *        specified
   * @param writer Writer where HTTP headers will be serialized to or {@code null} for none
   *
   * @since 1.9
   */
  public static void serializeHeadersForMultipartRequests(
      HttpHeaders headers, StringBuilder logbuf, Logger logger, Writer writer) throws IOException {
    serializeHeaders(headers, logbuf, null, logger, null, writer);
  }

  private static void serializeHeaders(HttpHeaders headers,
      StringBuilder logbuf,
      StringBuilder curlbuf,
      Logger logger,
      LowLevelHttpRequest lowLevelHttpRequest,
      Writer writer) throws IOException {
    HashSet<String> headerNames = new HashSet<String>();
    for (Map.Entry<String, Object> headerEntry : headers.entrySet()) {
      String name = headerEntry.getKey();
      Preconditions.checkArgument(headerNames.add(name),
          "multiple headers of the same name (headers are case insensitive): %s", name);
      Object value = headerEntry.getValue();
      if (value != null) {
        // compute the display name from the declared field name to fix capitalization
        String displayName = name;
        FieldInfo fieldInfo = headers.getClassInfo().getFieldInfo(name);
        if (fieldInfo != null) {
          displayName = fieldInfo.getName();
        }
        Class<? extends Object> valueClass = value.getClass();
        if (value instanceof Iterable<?> || valueClass.isArray()) {
          for (Object repeatedValue : Types.iterableOf(value)) {
            addHeader(logger,
                logbuf,
                curlbuf,
                lowLevelHttpRequest,
                displayName,
                repeatedValue,
                writer);
          }
        } else {
          addHeader(logger, logbuf, curlbuf, lowLevelHttpRequest, displayName, value, writer);
        }
      }
    }
    if (writer != null) {
      writer.flush();
    }
  }

  /**
   * Puts all headers of the {@link LowLevelHttpResponse} into this {@link HttpHeaders} object.
   *
   * @param response Response from which the headers are copied
   * @param logger {@link StringBuilder} to which logging output is added or {@code null} to disable
   *        logging
   * @since 1.10
   */
  public final void fromHttpResponse(LowLevelHttpResponse response, StringBuilder logger) {
    ParseHeaderState state = new ParseHeaderState(this, logger);
    int headerCount = response.getHeaderCount();
    for (int i = 0; i < headerCount; i++) {
      parseHeader(response.getHeaderName(i), response.getHeaderValue(i), state);
    }
    state.finish();
  }

  /** LowLevelHttpRequest which will call the .parseHeader() method for every header added. */
  private static class HeaderParsingFakeLevelHttpRequest extends LowLevelHttpRequest {
    private final HttpHeaders target;
    private final ParseHeaderState state;

    HeaderParsingFakeLevelHttpRequest(HttpHeaders target, ParseHeaderState state) {
      this.target = target;
      this.state = state;
    }

    @Override
    public void addHeader(String name, String value) {
      target.parseHeader(name, value, state);
    }

    @Override
    public void setContent(HttpContent content) throws IOException {
      throw new UnsupportedOperationException();
    }

    @Override
    public LowLevelHttpResponse execute() throws IOException {
      throw new UnsupportedOperationException();
    }
  }


  /**
   * Puts all headers of the {@link HttpHeaders} object into this {@link HttpHeaders} object.
   *
   * @param headers {@link HttpHeaders} from where the headers are taken
   * @since 1.10
   */
  public final void fromHttpHeaders(HttpHeaders headers) {
    try {
      ParseHeaderState state = new ParseHeaderState(this, null);
      serializeHeaders(
          headers, null, null, null, new HeaderParsingFakeLevelHttpRequest(this, state));
      state.finish();
    } catch (IOException ex) {
      // Should never occur as we are dealing with a FakeLowLevelHttpRequest
      throw new IllegalStateException(ex);
    }
  }

  /** State container for {@link #parseHeader(String, String, ParseHeaderState)}. */
  private static final class ParseHeaderState {
    /** Target map where parsed values are stored. */
    final ArrayValueMap arrayValueMap;

    /** Logger if logging is enabled or {@code null} otherwise. */
    final StringBuilder logger;

    /** ClassInfo of the HttpHeaders. */
    final ClassInfo classInfo;

    /** List of types in the header context. */
    final List<Type> context;

    /**
     * Initializes a new ParseHeaderState.
     *
     * @param headers HttpHeaders object for which the headers are being parsed
     * @param logger Logger if logging is enabled or {@code null}
     */
    public ParseHeaderState(HttpHeaders headers, StringBuilder logger) {
      Class<? extends HttpHeaders> clazz = headers.getClass();
      this.context = Arrays.<Type>asList(clazz);
      this.classInfo = ClassInfo.of(clazz, true);
      this.logger = logger;
      this.arrayValueMap = new ArrayValueMap(headers);
    }

    /**
     * Finishes the parsing-process by setting all array-values.
     */
    void finish() {
      arrayValueMap.setValues();
    }
  }

  /** Parses the specified case-insensitive header pair into this HttpHeaders instance. */
  void parseHeader(String headerName, String headerValue, ParseHeaderState state) {
    List<Type> context = state.context;
    ClassInfo classInfo = state.classInfo;
    ArrayValueMap arrayValueMap = state.arrayValueMap;
    StringBuilder logger = state.logger;

    if (logger != null) {
      logger.append(headerName + ": " + headerValue).append(StringUtils.LINE_SEPARATOR);
    }
    // use field information if available
    FieldInfo fieldInfo = classInfo.getFieldInfo(headerName);
    if (fieldInfo != null) {
      Type type = Data.resolveWildcardTypeOrTypeVariable(context, fieldInfo.getGenericType());
      // type is now class, parameterized type, or generic array type
      if (Types.isArray(type)) {
        // array that can handle repeating values
        Class<?> rawArrayComponentType = Types.getRawArrayComponentType(
            context, Types.getArrayComponentType(type));
        arrayValueMap.put(fieldInfo.getField(), rawArrayComponentType,
            parseValue(rawArrayComponentType, context, headerValue));
      } else if (Types.isAssignableToOrFrom(
          Types.getRawArrayComponentType(context, type), Iterable.class)) {
        // iterable that can handle repeating values
        @SuppressWarnings("unchecked")
        Collection<Object> collection = (Collection<Object>) fieldInfo.getValue(this);
        if (collection == null) {
          collection = Data.newCollectionInstance(type);
          fieldInfo.setValue(this, collection);
        }
        Type subFieldType = type == Object.class ? null : Types.getIterableParameter(type);
        collection.add(parseValue(subFieldType, context, headerValue));
      } else {
        // parse value based on field type
        fieldInfo.setValue(this, parseValue(type, context, headerValue));
      }
    } else {
      // store header values in an array list
      @SuppressWarnings("unchecked")
      ArrayList<String> listValue = (ArrayList<String>) this.get(headerName);
      if (listValue == null) {
        listValue = new ArrayList<String>();
        this.set(headerName, listValue);
      }
      listValue.add(headerValue);
    }
  }

  private static Object parseValue(Type valueType, List<Type> context, String value) {
    Type resolved = Data.resolveWildcardTypeOrTypeVariable(context, valueType);
    return Data.parsePrimitiveValue(resolved, value);
  }

  // TODO(yanivi): override equals and hashCode
}
TOP

Related Classes of com.google.api.client.http.HttpHeaders$HeaderParsingFakeLevelHttpRequest

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.