/// <summary> /// This function will decode the received data incrementally with the associated websocket's extensions. /// </summary> public void DecodeWithExtensions(WebSocket webSocket) { if (webSocket.Extensions != null) { for (int i = 0; i < webSocket.Extensions.Length; ++i) { var ext = webSocket.Extensions[i]; if (ext != null) { var newData = ext.Decode(this.Header, this.Data, (int)this.Length); if (this.Data != newData) { VariableSizedBufferPool.Release(this.Data); this.Data = newData; this.Length = (ulong)newData.Length; } } } } if (this.Type == WebSocketFrameTypes.Text && this.Data != null) { this.DataAsText = System.Text.Encoding.UTF8.GetString(this.Data, 0, (int)this.Length); VariableSizedBufferPool.Release(this.Data); this.Data = null; } }
public void Write(string str) { byte[] bytes = str.GetASCIIBytes(); this.Write(bytes, 0, bytes.Length); VariableSizedBufferPool.Release(bytes); }
/// <summary> /// Assembles all fragments into a final frame. Call this on the last fragment of a frame. /// </summary> /// <param name="fragments">The list of previously downloaded and parsed fragments of the frame</param> public void Assemble(List <WebSocketFrameReader> fragments) { // this way the following algorithms will handle this fragment's data too fragments.Add(this); UInt64 finalLength = 0; for (int i = 0; i < fragments.Count; ++i) { finalLength += fragments[i].Length; } byte[] buffer = fragments[0].Type == WebSocketFrameTypes.Text ? VariableSizedBufferPool.Get((long)finalLength, true) : new byte[finalLength]; UInt64 pos = 0; for (int i = 0; i < fragments.Count; ++i) { Array.Copy(fragments[i].Data, 0, buffer, (int)pos, (int)fragments[i].Length); VariableSizedBufferPool.Release(fragments[i].Data); pos += fragments[i].Length; } // All fragments of a message are of the same type, as set by the first fragment's opcode. this.Type = fragments[0].Type; // Reserver flags may be contained only in the first fragment this.Header = fragments[0].Header; this.Length = finalLength; this.Data = buffer; }
public static string NoTrimReadTo(Stream stream, byte blocker1, byte blocker2) { byte[] readBuf = VariableSizedBufferPool.Get(1024, true); try { int bufpos = 0; int ch = stream.ReadByte(); while (ch != blocker1 && ch != blocker2 && ch != -1) { if (ch > 0x7f) //replaces asciitostring { ch = '?'; } //make buffer larger if too short if (readBuf.Length <= bufpos) { VariableSizedBufferPool.Resize(ref readBuf, readBuf.Length * 2, true); } if (bufpos > 0 || !char.IsWhiteSpace((char)ch)) //trimstart { readBuf[bufpos++] = (byte)ch; } ch = stream.ReadByte(); } return(System.Text.Encoding.UTF8.GetString(readBuf, 0, bufpos)); } finally { VariableSizedBufferPool.Release(readBuf); } }
protected byte[] DecodeStream(BufferPoolMemoryStream streamToDecode) { streamToDecode.Seek(0, SeekOrigin.Begin); // The cache stores the decoded data var encoding = #if !BESTHTTP_DISABLE_CACHING IsFromCache ? null : #endif GetHeaderValues("content-encoding"); #if !UNITY_WEBGL || UNITY_EDITOR Stream decoderStream = null; #endif // Return early if there are no encoding used. if (encoding == null) { return(streamToDecode.ToArray()); } else { switch (encoding[0]) { #if !UNITY_WEBGL || UNITY_EDITOR case "gzip": decoderStream = new Decompression.Zlib.GZipStream(streamToDecode, Decompression.Zlib.CompressionMode.Decompress); break; case "deflate": decoderStream = new Decompression.Zlib.DeflateStream(streamToDecode, Decompression.Zlib.CompressionMode.Decompress); break; #endif //identity, utf-8, etc. default: // Do not copy from one stream to an other, just return with the raw bytes return(streamToDecode.ToArray()); } } #if !UNITY_WEBGL || UNITY_EDITOR using (var ms = new BufferPoolMemoryStream((int)streamToDecode.Length)) { var buf = VariableSizedBufferPool.Get(1024, true); int byteCount = 0; while ((byteCount = decoderStream.Read(buf, 0, buf.Length)) > 0) { ms.Write(buf, 0, byteCount); } VariableSizedBufferPool.Release(buf); decoderStream.Dispose(); return(ms.ToArray()); } #endif }
private byte[] Decompress(byte[] data, int offset, int count, bool forceDecompress = false) { if (decompressorInputStream == null) { decompressorInputStream = new BufferPoolMemoryStream(count); } if (data != null) { decompressorInputStream.Write(data, offset, count); } if (!forceDecompress && decompressorInputStream.Length < MinLengthToDecompress) { return(null); } decompressorInputStream.Position = 0; if (decompressorGZipStream == null) { decompressorGZipStream = new Decompression.Zlib.GZipStream(decompressorInputStream, Decompression.Zlib.CompressionMode.Decompress, Decompression.Zlib.CompressionLevel.Default, true); decompressorGZipStream.FlushMode = Decompression.Zlib.FlushType.Sync; } if (decompressorOutputStream == null) { decompressorOutputStream = new BufferPoolMemoryStream(); } decompressorOutputStream.SetLength(0); byte[] copyBuffer = VariableSizedBufferPool.Get(1024, true); int readCount; while ((readCount = decompressorGZipStream.Read(copyBuffer, 0, copyBuffer.Length)) != 0) { decompressorOutputStream.Write(copyBuffer, 0, readCount); } VariableSizedBufferPool.Release(copyBuffer); decompressorGZipStream.SetLength(0); // TODO: be able to use a larger array byte[] result = decompressorOutputStream.ToArray(); return(result); }
/// <summary> /// A function to decompress and return the data parameter with possible context takeover support (reusing the DeflateStream). /// </summary> private byte[] Decompress(byte[] data, int length) { if (decompressorInputStream == null) { decompressorInputStream = new BufferPoolMemoryStream(data.Length + 4); } decompressorInputStream.Write(data, 0, length); // http://tools.ietf.org/html/rfc7692#section-7.2.2 // Append 4 octets of 0x00 0x00 0xff 0xff to the tail end of the payload of the message. decompressorInputStream.Write(PerMessageCompression.Trailer, 0, PerMessageCompression.Trailer.Length); decompressorInputStream.Position = 0; if (decompressorDeflateStream == null) { decompressorDeflateStream = new DeflateStream(decompressorInputStream, CompressionMode.Decompress, CompressionLevel.Default, true, this.ServerMaxWindowBits); decompressorDeflateStream.FlushMode = FlushType.Sync; } if (decompressorOutputStream == null) { decompressorOutputStream = new BufferPoolMemoryStream(); } decompressorOutputStream.SetLength(0); byte[] copyBuffer = VariableSizedBufferPool.Get(1024, true); int readCount; while ((readCount = decompressorDeflateStream.Read(copyBuffer, 0, copyBuffer.Length)) != 0) { decompressorOutputStream.Write(copyBuffer, 0, readCount); } VariableSizedBufferPool.Release(copyBuffer); decompressorDeflateStream.SetLength(0); byte[] result = decompressorOutputStream.ToArray(); if (this.ServerNoContextTakeover) { decompressorDeflateStream.Dispose(); decompressorDeflateStream = null; } return(result); }
static void OnBufferCallback(int nativeId, IntPtr pBuffer, int length) { WebGLConnection conn = null; if (!Connections.TryGetValue(nativeId, out conn)) { HTTPManager.Logger.Error("WebGLConnection - OnBufferCallback", "No WebGL connection found for nativeId: " + nativeId.ToString()); return; } byte[] buffer = VariableSizedBufferPool.Get(length, true); // Copy data from the 'unmanaged' memory to managed memory. Buffer will be reclaimed by the GC. Marshal.Copy(pBuffer, buffer, 0, length); conn.OnBuffer(buffer, length); VariableSizedBufferPool.Release(buffer); }
public WebSocketFrame(WebSocket webSocket, WebSocketFrameTypes type, byte[] data, UInt64 pos, UInt64 length, bool isFinal, bool useExtensions) { this.Type = type; this.IsFinal = isFinal; this.UseExtensions = useExtensions; this.DataLength = (int)length; if (data != null) { this.Data = VariableSizedBufferPool.Get(this.DataLength, true); Array.Copy(data, (int)pos, this.Data, 0, this.DataLength); } else { data = VariableSizedBufferPool.NoData; } // First byte: Final Bit + Rsv flags + OpCode byte finalBit = (byte)(IsFinal ? 0x80 : 0x0); this.Header = (byte)(finalBit | (byte)Type); if (this.UseExtensions && webSocket != null && webSocket.Extensions != null) { for (int i = 0; i < webSocket.Extensions.Length; ++i) { var ext = webSocket.Extensions[i]; if (ext != null) { this.Header |= ext.GetFrameHeader(this, this.Header); byte[] newData = ext.Encode(this); if (newData != this.Data) { VariableSizedBufferPool.Release(this.Data); this.Data = newData; this.DataLength = newData.Length; } } } } }
static void OnResponse(int nativeId, int httpStatus, IntPtr pBuffer, int length, int err) { HTTPManager.Logger.Information("WebGLConnection - OnResponse", string.Format("{0} {1} {2} {3}", nativeId, httpStatus, length, err)); WebGLConnection conn = null; if (!Connections.TryGetValue(nativeId, out conn)) { HTTPManager.Logger.Error("WebGLConnection - OnResponse", "No WebGL connection found for nativeId: " + nativeId.ToString()); return; } byte[] buffer = VariableSizedBufferPool.Get(length, true); // Copy data from the 'unmanaged' memory to managed memory. Buffer will be reclaimed by the GC. Marshal.Copy(pBuffer, buffer, 0, length); conn.OnResponse(httpStatus, buffer, length); VariableSizedBufferPool.Release(buffer); }
/// <summary> /// It will send the given message to the server in one frame. /// </summary> public void Send(string message) { if (message == null) { throw new ArgumentNullException("message must not be null!"); } int count = System.Text.Encoding.UTF8.GetByteCount(message); byte[] data = VariableSizedBufferPool.Get(count, true); System.Text.Encoding.UTF8.GetBytes(message, 0, message.Length, data, 0); var frame = new WebSocketFrame(this.WebSocket, WebSocketFrameTypes.Text, data, 0, (ulong)count, true, true); if (frame.Data != null && frame.Data.Length > this.MaxFragmentSize) { WebSocketFrame[] additionalFrames = frame.Fragment(this.MaxFragmentSize); lock (SendLock) { Send(frame); if (additionalFrames != null) { for (int i = 0; i < additionalFrames.Length; ++i) { Send(additionalFrames[i]); } } } } else { Send(frame); } VariableSizedBufferPool.Release(data); }
internal void Read(Stream stream) { // For the complete documentation for this section see: // http://tools.ietf.org/html/rfc6455#section-5.2 this.Header = ReadByte(stream); // The first byte is the Final Bit and the type of the frame IsFinal = (this.Header & 0x80) != 0; Type = (WebSocketFrameTypes)(this.Header & 0xF); byte maskAndLength = ReadByte(stream); // The second byte is the Mask Bit and the length of the payload data HasMask = (maskAndLength & 0x80) != 0; // if 0-125, that is the payload length. Length = (UInt64)(maskAndLength & 127); // If 126, the following 2 bytes interpreted as a 16-bit unsigned integer are the payload length. if (Length == 126) { byte[] rawLen = VariableSizedBufferPool.Get(2, true); stream.ReadBuffer(rawLen, 2); if (BitConverter.IsLittleEndian) { Array.Reverse(rawLen, 0, 2); } Length = (UInt64)BitConverter.ToUInt16(rawLen, 0); VariableSizedBufferPool.Release(rawLen); } else if (Length == 127) { // If 127, the following 8 bytes interpreted as a 64-bit unsigned integer (the // most significant bit MUST be 0) are the payload length. byte[] rawLen = VariableSizedBufferPool.Get(8, true); stream.ReadBuffer(rawLen, 8); if (BitConverter.IsLittleEndian) { Array.Reverse(rawLen, 0, 8); } Length = (UInt64)BitConverter.ToUInt64(rawLen, 0); VariableSizedBufferPool.Release(rawLen); } // The sent byte array as a mask to decode the data. byte[] mask = null; // Read the Mask, if has any if (HasMask) { mask = VariableSizedBufferPool.Get(4, true); if (stream.Read(mask, 0, 4) < mask.Length) { throw ExceptionHelper.ServerClosedTCPStream(); } } if (Type == WebSocketFrameTypes.Text || Type == WebSocketFrameTypes.Continuation) { Data = VariableSizedBufferPool.Get((long)Length, true); } else if (Length == 0) { Data = VariableSizedBufferPool.NoData; } else { Data = new byte[Length]; } //Data = Type == WebSocketFrameTypes.Text ? VariableSizedBufferPool.Get((long)Length, true) : new byte[Length]; if (Length == 0L) { return; } uint readLength = 0; do { int read = stream.Read(Data, (int)readLength, (int)(Length - readLength)); if (read <= 0) { throw ExceptionHelper.ServerClosedTCPStream(); } readLength += (uint)read; } while (readLength < Length); if (HasMask) { for (uint i = 0; i < Length; ++i) { Data[i] = (byte)(Data[i] ^ mask[i % 4]); } VariableSizedBufferPool.Release(mask); } }
protected void ReadUnknownSize(Stream stream) { string encoding = #if !BESTHTTP_DISABLE_CACHING IsFromCache ? null : #endif GetFirstHeaderValue("content-encoding"); bool gzipped = !string.IsNullOrEmpty(encoding) && encoding == "gzip"; using (var output = new BufferPoolMemoryStream()) { byte[] buffer = VariableSizedBufferPool.Get(Math.Max(baseRequest.StreamFragmentSize, MinBufferSize), false); if (HTTPManager.Logger.Level == Logger.Loglevels.All) { VerboseLogging(string.Format("ReadUnknownSize - buffer size: {0:N0}", buffer.Length)); } int readBytes = 0; int bytes = 0; do { readBytes = 0; do { bytes = 0; #if !NETFX_CORE || UNITY_EDITOR NetworkStream networkStream = stream as NetworkStream; // If we have the good-old NetworkStream, than we can use the DataAvailable property. On WP8 platforms, these are omitted... :/ if (networkStream != null && baseRequest.EnableSafeReadOnUnknownContentLength) { for (int i = readBytes; i < buffer.Length && networkStream.DataAvailable; ++i) { int read = stream.ReadByte(); if (read >= 0) { buffer[i] = (byte)read; bytes++; } else { break; } } } else // This will be good anyway, but a little slower. #endif { bytes = stream.Read(buffer, readBytes, buffer.Length - readBytes); } readBytes += bytes; // Progress report: baseRequest.Downloaded += bytes; baseRequest.DownloadLength = baseRequest.Downloaded; baseRequest.DownloadProgressChanged = this.IsSuccess #if !BESTHTTP_DISABLE_CACHING || this.IsFromCache #endif ; } while (readBytes < buffer.Length && bytes > 0); if (baseRequest.UseStreaming) { if (gzipped) { var decompressed = Decompress(buffer, 0, readBytes); if (decompressed != null) { FeedStreamFragment(decompressed, 0, decompressed.Length); } } else { FeedStreamFragment(buffer, 0, readBytes); } } else { output.Write(buffer, 0, readBytes); } } while (bytes > 0); VariableSizedBufferPool.Release(buffer); if (baseRequest.UseStreaming) { if (gzipped) { var decompressed = Decompress(null, 0, 0, true); if (decompressed != null) { FeedStreamFragment(decompressed, 0, decompressed.Length); } } FlushRemainingFragmentBuffer(); } if (!baseRequest.UseStreaming) { this.Data = DecodeStream(output); } } CloseDecompressors(); }
// No transfer-encoding just raw bytes. internal void ReadRaw(Stream stream, long contentLength) { BeginReceiveStreamFragments(); // Progress report: baseRequest.DownloadLength = contentLength; baseRequest.DownloadProgressChanged = this.IsSuccess #if !BESTHTTP_DISABLE_CACHING || this.IsFromCache #endif ; if (HTTPManager.Logger.Level == Logger.Loglevels.All) { VerboseLogging(string.Format("ReadRaw - contentLength: {0:N0}", contentLength)); } string encoding = #if !BESTHTTP_DISABLE_CACHING IsFromCache ? null : #endif GetFirstHeaderValue("content-encoding"); bool gzipped = !string.IsNullOrEmpty(encoding) && encoding == "gzip"; if (!baseRequest.UseStreaming && contentLength > 2147483646) { throw new OverflowException("You have to use STREAMING to download files bigger than 2GB!"); } using (var output = new BufferPoolMemoryStream(baseRequest.UseStreaming ? 0 : (int)contentLength)) { byte[] buffer = VariableSizedBufferPool.Get(Math.Max(baseRequest.StreamFragmentSize, MinBufferSize), true); int readBytes = 0; while (contentLength > 0) { readBytes = 0; do { int readbuffer = (int)Math.Min(2147483646, (uint)contentLength); int bytes = stream.Read(buffer, readBytes, Math.Min(readbuffer, buffer.Length - readBytes)); if (bytes <= 0) { throw ExceptionHelper.ServerClosedTCPStream(); } readBytes += bytes; contentLength -= bytes; // Progress report: baseRequest.Downloaded += bytes; baseRequest.DownloadProgressChanged = this.IsSuccess #if !BESTHTTP_DISABLE_CACHING || this.IsFromCache #endif ; } while (readBytes < buffer.Length && contentLength > 0); if (baseRequest.UseStreaming) { if (gzipped) { var decompressed = Decompress(buffer, 0, readBytes); if (decompressed != null) { FeedStreamFragment(decompressed, 0, decompressed.Length); } } else { FeedStreamFragment(buffer, 0, readBytes); } } else { output.Write(buffer, 0, readBytes); } } ; VariableSizedBufferPool.Release(buffer); if (baseRequest.UseStreaming) { if (gzipped) { var decompressed = Decompress(null, 0, 0, true); if (decompressed != null) { FeedStreamFragment(decompressed, 0, decompressed.Length); } } FlushRemainingFragmentBuffer(); } if (!baseRequest.UseStreaming) { this.Data = DecodeStream(output); } } CloseDecompressors(); }
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.6.1 protected void ReadChunked(Stream stream) { BeginReceiveStreamFragments(); string contentLengthHeader = GetFirstHeaderValue("Content-Length"); bool hasContentLengthHeader = !string.IsNullOrEmpty(contentLengthHeader); int realLength = 0; if (hasContentLengthHeader) { hasContentLengthHeader = int.TryParse(contentLengthHeader, out realLength); } if (HTTPManager.Logger.Level == Logger.Loglevels.All) { VerboseLogging(string.Format("ReadChunked - hasContentLengthHeader: {0}, contentLengthHeader: {1} realLength: {2:N0}", hasContentLengthHeader.ToString(), contentLengthHeader, realLength)); } using (var output = new BufferPoolMemoryStream()) { int chunkLength = ReadChunkLength(stream); if (HTTPManager.Logger.Level == Logger.Loglevels.All) { VerboseLogging(string.Format("chunkLength: {0:N0}", chunkLength)); } byte[] buffer = VariableSizedBufferPool.Get(Mathf.NextPowerOfTwo(chunkLength), true); int contentLength = 0; // Progress report: baseRequest.DownloadLength = hasContentLengthHeader ? realLength : chunkLength; baseRequest.DownloadProgressChanged = this.IsSuccess #if !BESTHTTP_DISABLE_CACHING || this.IsFromCache #endif ; string encoding = #if !BESTHTTP_DISABLE_CACHING IsFromCache ? null : #endif GetFirstHeaderValue("content-encoding"); bool gzipped = !string.IsNullOrEmpty(encoding) && encoding == "gzip"; while (chunkLength != 0) { // To avoid more GC garbage we use only one buffer, and resize only if the next chunk doesn't fit. if (buffer.Length < chunkLength) { VariableSizedBufferPool.Resize(ref buffer, chunkLength, true); } int readBytes = 0; // Fill up the buffer do { int bytes = stream.Read(buffer, readBytes, chunkLength - readBytes); if (bytes <= 0) { throw ExceptionHelper.ServerClosedTCPStream(); } readBytes += bytes; // Progress report: // Placing reporting inside this cycle will report progress much more frequent baseRequest.Downloaded += bytes; baseRequest.DownloadProgressChanged = this.IsSuccess #if !BESTHTTP_DISABLE_CACHING || this.IsFromCache #endif ; } while (readBytes < chunkLength); if (baseRequest.UseStreaming) { if (gzipped) { var decompressed = Decompress(buffer, 0, readBytes); if (decompressed != null) { FeedStreamFragment(decompressed, 0, decompressed.Length); } } else { FeedStreamFragment(buffer, 0, readBytes); } } else { output.Write(buffer, 0, readBytes); } // Every chunk data has a trailing CRLF ReadTo(stream, LF); contentLength += readBytes; // read the next chunk's length chunkLength = ReadChunkLength(stream); if (HTTPManager.Logger.Level == Logger.Loglevels.All) { VerboseLogging(string.Format("chunkLength: {0:N0}", chunkLength)); } if (!hasContentLengthHeader) { baseRequest.DownloadLength += chunkLength; } baseRequest.DownloadProgressChanged = this.IsSuccess #if !BESTHTTP_DISABLE_CACHING || this.IsFromCache #endif ; } VariableSizedBufferPool.Release(buffer); if (baseRequest.UseStreaming) { if (gzipped) { var decompressed = Decompress(null, 0, 0, true); if (decompressed != null) { FeedStreamFragment(decompressed, 0, decompressed.Length); } } FlushRemainingFragmentBuffer(); } // Read the trailing headers or the CRLF ReadHeaders(stream); // HTTP servers sometimes use compression (gzip) or deflate methods to optimize transmission. // How both chunked and gzip encoding interact is dictated by the two-staged encoding of HTTP: // first the content stream is encoded as (Content-Encoding: gzip), after which the resulting byte stream is encoded for transfer using another encoder (Transfer-Encoding: chunked). // This means that in case both compression and chunked encoding are enabled, the chunk encoding itself is not compressed, and the data in each chunk should not be compressed individually. // The remote endpoint can decode the incoming stream by first decoding it with the Transfer-Encoding, followed by the specified Content-Encoding. // It would be a better implementation when the chunk would be decododed on-the-fly. Becouse now the whole stream must be downloaded, and then decoded. It needs more memory. if (!baseRequest.UseStreaming) { this.Data = DecodeStream(output); } } CloseDecompressors(); }
private void SendThreadFunc() { try { using (WriteOnlyBufferedStream bufferedStream = new WriteOnlyBufferedStream(this.Stream, 16 * 1024)) { while (!closed && !closeSent) { //if (HTTPManager.Logger.Level <= Logger.Loglevels.All) // HTTPManager.Logger.Information("WebSocketResponse", "SendThread - Waiting..."); newFrameSignal.WaitOne(); try { //if (HTTPManager.Logger.Level <= Logger.Loglevels.All) // HTTPManager.Logger.Information("WebSocketResponse", "SendThread - Wait is over, " + localFrames.Count.ToString() + " new frames!"); WebSocketFrame frame; while (this.unsentFrames.TryDequeue(out frame)) { if (!closeSent) { using (var rawData = frame.Get()) Stream.Write(rawData.Data, 0, rawData.Length); VariableSizedBufferPool.Release(frame.Data); if (frame.Type == WebSocketFrameTypes.ConnectionClose) { closeSent = true; } } Interlocked.Add(ref this._bufferedAmount, -frame.DataLength); } Stream.Flush(); } catch (Exception ex) { if (HTTPUpdateDelegator.IsCreated) { this.baseRequest.Exception = ex; this.baseRequest.State = HTTPRequestStates.Error; } else { this.baseRequest.State = HTTPRequestStates.Aborted; } closed = true; } } } } finally { sendThreadCreated = false; (newFrameSignal as IDisposable).Dispose(); newFrameSignal = null; HTTPManager.Logger.Information("WebSocketResponse", "SendThread - Closed!"); } }
/// <summary> /// Internal function to send out received messages. /// </summary> void IProtocol.HandleEvents() { WebSocketFrameReader frame; while (CompletedFrames.TryDequeue(out frame)) { // Bugs in the clients shouldn't interrupt the code, so we need to try-catch and ignore any exception occurring here try { switch (frame.Type) { case WebSocketFrameTypes.Continuation: if (OnIncompleteFrame != null) { OnIncompleteFrame(this, frame); } break; case WebSocketFrameTypes.Text: // Any not Final frame is handled as a fragment if (!frame.IsFinal) { goto case WebSocketFrameTypes.Continuation; } if (OnText != null) { OnText(this, frame.DataAsText); } break; case WebSocketFrameTypes.Binary: // Any not Final frame is handled as a fragment if (!frame.IsFinal) { goto case WebSocketFrameTypes.Continuation; } if (OnBinary != null) { OnBinary(this, frame.Data); } break; } } catch (Exception ex) { HTTPManager.Logger.Exception("WebSocketResponse", "HandleEvents", ex); } } // 2015.05.09 // State checking added because if there is an error the OnClose called first, and then the OnError. // Now, when there is an error only the OnError event will be called! if (IsClosed && OnClosed != null && baseRequest.State == HTTPRequestStates.Processing) { try { UInt16 statusCode = 0; string msg = string.Empty; // If we received any data, we will get the status code and the message from it if (/*CloseFrame != null && */ CloseFrame.Data != null && CloseFrame.Data.Length >= 2) { if (BitConverter.IsLittleEndian) { Array.Reverse(CloseFrame.Data, 0, 2); } statusCode = BitConverter.ToUInt16(CloseFrame.Data, 0); if (CloseFrame.Data.Length > 2) { msg = Encoding.UTF8.GetString(CloseFrame.Data, 2, CloseFrame.Data.Length - 2); } VariableSizedBufferPool.Release(CloseFrame.Data); } OnClosed(this, statusCode, msg); } catch (Exception ex) { HTTPManager.Logger.Exception("WebSocketResponse", "HandleEvents - OnClosed", ex); } } }
private void SendThreadFunc(object param) { List <WebSocketFrame> localFrames = new List <WebSocketFrame>(); try { while (!closed && !closeSent) { //if (HTTPManager.Logger.Level <= Logger.Loglevels.All) // HTTPManager.Logger.Information("WebSocketResponse", "SendThread - Waiting..."); newFrameSignal.WaitOne(); try { lock (SendLock) { // add frames int reversed order for (int i = this.unsentFrames.Count - 1; i >= 0; --i) { localFrames.Add(this.unsentFrames[i]); } this.unsentFrames.Clear(); } //if (HTTPManager.Logger.Level <= Logger.Loglevels.All) // HTTPManager.Logger.Information("WebSocketResponse", "SendThread - Wait is over, " + localFrames.Count.ToString() + " new frames!"); while (localFrames.Count > 0) { WebSocketFrame frame = localFrames[localFrames.Count - 1]; localFrames.RemoveAt(localFrames.Count - 1); if (!closeSent) { using (var rawData = frame.Get()) { Stream.Write(rawData.Data, 0, rawData.Length); } //if (frame.Type == WebSocketFrameTypes.Text) VariableSizedBufferPool.Release(frame.Data); if (frame.Type == WebSocketFrameTypes.ConnectionClose) { closeSent = true; } } Interlocked.Add(ref this._bufferedAmount, -frame.DataLength); } Stream.Flush(); } catch (Exception ex) { if (HTTPUpdateDelegator.IsCreated) { this.baseRequest.Exception = ex; this.baseRequest.State = HTTPRequestStates.Error; } else { this.baseRequest.State = HTTPRequestStates.Aborted; } closed = true; } } } finally { sendThreadCreated = false; (newFrameSignal as IDisposable).Dispose(); newFrameSignal = null; HTTPManager.Logger.Information("WebSocketResponse", "SendThread - Closed!"); } }
internal override void Connect(Stream stream, HTTPRequest request) { bool isSecure = HTTPProtocolFactory.IsSecureProtocol(request.CurrentUri); if ((!this.IsTransparent || (isSecure && this.NonTransparentForHTTPS))) { using (var bufferedStream = new WriteOnlyBufferedStream(stream, HTTPRequest.UploadChunkSize)) using (var outStream = new BinaryWriter(bufferedStream, Encoding.UTF8)) { bool retry; do { // If we have to because of a authentication request, we will switch it to true retry = false; string connectStr = string.Format("CONNECT {0}:{1} HTTP/1.1", request.CurrentUri.Host, request.CurrentUri.Port.ToString()); HTTPManager.Logger.Information("HTTPConnection", "Sending " + connectStr); outStream.SendAsASCII(connectStr); outStream.Write(HTTPRequest.EOL); outStream.SendAsASCII("Proxy-Connection: Keep-Alive"); outStream.Write(HTTPRequest.EOL); outStream.SendAsASCII("Connection: Keep-Alive"); outStream.Write(HTTPRequest.EOL); outStream.SendAsASCII(string.Format("Host: {0}:{1}", request.CurrentUri.Host, request.CurrentUri.Port.ToString())); outStream.Write(HTTPRequest.EOL); // Proxy Authentication if (this.Credentials != null) { switch (this.Credentials.Type) { case AuthenticationTypes.Basic: // With Basic authentication we don't want to wait for a challenge, we will send the hash with the first request outStream.Write(string.Format("Proxy-Authorization: {0}", string.Concat("Basic ", Convert.ToBase64String(Encoding.UTF8.GetBytes(this.Credentials.UserName + ":" + this.Credentials.Password)))).GetASCIIBytes()); outStream.Write(HTTPRequest.EOL); break; case AuthenticationTypes.Unknown: case AuthenticationTypes.Digest: var digest = DigestStore.Get(this.Address); if (digest != null) { string authentication = digest.GenerateResponseHeader(request, this.Credentials, true); if (!string.IsNullOrEmpty(authentication)) { string auth = string.Format("Proxy-Authorization: {0}", authentication); if (HTTPManager.Logger.Level <= Logger.Loglevels.Information) { HTTPManager.Logger.Information("HTTPConnection", "Sending proxy authorization header: " + auth); } var bytes = auth.GetASCIIBytes(); outStream.Write(bytes); outStream.Write(HTTPRequest.EOL); VariableSizedBufferPool.Release(bytes); } } break; } } outStream.Write(HTTPRequest.EOL); // Make sure to send all the wrote data to the wire outStream.Flush(); request.ProxyResponse = new HTTPResponse(request, stream, false, false); // Read back the response of the proxy if (!request.ProxyResponse.Receive(-1, true)) { throw new Exception("Connection to the Proxy Server failed!"); } if (HTTPManager.Logger.Level <= Logger.Loglevels.Information) { HTTPManager.Logger.Information("HTTPConnection", "Proxy returned - status code: " + request.ProxyResponse.StatusCode + " message: " + request.ProxyResponse.Message + " Body: " + request.ProxyResponse.DataAsText); } switch (request.ProxyResponse.StatusCode) { // Proxy authentication required // http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.8 case 407: { string authHeader = DigestStore.FindBest(request.ProxyResponse.GetHeaderValues("proxy-authenticate")); if (!string.IsNullOrEmpty(authHeader)) { var digest = DigestStore.GetOrCreate(this.Address); digest.ParseChallange(authHeader); if (this.Credentials != null && digest.IsUriProtected(this.Address) && (!request.HasHeader("Proxy-Authorization") || digest.Stale)) { retry = true; } } if (!retry) { throw new Exception(string.Format("Can't authenticate Proxy! Status Code: \"{0}\", Message: \"{1}\" and Response: {2}", request.ProxyResponse.StatusCode, request.ProxyResponse.Message, request.ProxyResponse.DataAsText)); } break; } default: if (!request.ProxyResponse.IsSuccess) { throw new Exception(string.Format("Proxy returned Status Code: \"{0}\", Message: \"{1}\" and Response: {2}", request.ProxyResponse.StatusCode, request.ProxyResponse.Message, request.ProxyResponse.DataAsText)); } break; } } while (retry); }// using outstream } }
public void Dispose() { VariableSizedBufferPool.Release(Data); Data = null; }