/// <summary> /// Are about to send a new message /// </summary> /// <param name="message">Message to send</param> /// <remarks> /// Can be used to prepare the next message. for instance serialize it etc. /// </remarks> /// <exception cref="NotSupportedException">Message is of a type that the encoder cannot handle.</exception> public void Prepare(object message) { if (message is IWebSocketMessage) { _message = (IWebSocketMessage)message; _message.Payload.Position = 0; _totalAmountToSend = (int)_message.Payload.Length; } else { try { _httpMessageEncoder.Prepare(message); var httpMessage = message as IHttpMessage; if (WebSocketUtils.IsWebSocketUpgrade(httpMessage)) { _handshake = httpMessage; } } catch (Exception e) { throw new InvalidOperationException("This encoder only supports messages deriving from 'HttpMessage' or 'WebSocketMessage'", e); } } }
public WebSocketFrame( WebSocketFin fin, WebSocketRsv rsv1, WebSocketRsv rsv2, WebSocketRsv rsv3, WebSocketOpcode opcode, WebSocketMask mask, byte[] maskingKey, Stream payload ) : this(fin, rsv1, rsv2, rsv3, opcode, mask, maskingKey, 0, new byte[0], payload) { if (payload != null) { var len = payload.Length; if (len < 126) { _payloadLength = (byte)len; _extPayloadLength = new byte[0]; } else if (len < 0x010000) { _payloadLength = (byte)126; _extPayloadLength = WebSocketUtils.GetBigEndianBytes((ushort)len); } else { _payloadLength = (byte)127; _extPayloadLength = WebSocketUtils.GetBigEndianBytes((ulong)len); } } }
/// <summary> /// Create a new instance of <see cref="WebSocketUpgradeResponse"/> /// </summary> /// <param name="webSocketKey">Key from the HTTP request.</param> public WebSocketUpgradeResponse(string webSocketKey) : base(HttpStatusCode.SwitchingProtocols, "Switching Protocols", "HTTP/1.1") { Headers["Upgrade"] = "websocket"; Headers["Connection"] = "Upgrade"; Headers["Sec-WebSocket-Accept"] = WebSocketUtils.HashWebSocketKey(webSocketKey); }
/// <summary> /// Intercept http messages and look for websocket upgrade requests /// </summary> /// <param name="message">message from http decoder</param> private void OnHttpMessage(object message) { var httpMessage = message as IHttpMessage; // TODO: is there a better way to detect WebSocket upgrade? if (WebSocketUtils.IsWebSocketUpgrade(httpMessage)) { _handshake = httpMessage; _isWebSocket = true; } _messageReceived(message); }
public WebSocketFrame( WebSocketFin fin, WebSocketOpcode opcode, WebSocketMask mask, Stream payload ) : this(fin, WebSocketRsv.Off, WebSocketRsv.Off, WebSocketRsv.Off, opcode, mask, mask == WebSocketMask.Mask ? WebSocketUtils.CreateMaskingKey() : new byte[0], payload) { }
/// <summary> /// Buffer structure used for socket send operations. /// </summary> /// <param name="buffer"> /// Do note that there are not buffer attached to the structure, you have to assign one yourself using /// <see cref="ISocketBuffer.SetBuffer(int,int)" />. This choice was made /// to prevent unnecessary copy operations. /// </param> public void Send(ISocketBuffer buffer) { if (_message == null) { _httpMessageEncoder.Send(buffer); } else { // last send operation did not send all bytes enqueued in the buffer // so let's just continue until doing next message if (_bytesToSend > 0) { buffer.SetBuffer(_buffer, _offset, _bytesToSend); return; } var offset = (int)_message.Payload.Position; var length = (int)_message.Payload.Length; var frameLength = length - offset; var fin = WebSocketFin.Final; if (frameLength > WebSocketFrame.FragmentLength) { frameLength = WebSocketFrame.FragmentLength; fin = WebSocketFin.More; } var opcode = WebSocketOpcode.Continuation; if (offset == 0) // first frame { opcode = _message.Opcode; } var buff = new byte[frameLength]; _message.Payload.Read(buff, 0, buff.Length); var payload = new MemoryStream(buff); WebSocketFrame frame = new WebSocketFrame(fin, opcode, (_handshake is IHttpRequest) ? WebSocketMask.Mask : WebSocketMask.Unmask, payload); using (var stream = new MemoryStream()) { var header = (int)frame.Fin; header = (header << 1) + (int)frame.Rsv1; header = (header << 1) + (int)frame.Rsv2; header = (header << 1) + (int)frame.Rsv3; header = (header << 4) + (int)frame.Opcode; header = (header << 1) + (int)frame.Mask; header = (header << 7) + (int)frame.PayloadLength; stream.Write(WebSocketUtils.GetBigEndianBytes((ushort)header), 0, 2); if (frame.PayloadLength > 125) { stream.Write(frame.ExtPayloadLength, 0, frame.ExtPayloadLength.Length); } if (frame.Mask == WebSocketMask.Mask) { stream.Write(frame.MaskingKey, 0, frame.MaskingKey.Length); frame.Unmask(); } _totalAmountToSend += (int)stream.Length; if (frame.PayloadLength > 0) { frame.Payload.CopyTo(stream); } buffer.UserToken = _message; _buffer = stream.ToArray(); _bytesToSend = _buffer.Length; buffer.SetBuffer(_buffer, 0, _bytesToSend); } } }
/// <summary> /// We've received bytes from the socket. Build a message out of them. /// </summary> /// <param name="buffer">Buffer</param> public void ProcessReadBytes(ISocketBuffer buffer) { if (!_isWebSocket) { _httpMessageDecoder.ProcessReadBytes(buffer); } else { var receiveBufferOffset = buffer.Offset; var bytesLeftInReceiveBuffer = buffer.BytesTransferred; while (true) { if (bytesLeftInReceiveBuffer <= 0) { break; } if (_frame == null) { var first = buffer.Buffer[receiveBufferOffset + 0]; var second = buffer.Buffer[receiveBufferOffset + 1]; var fin = (first & 0x80) == 0x80 ? WebSocketFin.Final : WebSocketFin.More; var rsv1 = (first & 0x40) == 0x40 ? WebSocketRsv.On : WebSocketRsv.Off; var rsv2 = (first & 0x20) == 0x20 ? WebSocketRsv.On : WebSocketRsv.Off; var rsv3 = (first & 0x10) == 0x10 ? WebSocketRsv.On : WebSocketRsv.Off; var opcode = (WebSocketOpcode)(first & 0x0f); var mask = (second & 0x80) == 0x80 ? WebSocketMask.Mask : WebSocketMask.Unmask; var payloadLen = (byte)(second & 0x7f); receiveBufferOffset += 2; bytesLeftInReceiveBuffer -= 2; // TODO: // check if valid headers // control frame && payloadLen > 125 // control frame && more // not data && compressed var size = payloadLen < 126 ? 0 : payloadLen == 126 ? 2 : 8; var extPayloadLen = new byte[size]; for (var i = 0; i < size; i++) { extPayloadLen[i] = buffer.Buffer[receiveBufferOffset + i]; } receiveBufferOffset += size; bytesLeftInReceiveBuffer -= size; var maskingKey = new byte[0]; if (mask == WebSocketMask.Mask) { maskingKey = new byte[] { buffer.Buffer[receiveBufferOffset + 0], buffer.Buffer[receiveBufferOffset + 1], buffer.Buffer[receiveBufferOffset + 2], buffer.Buffer[receiveBufferOffset + 3], }; receiveBufferOffset += 4; bytesLeftInReceiveBuffer -= 4; } ulong len = payloadLen < 126 ? payloadLen : payloadLen == 126 ? WebSocketUtils.ToBigEndianUInt16(extPayloadLen) : WebSocketUtils.ToBigEndianUInt64(extPayloadLen); _frameContentBytesLeft = (int)len; _frame = new WebSocketFrame(fin, rsv1, rsv2, rsv3, opcode, mask, maskingKey, payloadLen, extPayloadLen, new MemoryStream(_frameContentBytesLeft)); if (_frame.Fin == WebSocketFin.More || _frame.Opcode == WebSocketOpcode.Continuation) { _frames.Add(_frame); } } if (_frameContentBytesLeft > 0) { var bytesRead = BytesProcessed(buffer.Offset, receiveBufferOffset); var bytesToWrite = Math.Min(_frameContentBytesLeft, buffer.BytesTransferred - bytesRead); _frame.Payload.Write(buffer.Buffer, receiveBufferOffset, bytesToWrite); _frameContentBytesLeft -= bytesToWrite; receiveBufferOffset += bytesToWrite; bytesLeftInReceiveBuffer -= bytesToWrite; } if (_frameContentBytesLeft == 0) { _frame.Payload.Position = 0; if (_frame.Fin == WebSocketFin.Final) { if (_frame.Opcode == WebSocketOpcode.Continuation) { TriggerMessageReceived(_frames); _frames = new List <WebSocketFrame>(); } else { TriggerMessageReceived(new[] { _frame }); } } _frame = null; } } } }
/// <summary> /// Handles the upgrade /// </summary> /// <param name="source">Channel that we've received a request from</param> /// <param name="msg">Message received.</param> protected override void OnMessage(ITcpChannel source, object msg) { var httpMessage = msg as IHttpMessage; if (WebSocketUtils.IsWebSocketUpgrade(httpMessage)) { if (httpMessage is IHttpRequest) // server mode { var args = new WebSocketClientConnectEventArgs(source, (IHttpRequest)httpMessage); WebSocketClientConnect(this, args); if (args.MayConnect) { var webSocketKey = httpMessage.Headers["Sec-WebSocket-Key"]; // TODO: why not provide the response in the WebSocketClientConnectEventArgs event? var response = new WebSocketUpgradeResponse(webSocketKey); source.Send(response); WebSocketClientConnected(this, new WebSocketClientConnectedEventArgs(source, (IHttpRequest)httpMessage, response)); } else { var response = new HttpResponseBase(HttpStatusCode.NotImplemented, "Not Implemented", "HTTP/1.1"); if (args.Response != null) { response.Body = args.Response; } source.Send(response); } return; } if (httpMessage is IHttpResponse) // client mode { WebSocketClientConnected(this, new WebSocketClientConnectedEventArgs(source, null, (IHttpResponse)httpMessage)); } } var webSocketMessage = msg as IWebSocketMessage; if (webSocketMessage != null) { // standard message responses handled by listener switch (webSocketMessage.Opcode) { case WebSocketOpcode.Ping: source.Send(new WebSocketMessage(WebSocketOpcode.Pong, webSocketMessage.Payload)); return; case WebSocketOpcode.Close: source.Send(new WebSocketMessage(WebSocketOpcode.Close)); source.Close(); WebSocketClientDisconnected(this, new ClientDisconnectedEventArgs(source, new Exception("WebSocket closed"))); return; } _webSocketMessageReceived(source, webSocketMessage); return; } base.OnMessage(source, msg); }