📄 sslchannel.java
字号:
* Raises an event when there is space available for
* writing more data to this socket.
*/
public void registerForWrite() throws IOException {
checkChannelStillValid();
if (!appWriteInterestSet) {
log.fine("Activating write interest");
appWriteInterestSet = true;
if (initialHandshake) {
return;
} else {
// Check if we can write now
if (netData.hasRemaining()) {
assert channelWriteInterestSet : "Write interest should be active";
// The buffer is full, the application can't write anymore.
// The write interest must be set...
} else {
assert !channelWriteInterestSet :
"Write interest should not be active";
// netData is empty. But don't fire the write event right
// now. Instead, register with the SecureChannelManager and
// wait for the SelectorThread to call for these events.
st.getSscManager().registerForWrite(this);
}
}
}
}
/**
* Cancel the write interest.
*/
public void unregisterForWrite() throws IOException {
checkChannelStillValid();
appWriteInterestSet = false;
st.getSscManager().unregisterForWrite(this);
}
/**
* Called from the SSLChannelManager when it's time for launching
* the application events
*/
void fireReadEvent() {
appReadInterestSet = false;
listener.handleRead();
}
/**
* Called from the SSLChannelManager when it's time for launching
* the application events
*/
void fireWriteEvent() {
appWriteInterestSet = false;
listener.handleWrite();
}
private void doShutdown() throws IOException {
// log.fine("");
assert !netData.hasRemaining() : "Buffer was not empty.";
// Either shutdown was initiated now or we are on the middle
// of shutting down and this method was called after emptying
// the out buffer
// If the engine has nothing else to do, close the socket. If
// this socket is dead because of an exception, close it
// immediately
if (asynchException != null || engine.isOutboundDone()) {
log.fine("Outbound is done. Closing socket");
try {
// If no data was produced by the call to wrap, shutdown is complete
sc.close();
} catch (IOException e) { /* Ignore. */ }
return;
}
// The engine has more things to send
/*
* By RFC 2616, we can "fire and forget" our close_notify
* message, so that's what we'll do here.
*/
netData.clear();
try {
SSLEngineResult res = engine.wrap(dummy, netData);
log.info("Wrapping:\n" + res);
} catch (SSLException e1) {
// Problems with the engine. Probably it is dead. So close
// the socket and forget about it.
log.warning("Error during shutdown.\n" + e1.toString());
try {
sc.close();
} catch (IOException e) { /* Ignore. */ }
return;
}
netData.flip();
flushData();
}
/**
*
*/
public void close() throws IOException {
log.fine("Shutting down SSL Channel");
if (shutdown) {
log.fine("Shutdown already in progress");
return;
}
// Initiate the shutdown process
shutdown = true;
closed = true;
// We don't need it anymore
asynchException = null;
engine.closeOutbound();
if (netData.hasRemaining()) {
// If this method is called after an exception, we should
// close the socket regardless having some data to send.
assert channelWriteInterestSet : "Data to be sent but no write interest.";
log.fine("There is some data left to be sent. Waiting: " + netData);
// We are waiting to send the data
return;
} else {
doShutdown();
}
}
private void finishInitialHandshake() throws IOException {
// Only during the initial handshake is it necessary to check
// these flags
initialHandshake = false;
// Activate interest
if (appReadInterestSet) {
// We are not sure that there is data left to be read on the
// socket. Therefore, wait for a selector event.
selectorRegisterForRead();
}
if (appWriteInterestSet) {
assert !netData.hasRemaining() : "There is data left to send after handshake!";
// We don't need to register with the selector, since we
// know that the netData buffer is empty after the handshake.
// Just send the write event to the application.
st.getSscManager().registerForWrite(this);
}
}
private void doHandshake() throws IOException {
while (true) {
SSLEngineResult res;
log.fine(hsStatus.toString());
switch (hsStatus) {
case FINISHED:
if (initialHandshake) {
finishInitialHandshake();
}
return;
case NEED_TASK:
doTasks();
// The hs status was updated, so go back to the switch
break;
case NEED_UNWRAP:
readAndUnwrap();
// During normal operation a call to readAndUnwrap() that results in underflow
// does not cause the channel to activate read interest with the selector.
// Therefore, if we are at the initial handshake, we must activate the read
// insterest explicitily.
if (initialHandshake &&
status == SSLEngineResult.Status.BUFFER_UNDERFLOW) {
selectorRegisterForRead();
}
return;
case NEED_WRAP:
// First make sure that the out buffer is completely empty. Since we
// cannot call wrap with data left on the buffer
if (netData.hasRemaining()) {
assert channelWriteInterestSet :
"Write interest should be active: " + netData;
return;
}
// Prepare to write
netData.clear();
res = engine.wrap(dummy, netData);
log.info("Wrapping:\n" + res);
assert res.bytesProduced() != 0 : "No net data produced during handshake wrap.";
assert res.bytesConsumed() == 0 : "App data consumed during handshake wrap.";
hsStatus = res.getHandshakeStatus();
netData.flip();
// Now send the data and come back here only when
// the data is all sent
if (!flushData()) {
// There is data left to be send. Wait for it
return;
}
// All data was sent. Break from the switch but don't
// exit this method. It will loop again, since there may be more
// operations that can be done without blocking.
break;
case NOT_HANDSHAKING:
assert false : "doHandshake() should never reach the NOT_HANDSHAKING state";
return;
}
}
}
/**
* Called from the selector thread.
*/
public void handleRead() {
assert initialHandshake || appReadInterestSet :
"Trying to read when there is no read interest set";
assert channelReadInterestSet : "Method called when no read interest was set";
channelReadInterestSet = false;
// log.fine("");
try {
if (initialHandshake) {
doHandshake();
} else if (shutdown) {
doShutdown();
} else {
// The read interest is always set when this method is called
assert appReadInterestSet : "handleRead() called without read interest being set";
int bytesUnwrapped = readAndUnwrap();
if (bytesUnwrapped == -1) {
// End of stream.
assert engine.isInboundDone() :
"End of stream but engine inbound is not closed";
// We must inform the client of the EOF
st.getSscManager().registerForRead(this);
} else if (bytesUnwrapped == 0) {
// Must read more data
selectorRegisterForRead();
} else {
// There is data to be read by the application. Notify it.
st.getSscManager().registerForRead(this);
}
}
} catch (IOException e) {
handleAsynchException(e);
}
}
/**
* Tries to write the data on the netData buffer to the socket.
* If not all data is sent, the write interest is activated with
* the selector thread.
*
* @return True if all data was sent. False otherwise.
*/
private boolean flushData() throws IOException {
assert netData.hasRemaining() : "Trying to write but netData buffer is empty";
int written;
try {
written = sc.write(netData);
} catch (IOException ioe) {
// Clear the buffer. If write failed, the socket is dead. Clearing
// the buffer indicates that no more write should be attempted.
netData.position(netData.limit());
throw ioe;
}
log.fine("Written to socket: " + written);
if (netData.hasRemaining()) {
// The buffer is not empty. Register again with the selector
selectorRegisterForWrite();
return false;
} else {
return true;
}
}
/**
* Called from the selector thread.
*/
public void handleWrite() {
assert channelWriteInterestSet : "Write event when no write interest set";
channelWriteInterestSet = false;
// log.fine("");
try {
if (flushData()) {
// The buffer was sent completely
if (initialHandshake) {
doHandshake();
} else if (shutdown) {
doShutdown();
} else {
// If the listener is interested in writing,
// prepare to fire the event.
if (appWriteInterestSet) {
st.getSscManager().registerForWrite(this);
}
}
} else {
// There is still more data to be sent. Wait for another
// write event. Calling flush data already resulted in the
// write interest being reactivated.
}
} catch (IOException e) {
handleAsynchException(e);
}
}
private void handleAsynchException(IOException e) {
// Will be sent back to the application next time a public
// method is called
asynchException = e;
// If the application has any interest set, fire an event.
// Otherwise, the event will be fired next time a public method
// is called.
if (appWriteInterestSet) {
st.getSscManager().registerForWrite(this);
}
if (appReadInterestSet) {
st.getSscManager().registerForRead(this);
}
// We won't be sending any more data.
engine.closeOutbound();
}
private void selectorRegisterForRead() throws IOException {
log.fine("");
if (channelReadInterestSet) {
return;
}
channelReadInterestSet = true;
st.addChannelInterestNow(sc, SelectionKey.OP_READ);
}
private void selectorRegisterForWrite() throws IOException {
log.fine("");
if (channelWriteInterestSet) {
return;
}
channelWriteInterestSet = true;
st.addChannelInterestNow(sc, SelectionKey.OP_WRITE);
}
/**
* Execute delegated tasks in the main thread. These are compute
* intensive tasks, so there's no point in scheduling them in a different
* thread.
*/
private void doTasks() {
Runnable task;
while ((task = engine.getDelegatedTask()) != null) {
task.run();
}
hsStatus = engine.getHandshakeStatus();
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -