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.
Related
I don't know how to avoid it, i have closed the response body already!
Can someone help me solve this problem?
my code:
public String getPageFromServer(String activityKey) throws Exception {
String address = pageServerHolder.getServerAddressRandom();
String url = MessageFormat.format(URL, address, activityKey);
log.debug("=============== url [{}] ================", url);
OkHttpClient client = new OkHttpClient().newBuilder().connectTimeout(60, TimeUnit.SECONDS).callTimeout(1, TimeUnit.SECONDS).build();
final Request request = new Request.Builder().url(url).get().build();
Response response = client.newCall(request).execute();
try (ResponseBody body = response.body()) {
if (response.isSuccessful() && null != body) {
String bodyString = body.string();
if (StringUtils.isNotBlank(bodyString)) {
body.close();
return bodyString;
}
}
}
throw new RuntimeException(MessageFormat.format("获取活动页信息异常,url [{0}], response.code [{1}],response.message [{2}] ", url, response.code(), response.message()));
}
Your problem is that the Response is not closed. Your code should be something like this:
try (Response response = client.newCall(request).execute();
ResponseBody body = response.body()) {
if (response.isSuccessful() && null != body) {
String bodyString = body.string();
if (StringUtils.isNotBlank(bodyString)) {
return bodyString;
}
}
}
I'm not sure if you need to close the ResponseBody. It is possible that closing response will deal with it. However, there is little harm in a redundant close.
Check out the example here: https://square.github.io/okhttp/
ResponseBody implements the Closeable interface. You are creating ResponseBody in the resource section of the try block, it will be closed for you as you leave the try block.
Your "body.close()" should not be there.
try (Response response = httpClient.newCall(request).execute();
ResponseBody responseBody = response.body()) {
if (response.isSuccessful()) {
log.info("access tree from {} success", url);
return objectMapper.readValue(responseBody.string(), StorageTreeResponse.class);
} else {
return null;
}
} catch (IOException e) {
e.printStackTrace();
return null;
}
I am wondering this is just a bug from okhttp client. Even the try resource syntax didn't solve the warnings.
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
RestTemplate example is below.
public class SimpleClient {
private final String URL;
private AsyncRestTemplate rest = new AsyncRestTemplate(new Netty4ClientHttpRequestFactory());
private RestTemplate restTemplate = new RestTemplate(new Netty4ClientHttpRequestFactory());
public SimpleClient(String url) {
this.URL = url;
Netty4ClientHttpRequestFactory nettyFactory = new Netty4ClientHttpRequestFactory();
try {
nettyFactory.setSslContext(SslContextBuilder.forClient().build());
} catch (SSLException e) {
e.printStackTrace();
}
rest = new AsyncRestTemplate(nettyFactory);
}
#Override
public ResponseEntity<ResponseData> doSendByPOST(RequestData data,Class<ResponseData> clazz) {
List<HttpMessageConverter<?>> messageConvertors = new ArrayList<>();
messageConvertors.add(new MappingJackson2HttpMessageConverter());
rest.setMessageConverters(messageConvertors);
restTemplate.setMessageConverters(messageConvertors);
HttpHeaders headers = new HttpHeaders();
ObjectMapper objectMapper = new ObjectMapper();
StringWriter writer = new StringWriter();
try {
objectMapper.writeValue(writer, data);
} catch (IOException e) {
e.printStackTrace();
}
headers.set(HttpHeaders.CONTENT_LENGTH,String.valueOf(writer.toString().getBytes(Charset.forName("UTF-8")).length));
headers.set(HttpHeaders.CONTENT_TYPE,"application/json");
HttpEntity<ResponseData> request = new HttpEntity<ResponseData>(headers);
MultiValueMap<String, Object> parts = new LinkedMultiValueMap<String, Object>();
try {
parts.add("requestData", objectMapper.writeValueAsString(data));
} catch (JsonProcessingException e) {
e.printStackTrace();
}
// return restTemplate.exchange(this.URL,HttpMethod.POST ,request, clazz, parts);
ListenableFuture<ResponseEntity<ResponseData>> entity = rest.exchange(this.URL,HttpMethod.POST ,request, clazz, parts);
return extractResponseEntity(entity);
}
// ...
}
Netty read data from request channelRead method
#Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if (msg instanceof HttpRequest) {
DefaultHttpRequest defaultHttpRequest = (DefaultHttpRequest) msg;
if (EmptyHttpHeaders.is100ContinueExpected(defaultHttpRequest)) {
ctx.write(new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,HttpResponseStatus.CONTINUE));
}
boolean keepAlive = EmptyHttpHeaders.isKeepAlive(defaultHttpRequest);
handle = frontController.dispatchRequest(defaultHttpRequest);
}
if (msg instanceof HttpContent) {
HttpContent httpContent = (HttpContent) msg;
ByteArrayOutputStream body = new ByteArrayOutputStream(64);
ByteBuf content = httpContent.content();
if (content.isReadable()) {
//body.write(content.array());
content.readBytes(body,content.readableBytes());
//body.append(content.toString(CharsetUtil.UTF_8));
FullHttpResponse response = handle.handle(body);
if(response == null){
response = prepareDefaultResponse();
}
response.headers().set("content-type", "application/json");
response.headers().set("content-length", response.content().readableBytes());
response.headers().set("connection", HttpHeaderValues.KEEP_ALIVE);
}
if (msg instanceof LastHttpContent) {
//process request
ctx.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
}
}
The code below is working fine but I guess there is a problem with blocking io and nonblocking io. When the request is dispatched, I can not reach the HttpContent I only get HttpRequest as a msg parameter. Spring resttemplate waits for a response but Netty does not care :)
if (msg instanceof HttpRequest) {
DefaultHttpRequest defaultHttpRequest = (DefaultHttpRequest) msg;
if (EmptyHttpHeaders.is100ContinueExpected(defaultHttpRequest)) {
ctx.write(new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,HttpResponseStatus.CONTINUE));
}
boolean keepAlive = EmptyHttpHeaders.isKeepAlive(defaultHttpRequest);
handle = frontController.dispatchRequest(defaultHttpRequest);
}
My problem is how to get response from netty server by rest template.
I have tried many ways to accomplish full req/resp.
When restTemplate request to Netty server it hangs the thread so I can not move on the distributed in memory cache implementation.
Hanging in RestTemplate.java Line : 681
Method waits forever when using Netty4ClientHttpRequestFactory.
response = request.execute();
From my understanding, you read HTTP post request that from Rest Client as HttpRequest Object lets call it first case so that means you don't even branch on the if (msg instanceof HttpContent) {} case (second one) your HTTP server just writes the default response without any content or header that you're setting in the second case. If this is the cause for the blocking on the client side you have to fill that default response just like on the second case an see what client do.
I think netty API provides this
https://netty.io/4.1/api/io/netty/handler/codec/http/DefaultFullHttpResponse.html
Also this example could give you an idea of what could be wrong server side.
http://www.seepingmatter.com/2016/03/30/a-simple-standalone-http-server-with-netty.html
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
Let's say there is a 3rd party RESTful web service exposing a GET endpoint at:
http://someservice.com/api/askAnyQuestion
And I want to hit that service, placing my question on the query string:
http://someservice.com/api/askAnyQuestion&q=Does%20my%20dog%20know%20about%20math%3F
How do I hit this service from a client-side GWT application? I've been reading the RequestFactory tutorials, but RF seems to be only for providing a data access layer (DAL) and for CRUDding entities, and I'm not entirely sure if it's appropriate for this use case.
Extra super bonus points if anyone can provide a code sample, and not just a link to the GWT tutorials, which I have already read, or some Googler's blog, which I have also probably read ;-).
You can use RequestBuilder. Successfully used it to work with REST.
RequestBuilder builder = new RequestBuilder(RequestBuilder.GET, url);
try {
builder.sendRequest(null, new RequestCallback() {
#Override
public void onError(Request request, Throwable exception) {
// process error
}
#Override
public void onResponseReceived(Request request, Response response) {
if (200 == response.getStatusCode()) {
// process success
} else {
// process other HTTP response codes
}
}
});
} catch (RequestException e) {
// process exception
}
Please also take a look at this question for cross site requests related info.
I had the same problem few days ago and tried to implement it with requestBuilder. You will receive a Cross-Domain Scripting issue.
https://developers.google.com/web-toolkit/doc/1.6/FAQ_Server#How_can_I_dynamically_fetch_JSON_feeds_from_other_web_domains?
I did handle this by a RPC Request to my Server, and from there a Server-Side HTTP Request to the Cross-Domain URL.
https://developers.google.com/web-toolkit/doc/latest/tutorial/Xsite
public static void SendRequest(String method, String notifications) {
String url = SERVICE_BASE_URL + method;
JSONObject requestObject = new JSONObject();
JSONArray notificationsArray =null;
JSONObject mainRequest = new JSONObject();
try {
notificationsArray = new JSONArray(notifications);
requestObject.put("notifications", notificationsArray);
mainRequest.put("request", requestObject);
} catch (JSONException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
HttpURLConnection connection = null;
try
{
URL server = new URL(url);
connection = (HttpURLConnection) server.openConnection();
connection.setRequestMethod("POST");
connection.setRequestProperty("Content-Type", "application/json");
connection.setDoInput(true);
connection.setDoOutput(true);
DataOutputStream writer = new DataOutputStream(connection.getOutputStream());
writer.writeBytes(mainRequest.toString());
writer.flush();
writer.close();
parseResponse(connection);
}
catch (Exception e)
{
System.out.println("An error occurred: " + e.getMessage());
}
finally
{
if (connection != null)
{
connection.disconnect();
}
}
}