public TransactionQueue(string serverName,
                                string connectionString,
                                SigningKey key,
                                IConfigurationSection clientConfig
                                )
        {
            _client                     = new FederationClient(serverName, key, clientConfig);
            _userPresence               = new Dictionary <string, PresenceState>();
            _destOngoingTrans           = new Dictionary <string, Task>();
            _destPendingTransactions    = new Dictionary <string, Transaction>();
            _destLastDeviceMsgStreamId  = new Dictionary <string, long>();
            _destLastDeviceListStreamId = new Dictionary <string, long>();
            _presenceProcessing         = Task.CompletedTask;
            _eventsProcessing           = Task.CompletedTask;
            _serverName                 = serverName;
            _txnId         = (int)(DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds;
            _connString    = connectionString;
            _lastEventPoke = -1;
            _signingKey    = key;
            _backoff       = new Backoff();
            var txConcurrency = clientConfig.GetValue <int>("maxConcurrency");

            if (txConcurrency == 0)
            {
                txConcurrency = 100;
            }

            concurrentTransactionLock = new SemaphoreSlim(txConcurrency, txConcurrency);
        }
Exemple #2
0
        public FederationClient(string serverName, SigningKey key, IConfigurationSection config)
        {
            origin = serverName;

            client = new HttpClient(new HttpClientHandler
            {
                ServerCertificateCustomValidationCallback = ServerCertificateValidationCallback
            });

            client.Timeout = TimeSpan.FromSeconds(30);

            this.key        = key;
            destinationUris = new Dictionary <string, Uri>();
            hostResolver    = new HostResolver(config.GetValue <bool>("defaultToSecurePort") ? 8448 : 8008);
            allowSelfSigned = config.GetValue <bool>("allowSelfSigned");
        }
        public async Task Start()
        {
            log.Information("Starting FederationWorker");
            _synapseReplication             = new SynapseReplication();
            _synapseReplication.ClientName  = "NetCoreFederationWorker";
            _synapseReplication.ServerName += Replication_ServerName;

            var synapseConfig = _config.GetSection("Synapse");

            key = SigningKey.ReadFromFile(synapseConfig.GetValue <string>("signingKeyPath"));
            connectionString = _config.GetConnectionString("synapse");
            _presenceEnabled = synapseConfig.GetValue("presenceEnabled", true);

            await _synapseReplication.Connect(synapseConfig.GetValue <string>("replicationHost"),
                                              synapseConfig.GetValue <int>("replicationPort"));

            _fedStream          = _synapseReplication.BindStream <FederationStreamRow>();
            _fedStream.DataRow += OnFederationRow;
            _eventStream        = _synapseReplication.BindStream <EventStreamRow>();
            _eventStream.PositionUpdate /**/ += OnEventPositionUpdate;
            _stream_position = await GetFederationPos("federation");
        }
Exemple #4
0
        public async Task SendTransaction(Transaction transaction)
        {
            var record = await hostResolver.GetHostRecord(transaction.destination);

            var uri = new UriBuilder(record.GetUri())
            {
                Path   = $"/_matrix/federation/v1/send/{transaction.transaction_id}/",
                Scheme = "https"
            };

            var msg = new HttpRequestMessage
            {
                Method     = HttpMethod.Put,
                RequestUri = uri.Uri
            };

            msg.Headers.Host = record.GetHost();

            var body = SigningKey.SortPropertiesAlphabetically(JObject.FromObject(transaction));

            SignRequest(msg, transaction.destination, body);
            var json = JsonConvert.SerializeObject(body, Formatting.None);

            var content = new StringContent(json,
                                            Encoding.UTF8,
                                            "application/json");

            msg.Content = content;
            HttpResponseMessage resp;
            var sw = new Stopwatch();

            try
            {
                log.Information("[TX] {destination} PUT {uri} Host={hostHeader} PDUs={pduCount} EDUs={eduCount}"
                                , transaction.destination, uri, msg.Headers.Host, transaction.pdus.Count,
                                transaction.edus.Count);

                sw.Start();
                resp = await client.SendAsync(msg);
            }
            catch (HttpRequestException ex)
            {
                //TODO: This is probably a little extreme.
                log.Warning("Failed to reach {destination} {message}", transaction.destination, ex.Message);
                destinationUris.Remove(transaction.destination);
                throw;
            }
            finally
            {
                sw.Stop();
            }

            log.Information("[TX] {destination} Response: {statusCode} {timeTaken}ms",
                            transaction.destination, resp.StatusCode, sw.ElapsedMilliseconds);

            if (resp.IsSuccessStatusCode)
            {
                return;
            }

            if (resp.StatusCode == HttpStatusCode.NotFound)
            {
                destinationUris.Remove(transaction.destination);
            }
            // TODO: Should we drop well known for other reasons?

            var error = await resp.Content.ReadAsStringAsync();

            var err = JObject.Parse(error);

            if (resp.StatusCode == HttpStatusCode.Unauthorized)
            {
                try
                {
                    var errCode     = (string)err["errcode"];
                    var errorString = (string)err["error"];

                    if (errCode == "M_UNAUTHORIZED" && errorString.StartsWith("Invalid signature"))
                    {
                        log.Information("Got invalid signature, debug info:");

                        log.Information("Auth: {auth}\nBody: {body}",
                                        msg.Headers.Authorization.Parameter,
                                        body.ToString(Formatting.Indented));
                    }
                }
                catch
                {
                    // ignored
                }
            }

            throw new TransactionFailureException(transaction.destination, resp.StatusCode, err);
        }