Accept Content-Type header with charset parameter

Backport of 42541ac2
pull/45/head
Richard Körber 2017-11-02 23:19:31 +01:00
parent d666ec091c
commit 1289e2f5e8
3 changed files with 60 additions and 3 deletions

View File

@ -192,7 +192,8 @@ public class DefaultConnection implements Connection {
return match.getAsInt();
}
if (!"application/problem+json".equals(conn.getHeaderField(CONTENT_TYPE_HEADER))) {
String contentType = AcmeUtils.getContentType(conn.getHeaderField(CONTENT_TYPE_HEADER));
if (!"application/problem+json".equals(contentType)) {
throw new AcmeException("HTTP " + rc + ": " + conn.getResponseMessage());
}
@ -212,7 +213,7 @@ public class DefaultConnection implements Connection {
public JSON readJsonResponse() throws AcmeException {
assertConnectionIsOpen();
String contentType = conn.getHeaderField(CONTENT_TYPE_HEADER);
String contentType = AcmeUtils.getContentType(conn.getHeaderField(CONTENT_TYPE_HEADER));
if (!("application/json".equals(contentType)
|| "application/problem+json".equals(contentType))) {
throw new AcmeProtocolException("Unexpected content type: " + contentType);
@ -238,7 +239,7 @@ public class DefaultConnection implements Connection {
public X509Certificate readCertificate() throws AcmeException {
assertConnectionIsOpen();
String contentType = conn.getHeaderField(CONTENT_TYPE_HEADER);
String contentType = AcmeUtils.getContentType(conn.getHeaderField(CONTENT_TYPE_HEADER));
if (!("application/pkix-cert".equals(contentType))) {
throw new AcmeProtocolException("Unexpected content type: " + contentType);
}

View File

@ -51,6 +51,9 @@ public final class AcmeUtils {
private static final Pattern TZ_PATTERN = Pattern.compile(
"([+-])(\\d{2}):?(\\d{2})$");
private final static Pattern CONTENT_TYPE_PATTERN = Pattern.compile(
"([^;]+)(?:;.*?charset=(\"?)([a-z0-9_-]+)(\\2))?.*", Pattern.CASE_INSENSITIVE);
private AcmeUtils() {
// Utility class without constructor
}
@ -224,4 +227,27 @@ public final class AcmeUtils {
}
}
/**
* Extracts the content type of a Content-Type header.
*
* @param header
* Content-Type header
* @return Content-Type, or {@code null} if the header was invalid or empty
* @throws AcmeProtocolException
* if the Content-Type header contains a different charset than "utf-8".
*/
public static String getContentType(String header) {
if (header != null) {
Matcher m = CONTENT_TYPE_PATTERN.matcher(header);
if (m.matches()) {
String charset = m.group(3);
if (charset != null && !"utf-8".equalsIgnoreCase(charset)) {
throw new AcmeProtocolException("Unsupported charset " + charset);
}
return m.group(1).trim().toLowerCase();
}
}
return null;
}
}

View File

@ -32,6 +32,7 @@ import org.hamcrest.Description;
import org.jose4j.jwk.PublicJsonWebKey;
import org.junit.BeforeClass;
import org.junit.Test;
import org.shredzone.acme4j.exception.AcmeProtocolException;
/**
* Unit tests for {@link AcmeUtils}.
@ -231,6 +232,35 @@ public class AcmeUtilsTest {
assertThat(stripErrorPrefix(null), is(nullValue()));
}
/**
* Test {@link AcmeUtils#getContentType(String)}.
*/
@Test
public void testGetContentType() {
assertThat(AcmeUtils.getContentType(null), is(nullValue()));
assertThat(AcmeUtils.getContentType("application/json"),
is("application/json"));
assertThat(AcmeUtils.getContentType("Application/Problem+JSON"),
is("application/problem+json"));
assertThat(AcmeUtils.getContentType("application/json; charset=utf-8"),
is("application/json"));
assertThat(AcmeUtils.getContentType("application/json; charset=utf-8 (Plain text)"),
is("application/json"));
assertThat(AcmeUtils.getContentType("application/json; charset=\"utf-8\""),
is("application/json"));
assertThat(AcmeUtils.getContentType("application/json; charset=\"UTF-8\"; foo=4"),
is("application/json"));
assertThat(AcmeUtils.getContentType(" application/json ;foo=4"),
is("application/json"));
try {
AcmeUtils.getContentType("application/json; charset=\"iso-8859-1\"");
fail("Accepted bad charset");
} catch (AcmeProtocolException ex) {
// expected
}
}
/**
* Matches the given time.
*/