Skip to content

Commit 84cb6df

Browse files
committed
aadarsh-st/SK-2521-Enhance content-type handling and add validation tests for request bodies
1 parent 6c28b9a commit 84cb6df

File tree

8 files changed

+391
-22
lines changed

8 files changed

+391
-22
lines changed

src/main/java/com/skyflow/logs/InfoLogs.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ public enum InfoLogs {
1414

1515
// Bearer token generation
1616
EMPTY_BEARER_TOKEN("Bearer token is empty."),
17-
BEARER_TOKEN_EXPIRED("Bearer token is expired."),
17+
BEARER_TOKEN_EXPIRED("Bearer token is invalid or expired."),
1818
GET_BEARER_TOKEN_TRIGGERED("getBearerToken method triggered."),
1919
GET_BEARER_TOKEN_SUCCESS("Bearer token generated."),
2020
GET_SIGNED_DATA_TOKENS_TRIGGERED("getSignedDataTokens method triggered."),

src/main/java/com/skyflow/utils/HttpUtility.java

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import java.io.*;
88
import java.net.HttpURLConnection;
99
import java.net.URL;
10+
import java.net.URLEncoder;
1011
import java.nio.charset.StandardCharsets;
1112
import java.util.HashMap;
1213
import java.util.List;
@@ -32,9 +33,14 @@ public static String sendRequest(String method, URL url, JsonObject params, Map<
3233
try {
3334
connection = (HttpURLConnection) url.openConnection();
3435
connection.setRequestMethod(method);
35-
connection.setRequestProperty("content-type", "application/json");
3636
connection.setRequestProperty("Accept", "*/*");
3737

38+
// Set default content-type if not provided in headers
39+
boolean hasContentType = headers != null && headers.containsKey("content-type");
40+
if (!hasContentType && params != null && !params.isEmpty()) {
41+
connection.setRequestProperty("content-type", "application/json");
42+
}
43+
3844
if (headers != null && !headers.isEmpty()) {
3945
for (Map.Entry<String, String> entry : headers.entrySet())
4046
connection.setRequestProperty(entry.getKey(), entry.getValue());
@@ -52,9 +58,12 @@ public static String sendRequest(String method, URL url, JsonObject params, Map<
5258
byte[] input = null;
5359
String requestContentType = connection.getRequestProperty("content-type");
5460

55-
if (requestContentType.contains("application/x-www-form-urlencoded")) {
61+
// Check if this is a raw body (XML, plain text, etc.)
62+
if (params.has("__raw_body__") && params.size() == 1) {
63+
input = params.get("__raw_body__").getAsString().getBytes(StandardCharsets.UTF_8);
64+
} else if (requestContentType != null && requestContentType.contains("application/x-www-form-urlencoded")) {
5665
input = formatJsonToFormEncodedString(params).getBytes(StandardCharsets.UTF_8);
57-
} else if (requestContentType.contains("multipart/form-data")) {
66+
} else if (requestContentType != null && requestContentType.contains("multipart/form-data")) {
5867
input = formatJsonToMultiPartFormDataString(params, boundary).getBytes(StandardCharsets.UTF_8);
5968
} else {
6069
input = params.toString().getBytes(StandardCharsets.UTF_8);
@@ -159,7 +168,13 @@ public static String appendRequestId(String message, String requestId) {
159168
}
160169

161170
private static String makeFormEncodeKeyValuePair(String key, String value) {
162-
return key + "=" + value + "&";
171+
try {
172+
String encodedKey = URLEncoder.encode(key, StandardCharsets.UTF_8.toString());
173+
String encodedValue = URLEncoder.encode(value, StandardCharsets.UTF_8.toString());
174+
return encodedKey + "=" + encodedValue + "&";
175+
} catch (Exception e) {
176+
return key + "=" + value + "&";
177+
}
163178
}
164179

165180
}

src/main/java/com/skyflow/utils/Utils.java

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
import java.io.File;
1818
import java.net.MalformedURLException;
1919
import java.net.URL;
20+
import java.net.URLEncoder;
21+
import java.nio.charset.StandardCharsets;
2022
import java.security.KeyFactory;
2123
import java.security.NoSuchAlgorithmException;
2224
import java.security.PrivateKey;
@@ -110,7 +112,12 @@ public static String constructConnectionURL(ConnectionConfig config, InvokeConne
110112
for (Map.Entry<String, String> entry : invokeConnectionRequest.getPathParams().entrySet()) {
111113
String key = entry.getKey();
112114
String value = entry.getValue();
113-
filledURL = new StringBuilder(filledURL.toString().replace(String.format("{%s}", key), value));
115+
try {
116+
String encodedValue = URLEncoder.encode(value, StandardCharsets.UTF_8.toString());
117+
filledURL = new StringBuilder(filledURL.toString().replace(String.format("{%s}", key), encodedValue));
118+
} catch (Exception e) {
119+
filledURL = new StringBuilder(filledURL.toString().replace(String.format("{%s}", key), value));
120+
}
114121
}
115122
}
116123

@@ -119,7 +126,13 @@ public static String constructConnectionURL(ConnectionConfig config, InvokeConne
119126
for (Map.Entry<String, String> entry : invokeConnectionRequest.getQueryParams().entrySet()) {
120127
String key = entry.getKey();
121128
String value = entry.getValue();
122-
filledURL.append(key).append("=").append(value).append("&");
129+
try {
130+
String encodedKey = URLEncoder.encode(key, StandardCharsets.UTF_8.toString());
131+
String encodedValue = URLEncoder.encode(value, StandardCharsets.UTF_8.toString());
132+
filledURL.append(encodedKey).append("=").append(encodedValue).append("&");
133+
} catch (Exception e) {
134+
filledURL.append(key).append("=").append(value).append("&");
135+
}
123136
}
124137
filledURL = new StringBuilder(filledURL.substring(0, filledURL.length() - 1));
125138
}

src/main/java/com/skyflow/utils/validations/Validations.java

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.skyflow.utils.validations;
22

33
import com.google.gson.Gson;
4+
import com.google.gson.JsonElement;
45
import com.google.gson.JsonObject;
56
import com.skyflow.config.ConnectionConfig;
67
import com.skyflow.config.Credentials;
@@ -137,12 +138,27 @@ public static void validateInvokeConnectionRequest(InvokeConnectionRequest invok
137138
}
138139

139140
if (requestBody != null) {
140-
Gson gson = new Gson();
141-
JsonObject bodyObject = gson.toJsonTree(requestBody).getAsJsonObject();
142-
if (bodyObject.isEmpty()) {
143-
LogUtil.printErrorLog(Utils.parameterizedString(
144-
ErrorLogs.EMPTY_REQUEST_BODY.getLog(), InterfaceName.INVOKE_CONNECTION.getName()));
145-
throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.EmptyRequestBody.getMessage());
141+
if (requestBody.getClass().equals(Object.class)) {
142+
return;
143+
}
144+
if (requestBody instanceof String) {
145+
String bodyStr = (String) requestBody;
146+
if (bodyStr.trim().isEmpty()) {
147+
LogUtil.printErrorLog(Utils.parameterizedString(
148+
ErrorLogs.EMPTY_REQUEST_BODY.getLog(), InterfaceName.INVOKE_CONNECTION.getName()));
149+
throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.EmptyRequestBody.getMessage());
150+
}
151+
} else {
152+
Gson gson = new Gson();
153+
JsonElement bodyElement = gson.toJsonTree(requestBody);
154+
if (bodyElement.isJsonObject()) {
155+
JsonObject bodyObject = bodyElement.getAsJsonObject();
156+
if (bodyObject.isEmpty()) {
157+
LogUtil.printErrorLog(Utils.parameterizedString(
158+
ErrorLogs.EMPTY_REQUEST_BODY.getLog(), InterfaceName.INVOKE_CONNECTION.getName()));
159+
throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.EmptyRequestBody.getMessage());
160+
}
161+
}
146162
}
147163
}
148164
}

src/main/java/com/skyflow/vault/controller/ConnectionController.java

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -51,20 +51,48 @@ public InvokeConnectionResponse invoke(InvokeConnectionRequest invokeConnectionR
5151
headers.put(Constants.SDK_METRICS_HEADER_KEY, Utils.getMetrics().toString());
5252

5353
RequestMethod requestMethod = invokeConnectionRequest.getMethod();
54-
JsonObject requestBody = null;
5554
Object requestBodyObject = invokeConnectionRequest.getRequestBody();
55+
String contentType = headers
56+
.getOrDefault("content-type", "application/json")
57+
.toLowerCase();
58+
boolean isJsonRequest = contentType.contains("application/json");
5659

57-
if (requestBodyObject != null) {
58-
try {
59-
requestBody = convertObjectToJson(requestBodyObject);
60-
} catch (Exception e) {
61-
LogUtil.printErrorLog(ErrorLogs.INVALID_REQUEST_HEADERS.getLog());
62-
throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.InvalidRequestBody.getMessage());
60+
Object finalPayload;
61+
62+
try {
63+
if (requestBodyObject instanceof String) {
64+
if (isJsonRequest) {
65+
JsonObject jsonWrapper = new JsonObject();
66+
jsonWrapper.addProperty(
67+
"__raw_body__",
68+
(String) requestBodyObject
69+
);
70+
finalPayload = jsonWrapper;
71+
} else {
72+
finalPayload = requestBodyObject;
73+
}
74+
} else if (requestBodyObject instanceof JsonObject || requestBodyObject == null) {
75+
finalPayload = requestBodyObject;
76+
} else {
77+
finalPayload = convertObjectToJson(requestBodyObject);
6378
}
79+
} catch (Exception e) {
80+
LogUtil.printErrorLog(ErrorLogs.INVALID_REQUEST_HEADERS.getLog());
81+
throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.InvalidRequestBody.getMessage());
82+
}
83+
JsonObject payloadToSend;
84+
if (finalPayload instanceof JsonObject || finalPayload == null) {
85+
payloadToSend = (JsonObject) finalPayload;
86+
} else {
87+
payloadToSend = new JsonObject();
88+
payloadToSend.addProperty("__raw_body__", finalPayload.toString());
6489
}
6590

66-
String response = HttpUtility.sendRequest(requestMethod.name(), new URL(filledURL), requestBody, headers);
67-
JsonObject data = JsonParser.parseString(response).getAsJsonObject();
91+
String response = HttpUtility.sendRequest(requestMethod.name(), new URL(filledURL), payloadToSend, headers);
92+
Object data = response;
93+
try {
94+
data = JsonParser.parseString(response).getAsJsonObject();
95+
} catch (Exception ignored) {}
6896
HashMap<String, String> metadata = new HashMap<>();
6997
metadata.put("requestId", HttpUtility.getRequestID());
7098
connectionResponse = new InvokeConnectionResponse(data, metadata, null);

src/test/java/com/skyflow/utils/HttpUtilityTests.java

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,4 +124,71 @@ public void testSendRequestError() {
124124
fail(INVALID_EXCEPTION_THROWN);
125125
}
126126
}
127+
128+
// New test cases for content-type handling changes
129+
130+
@Test
131+
@PrepareForTest({URL.class, HttpURLConnection.class})
132+
public void testSendRequestWithRawBody() {
133+
try {
134+
given(mockConnection.getRequestProperty("content-type")).willReturn("application/xml");
135+
Map<String, String> headers = new HashMap<>();
136+
headers.put("content-type", "application/xml");
137+
JsonObject params = new JsonObject();
138+
params.addProperty("__raw_body__", "<xml>test</xml>");
139+
String response = httpUtility.sendRequest("POST", url, params, headers);
140+
Assert.assertEquals(expected, response);
141+
} catch (Exception e) {
142+
fail(INVALID_EXCEPTION_THROWN);
143+
}
144+
}
145+
146+
@Test
147+
@PrepareForTest({URL.class, HttpURLConnection.class})
148+
public void testSendRequestWithoutContentTypeHeader() {
149+
try {
150+
given(mockConnection.getRequestProperty("content-type")).willReturn("application/json");
151+
JsonObject params = new JsonObject();
152+
params.addProperty("key", "value");
153+
String response = httpUtility.sendRequest("POST", url, params, null);
154+
Assert.assertEquals(expected, response);
155+
} catch (Exception e) {
156+
fail(INVALID_EXCEPTION_THROWN);
157+
}
158+
}
159+
160+
@Test
161+
@PrepareForTest({URL.class, HttpURLConnection.class})
162+
public void testSendRequestWithNullRequestId() {
163+
try {
164+
given(mockConnection.getHeaderField(anyString())).willReturn(null);
165+
given(mockConnection.getRequestProperty("content-type")).willReturn("application/json");
166+
Map<String, String> headers = new HashMap<>();
167+
headers.put("content-type", "application/json");
168+
JsonObject params = new JsonObject();
169+
params.addProperty("key", "value");
170+
String response = httpUtility.sendRequest("GET", url, params, headers);
171+
Assert.assertEquals(expected, response);
172+
Assert.assertNotNull(HttpUtility.getRequestID());
173+
} catch (Exception e) {
174+
fail(INVALID_EXCEPTION_THROWN);
175+
}
176+
}
177+
178+
@Test
179+
@PrepareForTest({URL.class, HttpURLConnection.class})
180+
public void testSendRequestFormURLEncodedWithSpecialCharacters() {
181+
try {
182+
given(mockConnection.getRequestProperty("content-type")).willReturn("application/x-www-form-urlencoded");
183+
Map<String, String> headers = new HashMap<>();
184+
headers.put("content-type", "application/x-www-form-urlencoded");
185+
JsonObject params = new JsonObject();
186+
params.addProperty("key", "value with spaces");
187+
params.addProperty("special", "test@email.com");
188+
String response = httpUtility.sendRequest("POST", url, params, headers);
189+
Assert.assertEquals(expected, response);
190+
} catch (Exception e) {
191+
fail(INVALID_EXCEPTION_THROWN);
192+
}
193+
}
127194
}

0 commit comments

Comments
 (0)