protected override IFullHttpResponse NewHandshakeResponse(IFullHttpRequest req, HttpHeaders headers) { // Serve the WebSocket handshake request. if (!req.Headers.ContainsValue(HttpHeaderNames.Connection, HttpHeaderValues.Upgrade, true) || !req.Headers.TryGet(HttpHeaderNames.Upgrade, out ICharSequence value) || !HttpHeaderValues.Websocket.ContentEqualsIgnoreCase(value)) { throw new WebSocketHandshakeException("not a WebSocket handshake request: missing upgrade"); } // Hixie 75 does not contain these headers while Hixie 76 does bool isHixie76 = req.Headers.Contains(HttpHeaderNames.SecWebsocketKey1) && req.Headers.Contains(HttpHeaderNames.SecWebsocketKey2); // Create the WebSocket handshake response. var res = new DefaultFullHttpResponse(HttpVersion.Http11, new HttpResponseStatus(101, new AsciiString(isHixie76 ? "WebSocket Protocol Handshake" : "Web Socket Protocol Handshake"))); if (headers != null) { res.Headers.Add(headers); } res.Headers.Add(HttpHeaderNames.Upgrade, HttpHeaderValues.Websocket); res.Headers.Add(HttpHeaderNames.Connection, HttpHeaderValues.Upgrade); // Fill in the headers and contents depending on handshake getMethod. if (isHixie76) { // New handshake getMethod with a challenge: value = req.Headers.Get(HttpHeaderNames.Origin, null); Debug.Assert(value != null); res.Headers.Add(HttpHeaderNames.SecWebsocketOrigin, value); res.Headers.Add(HttpHeaderNames.SecWebsocketLocation, this.Uri); if (req.Headers.TryGet(HttpHeaderNames.SecWebsocketProtocol, out ICharSequence subprotocols)) { string selectedSubprotocol = this.SelectSubprotocol(subprotocols.ToString()); if (selectedSubprotocol == null) { if (Logger.DebugEnabled) { Logger.Debug("Requested subprotocol(s) not supported: {}", subprotocols); } } else { res.Headers.Add(HttpHeaderNames.SecWebsocketProtocol, selectedSubprotocol); } } // Calculate the answer of the challenge. value = req.Headers.Get(HttpHeaderNames.SecWebsocketKey1, null); Debug.Assert(value != null, $"{HttpHeaderNames.SecWebsocketKey1} must exist"); string key1 = value.ToString(); value = req.Headers.Get(HttpHeaderNames.SecWebsocketKey2, null); Debug.Assert(value != null, $"{HttpHeaderNames.SecWebsocketKey2} must exist"); string key2 = value.ToString(); int a = (int)(long.Parse(BeginningDigit.Replace(key1, "")) / BeginningSpace.Replace(key1, "").Length); int b = (int)(long.Parse(BeginningDigit.Replace(key2, "")) / BeginningSpace.Replace(key2, "").Length); long c = req.Content.ReadLong(); IByteBuffer input = Unpooled.Buffer(16); input.WriteInt(a); input.WriteInt(b); input.WriteLong(c); res.Content.WriteBytes(WebSocketUtil.Md5(input.Array)); } else { // Old Hixie 75 handshake getMethod with no challenge: value = req.Headers.Get(HttpHeaderNames.Origin, null); Debug.Assert(value != null); res.Headers.Add(HttpHeaderNames.WebsocketOrigin, value); res.Headers.Add(HttpHeaderNames.WebsocketLocation, this.Uri); if (req.Headers.TryGet(HttpHeaderNames.WebsocketProtocol, out ICharSequence protocol)) { res.Headers.Add(HttpHeaderNames.WebsocketProtocol, this.SelectSubprotocol(protocol.ToString())); } } return(res); }
protected internal override unsafe IFullHttpRequest NewHandshakeRequest() { // Make keys int spaces1 = WebSocketUtil.RandomNumber(1, 12); int spaces2 = WebSocketUtil.RandomNumber(1, 12); int max1 = int.MaxValue / spaces1; int max2 = int.MaxValue / spaces2; int number1 = WebSocketUtil.RandomNumber(0, max1); int number2 = WebSocketUtil.RandomNumber(0, max2); int product1 = number1 * spaces1; int product2 = number2 * spaces2; string key1 = Convert.ToString(product1); string key2 = Convert.ToString(product2); key1 = InsertRandomCharacters(key1); key2 = InsertRandomCharacters(key2); key1 = InsertSpaces(key1, spaces1); key2 = InsertSpaces(key2, spaces2); byte[] key3 = WebSocketUtil.RandomBytes(8); var challenge = new byte[16]; fixed(byte *bytes = challenge) { Unsafe.WriteUnaligned(bytes, number1); Unsafe.WriteUnaligned(bytes + 4, number2); PlatformDependent.CopyMemory(key3, 0, bytes + 8, 8); } this.expectedChallengeResponseBytes = Unpooled.WrappedBuffer(WebSocketUtil.Md5(challenge)); // Get path Uri wsUrl = this.Uri; string path = RawPath(wsUrl); // Format request var request = new DefaultFullHttpRequest(HttpVersion.Http11, HttpMethod.Get, path); HttpHeaders headers = request.Headers; headers.Add(HttpHeaderNames.Upgrade, Websocket) .Add(HttpHeaderNames.Connection, HttpHeaderValues.Upgrade) .Add(HttpHeaderNames.Host, WebsocketHostValue(wsUrl)) .Add(HttpHeaderNames.Origin, WebsocketOriginValue(wsUrl)) .Add(HttpHeaderNames.SecWebsocketKey1, key1) .Add(HttpHeaderNames.SecWebsocketKey2, key2); string expectedSubprotocol = this.ExpectedSubprotocol; if (!string.IsNullOrEmpty(expectedSubprotocol)) { headers.Add(HttpHeaderNames.SecWebsocketProtocol, expectedSubprotocol); } if (this.CustomHeaders != null) { headers.Add(this.CustomHeaders); } // Set Content-Length to workaround some known defect. // See also: http://www.ietf.org/mail-archive/web/hybi/current/msg02149.html headers.Set(HttpHeaderNames.ContentLength, key3.Length); request.Content.WriteBytes(key3); return(request); }
/// <summary> /// <para> /// Handle the web socket handshake for the web socket specification <a href= /// "http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-00">HyBi version 0</a> and lower. This standard /// is really a rehash of <a href="http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-76" >hixie-76</a> and /// <a href="http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-75" >hixie-75</a>. /// </para> /// /// <para> /// Browser request to the server: /// </para> /// /// <![CDATA[ /// GET /demo HTTP/1.1 /// Upgrade: WebSocket /// Connection: Upgrade /// Host: example.com /// Origin: http://example.com /// Sec-WebSocket-Protocol: chat, sample /// Sec-WebSocket-Key1: 4 @1 46546xW%0l 1 5 /// Sec-WebSocket-Key2: 12998 5 Y3 1 .P00 /// /// ^n:ds[4U /// ]]> /// /// <para> /// Server response: /// </para> /// /// <![CDATA[ /// HTTP/1.1 101 WebSocket Protocol Handshake /// Upgrade: WebSocket /// Connection: Upgrade /// Sec-WebSocket-Origin: http://example.com /// Sec-WebSocket-Location: ws://example.com/demo /// Sec-WebSocket-Protocol: sample /// /// 8jKS'y:G*Co,Wxa- /// ]]> /// </summary> /// <param name="req"></param> /// <param name="headers"></param> /// <returns></returns> protected override IFullHttpResponse NewHandshakeResponse(IFullHttpRequest req, HttpHeaders headers) { // Serve the WebSocket handshake request. if (!req.Headers.ContainsValue(HttpHeaderNames.Connection, HttpHeaderValues.Upgrade, true) || !req.Headers.TryGet(HttpHeaderNames.Upgrade, out ICharSequence value) || !HttpHeaderValues.Websocket.ContentEqualsIgnoreCase(value)) { ThrowHelper.ThrowWebSocketHandshakeException_MissingUpgrade(); } // Hixie 75 does not contain these headers while Hixie 76 does bool isHixie76 = req.Headers.Contains(HttpHeaderNames.SecWebsocketKey1) && req.Headers.Contains(HttpHeaderNames.SecWebsocketKey2); var origin = req.Headers.Get(HttpHeaderNames.Origin, null); //throw before allocating FullHttpResponse if (origin is null && !isHixie76) { ThrowHelper.ThrowWebSocketHandshakeException_Missing_origin_header(req); } // Create the WebSocket handshake response. var res = new DefaultFullHttpResponse(HttpVersion.Http11, new HttpResponseStatus(101, new AsciiString(isHixie76 ? "WebSocket Protocol Handshake" : "Web Socket Protocol Handshake")), req.Content.Allocator.Buffer(0)); if (headers is object) { _ = res.Headers.Add(headers); } _ = res.Headers.Add(HttpHeaderNames.Upgrade, HttpHeaderValues.Websocket); _ = res.Headers.Add(HttpHeaderNames.Connection, HttpHeaderValues.Upgrade); // Fill in the headers and contents depending on handshake getMethod. if (isHixie76) { // New handshake getMethod with a challenge: _ = res.Headers.Add(HttpHeaderNames.SecWebsocketOrigin, origin); _ = res.Headers.Add(HttpHeaderNames.SecWebsocketLocation, this.Uri); if (req.Headers.TryGet(HttpHeaderNames.SecWebsocketProtocol, out ICharSequence subprotocols)) { string selectedSubprotocol = this.SelectSubprotocol(subprotocols.ToString()); if (selectedSubprotocol is null) { if (Logger.DebugEnabled) { Logger.RequestedSubprotocolNotSupported(subprotocols); } } else { _ = res.Headers.Add(HttpHeaderNames.SecWebsocketProtocol, selectedSubprotocol); } } // Calculate the answer of the challenge. value = req.Headers.Get(HttpHeaderNames.SecWebsocketKey1, null); Debug.Assert(value is object, $"{HttpHeaderNames.SecWebsocketKey1} must exist"); string key1 = value.ToString(); value = req.Headers.Get(HttpHeaderNames.SecWebsocketKey2, null); Debug.Assert(value is object, $"{HttpHeaderNames.SecWebsocketKey2} must exist"); string key2 = value.ToString(); int a = (int)(long.Parse(BeginningDigit.Replace(key1, "")) / BeginningSpace.Replace(key1, "").Length); int b = (int)(long.Parse(BeginningDigit.Replace(key2, "")) / BeginningSpace.Replace(key2, "").Length); long c = req.Content.ReadLong(); IByteBuffer input = Unpooled.WrappedBuffer(new byte[16]).SetIndex(0, 0); _ = input.WriteInt(a); _ = input.WriteInt(b); _ = input.WriteLong(c); _ = res.Content.WriteBytes(WebSocketUtil.Md5(input.Array)); } else { // Old Hixie 75 handshake getMethod with no challenge: _ = res.Headers.Add(HttpHeaderNames.WebsocketOrigin, origin); _ = res.Headers.Add(HttpHeaderNames.WebsocketLocation, this.Uri); if (req.Headers.TryGet(HttpHeaderNames.WebsocketProtocol, out ICharSequence protocol)) { _ = res.Headers.Add(HttpHeaderNames.WebsocketProtocol, this.SelectSubprotocol(protocol.ToString())); } } return(res); }