Reading from a socket using buffered reader blocks [duplicate] - java

This question already has answers here:
Buffered Reader HTTP POST
(4 answers)
Closed 7 years ago.
I am trying to create a http server in java.
The following is a fragment of my code.
ServerSocket s = new ServerSocket(80);
while(true){
Socket client = s.accept();
BufferedReader in = new BufferedReader(
new InputStreamReader(client.getInputStream()));
String input = in.readLine(), ff;
System.out.println(input);
while((ff = in.readLine()) != null){
System.out.println(ff);
}
System.out.println("asd");
in.close();
client.close();
}
Asd is never printed. When I try to do a POST on the local host, it sends me all the headers but doesn't send any content data. It just waits there doing nothing.
I am doing a post using Jquery
$.post("http://127.0.0.1/",
{
name: "Donald Duck",
city: "Duckburg"
},
function(data,status){
alert("Data: " + data + "\nStatus: " + status);
});
This is what is being printed on the screen:
POST / HTTP/1.1
Host: 127.0.0.1
Connection: keep-alive
Content-Length: 5
Accept: */*
Origin: http://www.w3schools.com
User-Agent: Mozilla/5.0 (X11; Linux i686) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/47.0.2526.73 Chrome/47.0.2526.73 Safari/537.36
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
That's it, nothing after this. It waits indefinitely at this point.

My guess would be that your actual content/payload is not terminated by the expected line delimiters as described in BufferedReader#readLine().
This blocks your in.readLine() invocation.
I would try to use the read() method instead and see if that helps. As in
while((value = in.read()) != -1) {
char c = (char)value;
System.out.println(c);
}
Edit: In fact, your question most likely duplicates this question: Buffered Reader HTTP POST

Related

Why InputStreamReader cannot read the content of a websocket package?

To understand the behaviour of the websocket, I created a simple SocketServer in java to exchange the messages. The server is expected to follow the operations as:
1) Listening at port 8080
2) A websocket handshake message generated manually on the browser client and received by the server.
3) Construct a response to the handshake message and reply to the client
4) Read out actually websocket info bytes with the same connection.
The problem has happened at step 4. when the server has responded with the handshake message, the InputStreamReader can no longer receive any new message. It blocked at the readline() method even though the client has sent the message already. From wireshark, I can see the client sent message and server respond ack. Any help would be appreciated, thanks.
Update: I just noted this question has been asked before. I will study the suggestions on the other posts first.
Update: The behavior is as same as this post:
Weird websocket behavior: only send data when close the tab
When the webtab closes, the read stream received the data package.
wireshark screen captures:
The tcp stream trace
The packages sequence
The log:
inputline: GET / HTTP/1.1
inputline: Host: localhost:8080
inputline: Connection: Upgrade
inputline: Pragma: no-cache
inputline: Cache-Control: no-cache
inputline: Upgrade: websocket
inputline: Origin: file://
inputline: Sec-WebSocket-Version: 13
inputline: User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5)AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36
inputline: Accept-Encoding: gzip, deflate, br
inputline: Accept-Language: en,zh-TW;q=0.8,zh;q=0.6,zh-CN;q=0.4
inputline: Sec-WebSocket-Key: Yin4xn04vr9iBH1b2dU15A==
inputline: Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
inputline:
response: HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: +Y9whLTzCdyN1INpAxjkO6yD2Nw=
The server socket code:
public class EchoServer {
public static String HTTP_VERSION_HEADER = "HTTP/1.1";
public static void main(String[] args) throws IOException {
int portNumber = 8080;
try (
ServerSocket serverSocket =
new ServerSocket(Integer.parseInt("8080"));
Socket clientSocket = serverSocket.accept();
PrintWriter out =
new PrintWriter(clientSocket.getOutputStream(), true);
BufferedReader in = new BufferedReader(
new InputStreamReader(clientSocket.getInputStream()));
) {
String inputLine;
StringBuilder sb = new StringBuilder();
while ( true) {
inputLine = in.readLine();
if(inputLine == null) {
System.out.println("input is null");
continue;
}
System.out.println("inputline: " + inputLine);
sb.append(inputLine).append(System.lineSeparator());
if(inputLine.equals("")) {
Message msg = new Message(sb.toString(), new Date());
HttpMessage tmpMessage = new HttpMessage(msg.getText(), new Date());
String response = generateHandshakeResponse(tmpMessage);
System.out.println("response: " + response);
out.println(response);
out.flush();
}
}
} catch (IOException e) {
System.out.println("Exception caught when trying to listen on port "
+ portNumber + " or listening for a connection");
System.out.println(e.getMessage());
}
}
private static String generateHandshakeResponse(HttpMessage message) {
String webSocketKey = message.getVal(HttpMessage.SEC_WEBSOCKET_KEY);
String webSocketAccept = webSocketKey + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
byte[] bytes = DigestUtils.sha1(webSocketAccept);
String secWebSocketAcceptVal = Base64.encodeBase64String(bytes);
StringBuilder sb = new StringBuilder();
sb.append(HTTP_VERSION_HEADER).append(" 101 ").append("Switching Protocols\r\n");
sb.append("Upgrade: websocket\r\n");
sb.append("Connection: Upgrade\r\n");
sb.append("Sec-WebSocket-Accept: ").append(secWebSocketAcceptVal).append("\r\n");
sb.append("\n\n") //<--- this line fixed the problem
//log.debug(sb.toString());
return sb.toString();
}
}
The client code:
<!doctype html>
<html lang="en">
<head>
<title>Websocket Client</title>
</head>
<body>
<script>
var exampleSocket = new WebSocket("ws://localhost:8080");
exampleSocket.onopen = function (event) {
console.log("connection opened..");
exampleSocket.send("Can you hear me?");
};
exampleSocket.onmessage = function (event) {
console.log(event.data);
}
function sendMsg(){
console.log("send message..");
exampleSocket.send("hello hello");
}
</script>
<button onclick="sendMsg()" title="send">send</button>
</body>
</html>
Thanks a lot to EJP, the problem is a missing blank line at the response handshake to indicate the end of the message.

websocket protocol error when server deflates payload

I'm writing a Java websocket server and it now receives the opening handshake message from the client (in Chrome Version 57.0.2987.133 (64-bit)) and responds to complete the handshake, both shown below.
Received: GET / HTTP/1.1
Received: Host: localhost:6789
Received: Connection: Upgrade
Received: Pragma: no-cache
Received: Cache-Control: no-cache
Received: Upgrade: websocket
Received: Origin: http://localhost:8080
Received: Sec-WebSocket-Version: 13
Received: User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36
Received: Accept-Encoding: gzip, deflate, sdch, br
Received: Accept-Language: en-US,en;q=0.8
Received: Sec-WebSocket-Key: L1IiUSGijbGmTpthWsebOg==
Received: Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
Sending: HTTP/1.1 101 Switching Protocols
Sending: Upgrade: websocket
Sending: Connection: Upgrade
Sending: Sec-WebSocket-Accept: L5HXnDJGDMYbWr5gRcQMOwKNf3Q=
Sending: Accept-Encoding: gzip, deflate
Sending: Sec-WebSocket-Extensions: permessage-deflate; client_no_context_takeover; server_no_context_takeover
Now, the client can send messages and it does so without a problem and my code successfully uncompresses them using java.util.zip.Deflater and... if my server responds with header bytes 0x81 (fin, no compression, text) and 0x5 then the bytes for 'hello' (as an example) then the websocket client in javascript on Chrome is entirely happy but when I try to compress the response, the client always closes the connection citing error code 1002 and the text 'Websocket protocol error'.
DEFLATE
public void sendMessageDeflated(String rxMessage, OutputStream streamOut) {
System.out.println("Message back to client is: " + rxMessage);
// And then compress the response and send it out.
Deflater compressor = new Deflater(Deflater.DEFLATED);
try {
int headerLength = 2;
byte unzippedMsg[] = rxMessage.getBytes("UTF-8");
compressor.setInput(unzippedMsg);
compressor.finish();
byte zippedMsg[] = new byte[2048]; // Nasty constant but will have to do for now.
int toCompressLength = unzippedMsg.length;
int compLength = compressor.deflate(zippedMsg, headerLength, zippedMsg.length - headerLength);
compressor.end();
zippedMsg[0] = (byte)0xC1; // FIN bit, compression plus opcode for TEXT MESSAGE
zippedMsg[1] = (byte)((byte)0x00 | (byte)compLength); // No mask on return data.
streamOut.write(zippedMsg, 0, compLength + headerLength);
} catch ( IOException ioEx ) {
// TBD
System.out.println("IOException: " + ioEx.toString());
} catch ( Exception ex ) {
// TBD
System.out.println("IOException: " + ex.toString());
}
}
GZIP
public void sendMessageGZipped(String rxMessage, OutputStream streamOut) {
// Do something with the message here...
System.out.println("Message back to client is: " + rxMessage);
// And then compress the response and send it out.
try {
int headerLength = 2;
byte unzippedMsg[] = rxMessage.getBytes("UTF-8");
int toCompressLength = unzippedMsg.length;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
GZIPOutputStream gzipOut = new GZIPOutputStream(baos);
gzipOut.write(unzippedMsg, 0, toCompressLength);
gzipOut.close();
byte[] payload = baos.toByteArray();
byte header[] = new byte[32];
header[0] = (byte)0xC1; // FIN bit plus opcode for TEXT MESSAGE
header[1] = (byte)((byte)0x00 | (byte)payload.length); // No mask on return data.
streamOut.write(header, 0, 2);
streamOut.write(payload);
} catch ( IOException ioEx ) {
// TBD
System.out.println("IOException: " + ioEx.toString());
} catch ( Exception ex ) {
// TBD
System.out.println("IOException: " + ex.toString());
}
}
I've tried switching to the opcode for binary thinking that perhaps compressed text = binary but that didn't work. I really can't see what I've done wrong or missed out. This implementation does not include a sliding compression window that spans messages. I think the response headers make that clear. Help gratefully accepted.
I solved this eventually by building the client-side websocket code and seeing the exception thrown when it tried to Inflate the message: "invalid stored block lengths". That led me to this post: Java decompressing array of bytes which talks about Deflate being able to compress with zlib wrapper or without. So change one line in processMessage2 from the example code above to this...
Deflater compressor = new Deflater(Deflater.DEFLATED, true);
...which sets the 'nowrap' to true.
In its headers the Chrome browser client claims to support gzip. If I get that working I'll come back here and post the answer to that too.

Extracting the body from HTTP post requestH

I have a weird problem when trying to extract the body of a given
HTTP post request.
If I try to extract only the header, it works fine. When I try to extract the body, the method blocks (even thought the stream still has data in it).
Here is my code:
private void extractHeader() throws Exception {
StringBuffer buffer = new StringBuffer();
InputStreamReader reader = new InputStreamReader(socket.getInputStream());
BufferedReader bufferedReader = new BufferedReader(reader);
boolean extractBody = false;
int bodyLength = 0;
String line;
while (!(line = bufferedReader.readLine()).equals("")) {
buffer.append(line + "");
if (line.startsWith("POST")) {
extractBody = true;
}
if (line.startsWith("Content-Length:")) {
bodyLength = Integer.valueOf(line.substring(line.indexOf(' ') + 1, line.length()));
}
}
requestHeader = buffer.toString();
if (extractBody) {
char[] body = new char[bodyLength];
reader.read(body, 0, bodyLength);
requestBody = new String(body);
}
}
And this is the request request:
POST /params_info.html HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.11; rv:42.0) Gecko/20100101 Firefox/42.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://localhost:8080/index.html
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded
Content-Length: 31
firstname=Mickey&lastname=Mouse
From what I understand, the loop will go until seeing the empty string
and then stoping. At this stage, the reader can read 'Content-Length' number of bytes. So it should have no problem reading the body and finish. Instead, it blocks on the line 'reader.read(body, 0, bodyLength);'
(The reason I don't use readLine() is because body does not end with \n).
I've tried debugging it in al kinds of ways but I get nothing. Can anyone please help with this?
You're reading the header using the bufferedReader:
while (!(line = bufferedReader.readLine()).equals("")) {
but read the body using reader, which has no data available, as this has been read and buffered by the bufferedReader:
reader.read(body, 0, bodyLength);
Change that line to
bufferedReader.read(body, 0, bodyLength);

Reading a POST in java

I'm having troubles trying to capture a POST request in a java web server (I'm not allowed to use specific HTTP libraries).
Reading the socket:
this.socketIn = new BufferedReader(new InputStreamReader(this.socket.getInputStream()));
String inputLine;
StringBuilder requestString = new StringBuilder();
int lines = 0;
while ((inputLine = this.socketIn.readLine()) != null && inputLine.length() > 0 && lines < 100) {
requestString.append(inputLine + "\n");
lines++;
}
// if (requestString.toString().split("\\n")[0].startsWith("POST")){
// System.out.println(this.socketIn.readLine());
//}
after that, if I do requestString.toString() I get the following:
POST {{uselessDir}}/ HTTP/1.1
Host: {{myIP}}
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; rv:34.0) Gecko/20100101 Firefox/34.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: es-ES,es;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Referer: http://{{myIP}}/{{uselessDir}}/
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded
Content-Length: 42
so, I tried adding a if at the end of the loop, because of there is a empty line between Content-Length: 42 and the line I'm looking for, and it blocked reading. In order to ensure using tcpdump I was able to capture also the last line.
testData1=someText&testData2=someOtherText
maybe the solution is very silly, and I'm not seing it.
Thanks.
actually you can use a BufferedInputStream but you must handle the empty line with care:
for example you leave your "readLine" loop when line is empty:
while (!(inputLine = this.socketIn.readLine()).equals("")) {
inside the loop you must read the content-length:
if (inputLine.startsWith("Content-Length: ")) {
contentLength = Integer.parseInt(inputLine.substring("Content-Length: ".length()));
}
and after the loop, you read exactly the number of bytes that you expect, not more:
char[] content = new char[contentLength];
this.socketIn.read(content);
System.out.println(new String(content));
Remove && inputLine.length() > 0 from your while condition. It's causing your while loop to exit at the first blank line. If you want to omit blank lines, rather use the snippet in this location:
while ((inputLine = this.socketIn.readLine()) != null && lines < 100) {
if (inputLine.length() > 0) {
requestString.append(inputLine + "\n");
lines++;
}
}

Java HTTP Server response incomplete

I am trying to build my own embedded HTTP Server for Java with very specific usage for an internal closed system. Using embedded solution that already exists meant that I need to strip them down to refit them for my internal system's specific use cases.
I have managed to get my Java HTTPD receive HTTP Requests from web browsers but the Requests that the HTTPD receives are incomplete.
Below is the server thread codes (pretending the ServerSocket works perfectly fine):
public class HttpThread implements Runnable {
private Socket socket;
private DataInputStream in;
private PrintStream out;
private BufferedReader br;
private String EOF = "\n";
private String STATUS_200 = "HTTP/1.1 200 OK" + EOF;
public HttpThread(Socket socket) throws IOException {
this.socket = socket;
this.in = new DataInputStream(this.socket.getInputStream());
this.out = new PrintStream(new BufferedOutputStream(this.socket.getOutputStream()));
}
#Override
public void run() {
System.out.println("New thread...");
try {
processInput();
//socket.close();
} catch (IOException ex) {
Logger.getLogger(HttpThread.class.getName()).log(Level.SEVERE, null, ex);
} finally {
if (br != null) {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
//System.out.println("Thread END...");
}
private String processInput() throws IOException {
String line;
StringBuilder buff = new StringBuilder();
br = new BufferedReader(new InputStreamReader(in));
while ((line = br.readLine()) != null) {
buff.append(line);
System.out.println(">>> " + line);
if (line.trim().isEmpty()) {
break;
}
}
out.print(STATUS_200);
out.print("Server: Test Server\n" +
"Content-Type: text/html; charset=UTF-8\n" +
"Connection: close");
out.print(EOF);
out.print("<html><body>yello</body></html>");
out.print(EOF);
out.flush();
System.out.println(STATUS_200);
return buff.toString();
}
}
I am using this HTML Script to test the server thread to simulate a POST request:
<html>
<body onLoad="document.test.submit();">
<form action="http://localhost:9999/" method="POST" name="test">
<input type=hidden name="userid" value="1443"/>
<input type=hidden name="password" value="1443"/>
</form>
</body>
</html>
When I use the browser to call the HTML codes the Java HTTPD receives an incomplete response:
New thread...
>>> POST / HTTP/1.1
>>> Host: localhost:9999
>>> Connection: keep-alive
>>> Content-Length: 25
>>> Cache-Control: max-age=0
>>> Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
>>> Origin: null
>>> User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.72 Safari/537.36
>>> Content-Type: application/x-www-form-urlencoded
>>> Accept-Encoding: gzip,deflate,sdch
>>> Accept-Language: en-GB,en-US;q=0.8,en;q=0.6
>>>
HTTP/1.1 200 OK
New thread...
>>> GET /favicon.ico HTTP/1.1
>>> Host: localhost:9999
>>> Connection: keep-alive
>>> Accept: */*
>>> User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.72 Safari/537.36
>>> Accept-Encoding: gzip,deflate,sdch
>>> Accept-Language: en-GB,en-US;q=0.8,en;q=0.6
>>>
HTTP/1.1 200 OK
It seems that the HTTPD only received the HTTP headers and stopped receiving the POST body.
May I know of some solutions to solve the above problem ?
Thanks.
The HTTP headers and body are separated by an empty line (also see the HTTP RFC, especially the chapter "5 Request"). Your server reads the socket Inputstream but breaks on an empty line :
if (line.trim().isEmpty()) {
break;
}
Therefore obviously you will not receive the body. You should fully consume the Inputstream instead.
Besides that, I would advise you to use already existing solutions. There is an abundance of HTTP server implementations, that are well tested and proven in real world usage. Save yourself alot of headache and use an existing lightweight server like e.g. Jetty, Netty or similar ones.
I switched out the while loop that does the readline() with the following code:
int i;
byte[] buffer = new byte[2048];
i = in.read(buffer);
for (int j = 0; j < i; j++) {
buff.append((char) buffer[j]);
}
System.out.println(buff.toString());
and the problem is solved.
Pyranja, thanks for the help.
Thanks to http://kcd.sytes.net/articles/simple_web_server.php IF you follow concisely. Somehow the br.readline() is not fully reading the lines properly after the empty line.
The code fragment should now look like:
private String processInput() throws IOException {
String line;
StringBuilder buff = new StringBuilder();
int i;
byte[] buffer = new byte[2048];
i = in.read(buffer);
for (int j = 0; j < i; j++) {
buff.append((char) buffer[j]);
}
System.out.println(buff.toString());
out.print(STATUS_200);
out.print("Server: Test Server\n" +
"Content-Type: text/html; charset=UTF-8\n" +
"Connection: close");
out.print(EOF);
out.print("<html><body>yello</body></html>");
out.print(EOF);
out.flush();
System.out.println(STATUS_200);
return buff.toString();
}
I guess it's a good experience learning how to build a simple HTTP Server :D .

Categories

Resources