I am trying to create a proxy server in java using Spark Java.
I listed for any incoming request and send any incoming request to the target server via Apache Http Client.
I then transform the incoming request by coping the headers, status and text.
Now this is working pretty fine, except the binary files. The binary files such as fonts are corrupted. My code is :
private static void mapStatus(HttpResponse response, spark.Response res) {
res.status(response.getStatusLine().getStatusCode());
}
private static void mapHeaders(HttpResponse response, spark.Response res) {
for (Header header : response.getAllHeaders()) {
res.header(header.getName(), header.getValue());
}
}
private static String result(HttpResponse response) throws ParseException, IOException {
HttpEntity entity = response.getEntity();
return entity == null ? "" : EntityUtils.toString(entity);
}
I suspect the issue is with the result() method as it treats all the responses as text. How ever I tried by coping the streams as is :
private static void extractResponse(HttpResponse httpResponse, HttpServletResponse response) {
InputStream inputStream = null;
try {
HttpEntity entity = httpResponse.getEntity();
if(entity == null)
return;
inputStream = entity.getContent();
copyStream(inputStream, response.getOutputStream());
} catch (IllegalStateException | IOException e) {
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
}
}
}
}
private static void copyStream(InputStream input, OutputStream output) throws IOException {
IOUtils.copy(input, output);
}
And the binary files are still bad.
WHat could be the issue here ?
Complete Code Link
Related
In the below code what exactly is sent in httpExchange.getResponseBody().write(rawResponseBody);
If the Client has sent a request on this server below and expects a JSON response in GET, how do we handle that? Is it handled by httpExchange.getResponseBody().write(rawResponseBody); or do we need to do something else? I need this functionality for both HTTPS and HTTP Client URLs sending data to this server.
try {
httpServer = HttpServer.create(new InetSocketAddress(URL.getHost(), URL.getPort()), 0);
httpServer.setExecutor(null);
httpServer.start();
} catch (Exception e) {
}
#Override
public void handle(HttpExchange httpExchange) throws IOException {
if (httpExchange.getRequestMethod().equalsIgnoreCase(HttpPost.METHOD_NAME)) {
try {
Headers requestHeaders = httpExchange.getRequestHeaders();
int contentLength = Integer.parseInt(requestHeaders.getFirst("Content-length"));
InputStream is = httpExchange.getRequestBody();
} catch (Exception e) {
logger.fatal(e.getCause());
int responseCode = 400;
setHttpExchange(httpExchange, response, responseCode);
}
}
}
private void httpExchange(HttpExchange httpExchange, TomResponse response, int responseCode) {
try {
Gson gson = new Gson();
String responseBody = gson.toJson(response);
Headers headers = httpExchange.getResponseHeaders();
headers.add(HEADER_CONTENT_TYPE, String.format("application/json; charset=%s", CHARSET));
final byte[] rawResponseBody = responseBody.getBytes(CHARSET);
httpExchange.sendResponseHeaders(responseCode, rawResponseBody.length);
OutputStream os = httpExchange.getResponseBody();
os.write(rawResponseBody);
os.flush();
os.close();
} catch (IOException e) {
logger.fatal(e.getCause());
} finally {
httpExchange.close();
}
}
I have a Servlet which makes a request to my Rest API, and I want it to return the API Response content to the final user through the HttpServletResponse.
The content is actually a .xls file to download which I put in the Response with the StreamingOutput Object.
How can I do that ? I can't cast the Response into a HttpServletResponse
Rest API method :
#GET
#Produces( MediaType.APPLICATION_JSON )
#Path("bla")
public Response getTopicByName() {
final Workbook wb = new HSSFWorkbook();
StreamingOutput stream = new StreamingOutput() {
#Override
public void write(OutputStream output) throws IOException, WebApplicationException {
wb.write(output);
}
};
responseBuilder = responseBuilder.entity(stream);
responseBuilder = responseBuilder.status(Response.Status.OK);
responseBuilder = responseBuilder.header("Content-Disposition", "attachment; filename=" + device + ".xls");
return responseBuilder.build();
}
Servlet POST method :
#Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Client client = ClientBuilder.newClient();
WebTarget target = client.target(url);
Response res = target. request().get();
if (res.getStatus() == 200) {
// how to put res stream into response stream ?
ServletOutputStream stream = response.getOutputStream();
}
client.close();
}
EDIT :
I tried TedTrippin method and after finding out the way to recover an InputStream from the Response, it worked well.
But I keep getting corrupted xls files. And it is quite annoying. I don't get those corrupted files when I make the request directly from the browser.
Got any clues where it comes from ?
POST method :
#Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Client client = ClientBuilder.newClient();
WebTarget target = client.target(url + param + format);
Response res = target.request().get();
if (res.getStatus() == 200) {
response.setHeader("Content-Disposition", "attachment; filename=test.xls");
InputStream in = res.readEntity(InputStream.class);
ServletOutputStream out = response.getOutputStream();
byte[] buffer = new byte[1024];
while (in.read(buffer) >= 0) {
out.write(buffer);
}
out.flush();
}
client.close();
}
Simplest way is to read the response stream and write it straight to the response output stream. Either use a library function from IOUtils or Guava or pure java...
try (InputStream in = ...;
OutputStream out = ...) {
byte[] buffer = new byte[1024];
while (in.read(buffer) >= 0)
out.write(buffer);
} catch (IOException ex) {
...
}
A nicer (depending on your view) way would be to read/save the response as a temporary file then you could return that or write it to the output stream.
Third approach would be to create a pipe, but I don't think that would be applicable here.
Basically I have a client-side File upload which could be used for uploading files to server, and in my server-side I have limited file size using MultipartConfig to 5MB and if the file has exceeded the limit I need to abort the uploading process.
Server-Side:
#MultipartConfig(location="/tmp", fileSizeThreshold=1024*1024,
maxFileSize=1024*1024*5, maxRequestSize=1024*1024*5*5)
public void doPost(HttpServletRequest request,
HttpServletResponse response) throws IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter() ;
try{
MultipartRequest multipartRequest = new MultipartRequest(request, "D:\\");
} catch(IOException e){
System.out.println(e.getMessage());
return;
}
out.print("Successfully Uploaded");
}
FYI: I have my #MultipartConfig out of my class
As you can see I have a try and catch which if the file limit has exceeded throws an exception saying about the file exceed exception, Here I need to abort the uploading process, and send a simple error to client that limit has exceeded.
You can use httpget.abort() to abort/cancel the request.
public class ClientAbortMethod {
public final static void main(String[] args) throws Exception {
CloseableHttpClient httpclient = HttpClients.createDefault();
try {
HttpGet httpget = new HttpGet("http://httpbin.org/get");
System.out.println("Executing request " + httpget.getURI());
CloseableHttpResponse response = httpclient.execute(httpget);
try {
System.out.println("----------------------------------------");
System.out.println(response.getStatusLine());
// Do not feel like reading the response body
// Call abort on the request object
httpget.abort();
} finally {
response.close();
}
} finally {
httpclient.close();
}
}
}
Refer this
I deploy the same war with jetty, a local tomcat and a remote tomcat.
For the same request :
With jetty and local tomcat everything is fine and I get a full response.
With remote tomcat response status is 200 but with empty body.
I don't know where to look at and why this difference.
If you can provide me another way to query an URL (which can be public or local to the network where the app is deployed), I'll be glad to hear it.
The code
private void get(String proxiedURIString, HttpServletResponse resp) throws IOException, ServletException {
HttpURLConnection connection = getHttpURLConnection(proxiedURIString, resp);
fillHeaders(resp, connection);
try {
IOUtils.copy(connection.getInputStream(), resp.getOutputStream());
} catch (IOException e) {
String message = String.format("Unable to parse response : %s", proxiedURIString);
LOGGER.error(message);
resp.sendError(500, message);
} finally {
resp.getOutputStream().flush();
connection.disconnect();
}
LOGGER.info("Successfully proxied URI : {}", proxiedURIString);
}
private static void fillHeaders(HttpServletResponse resp, HttpURLConnection connection) {
Map<String, List<String>> headers = connection.getHeaderFields();
for (String header : headers.keySet()) {
if(header == null || headers.get(header) == null) {
continue;
}
for (String headerValue : headers.get(header)) {
if(headerValue == null) {
continue;
}
resp.addHeader(header, headerValue);
}
}
}
private static HttpURLConnection getHttpURLConnection(String proxiedURIString, HttpServletResponse resp) throws ServletException, IOException {
HttpURLConnection connection = null;
try {
URL url = new URL(proxiedURIString);
connection = (HttpURLConnection) url.openConnection();
} catch (IOException e) {
String message = String.format("Unable to contact : %s", proxiedURIString);
LOGGER.error(message);
resp.sendError(404, message);
}
return connection;
}
Edit: Edit the code to only work with input/output streams. Also to provide further information, I contact the remote tomcat through an apache http server and the response body is big.
Edit2: Futher information. I mapped the proxied URI with one sending much less data and this time I get a full answer for every configuration.
Hi, I wanted to return a file from a resteasy server. For this purpose, I have a link at the client side which is calling a rest service with ajax. I want to return the file in the rest service. I tried these two blocks of code, but both didn't work as I wanted them to.
#POST
#Path("/exportContacts")
public Response exportContacts(#Context HttpServletRequest request, #QueryParam("alt") String alt) throws IOException {
String sb = "Sedat BaSAR";
byte[] outputByte = sb.getBytes();
return Response
.ok(outputByte, MediaType.APPLICATION_OCTET_STREAM)
.header("content-disposition","attachment; filename = temp.csv")
.build();
}
.
#POST
#Path("/exportContacts")
public Response exportContacts(#Context HttpServletRequest request, #Context HttpServletResponse response, #QueryParam("alt") String alt) throws IOException {
response.setContentType("application/octet-stream");
response.setHeader("Content-Disposition", "attachment;filename=temp.csv");
ServletOutputStream out = response.getOutputStream();
try {
StringBuilder sb = new StringBuilder("Sedat BaSAR");
InputStream in =
new ByteArrayInputStream(sb.toString().getBytes("UTF-8"));
byte[] outputByte = sb.getBytes();
//copy binary contect to output stream
while (in.read(outputByte, 0, 4096) != -1) {
out.write(outputByte, 0, 4096);
}
in.close();
out.flush();
out.close();
} catch (Exception e) {
}
return null;
}
When I checked from the firebug console, both of these blocks of code wrote "Sedat BaSAR" in response to the ajax call. However, I want to return "Sedat BaSAR" as a file. How can I do that?
Thanks in advance.
There're two ways to to it.
1st - return a StreamingOutput instace.
#Produces(MediaType.APPLICATION_OCTET_STREAM)
public Response download() {
InputStream is = getYourInputStream();
StreamingOutput stream = new StreamingOutput() {
public void write(OutputStream output) throws IOException, WebApplicationException {
try {
output.write(IOUtils.toByteArray(is));
}
catch (Exception e) {
throw new WebApplicationException(e);
}
}
};
return Response.ok(stream, MediaType.APPLICATION_OCTET_STREAM).header("content-disposition", "attachment; filename=\"temp.csv\"").build();
}
You can return the filesize adding Content-Length header, as the following example:
return Response.ok(stream, MediaType.APPLICATION_OCTET_STREAM).header("content-disposition", "attachment; filename=\"temp.csv\"").header("Content-Length", getFileSize()).build();
But if you don't want to return a StreamingOutput instance, there's other option.
2nd - Define the inputstream as an entity response.
#Produces(MediaType.APPLICATION_OCTET_STREAM)
public Response download() {
InputStream is = getYourInputStream();
return Response.code(200).entity(is).build();
}