/// <summary> /// Create an send-side HybridConnectionStream, send N bytes to it, receive N bytes response from it, /// then close the HybridConnectionStream. /// </summary> async Task RunEchoClientAsync(HybridConnectionStream clientStream, int byteCount) { var cancelSource = new CancellationTokenSource(TimeSpan.FromSeconds(20)); try { byte[] sendBuffer = this.CreateBuffer(byteCount, new[] { (byte)(byteCount % byte.MaxValue) }); await clientStream.WriteAsync(sendBuffer, 0, sendBuffer.Length, cancelSource.Token); byte[] readBuffer = new byte[sendBuffer.Length + 10]; int bytesRead = await clientStream.ReadAsync(readBuffer, 0, readBuffer.Length, cancelSource.Token); Assert.Equal(sendBuffer.Length, bytesRead); await clientStream.CloseAsync(cancelSource.Token); } catch (Exception e) { TestUtility.Log($"[byteCount={byteCount}] {e.GetType().Name}: {e.Message}"); await clientStream.CloseAsync(cancelSource.Token); throw; } finally { cancelSource.Dispose(); } }
static async void RunConnectionPump(HybridConnectionStream stream, string connectionName, bool echoBytes = false) { try { var buffer = new byte[256]; while (true) { int read = await stream.ReadAsync(buffer, 0, buffer.Length); RelayTraceSource.TraceVerbose($"{connectionName} received {read} bytes: \"{Encoding.UTF8.GetString(buffer, 0, read)}\""); if (read == 0) { using (var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10))) { RelayTraceSource.TraceVerbose($"{connectionName} closing"); await stream.CloseAsync(cts.Token); RelayTraceSource.TraceInfo($"{connectionName} closed"); } return; } if (echoBytes) { await stream.WriteAsync(buffer, 0, read); RelayTraceSource.TraceVerbose($"{connectionName} echoed {read} bytes"); } } } catch (Exception exception) { RelayTraceSource.TraceException(exception, nameof(RunConnectionPump)); } }
private Task OnNewClient(Guid streamId, HybridConnectionStream stream, CancellationToken token) { return(Task.Factory.StartNew(async() => { var buffer = new byte[65536]; while (true) { var id = Guid.Empty; int remotePort = 0; var count = 0; Int32 controlCommand = ControlCommands.Forward; Int32 frameSize = 0; Int32 bytesRead = 0; var memStream = new MemoryStream(); // read control command count = await stream.ReadAsync(buffer, 0, sizeof(Int32)); if (0 == count || token.IsCancellationRequested) { break; } controlCommand = BitConverter.ToInt32(new ArraySegment <byte>(buffer, 0, sizeof(Int32)).ToArray(), 0); if (ControlCommands.Forward == controlCommand) { // read forwarding preamble count = await stream.ReadAsync(buffer, 0, 16 + sizeof(Int32) + sizeof(Int32)); if (0 == count || token.IsCancellationRequested) { break; } id = new Guid(new ArraySegment <byte>(buffer, 0, 16).ToArray()); remotePort = BitConverter.ToInt32(new ArraySegment <byte>(buffer, 16, sizeof(Int32)).ToArray(), 0); frameSize = BitConverter.ToInt32(new ArraySegment <byte>(buffer, 16 + sizeof(Int32), sizeof(Int32)).ToArray(), 0); if (!_validPorts.Contains(remotePort)) { _logger.LogError($"Connection on port {remotePort} not allowed for hybrid connectio {_connectionName}."); stream.Close(); } while (true) { var length = frameSize - bytesRead > buffer.Length ? buffer.Length : frameSize - bytesRead; count = await stream.ReadAsync(buffer, 0, length); if (0 == count || token.IsCancellationRequested) { break; } bytesRead += count; await memStream.WriteAsync(buffer, 0, count); if (bytesRead == frameSize) { await _demultiplexer.Demultiplex(streamId, id, remotePort, memStream.ToArray()); break; } } if (0 == count || token.IsCancellationRequested) { break; } } else { count = await stream.ReadAsync(buffer, 0, 16); if (0 == count || token.IsCancellationRequested) { break; } id = new Guid(new ArraySegment <byte>(buffer, 0, 16).ToArray()); await _demultiplexer.ClientConnectionClosed(streamId, id); } } lock (_syncRoot) { _hybridConnectionStreams.Remove(streamId); } await stream.ShutdownAsync(_cts.Token); })); }
async Task HandleRelayConnectionAsync(HybridConnectionStream hybridConnectionStream) { try { using (hybridConnectionStream) { string portName = null; hybridConnectionStream.WriteTimeout = 60000; // read and write 4-byte header // we don't do anything with this version preamble just yet; it really // is insurance for when we might have to break protocol. var versionPreamble = new byte[3]; for (int read = 0; read < versionPreamble.Length;) { var r = await hybridConnectionStream.ReadAsync(versionPreamble, read, versionPreamble.Length - read); if (r == 0) { await hybridConnectionStream.ShutdownAsync(CancellationToken.None); return; } read += r; } // version 1.0 and stream mode (0) if (versionPreamble[0] == 1 && versionPreamble[1] == 0 && (versionPreamble[2] == 0 || versionPreamble[2] == 1)) { // For version 1.0, the version preamble is followed by a single byte // length indicator and then that number of bytes with of UTF-8 encoded // port-name string. var portNameBuffer = new byte[256]; var r = await hybridConnectionStream.ReadAsync(portNameBuffer, 0, 1); if (r == 0) { await hybridConnectionStream.ShutdownAsync(CancellationToken.None); return; } for (int read = 0; read < portNameBuffer[0];) { r = await hybridConnectionStream.ReadAsync(portNameBuffer, read + 1, portNameBuffer[0] - read); if (r == 0) { await hybridConnectionStream.ShutdownAsync(CancellationToken.None); return; } read += r; } portName = Encoding.UTF8.GetString(portNameBuffer, 1, portNameBuffer[0]); } else { // if we don't understand the version, we write a 0.0 version preamble back and shut down the connection versionPreamble = new byte[] { 0, 0, 0 }; await hybridConnectionStream.WriteAsync(versionPreamble, 0, versionPreamble.Length); await CloseConnection(hybridConnectionStream); return; } if (remoteForwarders.TryGetValue(portName, out var forwarder)) { if (forwarder is UdpRemoteForwarder && versionPreamble[2] != 1) { // bad datagram indicator versionPreamble = new byte[] { 0, 0, 1 }; await hybridConnectionStream.WriteAsync(versionPreamble, 0, versionPreamble.Length); await CloseConnection(hybridConnectionStream); return; } else if (!(forwarder is UdpRemoteForwarder) && versionPreamble[2] == 1) { // mismatch versionPreamble = new byte[] { 0, 0, 255 }; await hybridConnectionStream.WriteAsync(versionPreamble, 0, versionPreamble.Length); await CloseConnection(hybridConnectionStream); return; } // write out 1.0 and handle the stream versionPreamble = new byte[] { 1, 0, versionPreamble[2] }; await hybridConnectionStream.WriteAsync(versionPreamble, 0, versionPreamble.Length); await forwarder.HandleConnectionAsync(hybridConnectionStream); await CloseConnection(hybridConnectionStream); } else { await CloseConnection(hybridConnectionStream); } } } catch (Exception e) { BridgeEventSource.Log.HandledExceptionAsWarning(activity, e); } }