public ByteConnection(IByteSender sender, IByteReceiver receiver)
 {
     this.sender = sender;
     this.receiver = receiver;
     receiver.Received.Subscribe(HandleReceived);
     Received = new MessageQueue<byte[]>();
     sender.Error += HandleError;
     receiver.Error += HandleError;
 }
Exemple #2
0
        public async Task Listen(IByteReceiver byteReceiver,
                                 IStreamFactory streamFactory,
                                 string destinationPath = "",
                                 CancellationToken cancellationToken = default)
        {
            var observableScheduler =
                new TaskPoolScheduler(new TaskFactory(cancellationToken)); //Scheduler.Default; //

            await Listen(byteReceiver,
                         streamFactory,
                         observableScheduler,
                         destinationPath,
                         cancellationToken).ConfigureAwait(false);
        }
Exemple #3
0
        public static Socks4Request From(IByteReceiver receiver)
        {
            var structSize = Marshal.SizeOf(typeof(Socks4RequestHeader));

            var requestHeader =
                Socks4RequestHeaderFabric.FromHeader(receiver.Receive(structSize));

            var userName = ReadUserName(receiver);

            var socks4Request = new Socks4Request {
                Header = requestHeader, UserName = userName
            };

            return(socks4Request);
        }
Exemple #4
0
        private static string ReadUserName(IByteReceiver clientToHandle)
        {
            byte readedByte;
            var  username = new List <byte>();

            do
            {
                readedByte = clientToHandle.Receive(1).First();
                if (readedByte == 0)
                {
                    break;
                }
                username.Add(readedByte);
            } while (true);
            return(Encoding.ASCII.GetString(username.ToArray()));
        }
Exemple #5
0
        public async Task Listen(IByteReceiver byteReceiver,
                                 IStreamFactory streamFactory,
                                 IScheduler scheduler,
                                 string destinationPath = "",
                                 CancellationToken cancellationToken = default)
        {
            var basePath = string.IsNullOrWhiteSpace(destinationPath)
                ? ""
                : destinationPath;

            if (basePath != "")
            {
                Directory.CreateDirectory(basePath); //does not throw an exception if it does not exist
            }

            var cancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
            var token = cancellationTokenSource.Token;

            var packetBufferObservable = new Subject <byte[]>();
            var protoMessageObservable = packetBufferObservable
                                         .ObserveOn(scheduler)
                                         .Select(bytes =>
            {
                ProtoMessage message;
                using (var memoryStream = new MemoryStream(bytes))
                {
                    var protoMessage = Serializer.Deserialize <ProtoMessage>(memoryStream);
                    message          = protoMessage;
                }

                return(message);
            });

            var headerCache  = new ConcurrentDictionary <Guid, Header>();
            var payloadCache = new ConcurrentDictionary <Guid, List <ProtoMessage> >();

            var fileWriteRequestSubject = new Subject <FileWriteWrapper>();

            var headerObservable = protoMessageObservable
                                   .Where(message => !string.IsNullOrWhiteSpace(message.FileName) && message.PayloadCount.HasValue);

            var payloadObservable = protoMessageObservable
                                    .Where(message => message.PayloadIndex.HasValue && message.Payload != null);

            string GetFileName(ProtoMessage header)
            {
                return($"{System.DateTime.Now:HH-mm-ss-ffff}.{header.FileName}");
            }

            Header ConvertToHeader(ProtoMessage header)
            {
                return(new Header
                {
                    BroadcastId = header.GetBroadcastId(), FileName = GetFileName(header),
                    PayloadCount = header.PayloadCount.Value, PayloadMaxBytes = header.PayloadMaxSize.Value
                });
            }

            void HandleCachedPayloads(Guid guid, Header header1)
            {
                if (payloadCache.TryRemove(guid, out var payloads))
                {
                    var p = payloads.ToArray(); //todo:figure out why this bombed - List changed exception
                    payloads.Clear();
                    foreach (var payload in p)
                    {
                        fileWriteRequestSubject.OnNext(new FileWriteWrapper()
                        {
                            Header = header1, Payload = payload
                        });
                    }
                }
            }

            headerObservable
            .ObserveOn(scheduler)
            .Subscribe(protoHeader => headerCache.AddOrUpdate(protoHeader.GetBroadcastId(),
                                                              bcid =>
            {
                //todo: decide if header.payloadCount X headerMaxPayloadSize is too much
                var header = ConvertToHeader(protoHeader);
                HandleCachedPayloads(bcid, header);

                return(header);
            },
                                                              (bcid, header) =>
            {
                HandleCachedPayloads(bcid, header);
                return(header);
            })
                       );

            var md5 = MD5.Create();

            bool CheckHash(ProtoMessage payload)
            {
                var expected = payload.Hash;

                if (expected == null || expected.Length == 0)
                {
                    return(false);
                }

                var actual = md5.ComputeHash(payload.Payload);

                if (actual.Length == 0 || expected.Length != actual.Length)
                {
                    return(false);
                }

                for (var index = 0; index < expected.Length; index++)
                {
                    var expectedByte = expected[index];
                    var actualByte   = actual[index];
                    if (expectedByte != actualByte)
                    {
                        return(false);
                    }
                }

                return(true);
            }

            payloadObservable
            .ObserveOn(scheduler)
            .Subscribe(payload =>
            {
                if (!CheckHash(payload))
                {
                    throw new InvalidDataException("Hash check failed");
                    //todo: return error statistics to the caller
                }
                if (headerCache.TryGetValue(payload.GetBroadcastId(), out var header))
                {
                    fileWriteRequestSubject.OnNext(new FileWriteWrapper()
                    {
                        Header = header, Payload = payload
                    });
                }
                else
                {
                    payloadCache.AddOrUpdate(payload.GetBroadcastId(),
                                             bcid =>
                    {
                        var list = new List <ProtoMessage>();
                        list.Add(payload);
                        return(list);
                    },
                                             (bcid, list) =>
                    {
                        list.Add(payload);
                        return(list);
                    });
                }
            });

            var writeCompleteObservable = fileWriteRequestSubject
                                          //.ObserveOn(observableScheduler)
                                          .Select(writeRequest =>
            {
                if (writeRequest.Payload.Payload.Length > writeRequest.Header.PayloadMaxBytes)
                {
                    throw new ArgumentException("Payload exceeds max byte length");
                }
                return(Observable.FromAsync(async() =>
                {
                    var fullPath = Path.Combine(basePath, writeRequest.Header.FileName);
                    using var writer = streamFactory.CreateWriter(fullPath);
                    var writeIndex = writeRequest.Payload.PayloadIndex.Value *
                                     writeRequest.Header.PayloadMaxBytes;
                    await writer.Write(writeIndex, writeRequest.Payload.Payload, token).ConfigureAwait(false);
                    return writeRequest.Header;
                }));
            }).Merge(1);

            var fileWriteObservable = writeCompleteObservable
                                      .ObserveOn(scheduler)
                                      .GroupBy(w => w.BroadcastId);

            const int fileCompleteTimeout   = 10;
            var       fileTimeout           = TimeSpan.FromSeconds(fileCompleteTimeout);
            var       fileStoppedObservable = CreateTimeoutObservable(fileTimeout, fileWriteObservable, scheduler);

            var fileStoppedSub = fileStoppedObservable.Subscribe(header =>
            {
                OnBroadcastEnded(new BroadcastResult(header.BroadcastId, header.FileName));
            });

            token.Register(() => fileStoppedSub.Dispose());

            void OnBytesReceived(object sender, IBytesReceived bytesReceived)
            {
                packetBufferObservable.OnNext(bytesReceived.Bytes);
            }

            byteReceiver.BytesReceived += OnBytesReceived;
            cancellationToken.Register(() => { byteReceiver.BytesReceived -= OnBytesReceived; });
            await byteReceiver.Listen(cancellationToken);
        }