/// <summary> /// Deserialize request number 0 /// </summary> /// <param name="chunks"></param> /// <param name="payload"></param> /// <param name="request"></param> /// <returns></returns> private byte[] DeserializeRequest0(byte[] payload, out HttpTunnelRequestModel request, out int chunks) { // Deserialize data using (var header = new MemoryStream(payload)) using (var reader = new BinaryReader(header)) { var headerLen = reader.ReadInt32(); if (headerLen > payload.Length - 8) { throw new ArgumentException("Bad encoding length"); } var headerBuf = reader.ReadBytes(headerLen); var bufferLen = reader.ReadInt32(); if (bufferLen > payload.Length - (headerLen + 8)) { throw new ArgumentException("Bad encoding length"); } var chunk0 = bufferLen > 0 ? reader.ReadBytes(bufferLen) : null; chunks = reader.ReadInt32(); if (chunks > kMaxNumberOfChunks) { throw new ArgumentException("Bad encoding length"); } request = _serializer.Deserialize <HttpTunnelRequestModel>( headerBuf.Unzip()); return(chunk0); } }
/// <summary> /// Create chunk /// </summary> /// <param name="outer"></param> /// <param name="deviceId"></param> /// <param name="moduleId"></param> /// <param name="requestId"></param> /// <param name="request"></param> /// <param name="timeout"></param> public HttpRequestProcessor(HttpTunnelServer outer, string deviceId, string moduleId, string requestId, HttpTunnelRequestModel request, TimeSpan?timeout) { RequestId = requestId ?? throw new ArgumentNullException(nameof(requestId)); _outer = outer ?? throw new ArgumentNullException(nameof(outer)); _deviceId = deviceId; _moduleId = moduleId; _timeout = timeout ?? TimeSpan.FromSeconds(20); _request = request; _payload = new List <byte[]>(request.Chunks); }
/// <summary> /// Serialize request into buffer chunks /// </summary> /// <param name="tunnelRequest"></param> /// <param name="payload"></param> /// <returns></returns> private List <byte[]> SerializeRequest(HttpTunnelRequestModel tunnelRequest, byte[] payload) { // Serialize data var buffers = new List <byte[]>(); var remainingRoom = 0; using (var header = new MemoryStream()) using (var writer = new BinaryWriter(header)) { // Serialize header (0) var headerBuffer = _outer._serializer.SerializeToBytes(tunnelRequest).ToArray().Zip(); writer.Write(headerBuffer.Length); writer.Write(headerBuffer); // Assume chunk size and payload size also written remainingRoom = _maxSize - (int)(header.Position + 8); if (remainingRoom < 0) { throw new ArgumentException("Header too large to sent"); } // Create chunks from payload if (payload != null && payload.Length > 0) { // Fill remaining room with payload remainingRoom = Math.Min(remainingRoom, payload.Length); writer.Write(remainingRoom); writer.Write(payload, 0, remainingRoom); // Create remaining chunks for (; remainingRoom < payload.Length; remainingRoom += _maxSize) { var length = Math.Min(payload.Length - remainingRoom, _maxSize); var chunk = payload.AsSpan(remainingRoom, length).ToArray(); buffers.Add(chunk); } writer.Write(buffers.Count); } else { writer.Write(0); writer.Write(0); } // Insert header as first buffer buffers.Insert(0, header.ToArray()); } return(buffers); }
/// <summary> /// Create chunk /// </summary> /// <param name="outer"></param> /// <param name="deviceId"></param> /// <param name="moduleId"></param> /// <param name="requestId"></param> /// <param name="request"></param> /// <param name="chunks"></param> /// <param name="chunk0"></param> /// <param name="timeout"></param> public HttpRequestProcessor(HttpTunnelServer outer, string deviceId, string moduleId, string requestId, HttpTunnelRequestModel request, int chunks, byte[] chunk0, TimeSpan?timeout) { RequestId = requestId ?? throw new ArgumentNullException(nameof(requestId)); _outer = outer ?? throw new ArgumentNullException(nameof(outer)); _deviceId = deviceId; _moduleId = moduleId; _timeout = timeout ?? TimeSpan.FromSeconds(20); _request = request; _chunks = chunks + 1; _payload = new byte[_chunks][]; _payload[0] = chunk0 ?? new byte[0]; }
/// <inheritdoc/> protected override async Task <HttpResponseMessage> SendAsync( HttpRequestMessage request, CancellationToken ct) { if (request.Headers.TryGetValues(HttpHeader.UdsPath, out var paths)) { // On edge we must still support unix sockets to talk to edgelet return(await base.SendAsync(request, ct)); } // Create tunnel request var tunnelRequest = new HttpTunnelRequestModel { ResourceId = null, // TODO - fill in somehow from outer handler Uri = request.RequestUri.ToString(), RequestHeaders = request.Headers? .ToDictionary(h => h.Key, h => h.Value.ToList()), Method = request.Method.ToString() }; // Get content byte[] payload = null; if (request.Content != null) { payload = await request.Content.ReadAsByteArrayAsync(); payload = payload.Zip(); tunnelRequest.ContentHeaders = request.Content.Headers? .ToDictionary(h => h.Key, h => h.Value.ToList()); } // Serialize var buffers = SerializeRequest(tunnelRequest, payload); var requestId = Guid.NewGuid().ToString(); var requestTask = new RequestTask(kDefaultTimeout, ct); if (!_outer._outstanding.TryAdd(requestId, requestTask)) { throw new InvalidOperationException("Could not add completion."); } // Send events for (var messageId = 0; messageId < buffers.Count; messageId++) { await _outer._client.SendEventAsync(buffers[messageId], requestId + "_" + messageId.ToString(), HttpTunnelRequestModel.SchemaName, ContentMimeType.Binary); } // Wait for completion try { return(await requestTask.Completion.Task); } catch { // If thrown remove and dispose first if (_outer._outstanding.TryRemove(requestId, out requestTask)) { requestTask.Dispose(); } throw; } }