mirror of https://github.com/shred/acme4j
Enhance Accept-Language header
Before this patch, it was only the language tag of the selected Locale. Now it also offers the language itself (without the country) and any other available language as fallback. It is also possible to set the locale to null, which will accept any language.pull/140/head
parent
b0287d4d94
commit
41bc574f75
|
@ -32,6 +32,7 @@ import org.shredzone.acme4j.connector.Resource;
|
|||
import org.shredzone.acme4j.exception.AcmeException;
|
||||
import org.shredzone.acme4j.provider.AcmeProvider;
|
||||
import org.shredzone.acme4j.provider.GenericAcmeProvider;
|
||||
import org.shredzone.acme4j.toolbox.AcmeUtils;
|
||||
import org.shredzone.acme4j.toolbox.JSON;
|
||||
import org.shredzone.acme4j.toolbox.JSON.Value;
|
||||
|
||||
|
@ -49,7 +50,8 @@ public class Session {
|
|||
private final AcmeProvider provider;
|
||||
|
||||
private @Nullable String nonce;
|
||||
private Locale locale = Locale.getDefault();
|
||||
private @Nullable Locale locale = Locale.getDefault();
|
||||
private String languageHeader = AcmeUtils.localeToLanguageHeader(Locale.getDefault());
|
||||
protected @Nullable ZonedDateTime directoryLastModified;
|
||||
protected @Nullable ZonedDateTime directoryExpires;
|
||||
|
||||
|
@ -147,8 +149,10 @@ public class Session {
|
|||
}
|
||||
|
||||
/**
|
||||
* Gets the current locale of this session.
|
||||
* Gets the current locale of this session, or {@code null} if no special language is
|
||||
* selected.
|
||||
*/
|
||||
@Nullable
|
||||
public Locale getLocale() {
|
||||
return locale;
|
||||
}
|
||||
|
@ -156,9 +160,21 @@ public class Session {
|
|||
/**
|
||||
* Sets the locale used in this session. The locale is passed to the server as
|
||||
* Accept-Language header. The server <em>may</em> respond with localized messages.
|
||||
* The default is the system's language. If set to {@code null}, no special language
|
||||
* is selected.
|
||||
*/
|
||||
public void setLocale(@Nullable Locale locale) {
|
||||
this.locale = locale != null ? locale : Locale.getDefault();
|
||||
this.locale = locale;
|
||||
this.languageHeader = AcmeUtils.localeToLanguageHeader(locale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an Accept-Language header value that matches the current locale.
|
||||
*
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public String getLanguageHeader() {
|
||||
return languageHeader;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -347,7 +347,7 @@ public class DefaultConnection implements Connection {
|
|||
try {
|
||||
var builder = httpConnector.createRequestBuilder(url)
|
||||
.header(ACCEPT_CHARSET_HEADER, DEFAULT_CHARSET)
|
||||
.header(ACCEPT_LANGUAGE_HEADER, session.getLocale().toLanguageTag());
|
||||
.header(ACCEPT_LANGUAGE_HEADER, session.getLanguageHeader());
|
||||
|
||||
if (session.networkSettings().isCompressionEnabled()) {
|
||||
builder.header(ACCEPT_ENCODING_HEADER, "gzip");
|
||||
|
|
|
@ -26,6 +26,7 @@ import java.time.Instant;
|
|||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.Base64;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
|
@ -221,6 +222,29 @@ public final class AcmeUtils {
|
|||
ZoneId.of(tz)).toInstant();
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the given locale to an Accept-Language header value.
|
||||
*
|
||||
* @param locale
|
||||
* {@link Locale} to be used in the header
|
||||
* @return Value that can be used in an Accept-Language header
|
||||
*/
|
||||
public static String localeToLanguageHeader(@Nullable Locale locale) {
|
||||
if (locale == null || "und".equals(locale.toLanguageTag())) {
|
||||
return "*";
|
||||
}
|
||||
|
||||
var langTag = locale.toLanguageTag();
|
||||
|
||||
var header = new StringBuilder(langTag);
|
||||
if (langTag.indexOf('-') >= 0) {
|
||||
header.append(',').append(locale.getLanguage()).append(";q=0.8");
|
||||
}
|
||||
header.append(",*;q=0.1");
|
||||
|
||||
return header.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Strips the acme error prefix from the error string.
|
||||
* <p>
|
||||
|
|
|
@ -23,6 +23,7 @@ import java.net.URI;
|
|||
import java.net.URL;
|
||||
import java.time.Duration;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.Locale;
|
||||
|
||||
import org.assertj.core.api.AutoCloseableSoftAssertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
@ -31,6 +32,7 @@ import org.shredzone.acme4j.connector.Resource;
|
|||
import org.shredzone.acme4j.exception.AcmeException;
|
||||
import org.shredzone.acme4j.provider.AcmeProvider;
|
||||
import org.shredzone.acme4j.provider.GenericAcmeProvider;
|
||||
import org.shredzone.acme4j.toolbox.AcmeUtils;
|
||||
import org.shredzone.acme4j.toolbox.TestUtils;
|
||||
|
||||
/**
|
||||
|
@ -212,4 +214,28 @@ public class SessionTest {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that the locale is properly set.
|
||||
*/
|
||||
@Test
|
||||
public void testLocale() {
|
||||
var session = new Session(URI.create(TestUtils.ACME_SERVER_URI));
|
||||
|
||||
// default configuration
|
||||
assertThat(session.getLocale())
|
||||
.isEqualTo(Locale.getDefault());
|
||||
assertThat(session.getLanguageHeader())
|
||||
.isEqualTo(AcmeUtils.localeToLanguageHeader(Locale.getDefault()));
|
||||
|
||||
// null
|
||||
session.setLocale(null);
|
||||
assertThat(session.getLocale()).isNull();
|
||||
assertThat(session.getLanguageHeader()).isEqualTo("*");
|
||||
|
||||
// a locale
|
||||
session.setLocale(Locale.CANADA_FRENCH);
|
||||
assertThat(session.getLocale()).isEqualTo(Locale.CANADA_FRENCH);
|
||||
assertThat(session.getLanguageHeader()).isEqualTo("fr-CA,fr;q=0.8,*;q=0.1");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -72,6 +72,9 @@ public class DefaultConnectionTest {
|
|||
private static final String DIRECTORY_PATH = "/dir";
|
||||
private static final String NEW_NONCE_PATH = "/newNonce";
|
||||
private static final String REQUEST_PATH = "/test/test";
|
||||
private static final String TEST_ACCEPT_LANGUAGE = "ja-JP,ja;q=0.8,*;q=0.1";
|
||||
private static final String TEST_ACCEPT_CHARSET = "utf-8";
|
||||
private static final String TEST_USER_AGENT_PATTERN = "^acme4j/.*$";
|
||||
|
||||
private final URL accountUrl = TestUtils.url(TestUtils.ACCOUNT_URL);
|
||||
private Session session;
|
||||
|
@ -564,9 +567,9 @@ public class DefaultConnectionTest {
|
|||
|
||||
verify(getRequestedFor(urlEqualTo(REQUEST_PATH))
|
||||
.withHeader("Accept", equalTo("application/json"))
|
||||
.withHeader("Accept-Charset", equalTo("utf-8"))
|
||||
.withHeader("Accept-Language", equalTo("ja-JP"))
|
||||
.withHeader("User-Agent", matching("^acme4j/.*$"))
|
||||
.withHeader("Accept-Charset", equalTo(TEST_ACCEPT_CHARSET))
|
||||
.withHeader("Accept-Language", equalTo(TEST_ACCEPT_LANGUAGE))
|
||||
.withHeader("User-Agent", matching(TEST_USER_AGENT_PATTERN))
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -589,9 +592,9 @@ public class DefaultConnectionTest {
|
|||
verify(getRequestedFor(urlEqualTo(REQUEST_PATH))
|
||||
.withHeader("If-Modified-Since", equalToDateTime(ifModifiedSince))
|
||||
.withHeader("Accept", equalTo("application/json"))
|
||||
.withHeader("Accept-Charset", equalTo("utf-8"))
|
||||
.withHeader("Accept-Language", equalTo("ja-JP"))
|
||||
.withHeader("User-Agent", matching("^acme4j/.*$"))
|
||||
.withHeader("Accept-Charset", equalTo(TEST_ACCEPT_CHARSET))
|
||||
.withHeader("Accept-Language", equalTo(TEST_ACCEPT_LANGUAGE))
|
||||
.withHeader("User-Agent", matching(TEST_USER_AGENT_PATTERN))
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -620,9 +623,9 @@ public class DefaultConnectionTest {
|
|||
|
||||
verify(postRequestedFor(urlEqualTo(REQUEST_PATH))
|
||||
.withHeader("Accept", equalTo("application/json"))
|
||||
.withHeader("Accept-Charset", equalTo("utf-8"))
|
||||
.withHeader("Accept-Language", equalTo("ja-JP"))
|
||||
.withHeader("User-Agent", matching("^acme4j/.*$"))
|
||||
.withHeader("Accept-Charset", equalTo(TEST_ACCEPT_CHARSET))
|
||||
.withHeader("Accept-Language", equalTo(TEST_ACCEPT_LANGUAGE))
|
||||
.withHeader("User-Agent", matching(TEST_USER_AGENT_PATTERN))
|
||||
);
|
||||
|
||||
var requests = findAll(postRequestedFor(urlEqualTo(REQUEST_PATH)));
|
||||
|
@ -673,10 +676,10 @@ public class DefaultConnectionTest {
|
|||
|
||||
verify(postRequestedFor(urlEqualTo(REQUEST_PATH))
|
||||
.withHeader("Accept", equalTo("application/json"))
|
||||
.withHeader("Accept-Charset", equalTo("utf-8"))
|
||||
.withHeader("Accept-Language", equalTo("ja-JP"))
|
||||
.withHeader("Accept-Charset", equalTo(TEST_ACCEPT_CHARSET))
|
||||
.withHeader("Accept-Language", equalTo(TEST_ACCEPT_LANGUAGE))
|
||||
.withHeader("Content-Type", equalTo("application/jose+json"))
|
||||
.withHeader("User-Agent", matching("^acme4j/.*$"))
|
||||
.withHeader("User-Agent", matching(TEST_USER_AGENT_PATTERN))
|
||||
);
|
||||
|
||||
var requests = findAll(postRequestedFor(urlEqualTo(REQUEST_PATH)));
|
||||
|
@ -727,10 +730,10 @@ public class DefaultConnectionTest {
|
|||
|
||||
verify(postRequestedFor(urlEqualTo(REQUEST_PATH))
|
||||
.withHeader("Accept", equalTo("application/pem-certificate-chain"))
|
||||
.withHeader("Accept-Charset", equalTo("utf-8"))
|
||||
.withHeader("Accept-Language", equalTo("ja-JP"))
|
||||
.withHeader("Accept-Charset", equalTo(TEST_ACCEPT_CHARSET))
|
||||
.withHeader("Accept-Language", equalTo(TEST_ACCEPT_LANGUAGE))
|
||||
.withHeader("Content-Type", equalTo("application/jose+json"))
|
||||
.withHeader("User-Agent", matching("^acme4j/.*$"))
|
||||
.withHeader("User-Agent", matching(TEST_USER_AGENT_PATTERN))
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -758,10 +761,10 @@ public class DefaultConnectionTest {
|
|||
|
||||
verify(postRequestedFor(urlEqualTo(REQUEST_PATH))
|
||||
.withHeader("Accept", equalTo("application/json"))
|
||||
.withHeader("Accept-Charset", equalTo("utf-8"))
|
||||
.withHeader("Accept-Language", equalTo("ja-JP"))
|
||||
.withHeader("Accept-Charset", equalTo(TEST_ACCEPT_CHARSET))
|
||||
.withHeader("Accept-Language", equalTo(TEST_ACCEPT_LANGUAGE))
|
||||
.withHeader("Content-Type", equalTo("application/jose+json"))
|
||||
.withHeader("User-Agent", matching("^acme4j/.*$"))
|
||||
.withHeader("User-Agent", matching(TEST_USER_AGENT_PATTERN))
|
||||
);
|
||||
|
||||
var requests = findAll(postRequestedFor(urlEqualTo(REQUEST_PATH)));
|
||||
|
|
|
@ -27,6 +27,7 @@ import java.net.URI;
|
|||
import java.security.Security;
|
||||
import java.security.cert.CertificateEncodingException;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.Locale;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||
|
@ -201,6 +202,25 @@ public class AcmeUtilsTest {
|
|||
"accepted string without time");
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that locales are correctly converted to language headers.
|
||||
*/
|
||||
@Test
|
||||
public void testLocaleToLanguageHeader() {
|
||||
assertThat(localeToLanguageHeader(Locale.ENGLISH))
|
||||
.isEqualTo("en,*;q=0.1");
|
||||
assertThat(localeToLanguageHeader(new Locale("en", "US")))
|
||||
.isEqualTo("en-US,en;q=0.8,*;q=0.1");
|
||||
assertThat(localeToLanguageHeader(Locale.GERMAN))
|
||||
.isEqualTo("de,*;q=0.1");
|
||||
assertThat(localeToLanguageHeader(Locale.GERMANY))
|
||||
.isEqualTo("de-DE,de;q=0.8,*;q=0.1");
|
||||
assertThat(localeToLanguageHeader(new Locale("")))
|
||||
.isEqualTo("*");
|
||||
assertThat(localeToLanguageHeader(null))
|
||||
.isEqualTo("*");
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that error prefix is correctly removed.
|
||||
*/
|
||||
|
|
Loading…
Reference in New Issue