I don't know why but half the sites that go through ssl get a buffer_underflow during my read.
When I have my program chained to call different ssl sites consecutively it doesn't work on half the links, but if I call one by one individually, they work. For example, I used Chrome's developer tools to call https://www.facebook.com on my nexus 7 tablet. When I see the requests, The links called are:
- https://www.facebook.com/
- https://fbstatic-a.akamaihd.net/rsrc.php/v2/y7/r/Zj_YpNlIRKt.css
- https://fbstatic-a.akamaihd.net/rsrc.php/v2/yI/r/5EMLHs-7t29.css etc
- ... (about 26 links).
If I chain them together (to simulate a call to https://www.facebook.com from the browser), I get half the links getting buffer underflows until eventually I have to close their connections (Reading 0 bytes). However, if I cal them one by one individually, they are always fine. Here is my code for reading:
public int readSSLFrom(SelectionKey key, SecureIO session) throws IOException{
int result = 0;
String TAG = "readSSLFrom";
Log.i(TAG,"Hanshake status: "+session.sslEngine.getHandshakeStatus().toString());
synchronized (buffer){
ByteBuffer sslIn = ByteBuffer.allocate(session.getApplicationSizeBuffer());
ByteBuffer tmp = ByteBuffer.allocate(session.getApplicationSizeBuffer());
ReadableByteChannel channel = (ReadableByteChannel) key.channel();
if (buffer.remaining() < session.getPacketBufferSize()){
increaseSize(session.getPacketBufferSize());
}
int read = 0;
while (((read = channel.read(sslIn)) > 0) &&
buffer.remaining() >= session.getApplicationSizeBuffer()){
if (read < 0){
session.sslEngine.closeInbound();
return -1;
}
inner: while (sslIn.position() > 0){
sslIn.flip();
tmp.clear();
SSLEngineResult res = session.sslEngine.unwrap(sslIn, tmp);
result = result + res.bytesProduced();
sslIn.compact();
tmp.flip();
if (tmp.hasRemaining()){
buffer.put(tmp);
}
switch (res.getStatus()){
case BUFFER_OVERFLOW:
Log.i(TAG,"Buffer overflow");
throw new Error();
case BUFFER_UNDERFLOW:
Log.i(TAG,"Buffer underflow");
if (session.getPacketBufferSize() > tmp.capacity()){
Log.i(TAG,"increasing capacity");
ByteBuffer b = ByteBuffer.allocate(session.getPacketBufferSize());
sslIn.flip();
b.put(sslIn);
sslIn = b;
}
break inner;
case CLOSED:
Log.i(TAG,"Closed");
if (sslIn.position() == 0){
break inner;
} else{
return -1;
}
case OK:
Log.i(TAG,"OK");
session.checkHandshake(key);
break;
default:
break;
}
}
}
if (read < 0){
//session.sslEngine.closeInbound();
return -1;
}
}
dataEnd = buffer.position();
return result;
}
Thank you.
Buffer underflows are acceptable during unwrap and occur often. This happens when you have a partial TLS record (<16KB). This can happen in two cases, 1) When you have less than 16KB, so you get no result when you unwrap - just cache all this data and wait for the remainder to arrive to unwrap it. 2) When you have more than 16KB but the last TLS packet isn't complete, for example 20KB or 36KB. In this case the first 16KB/32KB will give you a result during an unwrap and you need to cache the remaining 4KB and just wait for the rest of the 12KB that completes this TLS packet - before you can unwrap.
I hope this helps, try out my code and see if it works for you.
Sorry this didn't fit in the comment so I responded with an answer instead.