I'm trying to create a http server in Java which is capable of providing keep-alive connections. I'm using the com.sun.net.httpserver.HttpServer class.
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import com.sun.net.httpserver.Headers;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
public class httpHandler implements HttpHandler {
private String resp = "<?xml version='1.0'?><root-node></root-node>";
private OutputStream os = null;
public void handle(HttpExchange t) throws IOException {
System.out.println("Handling message...");
java.io.InputStream is = t.getRequestBody();
System.out.println("Got request body. Reading request body...");
byte[] b = new byte[500];
is.read(b);
System.out.println("This is the request: " + new String(b));
String response = resp;
Headers header = t.getResponseHeaders();
header.add("Connection", "Keep-Alive");
header.add("Keep-Alive", "timeout=14 max=100");
header.add("Content-Type", "application/soap+xml");
t.sendResponseHeaders(200, response.length());
if(os == null) {
os = t.getResponseBody();
}
os.write(response.getBytes());
System.out.println("Done with exchange. Closing connection");
os.close();
}
public static void main(String[] args) {
HttpServer server = null;
try {
server = HttpServer.create(new InetSocketAddress(8080), 5);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
server.createContext("/", new httpHandler());
server.setExecutor(null); // creates a default executor
System.out.println("Starting server...");
server.start();
}
}
The client does not close the connection. The server seems to close it instead directly after the exchange has occurred. I tried deleting the os.close line but then the server will not reply to the second request. But it doesn't close it either. I have a feeling it involves doing something in the main code with the server object but I have no idea what. Google isn't turning up much either.
Anyone here got any ideas? Any help would be much appreciated.
It looks like the problem is you didn't drain all data from request. You should keep doing is.read() until it returns -1, then close it.
Since you didn't drain the request, there are still bytes left. The server cannot simply "jump" to the next request; it's not like a disk, more like a tape. Server has to read(and discard) all data from current request, before the it can reach the next request.
Without a limit this can be used to attack the server; so the server would only attempt to drain up to a limit, which is 64K by default. You are probably receiving a request bigger than 64K.
Usually the handler should read the entire request first. Otherwise how does it know how to serve the request?
More seriously, if the request is not drained first, deadlock can happen. Clients are typically simple: they writes the request, then read the response. If the server writes the response before it reads all the request, the client could be still writing the request. Both are writing to each other but neither is reading. If buffers are full then we are in a deadlock, both are blocked on write. Note there's no timeout for write()!
Why do you get the ResponseBody OutputStream only if the previous was null?
if(os == null) {
os = t.getResponseBody();
}
Might be better to get the OutputStream each time as you cannot be sure if the previous one is identical to the one of current Request.
try to call t.close(), the server will reply
It's the client that sends the keep-alive headers. If it does that, HttpServer doesn't close the connection. You don't have to do anything about it.
Related
I have a setup a simple http server in java that only deals with one type of post request.
The server code:
int port = 5555;
HttpServer server = HttpServer.create(new InetSocketAddress(port), 0);
System.out.println("server started at " + port);
server.createContext("/echoPost", new echoPost());
server.setExecutor(null);
server.start();
The echoPost() class:
public class echoPost implements HttpHandler {
#Override
public void handle(HttpExchange http) throws IOException {
InputStreamReader inputStreamReader = new InputStreamReader(http.getRequestBody(), "utf-8");
BufferedReader bufferReader = new BufferedReader(inputStreamReader);
String incoming = br.readLine();
System.out.println(incoming);
String response = "response";
http.sendResponseHeaders(200, response.length());
OutputStream outputStream = http.getResponseBody();
os.write(response.toString().getBytes());
os.close();
}
}
I want the server to stop after a post request is received. Is there a way to do this so straight after one post request is handled the server stops?
What's wrong with calling server.stop(delay), where the delay is small enough (0 or 1)? You'll need to pass server as argument to the echoPost constructor.
Use
System.exit(0);
It doesn't matter where this line of code is executed, it will terminate the JVM and quit.
I have a problem with getting XML from this webpage. In the browser it shows correctly and there is no issue, but when it comes to Java, it is different.
I've tried two methods which both of them resulted in exception.
// Method 1 - Using Java's URL
URL url = new URL(/* mentioned link */);
String rawXML = new String(url.openStream().readAllBytes(), StandardCharsets.UTF_8); // java.io.IOException: Invalid Http response
// Method 2 - Using Apache's HTTP client
HttpGet httpGet = new HttpGet(/* mentioned link */);
String rawXML = EntityUtils.toString(HttpClients.createDefault().execute(httpGet).getEntity()); // org.apache.http.ProtocolException: The server failed to respond with a valid HTTP response
Downloading this webpage with wget and using argument --content-on-error works but it is unreliable since wget is not always available on all systems like Windows.
The response does not contains headers so java rejects it
wget "https://www.strava.cz/foxisapi/foxisapi.dll/istravne.istravne.process?xmljidelnickyA&zarizeni=3148" -O so-69226464.html
--2021-09-17 13:44:29-- https://www.strava.cz/foxisapi/foxisapi.dll/istravne.istravne.process?xmljidelnickyA&zarizeni=3148
Resolving www.strava.cz (www.strava.cz)... 82.99.180.77
Connecting to www.strava.cz (www.strava.cz)|82.99.180.77|:443... connected.
HTTP request sent, awaiting response... 200 No headers, assuming HTTP/0.9
Length: unspecified
This java class making a raw HTTP GET request is able to get the contents. Based on this page.
The request sent is
GET /foxisapi/foxisapi.dll/istravne.istravne.process?xmljidelnickyA&zarizeni=3148 HTTP/1.1\r\n
User-Agent: RawHttpGet\r\n
Host: www.strava.cz\r\n
Accept: */*\r\n
Java code:
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import javax.net.ssl.SSLSocketFactory;
public class RawHttpGet {
private static String hostname = "www.strava.cz";
public static void main(String[] args) throws IOException {
Socket socket = SSLSocketFactory.getDefault().createSocket(hostname, 443);
// UTF-8 encdoding
//BufferedWriter out = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(), StandardCharsets.UTF_8));
// Encoding for this request
BufferedWriter out = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(), "Cp1250"));
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
StringBuffer buff = new StringBuffer("GET /foxisapi/foxisapi.dll/istravne.istravne.process?xmljidelnickyA&zarizeni=3148 HTTP/1.1\r\n");
buff.append("User-Agent: RawHttpGet\r\n");
buff.append("Accept: */*\r\n");
buff.append("Host: " + hostname + "\r\n");
buff.append("\r\n");
System.out.println(" * Request");
System.out.println(buff.toString());
// send message
out.write(buff.toString());
out.flush();
// read response
System.out.println(" * Response");
// Default system encoding
//System.out.println(new String(socket.getInputStream().readAllBytes()));
// Encoding for this request
System.out.println(new String(socket.getInputStream().readAllBytes(), "Cp1250"));
out.close();
in.close();
}
}
I am trying to write a java program that will automatically download and name some of my favorite web comics. Since I will be requesting multiple objects from the same domain, I wanted to have a persistent http connection that I could keep open until all the comics have been downloaded. Below is my work-in-progress. How do I make another request from the same domain but different path without opening a new http connection?
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
public class ComicDownloader
{
public static void main(String[] args)
{
URL url = null;
HttpURLConnection httpc = null;
BufferedReader input = null;
try
{
url = new URL("http://www.cad-comic.com/cad/archive/2002");
httpc = (HttpURLConnection) url.openConnection();
input = new BufferedReader(new InputStreamReader(httpc.getInputStream()));
String inputLine;
while ((inputLine = input.readLine()) != null)
{
System.out.println(inputLine);
}
input.close();
httpc.disconnect();
}
catch (IOException ex)
{
System.out.println(ex);
}
}
}
According to the documentation here, HTTP persistence is being handled transparently in Java, although it gives you the options to control it too via http.keepAlive and http.maxConnections system properties.
However,
The current implementation doesn't
buffer the response body. Which means
that the application has to finish
reading the response body or call
close() to abandon the rest of the
response body, in order for that
connection to be reused. Furthermore,
current implementation will not try
block-reading when cleaning up the
connection, meaning if the whole
response body is not available, the
connection will not be reused.
Take a look at the link and see if it really helps you.
According to this link http://docs.oracle.com/javase/6/docs/technotes/guides/net/http-keepalive.html, HTTP connection reuse is enabled by default, you can use Wireshark to check the interactions between your client and server. The first request contains TCP and SSL handshakes(if your request is https), the subsequent requests fired in the keep-alive time, contains no TCP and SSL handshakes, just application data transfers.
Even though HttpURLConnection enable keep-alive by default, it is not guaranteed that HttpURLConnection uses same TCP connection for multiple HTTP requests. I faced same kind of issue when writing HTTPS client application. Solved this issue by using single instance of SSLContext, SSLSocketFactory and HttpsURLConnection.
public class MyHTTPClient {
private SSLContext mSSLContext = null;
private SSLSocketFactory mSSLSocketFactory = null;
private HttpsURLConnection mConnection = null;
public void init() {
//Setup SSL context and Socket factory here
}
pubblic void sendRequest() {
URL url = new URL("https://example.com/request_receiver");
mConnection = (HttpsURLConnection) url.openConnection();
mConnection.setSSLSocketFactory(mSSLSocketFactory);
// Setup request property and send request
// Open input stream to read response
// Close output, input streams
mConnection.disconnect();
}
}
I'm trying to send a http post request as part of a concurrent thread to an application launch.
The code below shows the current code I have now. I tried using the code from Baeldung and similar tutorial sites but I can't seem to get this working.
public static void main(String[] args) throws Exception {
HttpURLConnection con = (HttpURLConnection) new URL("http://localhost:5000/").openConnection();
con.setRequestMethod("POST");
con.setRequestProperty("Content-Type", "application/json; utf-8");
con.setDoOutput(true);
String jsonInputString = "{\"status\": \"UP\"}";
OutputStream os = con.getOutputStream();
//random code here not involved with this quesiton
new Thread(new Runnable() {
public void run() {
while (true) {
try {
os.write(jsonInputString.getBytes());
os.flush();
}
catch (Exception exception) {
System.out.println("Its not working");
}
}
}
}).start();
launch(args);
When I go to type in localhost:5000 in a browser it says it can't connect.
First step make sure local host is working on port 5000, go to http://localhost:5000/ in a regular browser, if thats not working you need to make sure your machine is serving a page on port 5000 properly.
If thats working, the HTTP request may be getting blocked because its not connecting to a server with a valid SSL certificate, you can try changing the webpage to http://www.google.com and see if it gets a response.
I have a test server and a client application. Server is built with Vert.x 3.2.1 (users Netty 4.0.33.Final) and client with Apache httpclient 4.5.1.
Have configured server to accept maxHeaderSize as 8KB. As I send requests from client I see that requests bearing header sizes of around 3KB starts failing with below exception on server.
io.netty.handler.codec.TooLongFrameException: HTTP header is larger than 8192 bytes
Sharing the server and client code for reference.
Server:
public static void main(String[] args) {
Vertx vertx = Vertx.vertx();
HttpServerOptions options = new HttpServerOptions();
options.setMaxHeaderSize(1024*8);
HttpServer httpServer = (HttpServer) vertx.createHttpServer(options );
httpServer.requestHandler(req -> {
System.out.println(req.headers().names());
for(String h : req.headers().names()){
System.out.println(h+": "+req.headers().get(h).length()+": "+(h.length()+req.headers().get(h).length()));
}
req.response().setStatusCode(200).end();
});
httpServer.listen(6565);
}
Client:
public static void main(String[] args) {
try {
CloseableHttpClient httpclient = HttpClients.custom().build();
HttpGet get = new HttpGet("http://localhost:6565");
String headerValue = generateHeader(3045);
get.addHeader("myheader", headerValue);
HttpResponse response = httpclient.execute(get);
String resp = IOUtils.toString(response.getEntity().getContent());
System.out.println(response.getStatusLine());
httpclient.close();
} catch (Exception e) {
e.printStackTrace();
}
}
generateHeader() is a method that generates a String of specified length - when input with 3, result is "aaa".
Apache http client also adds some more headers - Host, Connection, User-Agent and Accept-Encoding. Along with all these, The behavior I notice is that the server fails when I pass 3045 or above as input to generateHeader(), but succeeds for any lower value.
Though configured is 8k, I fail to understand why failures occur for such lower values.
This may be a bug in Netty. Please open a bug-report in Netty issue tracker and provide details how to reproduce it:
https://github.com/netty/netty/issues