private bool preRead(Buffer buf) { while(true) { if(_readState == ReadStateOpcode) { // // Is there enough data available to read the opcode? // if(!readBuffered(2)) { return true; } // // Most-significant bit indicates whether this is the // last frame. Least-significant four bits hold the // opcode. // int ch = _readBuffer.b.get(_readBufferPos++); _readOpCode = ch & 0xf; // // Remember if last frame if we're going to read a data or // continuation frame, this is only for protocol // correctness checking purpose. // if(_readOpCode == OP_DATA) { if(!_readLastFrame) { throw new Ice.ProtocolException("invalid data frame, no FIN on previous frame"); } _readLastFrame = (ch & FLAG_FINAL) == FLAG_FINAL; } else if(_readOpCode == OP_CONT) { if(_readLastFrame) { throw new Ice.ProtocolException("invalid continuation frame, previous frame FIN set"); } _readLastFrame = (ch & FLAG_FINAL) == FLAG_FINAL; } ch = _readBuffer.b.get(_readBufferPos++); // // Check the MASK bit. Messages sent by a client must be masked; // messages sent by a server must not be masked. // bool masked = (ch & FLAG_MASKED) == FLAG_MASKED; if(masked != _incoming) { throw new Ice.ProtocolException("invalid masking"); } // // Extract the payload length, which can have the following values: // // 0-125: The payload length // 126: The subsequent two bytes contain the payload length // 127: The subsequent eight bytes contain the payload length // _readPayloadLength = (ch & 0x7f); if(_readPayloadLength < 126) { _readHeaderLength = 0; } else if(_readPayloadLength == 126) { _readHeaderLength = 2; // Need to read a 16-bit payload length. } else { _readHeaderLength = 8; // Need to read a 64-bit payload length. } if(masked) { _readHeaderLength += 4; // Need to read a 32-bit mask. } _readState = ReadStateHeader; } if(_readState == ReadStateHeader) { // // Is there enough data available to read the header? // if(_readHeaderLength > 0 && !readBuffered(_readHeaderLength)) { return true; } if(_readPayloadLength == 126) { _readPayloadLength = _readBuffer.b.getShort(_readBufferPos); // Uses network byte order. if(_readPayloadLength < 0) { _readPayloadLength += 65536; } _readBufferPos += 2; } else if(_readPayloadLength == 127) { long l = _readBuffer.b.getLong(_readBufferPos); // Uses network byte order. _readBufferPos += 8; if(l < 0 || l > Int32.MaxValue) { throw new Ice.ProtocolException("invalid WebSocket payload length: " + l); } _readPayloadLength = (int)l; } // // Read the mask if this is an incoming connection. // if(_incoming) { // // We must have needed to read the mask. // Debug.Assert(_readBuffer.b.position() - _readBufferPos >= 4); for(int i = 0; i < 4; ++i) { _readMask[i] = _readBuffer.b.get(_readBufferPos++); // Copy the mask. } } switch(_readOpCode) { case OP_TEXT: // Text frame { throw new Ice.ProtocolException("text frames not supported"); } case OP_DATA: // Data frame case OP_CONT: // Continuation frame { if(_instance.traceLevel() >= 2) { _instance.logger().trace(_instance.traceCategory(), "received " + protocol() + (_readOpCode == OP_DATA ? " data" : " continuation") + " frame with payload length of " + _readPayloadLength + " bytes\n" + ToString()); } if(_readPayloadLength <= 0) { throw new Ice.ProtocolException("payload length is 0"); } _readState = ReadStatePayload; Debug.Assert(buf.b.hasRemaining()); _readFrameStart = buf.b.position(); break; } case OP_CLOSE: // Connection close { if(_instance.traceLevel() >= 2) { _instance.logger().trace(_instance.traceCategory(), "received " + protocol() + " connection close frame\n" + ToString()); } _readState = ReadStateControlFrame; int s = _nextState == StateOpened ? _state : _nextState; if(s == StateClosingRequestPending) { // // If we receive a close frame while we were actually // waiting to send one, change the role and send a // close frame response. // if(!_closingInitiator) { _closingInitiator = true; } if(_state == StateClosingRequestPending) { _state = StateClosingResponsePending; } else { _nextState = StateClosingResponsePending; } return false; // No longer interested in reading } else { throw new Ice.ConnectionLostException(); } } case OP_PING: { if(_instance.traceLevel() >= 2) { _instance.logger().trace(_instance.traceCategory(), "received " + protocol() + " connection ping frame\n" + ToString()); } _readState = ReadStateControlFrame; break; } case OP_PONG: // Pong { if(_instance.traceLevel() >= 2) { _instance.logger().trace(_instance.traceCategory(), "received " + protocol() + " connection pong frame\n" + ToString()); } _readState = ReadStateControlFrame; break; } default: { throw new Ice.ProtocolException("unsupported opcode: " + _readOpCode); } } } if(_readState == ReadStateControlFrame) { if(_readPayloadLength > 0 && !readBuffered(_readPayloadLength)) { return true; } if(_readPayloadLength > 0 && _readOpCode == OP_PING) { _pingPayload = new byte[_readPayloadLength]; System.Buffer.BlockCopy(_readBuffer.b.rawBytes(), _readBufferPos, _pingPayload, 0, _readPayloadLength); } _readBufferPos += _readPayloadLength; _readPayloadLength = 0; if(_readOpCode == OP_PING) { if(_state == StateOpened) { _state = StatePongPending; // Send pong frame now } else if(_nextState < StatePongPending) { _nextState = StatePongPending; // Send pong frame next } } // // We've read the payload of the PING/PONG frame, we're ready // to read a new frame. // _readState = ReadStateOpcode; } if(_readState == ReadStatePayload) { // // This must be assigned before the check for the buffer. If the buffer is empty // or already read, postRead will return false. // _readStart = buf.b.position(); if(buf.empty() || !buf.b.hasRemaining()) { return false; } int n = Math.Min(_readBuffer.b.position() - _readBufferPos, buf.b.remaining()); if(n > _readPayloadLength) { n = _readPayloadLength; } if(n > 0) { System.Buffer.BlockCopy(_readBuffer.b.rawBytes(), _readBufferPos, buf.b.rawBytes(), buf.b.position(), n); buf.b.position(buf.b.position() + n); _readBufferPos += n; } // // Continue reading if we didn't read the full message, otherwise give back // the control to the connection // return buf.b.hasRemaining() && n < _readPayloadLength; } } }
public int write(Buffer buf) { if(_writePending) { return SocketOperation.Write; } if(_state < StateOpened) { if(_state < StateConnected) { return _delegate.write(buf); } else { return _delegate.write(_writeBuffer); } } int s = SocketOperation.None; do { if(preWrite(buf)) { if(_writeState == WriteStateFlush) { // // Invoke write() even though there's nothing to write. // Debug.Assert(!buf.b.hasRemaining()); s = _delegate.write(buf); } if(s == SocketOperation.None && _writeBuffer.b.hasRemaining()) { s = _delegate.write(_writeBuffer); } else if(s == SocketOperation.None && _incoming && !buf.empty() && _writeState == WriteStatePayload) { s = _delegate.write(buf); } } } while(postWrite(buf, s)); if(s != SocketOperation.None) { return s; } if(_state == StateClosingResponsePending && !_closingInitiator) { return SocketOperation.Read; } return SocketOperation.None; }
public void finishWrite(Buffer buf) { _writePending = false; if(_state < StateOpened) { if(_state < StateConnected) { _delegate.finishWrite(buf); } else { _delegate.finishWrite(_writeBuffer); } return; } if(_writeBuffer.b.hasRemaining()) { _delegate.finishWrite(_writeBuffer); } else if(!buf.empty() && buf.b.hasRemaining()) { Debug.Assert(_incoming); _delegate.finishWrite(buf); } if(_state == StateClosed) { _writeBuffer.clear(); return; } postWrite(buf, SocketOperation.None); }
private bool preWrite(Buffer buf) { if(_writeState == WriteStateHeader) { if(_state == StateOpened) { if(buf.empty() || !buf.b.hasRemaining()) { return false; } Debug.Assert(buf.b.position() == 0); prepareWriteHeader((byte)OP_DATA, buf.size()); _writeState = WriteStatePayload; } else if(_state == StatePingPending) { prepareWriteHeader((byte)OP_PING, 0); // Don't send any payload _writeState = WriteStateControlFrame; _writeBuffer.b.flip(); } else if(_state == StatePongPending) { prepareWriteHeader((byte)OP_PONG, _pingPayload.Length); if(_pingPayload.Length > _writeBuffer.b.remaining()) { int pos = _writeBuffer.b.position(); _writeBuffer.resize(pos + _pingPayload.Length, false); _writeBuffer.b.position(pos); } _writeBuffer.b.put(_pingPayload); _pingPayload = new byte[0]; _writeState = WriteStateControlFrame; _writeBuffer.b.flip(); } else if((_state == StateClosingRequestPending && !_closingInitiator) || (_state == StateClosingResponsePending && _closingInitiator)) { prepareWriteHeader((byte)OP_CLOSE, 2); // Write closing reason _writeBuffer.b.putShort((short)_closingReason); if(!_incoming) { byte b; int pos = _writeBuffer.b.position() - 2; b = (byte)(_writeBuffer.b.get(pos) ^ _writeMask[0]); _writeBuffer.b.put(pos, b); pos++; b = (byte)(_writeBuffer.b.get(pos) ^ _writeMask[1]); _writeBuffer.b.put(pos, b); } _writeState = WriteStateControlFrame; _writeBuffer.b.flip(); } else { Debug.Assert(_state != StateClosed); return false; // Nothing to write in this state } _writePayloadLength = 0; } if(_writeState == WriteStatePayload) { // // For an outgoing connection, each message must be masked with a random // 32-bit value, so we copy the entire message into the internal buffer // for writing. For incoming connections, we just copy the start of the // message in the internal buffer after the hedaer. If the message is // larger, the reminder is sent directly from the message buffer to avoid // copying. // if(!_incoming && (_writePayloadLength == 0 || !_writeBuffer.b.hasRemaining())) { if(!_writeBuffer.b.hasRemaining()) { _writeBuffer.b.position(0); } int n = buf.b.position(); int sz = buf.size(); int pos = _writeBuffer.b.position(); int count = Math.Min(sz - n, _writeBuffer.b.remaining()); byte[] src = buf.b.rawBytes(); byte[] dest = _writeBuffer.b.rawBytes(); for(int i = 0; i < count; ++i, ++n, ++pos) { dest[pos] = (byte)(src[n] ^ _writeMask[n % 4]); } _writeBuffer.b.position(pos); _writePayloadLength = n; _writeBuffer.b.flip(); } else if(_writePayloadLength == 0) { Debug.Assert(_incoming); if(_writeBuffer.b.hasRemaining()) { Debug.Assert(buf.b.position() == 0); int n = Math.Min(_writeBuffer.b.remaining(), buf.b.remaining()); int pos = _writeBuffer.b.position(); System.Buffer.BlockCopy(buf.b.rawBytes(), 0, _writeBuffer.b.rawBytes(), pos, n); _writeBuffer.b.position(pos + n); _writePayloadLength = n; } _writeBuffer.b.flip(); } return true; } else if(_writeState == WriteStateControlFrame) { return _writeBuffer.b.hasRemaining(); } else { Debug.Assert(_writeState == WriteStateFlush); return true; } }