Reputation: 23200
I have two different ways to create ByteBuffer
object from String:
byte[]
from String an use ByteBuffer.put(byte[])
method:private ByteBuffer respWithPut() {
ByteBuffer respBuf = ByteBuffer.allocate(1024);
respBuf.put(httpResponse().getBytes(StandardCharsets.US_ASCII));
return respBuf;
}
Charset.encode(String)
method:private ByteBuffer respFromChar() {
return StandardCharsets.US_ASCII.encode(httpResponse());
}
I am trying to send a simple HTTP response using this (complete code at the end of the question). With respWithPut()
I get corrupted response on client, while respFromChar()
works fine.
What am I doing wrong in respWithPut()
?
Complete Sample Code:
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousServerSocketChannel;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.util.Arrays;
import java.util.concurrent.Future;
public class AsyncServer {
final String HTTP_DELIM = "\r\n";
private String httpResponse() {
String body = "HELLO";
String prologue = "HTTP/1.1 200 OK";
String header = String.join(HTTP_DELIM,
Arrays.asList(
"Date: " + Instant.now().toString(),
"Content-Type: text/plain",
String.format("Content-Length: %d", body.length()),
HTTP_DELIM
)
);
return prologue + HTTP_DELIM + header +body;
}
private ByteBuffer respWithPut() {
ByteBuffer respBuf = ByteBuffer.allocate(1024);
respBuf.put(httpResponse().getBytes(StandardCharsets.US_ASCII));
return respBuf;
}
private ByteBuffer respFromChar() {
return StandardCharsets.US_ASCII.encode(httpResponse());
}
public void startHttpServer() throws Exception {
AsynchronousServerSocketChannel listener
= AsynchronousServerSocketChannel.open().bind(new InetSocketAddress(8080));
while (true) {
Future<AsynchronousSocketChannel> asyncCh = listener.accept();
AsynchronousSocketChannel aSock = asyncCh.get();
aSock.write(respWithPut());
aSock.close();
}
}
public static void main(String[] args) throws Exception {
AsyncServer asyncServer = new AsyncServer();
asyncServer.startHttpServer();
}
}
To make a sample request, use: curl -v "http://localhost:8080/"
.
Upvotes: 1
Views: 342
Reputation: 44328
A ByteBuffer has a position indicating where the next byte should be read from. Your respWithPut method needs to call respBuf.flip() to make sure the buffer’s position is pointing to the data you just put in it.
After ByteBuffer.allocate
:
position limit
↓ ↓
|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_| … |_|
0 1 2 3 4 5 5 6 7 8 9 1 1 1 1 1 1 ↑
0 1 2 3 4 5 buffer size
After calling ByteBuffer.put
with, for example, a byte array of length eight:
position limit
↓ ↓
|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_| … |_|
0 1 2 3 4 5 5 6 7 8 9 1 1 1 1 1 1 ↑
0 1 2 3 4 5 buffer size
The next ByteBuffer.get
call will read the byte at index 8, which is still zero since you haven’t used put
to add any data there.
After calling ByteBuffer.flip
, the limit will be the old position, and the new position will be zero, making any existing data ready for reading:
next get operation will read from here
↓
position limit
↓ ↓
|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_| … |_|
0 1 2 3 4 5 5 6 7 8 9 1 1 1 1 1 1 ↑
0 1 2 3 4 5 buffer size
Upvotes: 3