void OnResponse(int httpStatus, byte[] buffer, int bufferLength) { try { using (var ms = new BufferPoolMemoryStream()) { Stream = ms; XHR_GetStatusLine(NativeId, OnBufferCallback); XHR_GetResponseHeaders(NativeId, OnBufferCallback); if (buffer != null && bufferLength > 0) { ms.Write(buffer, 0, bufferLength); } ms.Seek(0L, SeekOrigin.Begin); var internalBuffer = ms.GetBuffer(); string tmp = System.Text.Encoding.UTF8.GetString(internalBuffer); HTTPManager.Logger.Information(this.NativeId + " OnResponse - full response ", tmp); SupportedProtocols protocol = CurrentRequest.ProtocolHandler == SupportedProtocols.Unknown ? HTTPProtocolFactory.GetProtocolFromUri(CurrentRequest.CurrentUri) : CurrentRequest.ProtocolHandler; CurrentRequest.Response = HTTPProtocolFactory.Get(protocol, CurrentRequest, ms, CurrentRequest.UseStreaming, false); CurrentRequest.Response.Receive(buffer != null && bufferLength > 0 ? (int)bufferLength : -1, true); #if !BESTHTTP_DISABLE_COOKIES if (CurrentRequest.IsCookiesEnabled) { BestHTTP.Cookies.CookieJar.Set(CurrentRequest.Response); } #endif } } catch (Exception e) { HTTPManager.Logger.Exception(this.NativeId + " WebGLConnection", "OnResponse", e); if (CurrentRequest != null) { // Something gone bad, Response must be null! CurrentRequest.Response = null; switch (State) { case HTTPConnectionStates.AbortRequested: CurrentRequest.State = HTTPRequestStates.Aborted; break; case HTTPConnectionStates.TimedOut: CurrentRequest.State = HTTPRequestStates.TimedOut; break; default: CurrentRequest.Exception = e; CurrentRequest.State = HTTPRequestStates.Error; break; } } } finally { Connections.Remove(NativeId); Stream = null; if (CurrentRequest != null) { lock (HTTPManager.Locker) { State = HTTPConnectionStates.Closed; if (CurrentRequest.State == HTTPRequestStates.Processing) { if (CurrentRequest.Response != null) { CurrentRequest.State = HTTPRequestStates.Finished; } else { CurrentRequest.State = HTTPRequestStates.Error; } } } } LastProcessTime = DateTime.UtcNow; if (OnConnectionRecycled != null) { RecycleNow(); } XHR_Release(NativeId); } }
public BufferSegment EncodeMessage(Message message) { var memBuffer = BufferPool.Get(256, true); var stream = new BufferPoolMemoryStream(memBuffer, 0, memBuffer.Length, true, true, false, true); // Write 5 bytes for placeholder for length prefix stream.WriteByte(0); stream.WriteByte(0); stream.WriteByte(0); stream.WriteByte(0); stream.WriteByte(0); var bufferWriter = new BufferPoolBufferWriter(stream); var writer = new MessagePackWriter(bufferWriter); switch (message.type) { case MessageTypes.StreamItem: // https://github.com/aspnet/AspNetCore/blob/master/src/SignalR/docs/specs/HubProtocol.md#streamitem-message-encoding-1 // [2, Headers, InvocationId, Item] writer.WriteArrayHeader(4); writer.Write(2); WriteHeaders(ref writer); WriteString(ref writer, message.invocationId); WriteValue(ref writer, bufferWriter, message.item); break; case MessageTypes.Completion: // https://github.com/aspnet/AspNetCore/blob/master/src/SignalR/docs/specs/HubProtocol.md#completion-message-encoding-1 // [3, Headers, InvocationId, ResultKind, Result?] byte resultKind = (byte)(!string.IsNullOrEmpty(message.error) ? /*error*/ 1 : message.result != null ? /*non-void*/ 3 : /*void*/ 2); writer.WriteArrayHeader(resultKind == 2 ? 4 : 5); writer.Write(3); WriteHeaders(ref writer); WriteString(ref writer, message.invocationId); writer.Write(resultKind); if (resultKind == 1) // error { WriteString(ref writer, message.error); } else if (resultKind == 3) // non-void { WriteValue(ref writer, bufferWriter, message.result); } break; case MessageTypes.Invocation: // https://github.com/aspnet/AspNetCore/blob/master/src/SignalR/docs/specs/HubProtocol.md#invocation-message-encoding-1 // [1, Headers, InvocationId, NonBlocking, Target, [Arguments], [StreamIds]] case MessageTypes.StreamInvocation: // https://github.com/aspnet/AspNetCore/blob/master/src/SignalR/docs/specs/HubProtocol.md#streaminvocation-message-encoding-1 // [4, Headers, InvocationId, Target, [Arguments], [StreamIds]] writer.WriteArrayHeader(message.streamIds != null ? 6 : 5); writer.Write((int)message.type); WriteHeaders(ref writer); WriteString(ref writer, message.invocationId); WriteString(ref writer, message.target); writer.WriteArrayHeader(message.arguments != null ? message.arguments.Length : 0); if (message.arguments != null) { for (int i = 0; i < message.arguments.Length; ++i) { WriteValue(ref writer, bufferWriter, message.arguments[i]); } } if (message.streamIds != null) { writer.WriteArrayHeader(message.streamIds.Length); for (int i = 0; i < message.streamIds.Length; ++i) { WriteValue(ref writer, bufferWriter, message.streamIds[i]); } } break; case MessageTypes.CancelInvocation: // https://github.com/aspnet/AspNetCore/blob/master/src/SignalR/docs/specs/HubProtocol.md#cancelinvocation-message-encoding-1 // [5, Headers, InvocationId] writer.WriteArrayHeader(3); writer.Write(5); WriteHeaders(ref writer); WriteString(ref writer, message.invocationId); break; case MessageTypes.Ping: // https://github.com/aspnet/AspNetCore/blob/master/src/SignalR/docs/specs/HubProtocol.md#ping-message-encoding-1 // [6] writer.WriteArrayHeader(1); writer.Write(6); break; case MessageTypes.Close: // https://github.com/aspnet/AspNetCore/blob/master/src/SignalR/docs/specs/HubProtocol.md#close-message-encoding-1 // [7, Error, AllowReconnect?] writer.WriteArrayHeader(string.IsNullOrEmpty(message.error) ? 1 : 2); writer.Write(7); if (!string.IsNullOrEmpty(message.error)) { WriteString(ref writer, message.error); } break; } writer.Flush(); // get how much bytes got written to the buffer. This includes the 5 placeholder bytes too. int length = (int)stream.Position; // this is the length without the 5 placeholder bytes int contentLength = length - 5; // get the stream's internal buffer. We set the releaseBuffer flag to false, so we can use it safely. var buffer = stream.GetBuffer(); // add varint length prefix byte prefixBytes = GetRequiredBytesForLengthPrefix(contentLength); WriteLengthAsVarInt(buffer, 5 - prefixBytes, contentLength); // return with the final segment return(new BufferSegment(buffer, 5 - prefixBytes, contentLength + prefixBytes)); }
void OnResponse(int httpStatus, byte[] buffer, int bufferLength) { HTTPConnectionStates proposedConnectionState = HTTPConnectionStates.Processing; bool resendRequest = false; try { if (this.CurrentRequest.IsCancellationRequested) { return; } using (var ms = new BufferPoolMemoryStream()) { Stream = ms; XHR_GetStatusLine(NativeId, OnBufferCallback); XHR_GetResponseHeaders(NativeId, OnBufferCallback); if (buffer != null && bufferLength > 0) { ms.Write(buffer, 0, bufferLength); } ms.Seek(0L, SeekOrigin.Begin); var internalBuffer = ms.GetBuffer(); string tmp = System.Text.Encoding.UTF8.GetString(internalBuffer); HTTPManager.Logger.Information(this.NativeId + " OnResponse - full response ", tmp, this.Context); SupportedProtocols protocol = CurrentRequest.ProtocolHandler == SupportedProtocols.Unknown ? HTTPProtocolFactory.GetProtocolFromUri(CurrentRequest.CurrentUri) : CurrentRequest.ProtocolHandler; CurrentRequest.Response = HTTPProtocolFactory.Get(protocol, CurrentRequest, ms, CurrentRequest.UseStreaming, false); CurrentRequest.Response.Receive(buffer != null && bufferLength > 0 ? (int)bufferLength : -1, true); KeepAliveHeader keepAlive = null; ConnectionHelper.HandleResponse(this.ToString(), this.CurrentRequest, out resendRequest, out proposedConnectionState, ref keepAlive); } } catch (Exception e) { HTTPManager.Logger.Exception(this.NativeId + " WebGLConnection", "OnResponse", e, this.Context); if (this.ShutdownType == ShutdownTypes.Immediate) { return; } #if !BESTHTTP_DISABLE_CACHING if (this.CurrentRequest.UseStreaming) { HTTPCacheService.DeleteEntity(this.CurrentRequest.CurrentUri); } #endif // Something gone bad, Response must be null! this.CurrentRequest.Response = null; if (!this.CurrentRequest.IsCancellationRequested) { this.CurrentRequest.Exception = e; this.CurrentRequest.State = HTTPRequestStates.Error; } proposedConnectionState = HTTPConnectionStates.Closed; } finally { // Exit ASAP if (this.ShutdownType != ShutdownTypes.Immediate) { if (this.CurrentRequest.IsCancellationRequested) { // we don't know what stage the request is cancelled, we can't safely reuse the tcp channel. proposedConnectionState = HTTPConnectionStates.Closed; this.CurrentRequest.Response = null; this.CurrentRequest.State = this.CurrentRequest.IsTimedOut ? HTTPRequestStates.TimedOut : HTTPRequestStates.Aborted; } else if (resendRequest) { RequestEventHelper.EnqueueRequestEvent(new RequestEventInfo(this.CurrentRequest, RequestEvents.Resend)); } else if (this.CurrentRequest.Response != null && this.CurrentRequest.Response.IsUpgraded) { proposedConnectionState = HTTPConnectionStates.WaitForProtocolShutdown; } else if (this.CurrentRequest.State == HTTPRequestStates.Processing) { if (this.CurrentRequest.Response != null) { this.CurrentRequest.State = HTTPRequestStates.Finished; } else { this.CurrentRequest.Exception = new Exception(string.Format("[{0}] Remote server closed the connection before sending response header! Previous request state: {1}. Connection state: {2}", this.ToString(), this.CurrentRequest.State.ToString(), this.State.ToString())); this.CurrentRequest.State = HTTPRequestStates.Error; proposedConnectionState = HTTPConnectionStates.Closed; } } this.CurrentRequest = null; if (proposedConnectionState == HTTPConnectionStates.Processing) { proposedConnectionState = HTTPConnectionStates.Closed; } ConnectionEventHelper.EnqueueConnectionEvent(new ConnectionEventInfo(this, proposedConnectionState)); } } }
protected override void ThreadFunc() { // XmlHttpRequest setup this.NativeId = XHR_Create(HTTPRequest.MethodNames[(byte)CurrentRequest.MethodType], CurrentRequest.CurrentUri.OriginalString, CurrentRequest.Credentials != null ? CurrentRequest.Credentials.UserName : null, CurrentRequest.Credentials != null ? CurrentRequest.Credentials.Password : null, CurrentRequest.WithCredentials ? 1 : 0); Connections.Add(NativeId, this); CurrentRequest.EnumerateHeaders((header, values) => { if (!header.Equals("Content-Length")) { for (int i = 0; i < values.Count; ++i) { XHR_SetRequestHeader(NativeId, header, values[i]); } } }, /*callBeforeSendCallback:*/ true); XHR_SetResponseHandler(NativeId, WebGLConnection.OnResponse, WebGLConnection.OnError, WebGLConnection.OnTimeout, WebGLConnection.OnAborted); // Setting OnUploadProgress result in an addEventListener("progress", ...) call making the request non-simple. // https://forum.unity.com/threads/best-http-released.200006/page-49#post-3696220 XHR_SetProgressHandler(NativeId, CurrentRequest.OnDownloadProgress == null ? (OnWebGLProgressDelegate)null : WebGLConnection.OnDownloadProgress, CurrentRequest.OnUploadProgress == null ? (OnWebGLProgressDelegate)null : WebGLConnection.OnUploadProgress); XHR_SetTimeout(NativeId, (uint)(CurrentRequest.ConnectTimeout.TotalMilliseconds + CurrentRequest.Timeout.TotalMilliseconds)); byte[] body = CurrentRequest.GetEntityBody(); int length = 0; bool releaseBodyBuffer = false; if (body == null) { var upStreamInfo = CurrentRequest.GetUpStream(); if (upStreamInfo.Stream != null) { var internalBuffer = BufferPool.Get(upStreamInfo.Length > 0 ? upStreamInfo.Length : HTTPRequest.UploadChunkSize, true); using (BufferPoolMemoryStream ms = new BufferPoolMemoryStream(internalBuffer, 0, internalBuffer.Length, true, true, false, true)) { var buffer = BufferPool.Get(HTTPRequest.UploadChunkSize, true); int readCount = -1; while ((readCount = upStreamInfo.Stream.Read(buffer, 0, buffer.Length)) > 0) { ms.Write(buffer, 0, readCount); } BufferPool.Release(buffer); length = (int)ms.Position; body = ms.GetBuffer(); releaseBodyBuffer = true; } } } else { length = body.Length; XHR_Send(NativeId, body, length); } XHR_Send(NativeId, body, length); if (releaseBodyBuffer) { BufferPool.Release(body); } }