Hi guys, we have had a pleasant and useful working session this afternoon with Jean-François (as we both live in Paris, it was easy for us to meet and discuss)
Here are a few things we tried to figure out and some of the elements that need to be further analysed 1) General state machine The idea is to have one single state machine to handle all the incoming data : we should not differenciate handshake and non-handshake state when processing data. The state machine is pretty much the same : - we unwrap the data in all cases - if the unwrap status is OK, we check the HandshakeStatus (HS), which gives some indication about what to do next - if the HS status is NEED_TASK, we have to execute the task the SslEngine is returning. It's not something simple to do (see below) - if the HS status is UNDERFLOW_BUFFER, we don't have enough data to proceed an unwrap, and we have to wait for more data. A new select() will provide some more data. - if the HS status is OVERFLOW_BUFFER, that means the application buffer is not big enough to contain the decrypted data. This is possible if a compression has been done. We have to create a bigger application buffer, and redo the unwrap. - if the HS status is FINISHED, the handshake is done - if the HS status is NOT_HANDSHAKING, that means we already have handshaken. We can pass the applicatio buffer to the IoHandler. - in any case - except for a UNDERFLOW_BUFFER -, if there are more bytes in the readBuffer, we have to process them This cover the processing of incoming data. 2) Dealing with UNDERFLOW_BUFFER We have to read more data, but we must not lose the previous data. The problem is that the readBuffer is shared with all the sessions, so we can't do another read withoit potentially losing the dat we already gathered. In this case, we need to store the current unprocessed data in a temporary buffer and when we read some more data, we will add them to the temporary data. The algorithm will look like this : if we have a temporay buffer then concatenate the readBuffer in the temporary buffer and go through the processing of this data ... if we don't have a BUFFER_UNDERFLOW status then reset the temporary buffer The temporary buffer is associated with the session. We have an alternative solution : use a buffer associated with every session. The problem is that it would be wastinga lot of memory (if we have tens of thousand sessions, we have to keep such a buffer),. Here, we ust have to allocate a temporary buffer from time to time (see if we can't pool them). 3) Management of unsent data Let's say we have pending message waiting to be written (that means the outbound socket can't accept all the data). If we receive a renegociation, what should we do with those data ? We suppose that we should encrypt the data with the newly negotiated cipher, and not with the previous one. This is quite an issue as the data are enqueued *after* having been encrypted in the enqueueWriteRequest() IoSession method. We have to change the way we process the encyption, in order to differ the encryption until the last moment (ie when we actually try to write the data into the socket). Now, let say we have a queue containing messages to b written : we shoud not encrypt the newly processed message, we simply add it into the queue. If on the other hand the queue is empty, we encrypt the message, and try to send it. If we can't send it completely, we want untli the socket accept new data. The client SslEngine will be on an UNDERFLOW_BUFFERuntil the message until the complete message is sent (to be double checked). 4) NEED_TASK handling This is a tricky part : the task can be costly, so we should process it using a different thread. The problem is that once the task is done, we have to continue the processing when the task is done. At the same time, we should not accept any incoming data until the task is done, and we have to emove the OP_READ interest for the session selectionKey. The thread should also process the next step (which may be a wrap operation, followed by a write). We haven't yet found the best solution for that. 5) Close_notify We still have to see if we are correctly handling this case. 6) Conclusion Jean-François have a working solution, which probably need some improvements and checks. This is a first step, and we still have to see if it works in all the cases (that will need some specific tests). Jean-François, did I summarized the discussion we had accurately ? Thanks ! -- Regards, Cordialement, Emmanuel Lécharny www.iktek.com