This is an automated email from the ASF dual-hosted git repository.
JingsongLi pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/paimon.git
The following commit(s) were added to refs/heads/master by this push:
new 439ff3ce71 [core] Support text/plain content type for REST catalog
request body (#8198)
439ff3ce71 is described below
commit 439ff3ce71a3ce8d31195079cfedad9069579836
Author: Arnav Balyan <[email protected]>
AuthorDate: Sat Jun 20 01:18:28 2026 -0700
[core] Support text/plain content type for REST catalog request body (#8198)
- REST `create table` fails because post and delete are sent without a
content type, defaulting to plain text, which JSON expecting REST
servers reject before processing.
- Fix by setting the request body content type to json in the http
client, and by body type in the simple client, (form encoded for form
bodies and JSON otherwise).
---
.../src/main/java/org/apache/paimon/rest/HttpClient.java | 5 +++--
.../main/java/org/apache/paimon/rest/SimpleHttpClient.java | 7 ++++++-
.../test/java/org/apache/paimon/rest/HttpClientTest.java | 14 ++++++++++++++
3 files changed, 23 insertions(+), 3 deletions(-)
diff --git a/paimon-api/src/main/java/org/apache/paimon/rest/HttpClient.java
b/paimon-api/src/main/java/org/apache/paimon/rest/HttpClient.java
index 5b3c481c12..2477ff9db6 100644
--- a/paimon-api/src/main/java/org/apache/paimon/rest/HttpClient.java
+++ b/paimon-api/src/main/java/org/apache/paimon/rest/HttpClient.java
@@ -33,6 +33,7 @@ import org.apache.hc.client5.http.classic.methods.HttpGet;
import org.apache.hc.client5.http.classic.methods.HttpPost;
import org.apache.hc.client5.http.classic.methods.HttpUriRequestBase;
import org.apache.hc.core5.http.ClassicHttpResponse;
+import org.apache.hc.core5.http.ContentType;
import org.apache.hc.core5.http.Header;
import org.apache.hc.core5.http.io.entity.StringEntity;
import org.apache.hc.core5.http.message.BasicHeader;
@@ -94,7 +95,7 @@ public class HttpClient implements RESTClient {
HttpPost httpPost = new HttpPost(getRequestUrl(path, null));
String encodedBody = RESTUtil.encodedBody(body);
if (encodedBody != null) {
- httpPost.setEntity(new StringEntity(encodedBody));
+ httpPost.setEntity(new StringEntity(encodedBody,
ContentType.APPLICATION_JSON));
}
Header[] authHeaders = getHeaders(path, "POST", encodedBody,
restAuthFunction);
httpPost.setHeaders(authHeaders);
@@ -112,7 +113,7 @@ public class HttpClient implements RESTClient {
HttpDelete httpDelete = new HttpDelete(getRequestUrl(path, null));
String encodedBody = RESTUtil.encodedBody(body);
if (encodedBody != null) {
- httpDelete.setEntity(new StringEntity(encodedBody));
+ httpDelete.setEntity(new StringEntity(encodedBody,
ContentType.APPLICATION_JSON));
}
Header[] authHeaders = getHeaders(path, "DELETE", encodedBody,
restAuthFunction);
httpDelete.setHeaders(authHeaders);
diff --git
a/paimon-api/src/main/java/org/apache/paimon/rest/SimpleHttpClient.java
b/paimon-api/src/main/java/org/apache/paimon/rest/SimpleHttpClient.java
index c2e044d2cc..04aaf3bfea 100644
--- a/paimon-api/src/main/java/org/apache/paimon/rest/SimpleHttpClient.java
+++ b/paimon-api/src/main/java/org/apache/paimon/rest/SimpleHttpClient.java
@@ -24,6 +24,7 @@ import org.apache.hc.client5.http.classic.methods.HttpGet;
import org.apache.hc.client5.http.classic.methods.HttpPost;
import org.apache.hc.client5.http.classic.methods.HttpUriRequestBase;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
+import org.apache.hc.core5.http.ContentType;
import org.apache.hc.core5.http.Header;
import org.apache.hc.core5.http.io.entity.StringEntity;
import org.apache.hc.core5.http.message.BasicHeader;
@@ -55,7 +56,11 @@ public class SimpleHttpClient implements Closeable {
}
String encodedBody = RESTUtil.encodedBody(body);
if (encodedBody != null) {
- httpPost.setEntity(new StringEntity(encodedBody));
+ ContentType contentType =
+ body instanceof Map
+ ? ContentType.APPLICATION_FORM_URLENCODED
+ : ContentType.APPLICATION_JSON;
+ httpPost.setEntity(new StringEntity(encodedBody, contentType));
}
return exec(httpPost);
diff --git
a/paimon-core/src/test/java/org/apache/paimon/rest/HttpClientTest.java
b/paimon-core/src/test/java/org/apache/paimon/rest/HttpClientTest.java
index 6cf9f44802..a742419037 100644
--- a/paimon-core/src/test/java/org/apache/paimon/rest/HttpClientTest.java
+++ b/paimon-core/src/test/java/org/apache/paimon/rest/HttpClientTest.java
@@ -27,6 +27,7 @@ import org.apache.paimon.rest.responses.ErrorResponse;
import org.apache.paimon.shade.guava30.com.google.common.collect.ImmutableMap;
+import okhttp3.mockwebserver.RecordedRequest;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -37,6 +38,7 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
+import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
@@ -261,4 +263,16 @@ public class HttpClientTest {
"Error message should contain the original non-JSON
response");
}
}
+
+ @Test
+ public void testPostSetsJsonContentType() throws Exception {
+ server.enqueueResponse(mockResponseDataStr, 200);
+ httpClient.post(MOCK_PATH, mockResponseData, MockRESTData.class,
restAuthFunction);
+ RecordedRequest request = server.takeRequest(10, TimeUnit.SECONDS);
+ String contentType = request.getHeader("Content-Type");
+ Assertions.assertNotNull(contentType, "POST request must carry a
Content-Type header");
+ Assertions.assertTrue(
+ contentType.contains("application/json"),
+ "POST body must be sent as application/json, but was: " +
contentType);
+ }
}