You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2023/08/22 05:15:51 UTC
[camel] branch main updated: CAMEL-19768: Ensure content is encoded with expected charset (#11160)
This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/main by this push:
new dc1ca4e5580 CAMEL-19768: Ensure content is encoded with expected charset (#11160)
dc1ca4e5580 is described below
commit dc1ca4e55806faa5b17c91f4dd33d47c2b8ee007
Author: Michael Pätzold <pa...@gmail.com>
AuthorDate: Tue Aug 22 07:15:44 2023 +0200
CAMEL-19768: Ensure content is encoded with expected charset (#11160)
* CAMEL-19768: Ensure content type contains charset information to encode message body accordingly
* CAMEL-19768: Replace special chars with their escaped unicode representation
---------
Co-authored-by: Michael Pätzold <mi...@aoe.com>
---
.../apache/camel/component/http/HttpProducer.java | 14 ++-
.../HttpProducerContentTypeWithCharsetTest.java | 98 +++++++++++++++++++
.../http/HttpProducerWithSpecialCharsBodyTest.java | 104 +++++++++++++++++++++
3 files changed, 208 insertions(+), 8 deletions(-)
diff --git a/components/camel-http/src/main/java/org/apache/camel/component/http/HttpProducer.java b/components/camel-http/src/main/java/org/apache/camel/component/http/HttpProducer.java
index ba92e6e8c63..da9c7e0d034 100644
--- a/components/camel-http/src/main/java/org/apache/camel/component/http/HttpProducer.java
+++ b/components/camel-http/src/main/java/org/apache/camel/component/http/HttpProducer.java
@@ -772,15 +772,13 @@ public class HttpProducer extends DefaultProducer {
charset = cs.name();
}
}
- final StringEntity entity;
- if (contentType != null) {
- entity = new StringEntity(content, contentType);
- } else if (charset != null) {
- entity = new StringEntity(content, null, charset, false);
- } else {
- entity = new StringEntity(content, null, null, false);
+
+ // sync contentType.charset and charset
+ if (contentType != null && contentType.getCharset() == null && charset != null) {
+ contentType = ContentType.parse(contentType + ";charset=" + charset);
}
- answer = entity;
+
+ answer = new StringEntity(content, contentType, charset, false);
}
// fallback as input stream
diff --git a/components/camel-http/src/test/java/org/apache/camel/component/http/HttpProducerContentTypeWithCharsetTest.java b/components/camel-http/src/test/java/org/apache/camel/component/http/HttpProducerContentTypeWithCharsetTest.java
new file mode 100644
index 00000000000..eef598225ef
--- /dev/null
+++ b/components/camel-http/src/test/java/org/apache/camel/component/http/HttpProducerContentTypeWithCharsetTest.java
@@ -0,0 +1,98 @@
+/*
+ * 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.apache.camel.component.http;
+
+import java.nio.charset.StandardCharsets;
+
+import org.apache.camel.Exchange;
+import org.apache.hc.core5.http.HttpStatus;
+import org.apache.hc.core5.http.impl.bootstrap.HttpServer;
+import org.apache.hc.core5.http.impl.bootstrap.ServerBootstrap;
+import org.apache.hc.core5.http.io.entity.StringEntity;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+public class HttpProducerContentTypeWithCharsetTest extends BaseHttpTest {
+
+ private static final String CONTENT_TYPE_WITHOUT_CHARSET
+ = "multipart/form-data;boundary=---------------------------j2radvtrk";
+ private static final String CONTENT_TYPE_WITH_CHARSET = CONTENT_TYPE_WITHOUT_CHARSET + ";charset=utf-8";
+
+ private HttpServer localServer;
+
+ private String endpointUrl;
+
+ @BeforeEach
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+
+ localServer = ServerBootstrap.bootstrap().setHttpProcessor(getBasicHttpProcessor())
+ .setConnectionReuseStrategy(getConnectionReuseStrategy()).setResponseFactory(getHttpResponseFactory())
+ .setSslContext(getSSLContext())
+ .register("/content", (request, response, context) -> {
+ String contentType = request.getFirstHeader(Exchange.CONTENT_TYPE).getValue();
+
+ assertEquals(CONTENT_TYPE_WITH_CHARSET.replace(";", "; "), contentType);
+
+ response.setEntity(new StringEntity(contentType, StandardCharsets.US_ASCII));
+ response.setCode(HttpStatus.SC_OK);
+ }).create();
+ localServer.start();
+
+ endpointUrl = "http://localhost:" + localServer.getLocalPort();
+ }
+
+ @AfterEach
+ @Override
+ public void tearDown() throws Exception {
+ super.tearDown();
+
+ if (localServer != null) {
+ localServer.stop();
+ }
+ }
+
+ @Test
+ void testContentTypeWithCharsetInCharsetHeader() throws Exception {
+ Exchange out = template.request(endpointUrl + "/content", exchange -> {
+ exchange.getIn().setHeader(Exchange.CONTENT_TYPE, CONTENT_TYPE_WITHOUT_CHARSET);
+ exchange.getIn().setHeader(Exchange.CHARSET_NAME, "utf-8");
+ exchange.getIn().setBody("This is content");
+ });
+
+ assertNotNull(out);
+ assertFalse(out.isFailed(), "Should not fail");
+ assertEquals(CONTENT_TYPE_WITH_CHARSET.replace(";", "; "), out.getMessage().getBody(String.class));
+ }
+
+ @Test
+ void testContentTypeWithCharsetInContentTypeHeader() throws Exception {
+ Exchange out = template.request(endpointUrl + "/content", exchange -> {
+ exchange.getIn().setHeader(Exchange.CONTENT_TYPE, CONTENT_TYPE_WITH_CHARSET);
+ exchange.getIn().setBody("This is content");
+ });
+
+ assertNotNull(out);
+ assertFalse(out.isFailed(), "Should not fail");
+ assertEquals(CONTENT_TYPE_WITH_CHARSET.replace(";", "; "), out.getMessage().getBody(String.class));
+ }
+
+}
diff --git a/components/camel-http/src/test/java/org/apache/camel/component/http/HttpProducerWithSpecialCharsBodyTest.java b/components/camel-http/src/test/java/org/apache/camel/component/http/HttpProducerWithSpecialCharsBodyTest.java
new file mode 100644
index 00000000000..b651f7ff1a0
--- /dev/null
+++ b/components/camel-http/src/test/java/org/apache/camel/component/http/HttpProducerWithSpecialCharsBodyTest.java
@@ -0,0 +1,104 @@
+/*
+ * 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.apache.camel.component.http;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+
+import org.apache.camel.CamelExchangeException;
+import org.apache.camel.Exchange;
+import org.apache.camel.Message;
+import org.apache.hc.core5.http.HttpEntity;
+import org.apache.hc.core5.http.io.entity.StringEntity;
+import org.junit.jupiter.api.Test;
+
+import static org.apache.camel.Exchange.CHARSET_NAME;
+import static org.apache.hc.core5.http.ContentType.APPLICATION_JSON;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+class HttpProducerWithSpecialCharsBodyTest {
+
+ private static final String TEST_MESSAGE_WITH_SPECIAL_CHARACTERS
+ = """
+ {
+ "description": "Example with special characters",
+ "BasicLatin": "\u0021 \u0023 \u0024 \u0025 \u0026 \u0027 \u0028 \u0029 \u002A \u002B \u002C \u002D \u002E \u002F \u003A \u003B \u003C \u003D \u003E \u003F",
+ "Latin1": "\u00A1 \u00BF \u00A9 \u00AE \u00F1 \u00F6 \u00FC",
+ "Greek": "\u0391 \u03B2 \u03B3 \u0394 \u03A9",
+ "Cyrillic": "\u0410 \u0431 \u0432 \u0413 \u0434",
+ "Hebrew": "\u05D0 \u05D1 \u05D2 \u05D3 \u05D4",
+ "Arabic": "\u0627 \u0628 \u062A \u062B \u062C",
+ "Devanagari": "\u0905 \u0906 \u0907 \u0908 \u0909",
+ "CJK Unified Ideographs" : "\u4E00 \u4E8C \u4E09 \u56DB \u4E94",
+ "Emoticons": "\uD83D\uDE00 \uD83D\uDE0E \uD83D\uDE0A \uD83C\uDF0D"
+ }
+ """;
+
+ private static final String APPLICATION_JSON_UTF8
+ = APPLICATION_JSON.getMimeType() + "; charset=" + StandardCharsets.UTF_8.name();
+
+ @Test
+ void createRequestEntityJsonUtf8ThroughContentType() throws CamelExchangeException, IOException {
+ HttpEndpoint httpEndpoint = mock(HttpEndpoint.class);
+ HttpProducer httpProducer = new HttpProducer(httpEndpoint);
+
+ Message message = mock(Message.class);
+ when(message.getBody()).thenReturn(TEST_MESSAGE_WITH_SPECIAL_CHARACTERS);
+ when(message.getHeader(Exchange.CONTENT_TYPE, String.class)).thenReturn(APPLICATION_JSON_UTF8);
+
+ Exchange exchange = mock(Exchange.class);
+ when(exchange.getIn()).thenReturn(message);
+
+ HttpEntity requestEntity = httpProducer.createRequestEntity(exchange);
+
+ assertTrue(requestEntity instanceof StringEntity);
+ StringEntity entity = (StringEntity) requestEntity;
+ assertEquals(APPLICATION_JSON_UTF8, entity.getContentType(), "Content type should be given content type and charset");
+ assertEquals(StandardCharsets.UTF_8.name(), entity.getContentEncoding(), "Content encoding should be given charset");
+ assertEquals(TEST_MESSAGE_WITH_SPECIAL_CHARACTERS,
+ new String(entity.getContent().readAllBytes(), StandardCharsets.UTF_8),
+ "Reading entity content with intended charset should result in the original (readable) message");
+ }
+
+ @Test
+ void createRequestEntityJsonUtf8ThroughCharset() throws CamelExchangeException, IOException {
+ HttpEndpoint httpEndpoint = mock(HttpEndpoint.class);
+ HttpProducer httpProducer = new HttpProducer(httpEndpoint);
+
+ Message message = mock(Message.class);
+ when(message.getBody()).thenReturn(TEST_MESSAGE_WITH_SPECIAL_CHARACTERS);
+ when(message.getHeader(Exchange.CONTENT_TYPE, String.class)).thenReturn(APPLICATION_JSON.getMimeType());
+ when(message.getHeader(CHARSET_NAME, String.class)).thenReturn(StandardCharsets.UTF_8.name());
+
+ Exchange exchange = mock(Exchange.class);
+ when(exchange.getIn()).thenReturn(message);
+
+ HttpEntity requestEntity = httpProducer.createRequestEntity(exchange);
+
+ assertTrue(requestEntity instanceof StringEntity);
+ StringEntity entity = (StringEntity) requestEntity;
+ assertEquals(APPLICATION_JSON_UTF8, entity.getContentType(), "Content type should be given content type and charset");
+ assertEquals(StandardCharsets.UTF_8.name(), entity.getContentEncoding(), "Content encoding should be given charset");
+ assertEquals(TEST_MESSAGE_WITH_SPECIAL_CHARACTERS,
+ new String(entity.getContent().readAllBytes(), StandardCharsets.UTF_8),
+ "Reading entity content with intended charset should result in the original (readable) message");
+ }
+
+}