private async Task HandleConfigurationRequestAsync(RequestContext ctx, IOnPremiseTargetRequestInternal request) { _logger?.Debug("Received configuration request from RelayServer. request-id={RequestId}", request.RequestId); using (var sr = new JsonTextReader(new StreamReader(new MemoryStream(request.Body)))) { var config = JsonSerializer.Deserialize <ServerSideLinkConfiguration>(sr); TokenRefreshWindow = config.TokenRefreshWindow ?? TokenRefreshWindow; HeartbeatInterval = config.HeartbeatInterval ?? HeartbeatInterval; _minReconnectWaitTime = config.ReconnectMinWaitTime ?? _minReconnectWaitTime; _maxReconnectWaitTime = config.ReconnectMaxWaitTime ?? _maxReconnectWaitTime; _logSensitiveData = config.LogSensitiveData ?? _logSensitiveData; AbsoluteConnectionLifetime = config.AbsoluteConnectionLifetime ?? AbsoluteConnectionLifetime; SlidingConnectionLifetime = config.SlidingConnectionLifetime ?? SlidingConnectionLifetime; _logger?.Debug("Applied configuration from RelayServer. configuration={@Configuration}", config); } var response = BuildDefaultResponse(request.OriginId, request.RequestId); try { // No cancellation token here, to not cancel sending of an already fetched response await PostResponseAsync(ctx, response, CancellationToken.None).ConfigureAwait(false); } catch (Exception ex) { _logger?.Error(ex, "Error during posting configuration response to relay. request-id={RequestId}", request.RequestId); } }
private async Task RequestLocalTargetAsync(RequestContext ctx, string key, IOnPremiseTargetConnector connector, IOnPremiseTargetRequestInternal request, CancellationToken cancellationToken) { var uri = new Uri(new Uri("http://local-target/"), request.Url); _logger?.Debug("Relaying request to local target. request-id={RequestId}, request-url={RequestUrl}", request.RequestId, _logSensitiveData ? uri.PathAndQuery : uri.AbsolutePath); var url = (request.Url.Length > key.Length) ? request.Url.Substring(key.Length + 1) : String.Empty; if (request.Body != null) { if (request.Body.Length == 0) { // a length of 0 indicates that there is a larger body available on the server _logger?.Verbose("Requesting body. request-id={RequestId}", request.RequestId); // request the body from the RelayServer (because SignalR cannot handle large messages) var webResponse = await GetToRelayAsync("/request/" + request.RequestId, cancellationToken).ConfigureAwait(false); request.Stream = await webResponse.Content.ReadAsStreamAsync().ConfigureAwait(false); // this stream should not be disposed (owned by the Framework) } else { // the body is small enough to be used directly request.Stream = new MemoryStream(request.Body); } } else { // no body available (e.g. GET request) request.Stream = Stream.Null; } if (request.Stream.CanSeek && request.Stream.Position != 0) { request.Stream.Position = 0; } using (var response = await GetResponseFromLocalTargetAsync(connector, url, request, RelayedRequestHeader).ConfigureAwait(false)) { _logger?.Debug("Sending response. request-id={RequestId}", request.RequestId); try { // transfer the result to the RelayServer (need POST here, because SignalR does not handle large messages) await PostResponseAsync(ctx, response, cancellationToken).ConfigureAwait(false); } catch (Exception ex) { _logger?.Error(ex, "Error during posting local target response to relay. request-id={RequestId}", request.RequestId); } } }
protected override async void OnMessageReceived(JToken message) { IOnPremiseTargetRequestInternal request = null; var startDate = DateTime.UtcNow; var ctx = new RequestContext(); try { _logger?.Verbose("Received message from server. connection-id={ConnectionId}, message={Message}", ConnectionId, message); request = message.ToObject <OnPremiseTargetRequest>(); request.ConnectionId = ConnectionId; try { if (request.IsConfigurationRequest) { await HandleConfigurationRequestAsync(ctx, request).ConfigureAwait(false); return; } if (request.IsPingRequest) { await HandlePingRequestAsync(ctx, request).ConfigureAwait(false); return; } if (request.IsHeartbeatRequest) { await HandleHeartbeatRequestAsync(ctx, request).ConfigureAwait(false); return; } } finally { await AcknowledgeRequestAsync(request).ConfigureAwait(false); } var key = request.Url.Split('/').FirstOrDefault(); if (key != null) { if (_connectors.TryGetValue(key, out var connector)) { _logger?.Verbose("Found on-premise target and sending request. request-id={RequestId}, on-premise-key={OnPremiseTargetKey}", request.RequestId, key); LastActivity = DateTime.UtcNow; await RequestLocalTargetAsync(ctx, key, connector, request, CancellationToken.None).ConfigureAwait(false); // TODO no cancellation token here? return; } } _logger?.Information("No connector found for local server for request {RequestId} and url {RequestUrl}", request.RequestId, request.Url); } catch (Exception ex) { _logger?.Error(ex, "Error during handling received message. connection-id={ConnectionId}, message={Message}", ConnectionId, message); } finally { if (!ctx.IsRelayServerNotified && request != null) { _logger?.Warning("Unhandled request. connection-id={ConnectionId}, request-id={RequestId}, message={message}", ConnectionId, request.RequestId, message); var response = new OnPremiseTargetResponse() { RequestStarted = startDate, RequestFinished = DateTime.UtcNow, StatusCode = HttpStatusCode.NotFound, OriginId = request.OriginId, RequestId = request.RequestId, HttpHeaders = new Dictionary <string, string>(), }; try { // No cancellation token here, to not cancel sending of an already fetched response await PostResponseAsync(ctx, response, CancellationToken.None).ConfigureAwait(false); } catch (Exception ex) { _logger?.Error(ex, "Error during posting unhandled request response to relay. request-id={RequestId}", request.RequestId); } } } }