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

io.vtom.vertx.pipeline.component.web.client.enoa.HttpCookie Maven / Gradle / Ivy

There is a newer version: 1.1-beta
Show newest version
/*
 * Copyright (c) 2018, enoa ([email protected])
 *
 * 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 io.vtom.vertx.pipeline.component.web.client.enoa;

import java.net.IDN;
import java.util.Date;
import java.util.Locale;

public class HttpCookie {


  private final String name;
  private final String value;
  private final long expires;
  private final String domain;
  private final String path;
  private final boolean secure;
  private final boolean httpOnly;

  private final boolean persistent;
  private final boolean hostOnly;

  private HttpCookie(String name, String value, long expires, String domain, String path,
                     boolean secure, boolean httpOnly, boolean hostOnly, boolean persistent) {
    this.name = name;
    this.value = value;
    this.expires = expires;
    this.domain = domain;
    this.path = path;
    this.secure = secure;
    this.httpOnly = httpOnly;
    this.hostOnly = hostOnly;
    this.persistent = persistent;
  }

  HttpCookie(Builder builder) {
    if (builder.name == null) throw new NullPointerException("builder.name == null");
    if (builder.value == null) throw new NullPointerException("builder.value == null");
//    if (builder.domain == null) throw new NullPointerException("builder.domain == null");

    this.name = builder.name;
    this.value = builder.value;
    this.expires = builder.expiresAt;
    this.domain = builder.domain;
    this.path = builder.path;
    this.secure = builder.secure;
    this.httpOnly = builder.httpOnly;
    this.persistent = builder.persistent;
    this.hostOnly = builder.hostOnly;
  }

//  public static Set of(String text) {
//    if (text == null || "".equals(text))
//      return Collections.emptySet();
//    String[] lines = text.split("\n");
//    Set rets = new HashSet<>(lines.length);
//    for (String line : lines) {
//      rets.add(single(line));
//    }
//    return rets;
//  }

  public static HttpCookie single(String text) {
    if (text == null || "".equals(text))
      return null;
    String[] items = text.split(";");
    Builder builder = new Builder();
    for (String item : items) {
      int eix = item.indexOf("=");
//      if (eix == -1) {
//        builder.name(item.trim());
//        continue;
//      }
      String name = eix > -1 ? item.substring(0, eix).trim() : item.trim();
      String value = null;
      if (eix > -1) {
        value = item.substring(eix + 1).trim();
      }
      switch (name.toLowerCase()) {
        case "max-age":
          builder.expires(0);
          break;
        case "expires":
//          try {
//            if (value != null) {
//              int expires = Integer.parseInt(value);
//              builder.expires(expires);
//            }
//          } catch (Exception ignored) {
//          }
          // todo parse expires
          break;
        case "domain":
          if (value != null)
            builder.domain(value);
          break;
        case "path":
          if (value != null)
            builder.path(value);
          break;
        case "secure":
          builder.secure();
          break;
        case "httponly":
          builder.httpOnly();
          break;
        default:
          if (value != null) {
            builder.name(name);
            builder.value(value);
          }
          break;
      }
    }

    return builder.build();
  }

  public static Builder builder(String name, String value) {
    return new Builder().name(name).value(value);
  }

  public String name() {
    return name;
  }

  public String value() {
    return value;
  }

  public boolean persistent() {
    return persistent;
  }

  public long expires() {
    return expires;
  }

  public boolean hostOnly() {
    return hostOnly;
  }

  public String domain() {
    return domain;
  }

  public String path() {
    return path;
  }

  public boolean httpOnly() {
    return httpOnly;
  }

  public boolean secure() {
    return secure;
  }


  public static final class Builder {
    String name;
    String value;
    long expiresAt = HttpDate.MAX_DATE;
    String domain;
    String path = "/";
    boolean secure;
    boolean httpOnly;
    boolean persistent;
    boolean hostOnly;

    private static String domainToAscii(String input) {
      try {
        boolean dotfirst = input.charAt(0) == '.';
        if (input.charAt(0) == '.')
          input = input.substring(1);
        String result = IDN.toASCII(input).toLowerCase(Locale.US);
        if (result.isEmpty()) return null;
        if (dotfirst)
          result = '.' + result;
        if (containsInvalidHostnameAsciiCodes(result)) {
          return null;
        }
        return result;
      } catch (IllegalArgumentException e) {
        return null;
      }
    }

    private static boolean containsInvalidHostnameAsciiCodes(String hostnameAscii) {
      for (int i = 0; i < hostnameAscii.length(); i++) {
        char c = hostnameAscii.charAt(i);
        if (c <= '\u001f' || c >= '\u007f') {
          return true;
        }
        if (" #%/:?@[\\]".indexOf(c) != -1) {
          return true;
        }
      }
      return false;
    }

    public Builder name(String name) {
      if (name == null) throw new NullPointerException("name == null");
      if (!name.trim().equals(name)) throw new IllegalArgumentException("name is not trimmed");
      this.name = name;
      return this;
    }

    public Builder value(String value) {
      if (value == null) throw new NullPointerException("value == null");
      if (!value.trim().equals(value)) throw new IllegalArgumentException("value is not trimmed");
      this.value = value;
      return this;
    }

    public Builder expires(long expiresAt) {
      if (expiresAt <= 0) expiresAt = Long.MIN_VALUE;
      if (expiresAt > HttpDate.MAX_DATE) expiresAt = HttpDate.MAX_DATE;
      this.expiresAt = expiresAt;
      this.persistent = true;
      return this;
    }

    public Builder domain(String domain) {
      return domain(domain, false);
    }

    /**
     * Set the host-only domain for this cookie. The cookie will match {@code domain} but none of
     * its subdomains.
     */
    public Builder hostOnlyDomain(String domain) {
      return domain(domain, true);
    }

    private Builder domain(String domain, boolean hostOnly) {
      if (domain == null) throw new NullPointerException("domain == null");
      String canonicalDomain = domainToAscii(domain);
      if (canonicalDomain == null) {
        throw new IllegalArgumentException("unexpected domain: " + domain);
      }
      this.domain = canonicalDomain;
      this.hostOnly = hostOnly;
      return this;
    }

    public Builder path(String path) {
      if (path != null && !path.startsWith("/"))
        throw new IllegalArgumentException("path must start with '/'");
      this.path = path;
      return this;
    }

    public Builder secure() {
      this.secure = true;
      return this;
    }

    public Builder httpOnly() {
      this.httpOnly = true;
      return this;
    }

    public HttpCookie build() {
      return new HttpCookie(this);
    }
  }


  @Override
  public String toString() {
    return toString(false);
  }

  /**
   * @param forObsoleteRfc2965 true to include a leading {@code .} on the domain pattern. This is
   *                           necessary for {@code example.com} to match {@code www.example.com} under RFC 2965. This
   *                           extra dot is ignored by more recent specifications.
   */
  String toString(boolean forObsoleteRfc2965) {
    StringBuilder result = new StringBuilder();
    result.append(name);
    result.append('=');
    result.append(value);

    if (persistent) {
      if (expires == Long.MIN_VALUE) {
        result.append("; max-age=0");
      } else {
        result.append("; expires=").append(HttpDate.format(new Date(expires)));
      }
    }

    if (!hostOnly) {
      if (this.domain != null) {
        result.append("; domain=");
        if (forObsoleteRfc2965) {
          result.append(".");
        }
        result.append(domain);
      }
    }

    result.append("; path=").append(path);

    if (secure) {
      result.append("; secure");
    }

    if (httpOnly) {
      result.append("; httponly");
    }

    return result.toString();
  }

  @Override
  public boolean equals(Object other) {
    if (!(other instanceof HttpCookie)) return false;
    HttpCookie that = (HttpCookie) other;
    return that.name.equals(name)
      && that.value.equals(value)
      && (that.domain != null && this.domain != null) ? that.domain.equals(this.domain) : that.domain == null && this.domain == null
      && (that.path != null && this.path != null) ? that.path.equals(this.path) : that.path == null && this.path == null
      && that.expires == expires
      && that.secure == secure
      && that.httpOnly == httpOnly
      && that.persistent == persistent
      && that.hostOnly == hostOnly;
  }

  @Override
  public int hashCode() {
    int hash = 17;
    hash = 31 * hash + name.hashCode();
    hash = 31 * hash + value.hashCode();
    hash = 31 * hash + (domain == null ? 0 : domain.hashCode());
    hash = 31 * hash + (path == null ? 0 : path.hashCode());
    hash = 31 * hash + (int) (expires ^ (expires >>> 32));
    hash = 31 * hash + (secure ? 0 : 1);
    hash = 31 * hash + (httpOnly ? 0 : 1);
    hash = 31 * hash + (persistent ? 0 : 1);
    hash = 31 * hash + (hostOnly ? 0 : 1);
    return hash;
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy