Can I change the headers in a MultipartBody part? - okhttp

I'm trying to send a MultipartBody payload with OkHttp3.
Here's a MWE for creating the payload (plus getting it as a String):
MediaType JSON = MediaType.get("application/json");
RequestBody requestBody = new MultipartBody.Builder()
.setType(MultipartBody.MIXED)
.addPart(RequestBody.create(JSON, "{\"foo\": true, \"bar\": \"some string\"}"))
.build();
Buffer buffer = new Buffer();
requestBody.writeTo(buffer);
String requestBodyAsString = buffer.readUtf8();
That automatically adds headers and the result is this:
--8d863b3b-72a0-4b29-b4f4-5cc0b68d8117
Content-Type: application/json; charset=utf-8
Content-Length: 35
{"foo": true, "bar": "some string"}
--8d863b3b-72a0-4b29-b4f4-5cc0b68d8117
The server I am sending against requires that the character encoding and Content-Length header are removed as in the following:
--8d863b3b-72a0-4b29-b4f4-5cc0b68d8117
Content-Type: application/json
{"foo": true, "bar": "some string"}
--8d863b3b-72a0-4b29-b4f4-5cc0b68d8117
The MultipartBody.Builder() has .addPart(Headers headers, RequestBody body) but I cannot use that to remove headers or override the Content-Type header. Is there a way to change these via the OkHttp API or would I have to edit the payload string directly?

Here's my hacked solution, directly modifying the payload with an interceptor. Note that it's important to adjust the Content-Length header of the overall request as well.
public class MultipartHeaderInterceptor implements Interceptor {
#Override public Response intercept(Interceptor.Chain chain) throws IOException {
Request originalRequest = chain.request();
String requestBody = requestBodyToString(originalRequest.body());
String modified = stripHeadersFromMultipart(requestBody);
Request targetRequest = originalRequest.newBuilder()
.post(RequestBody.create(MediaType.get("text/plain"), modified))
.header("Content-Length", String.valueOf(modified.length()))
.build();
return chain.proceed(targetRequest);
}
private String stripHeadersFromMultipart(String requestBody) {
String regexToStrip = "; charset=utf-8\r\nContent-Length: \\d*";
return requestBody.replaceAll(regexToStrip, "");
}
private String requestBodyToString(RequestBody body) {
String bodyAsString = "";
try (Buffer buffer = new Buffer()) {
if (body != null) {
body.writeTo(buffer);
bodyAsString = buffer.readUtf8();
}
} catch (IOException e) {
log.info(e);
}
return bodyAsString;
}
}
Would be nice to have a less hacky solution though.

Related

Accept Dynamic value in request Body using spring boot

I want my request body accept dynamic value not accept hard coded
value, I have two token field brandwiseBearerToken and thirdpartyPaymentToken if
my token comes from brandwiseBearerToken it show the result with header other
wise header will not be shown.
I tried below way but my code not working
for reference I post my code.
private JsonObject gateWayToken(String url ) throws IOException, AuthenticationException{
String brandwiseBearerToken = "";
String token=;
ObjectWriter ow = new ObjectMapper().writer().withDefaultPrettyPrinter();
OkHttpClient client = new OkHttpClient();
MediaType mediaType = MediaType.parse("application/json");
String thirdPartyPaymentGatewayTokenJson = ow.writeValueAsString(token);
RequestBody body = RequestBody.create(mediaType,thirdPartyPaymentGatewayTokenJson );
Request request = null;
if (request.equals(thirdPartyPaymentGatewayTokenJson)) {
new Request.Builder().url(url).post(body)
.addHeader("Content-Type", "application/json").addHeader("Bearer", brandwiseBearerToken)
.build();
} else {
new Request.Builder().url(url).post(body)
.addHeader("Content-Type", "application/json").build();
}
Response response = client.newCall(request).execute();
ResponseBody responseBody = response.body();
return new Gson().fromJson(responseBody.string(), JsonObject.class);
}
I want to pass Dynamically brandwiseBearerToken and
thirdpartyPaymentGatewayToken inside my request body, How can I do it.
I want to make dynamic exact below point
String thirdPartyPaymentGatewayTokenJson = ow.writeValueAsString(token);
RequestBody body = RequestBody.create(mediaType,thirdPartyPaymentGatewayTokenJson );

Apache httpcomponents 5 chunked response

I am accessing an internal site that returns gzipped content. When the content reaches a certain size, the site returns a chunked response. I am using the Apache httpcomponents 5 CloseableHttpAsyncClient and the SimpleHttpRequest and SimpleHttpResponse. The internal site is a vendor product that can't be modified.
String encoding = getEncoding(response.getHeaders());
byte[] bytes;
byte[] bodyBytes = response.getBodyBytes();
if (encoding.equals("gzip")) {
ByteArrayInputStream inputStream = new ByteArrayInputStream(bodyBytes);
GZIPInputStream gzipInputStream = new GZIPInputStream(inputStream);
bytes = IOUtils.toByteArray(gzipInputStream);
} else {
bytes = bodyBytes;
}
String html = new String(bytes, StandardCharsets.UTF_8);
I check for the response type as follows
private String getEncoding(Header[] headers) {
for (Header header : headers) {
if (header.getName().toLowerCase().equals("transfer-encoding")) {
return header.getValue();
}
if (header.getName().toLowerCase().equals("content-encoding")) {
return header.getValue();
}
}
return "";
}
I know that there is a ChunkedInputStream class, but the inputs to the constructor are not obviously available from the response
ChunkedInputStream(SessionInputBuffer buffer, InputStream inputStream)
Wraps session input stream and reads chunk coded input.
ChunkedInputStream(SessionInputBuffer buffer, InputStream inputStream, Http1Config http1Config)
Default constructor.
Do I need to use a different response type? If so, which one? Or is there a different way that is better?
Thanks for your help.

AWS Java Lambda compressed JSON responses fails: "Execution failed due to configuration error: Malformed Lambda proxy response"

I am invoking a AWS Lambda function from the AWS API Gateway. The returned JSON needs to be zipped since it sometimes became too big (body size too large etc). However, I have some issues getting the response through the API Gateway. This is my Java code:
#Override
public JSONObject handleRequest(Object input, Context context) {
String json_string = "";
try {
Gson gson = new Gson();
json_string = gson.toJson(input, LinkedHashMap.class);
} catch (ClassCastException ex) {
json_string = (String) input;
}
GenerateJson generateJson = new GenerateJson ();
String body = "";
try {
JSONParser parser = new JSONParser();
Object jsonObj = parser.parse(json_string);
JSONObject matchesobj = (JSONObject) jsonObj;
if (matchesobj.containsKey("body")) {
body = (String) matchesobj.get("body");
} else {
JSONObject error = new JSONObject();
error.put("error", "No body with Base64 data in Request.");
System.out.println(error.toJSONString());
return error;
}
} catch (ParseException ex) {
ex.printStackTrace();
}
byte[] decodedBytes = Base64.getDecoder().decode(body);
String decodedString = new String(decodedBytes);
// System.out.println(decodedString);
JSONObject json = generateJson .getJson(decodedString, "", 2);
JSONObject returnObject = new JSONObject();
JSONObject headers = new JSONObject();
returnObject.put("statusCode", 205);
returnObject.put("isBase64Encoded", true);
// returnObject.put("Content-Encoding", "gzip");
returnObject.put("headers", headers);
returnObject.put("body", compressStringAndReturnBase64(json.toString()));
return (returnObject);
}
public static String compressStringAndReturnBase64(String srcTxt) {
ByteArrayOutputStream rstBao = new ByteArrayOutputStream();
GZIPOutputStream zos;
try {
zos = new GZIPOutputStream(rstBao);
zos.write(srcTxt.getBytes());
IOUtils.closeQuietly(zos);
byte[] bytes = rstBao.toByteArray();
String base64comp = Base64.getEncoder().encodeToString(bytes);
System.out.println("Json String is " + srcTxt.toString().getBytes().length + " compressed " + bytes.length + " compressed Base64 " + base64comp.getBytes().length);
return base64comp;
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return "";
}
I've checked the Base64 output and that seems to work fine (pasted it in https://www.base64decode.org/). In addition, when I check with Postman, I get a binary blob which can be unpacked with 7-zip if I save the response to something that ends with .gz.
Under settings, the API Gateway Binary Media Types has been set to /
But I'd like to have the client "see" that it is GZIPped and decode it on the fly. However, when I add the line
returnObject.put("Content-Encoding", "gzip");
I get {"message": "Internal server error"} and in the AWS API logs: Execution failed due to configuration error: Malformed Lambda proxy response
The Lambda logs are fine, so it did execute successfully, just wasn't able to be returned.
I am thinking I need some more tweaking on the API Gateway side, any ideas?
This sounds like the binary support setting on API Gateway isn't configured correctly; API gateway is trying to parse the response from your lambda rather than passing it on directly to the client.
You can update this setting in the console:
In your HTTP request add "Accept" header with payload content type.
Accept: application/gzip
Also in the HTTP response, there should be "Content-Type" header indicating response content type.
Content-Type: application/gzip
Your lambda returns Base64 encoded binary data to API Gateway. So in order to decode data your HTTP request's Accept header and Response's Content-type headers should there.

why we need to do sever side validation for google recaptcha?

In my new project, I am going to include google recaptcha.
my question is fairly simple even if we do client side validation that user is not a robot even though it is suggested to do server side validation.
I want to know why it is necessary to do server side validation for google recaptcha? how does it add the extra layer of security? and how to do in spring boot with spring security?
Server side validation is MUST !! reCAPTCHA is designed in a way that client side just generates the 'g-captcha-response' which along with secret key (stored at server-side) is sent to https://www.google.com/recaptcha/api/siteverify for validation. The response is a JSON which states sucesss true or false and it is further pushed to client side. Validating only at the client side is technically possible, but it defeats the purpose. Moreover, you may get CORS (Cross-Origin Resource Sharing) policy error in console if you do only client side validation. I can share steps to do simple java based server side validation in servlet. Let me know if you need that.
Here is the code. Few points to be noted:
The parameter userResponse = request.getParameter("recaptchaResponse") is the way by which i am getting the 'g-recaptcha-response' generated by the user when he clicked reCAPTCHA widget on UI. On your javascript, capture the value of field 'g-recaptcha-response' and pass it appended to request. Then in servlet, we can get it from request.getParameter.
Sample code:
var recaptchaResponse = document.getElementById("g-recaptcha-response").value;
//alert("g-recaptcha-response= "+recaptchaResponse);
if (recaptchaResponse.length > 0)
{
var xmlhttp1;
if (window.XMLHttpRequest)
{
xmlhttp1=new XMLHttpRequest();
}
else
{
xmlhttp1=new ActiveXObject("Microsoft.XMLHTTP");
}
var query1 = "?recaptchaResponse=" + recaptchaResponse;
xmlhttp1.open("POST","captchaVerificationServlet" + query1, false);
xmlhttp1.send(null);
var resp1 = xmlhttp1.responseText;
alert("resp1= "+resp1);
if(resp1=='matched'){
return true;
}
else{
alert("resp1 did not match");
return false;
}
}
else{
alert("error: recaptcha response is blank");
return false;
}
For simplicity i am checking presence of "success:true" in returned JSON response. As you know, returned JSON contains two parameters : success and error-codes. You may use a JSONReader to read and parse JSON and obtain all parameters fully. Sample code will be like
JsonReader rdr = Json.createReader(your_inputstream);
JsonObject jsonObject = rdr.readObject();
Needless to say, remove all alerts and sop statements in production!
public class CaptchaVerificationServlet extends HttpServlet {
private static final String sec = YOUR_SECRET_KEY;
public void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {}
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String userResponse = request.getParameter("recaptchaResponse");
response.setCharacterEncoding("UTF-8");
System.out.println("userResponse= "+userResponse);
//verify user response with Google ReCaptcha API
String ipAddress = request.getRemoteAddr(); //get client's ip address
System.out.println("ipAddress= "+ipAddress);
try{
String s = validateCaptcha(sec, userResponse, ipAddress);
Boolean success = (s.contains("\"success\": true"));
if(success)
response.getWriter().write("matched");
}
catch(Exception ioe){
ioe.printStackTrace();
ioe.printStackTrace(response.getWriter());
}
}
private String validateCaptcha(String secret, String response, String remoteip) throws IOException
{
URLConnection connection = null;
InputStream is = null;
String output = "";
String proxyHost = "YOUR_PROXY_NAME";
int proxyPort = 80; //proxy server port, generally 80 or 443 (confirm from sys-admin)
SocketAddress addr = new InetSocketAddress(proxyHost, proxyPort);
Proxy httpProxy = new Proxy(Proxy.Type.HTTP, addr);
String filename = System.getProperty("java.home") + "/lib/security/cacerts".replace('/', File.separatorChar);
String password = "changeit";
System.setProperty("javax.net.ssl.trustStore",filename);
System.setProperty("javax.net.ssl.trustAnchors",filename);
System.setProperty("javax.net.ssl.trustStorePassword",password);
String charset = Charset.forName("UTF-8").name();
String url = "https://www.google.com/recaptcha/api/siteverify";
try {
String query = String.format("secret=%s&response=%s&remoteip=%s",
URLEncoder.encode(secret, charset),
URLEncoder.encode(response, charset),
URLEncoder.encode(remoteip, charset));
URL fullURL = new URL(url + "?" + query);
connection = fullURL.openConnection(httpProxy);
connection.addRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:52.0) Gecko/20100101 Firefox/52.0");
is = connection.getInputStream();
System.out.println("connection InputStream");
BufferedReader reader = null;
String responseXXX = "";
reader = new BufferedReader(new InputStreamReader(is));
responseXXX = reader.readLine();
while (responseXXX!=null) {
output+= responseXXX;
responseXXX = reader.readLine();
}
System.out.println("Output: " + output);
}
finally {
if (is != null) {
try {
is.close();
} catch (IOException e) {
//cannot do anything here
}
}
}
return output;
}
}

httpWebRequest using POST method - receive unexpected xml response

I need to use POST to post a string to server and get xml response, the status code is OK but the string reponse is always ="" (0 byte). Is there any thing wrong with my code? I check the server from blackberry, works fine so the problem must come from my code:
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
s = NavigationContext.QueryString["parameter1"];
//string strConnectUrl = "http://www.contoso.com/example.aspx";
base.OnNavigatedTo(e);
// DoWebClient(s);
try
{
HttpWebRequest httpWebRequest = (HttpWebRequest)WebRequest.Create(strConnectUrl);
httpWebRequest.Method = "POST";
httpWebRequest.ContentType = "application/x-www-form-urlencoded;charset=UTF-8";
// start the asynchronous operation
httpWebRequest.BeginGetRequestStream(new AsyncCallback(GetRequestStreamCallback), httpWebRequest);
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
private static void GetRequestStreamCallback(IAsyncResult asynchronousResult)
{
// string XML_REQUEST = "<?xml version=\"1.0\"?><mybroker"><getConnections></mybroker>";
string post = "?&track=love";
try
{
HttpWebRequest request = (HttpWebRequest)asynchronousResult.AsyncState;
// End the operation
Stream postStream = request.EndGetRequestStream(asynchronousResult);
// Convert the string into a byte array.
byte[] postBytes = Encoding.UTF8.GetBytes(post);
// Write to the request stream.
postStream.Write(postBytes, 0, postBytes.Length);
postStream.Close();
// Start the asynchronous operation to get the response
request.BeginGetResponse(new AsyncCallback(GetResponseCallback), request);
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
static Stream str;
static string st;
private static void GetResponseCallback(IAsyncResult asynchronousResult)
{
HttpWebRequest request = (HttpWebRequest)asynchronousResult.AsyncState;
// End the operation
HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(asynchronousResult);
HttpStatusCode rcode = response.StatusCode;
Stream streamResponse = response.GetResponseStream();
StreamReader streamRead = new StreamReader(streamResponse);
//****THIS ALWAYS RETURN "" VALUE, EXPECT TO RETURN XML STRING****
string responseString = streamRead.ReadToEnd();
//Console.WriteLine(responseString);
// Close the stream object
streamResponse.Close();
streamRead.Close();
// Release the HttpWebResponse
response.Close();
}
*EDIT**
The code work, however the server return parameter require the Ampersand(&) which is not allow in silverlight framework I think, remove the & char server response but the result wasn't correct. I will ask a new question refer this Ampersand
Have checked your call using Fiddler. The server is returning an empty body. Your code is working correctly. The problem is server side.
The post value should be "track=love".
I tried and it worked but the response is gzip encoded.
Fiddler says that the request is good, and that the response really is blank. If it's working from Blackberry and not from WP7, could the server be doing some user agent checking and not returning anything because it doesn't recognize the WP7 user agent?
Also, it looks like you're POSTing a query string ("?&track=love") which is sort of unusual. Are you sure that's the right data to be sending in your request?

Resources