/**
* 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;
}
}