Reputation: 313
I'm messing around with HTTP and sockets in Java and was hoping you could shed some light on this:
When my HTTP server written in Java SE 11 does not read the entire request and then responds, the client does not get it or gets an error. Why is that? Is the client unable to read the response before the server has read the entire request? If the call to readBody is executed in the snippet below, this works fine. It also works fine if the response has the Content-Length header and a text body. That is actually more puzzling to me.
My example request is a POST with the data fds. Postman says "Could not get any request" and curl says "curl: (56) Recv failure: Connection reset by peer".
import java.io.*;
import java.net.Socket;
import java.util.*;
class Handler {
public synchronized void read(Socket incoming) {
try (incoming;
OutputStream outputStream = incoming.getOutputStream();
InputStream inputStream = incoming.getInputStream();
PrintWriter pw = new PrintWriter(outputStream)) {
writeRequest(inputStream);
pw.print("HTTP/1.1 200 OK\r\n");
pw.print("\r\n");
pw.flush();
} catch (IOException e) {
System.out.println("ERROR: " + e.getMessage());
e.printStackTrace();
}
}
private void writeRequest(InputStream inputStream) throws IOException {
String verbLine = readLine(inputStream);
Map<String, String> headers = readHeaders(inputStream);
//readBody(inputStream, headers);
}
private void readBody(InputStream inputStream, Map<String, String> headers) throws IOException {
Optional<String> optKey = headers.keySet().stream()
.filter(k -> k.equalsIgnoreCase("Content-Length"))
.findFirst();
if (optKey.isPresent()) {
int contentLength = Integer.parseInt(headers.get(optKey.get()));
byte[] bytes = inputStream.readNBytes(contentLength);
}
}
private Map<String, String> readHeaders(InputStream inputStream) throws IOException {
Map<String, String> headers = new HashMap<>();
while (true) {
String line = readLine(inputStream);
if (line == null || line.isEmpty()) {
return headers;
}
String key = line.split(":")[0].trim();
String value = line.split(":")[1].trim();
headers.put(key, value);
}
}
private String readLine(InputStream inputStream) throws IOException {
byte[] buf = new byte[200];
int offset = 0;
while (true) {
int read = inputStream.read();
if (read == -1) {
return null;
}
buf[offset] = (byte) read;
if (buf[0] == '\n' || (buf[0] == '\r' && buf[1] == '\n')) {
return "";
}
if (buf[offset] == 0x0A) {
int endOfLine = buf[offset - 1] == 0x0D ? offset - 1 : offset;
return new String(buf, 0, endOfLine);
} else {
offset++;
}
}
}
}
Upvotes: 0
Views: 1520
Reputation: 123531
If you close a socket at the server while there are still unread data it will result in a connection reset error at the client. This happens here since you don't read the full request. This error will be exposed to the user if the full response from the server was not read yet.
If you send the response with a content-length
and then the full body then the client will have read the full response and thus the error will be ignored. If instead you send neither content-length
nor use chunked encoding the client will expect the response to end with a proper close of the TCP connection. In this case the connection reset will be propagated to the user since the full response from the server was not (properly) read yet.
Upvotes: 3
Reputation: 3288
Your response needs to have either a Content-Length header or a Transfer-Encoding header - which tells the client how the response will be transmitted and allows it to figure out when all the bytes have been received. Without that it will need to wait for EOF and assume that the response body is terminated by EOF (this for compatibility with HTTP/1.0). It is possible that your client doesn't support that. It might help to know which client you are using.
Upvotes: 0