/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 org.jooby.internal; import java.time.Instant; import java.time.ZoneId; import java.time.format.DateTimeFormatter; import java.util.Locale; import java.util.Optional; import java.util.function.Consumer; import org.jooby.Cookie; public class CookieImpl implements Cookie { static final DateTimeFormatter fmt = DateTimeFormatter .ofPattern("EEE, dd-MMM-yyyy HH:mm:ss z", Locale.ENGLISH) .withZone(ZoneId.of("GMT")); private static final String __COOKIE_DELIM = "\",;\\ \t"; private String name; private Optional<String> value; private Optional<String> comment; private Optional<String> domain; private int maxAge; private Optional<String> path; private boolean secure; private boolean httpOnly; public CookieImpl(final Cookie.Definition cookie) { this.name = cookie.name().get(); this.value = cookie.value(); this.comment = cookie.comment(); this.domain = cookie.domain(); this.maxAge = cookie.maxAge().orElse(-1); this.path = cookie.path(); this.secure = cookie.secure().orElse(Boolean.FALSE); this.httpOnly = cookie.httpOnly().orElse(Boolean.FALSE); } @Override public String name() { return name; } @Override public Optional<String> value() { return value; } @Override public Optional<String> comment() { return comment; } @Override public Optional<String> domain() { return domain; } @Override public int maxAge() { return maxAge; } @Override public Optional<String> path() { return path; } @Override public boolean secure() { return secure; } @Override public boolean httpOnly() { return httpOnly; } @Override public String encode() { StringBuilder sb = new StringBuilder(); Consumer<String> appender = (str) -> { if (needQuote(str)) { sb.append('"'); for (int i = 0; i < str.length(); ++i) { char c = str.charAt(i); if (c == '"' || c == '\\') { sb.append('\\'); } sb.append(c); } sb.append('"'); } else { sb.append(str); } }; // name = value appender.accept(name()); sb.append("="); value().ifPresent(appender); sb.append(";Version=1"); // Path path().ifPresent(path -> { sb.append(";Path="); appender.accept(path); }); // Domain domain().ifPresent(domain -> { sb.append(";Domain="); appender.accept(domain); }); // Secure if (secure()) { sb.append(";Secure"); } // HttpOnly if (httpOnly()) { sb.append(";HttpOnly"); } // Max-Age int maxAge = maxAge(); if (maxAge >= 0) { sb.append(";Max-Age=").append(maxAge); Instant instant = Instant .ofEpochMilli(maxAge > 0 ? System.currentTimeMillis() + maxAge * 1000L : 0); sb.append(";Expires=").append(fmt.format(instant)); } // Comment comment().ifPresent(comment -> { sb.append(";Comment="); appender.accept(comment); }); return sb.toString(); } @Override public String toString() { return encode(); } private static boolean needQuote(final String s) { if (s.length() > 1 && s.charAt(0) == '"' && s.charAt(s.length() - 1) == '"') { return false; } for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); if (__COOKIE_DELIM.indexOf(c) >= 0) { return true; } if (c < 0x20 || c >= 0x7f) { throw new IllegalArgumentException("Illegal character fount at: [" + i + "]"); } } return false; } }