/// <summary> /// Copy properties from remote web orchestrator /// </summary> public void SetValues(WebServerOrchestrator webServerOrchestrator) { this.Options = webServerOrchestrator.Options; this.Setup = webServerOrchestrator.Setup; this.ConnectionString = webServerOrchestrator.Provider.ConnectionString; this.ProviderType = webServerOrchestrator.Provider.GetType(); }
/// <summary> /// Create a new web server orchestrator /// </summary> public WebServerOrchestrator CreateWebServerOrchestrator() { // Create provicer var provider = (CoreProvider)Activator.CreateInstance(this.ProviderType); provider.ConnectionString = this.ConnectionString; // Create orchestrator var webServerOrchestrator = new WebServerOrchestrator(provider, this.Options, this.Setup); return(webServerOrchestrator); }
private IConverter GetClientConverter(string cs, WebServerOrchestrator serverOrchestrator) { try { if (string.IsNullOrEmpty(cs)) { return(null); } var clientConverter = serverOrchestrator.Options.Converters.First(c => c.Key == cs); return(clientConverter); } catch { throw new HttpConverterNotConfiguredException(serverOrchestrator.Options.Converters.Select(sf => sf.Key)); } }
/// <summary> /// Call this method to handle requests on the server, sent by the client /// </summary> public async Task HandleRequestAsync(HttpContext context, Action <RemoteOrchestrator> action, CancellationToken cancellationToken) { var httpRequest = context.Request; var httpResponse = context.Response; var serAndsizeString = string.Empty; var converter = string.Empty; if (httpRequest.Method.ToLowerInvariant() == "get") { await this.WriteHelloAsync(context, cancellationToken); return; } // Get the serialization and batch size format if (TryGetHeaderValue(context.Request.Headers, "dotmim-sync-serialization-format", out var vs)) { serAndsizeString = vs.ToLowerInvariant(); } // Get the serialization and batch size format if (TryGetHeaderValue(context.Request.Headers, "dotmim-sync-converter", out var cs)) { converter = cs.ToLowerInvariant(); } if (!TryGetHeaderValue(context.Request.Headers, "dotmim-sync-session-id", out var sessionId)) { throw new HttpHeaderMissingExceptiopn("dotmim-sync-session-id"); } if (!TryGetHeaderValue(context.Request.Headers, "dotmim-sync-step", out string iStep)) { throw new HttpHeaderMissingExceptiopn("dotmim-sync-step"); } var step = (HttpStep)Convert.ToInt32(iStep); var readableStream = new MemoryStream(); try { // Copty stream to a readable and seekable stream // HttpRequest.Body is a HttpRequestStream that is readable but can't be Seek await httpRequest.Body.CopyToAsync(readableStream); httpRequest.Body.Close(); httpRequest.Body.Dispose(); // if Hash is present in header, check hash if (TryGetHeaderValue(context.Request.Headers, "dotmim-sync-hash", out string hashStringRequest)) { HashAlgorithm.SHA256.EnsureHash(readableStream, hashStringRequest); } // Create a web server remote orchestrator based on cache values if (this.WebServerOrchestrator == null) { this.WebServerOrchestrator = this.WebServerProperties.CreateWebServerOrchestrator(); } // try get schema from cache if (this.Cache.TryGetValue <SyncSet>(WebServerProperties.Key, out var cachedSchema)) { this.WebServerOrchestrator.Schema = cachedSchema; } // try get session cache from current sessionId if (!this.Cache.TryGetValue <SessionCache>(sessionId, out var sessionCache)) { sessionCache = new SessionCache(); } // action from user if available action?.Invoke(this.WebServerOrchestrator); // Get the serializer and batchsize (var clientBatchSize, var clientSerializerFactory) = GetClientSerializer(serAndsizeString, this.WebServerOrchestrator); // Get converter used by client // Can be null var clientConverter = GetClientConverter(converter, this.WebServerOrchestrator); this.WebServerOrchestrator.ClientConverter = clientConverter; byte[] binaryData = null; switch (step) { case HttpStep.EnsureScopes: var m1 = await clientSerializerFactory.GetSerializer <HttpMessageEnsureScopesRequest>().DeserializeAsync(readableStream); var s1 = await this.WebServerOrchestrator.EnsureScopeAsync(m1, sessionCache, cancellationToken).ConfigureAwait(false); binaryData = await clientSerializerFactory.GetSerializer <HttpMessageEnsureScopesResponse>().SerializeAsync(s1); await this.WebServerOrchestrator.Provider.InterceptAsync(new HttpMessageEnsureScopesResponseArgs(binaryData)).ConfigureAwait(false); break; case HttpStep.SendChanges: var m2 = await clientSerializerFactory.GetSerializer <HttpMessageSendChangesRequest>().DeserializeAsync(readableStream); var s2 = await this.WebServerOrchestrator.ApplyThenGetChangesAsync(m2, sessionCache, clientBatchSize, cancellationToken).ConfigureAwait(false); binaryData = await clientSerializerFactory.GetSerializer <HttpMessageSendChangesResponse>().SerializeAsync(s2); if (s2.Changes != null && s2.Changes.HasRows) { await this.WebServerOrchestrator.Provider.InterceptAsync(new HttpMessageSendChangesResponseArgs(binaryData)).ConfigureAwait(false); } break; case HttpStep.GetChanges: var m3 = await clientSerializerFactory.GetSerializer <HttpMessageGetMoreChangesRequest>().DeserializeAsync(readableStream); var s3 = await this.WebServerOrchestrator.GetMoreChangesAsync(m3, sessionCache, cancellationToken); binaryData = await clientSerializerFactory.GetSerializer <HttpMessageSendChangesResponse>().SerializeAsync(s3); if (s3.Changes != null && s3.Changes.HasRows) { await this.WebServerOrchestrator.Provider.InterceptAsync(new HttpMessageSendChangesResponseArgs(binaryData)).ConfigureAwait(false); } break; case HttpStep.GetSnapshot: var m4 = await clientSerializerFactory.GetSerializer <HttpMessageSendChangesRequest>().DeserializeAsync(readableStream); var s4 = await this.WebServerOrchestrator.GetSnapshotAsync(m4, sessionCache, cancellationToken); binaryData = await clientSerializerFactory.GetSerializer <HttpMessageSendChangesResponse>().SerializeAsync(s4); if (s4.Changes != null && s4.Changes.HasRows) { await this.WebServerOrchestrator.Provider.InterceptAsync(new HttpMessageSendChangesResponseArgs(binaryData)).ConfigureAwait(false); } break; } // Save schema to cache with a sliding expiration this.Cache.Set(WebServerProperties.Key, this.WebServerOrchestrator.Schema, this.WebServerProperties.Options.GetServerCacheOptions()); // Save session client to cache with a sliding expiration this.Cache.Set(sessionId, sessionCache, this.WebServerProperties.Options.GetClientCacheOptions()); // Adding the serialization format used and session id httpResponse.Headers.Add("dotmim-sync-session-id", sessionId.ToString()); httpResponse.Headers.Add("dotmim-sync-serialization-format", clientSerializerFactory.Key); // calculate hash var hash = HashAlgorithm.SHA256.Create(binaryData); var hashString = Convert.ToBase64String(hash); // Add hash to header httpResponse.Headers.Add("dotmim-sync-hash", hashString); // data to send back, as the response byte[] data = this.EnsureCompression(httpRequest, httpResponse, binaryData); await httpResponse.Body.WriteAsync(data, 0, data.Length).ConfigureAwait(false); } catch (Exception ex) { await WriteExceptionAsync(httpResponse, ex); } finally { readableStream.Close(); readableStream.Dispose(); } }
/// <summary> /// Get an instance of SyncMemoryProvider depending on session id. If the entry for session id does not exists, create a new one /// </summary> //private static WebServerOrchestrator GetCachedOrchestrator(HttpContext context, string syncSessionId) //{ // WebServerOrchestrator remoteOrchestrator; // var cache = context.RequestServices.GetService<IMemoryCache>(); // if (cache == null) // throw new HttpCacheNotConfiguredException(); // if (string.IsNullOrWhiteSpace(syncSessionId)) // throw new ArgumentNullException(nameof(syncSessionId)); // // get the sync provider associated with the session id // remoteOrchestrator = cache.Get(syncSessionId) as WebServerOrchestrator; // return remoteOrchestrator; //} /// <summary> /// Add a new instance of SyncMemoryProvider, created by DI /// </summary> /// <returns></returns> //private static WebServerOrchestrator AddNewOrchestratorToCacheFromDI(HttpContext context, string syncSessionId) //{ // var cache = context.RequestServices.GetService<IMemoryCache>(); // if (cache == null) // throw new HttpCacheNotConfiguredException(); // var remoteOrchestrator = DependencyInjection.GetNewOrchestrator(); // cache.Set(syncSessionId, remoteOrchestrator, TimeSpan.FromHours(1)); // return remoteOrchestrator; //} //private static WebServerOrchestrator AddNewOrchestratorToCache(HttpContext context, CoreProvider provider, SyncSetup setup, string sessionId, WebServerOptions options = null) //{ // WebServerOrchestrator remoteOrchestrator; // var cache = context.RequestServices.GetService<IMemoryCache>(); // if (cache == null) // throw new HttpCacheNotConfiguredException(); // remoteOrchestrator = new WebServerOrchestrator(provider, options, setup); // cache.Set(sessionId, remoteOrchestrator, TimeSpan.FromHours(1)); // return remoteOrchestrator; //} //private static WebServerOrchestrator AddNewWebServerOrchestratorToCache(HttpContext context, WebServerOrchestrator webServerOrchestrator, string sessionId) //{ // var cache = context.RequestServices.GetService<IMemoryCache>(); // if (cache == null) // throw new HttpCacheNotConfiguredException(); // cache.Set(sessionId, webServerOrchestrator, TimeSpan.FromHours(1)); // return webServerOrchestrator; //} private (int clientBatchSize, ISerializerFactory clientSerializer) GetClientSerializer(string serAndsizeString, WebServerOrchestrator serverOrchestrator) { try { if (string.IsNullOrEmpty(serAndsizeString)) { throw new Exception(); } var serAndsize = JsonConvert.DeserializeAnonymousType(serAndsizeString, new { f = "", s = 0 }); var clientBatchSize = serAndsize.s; var clientSerializerFactory = serverOrchestrator.Options.Serializers[serAndsize.f]; return(clientBatchSize, clientSerializerFactory); } catch { throw new HttpSerializerNotConfiguredException(serverOrchestrator.Options.Serializers.Select(sf => sf.Key)); } }
/// <summary> /// Call this method to handle requests on the server, sent by the client /// </summary> public async Task HandleRequestAsync(HttpContext context, Action <RemoteOrchestrator> action, CancellationToken cancellationToken) { var httpRequest = context.Request; var httpResponse = context.Response; var streamArray = GetBody(httpRequest); var serAndsizeString = string.Empty; var converter = string.Empty; // Get the serialization and batch size format if (TryGetHeaderValue(context.Request.Headers, "dotmim-sync-serialization-format", out var vs)) { serAndsizeString = vs.ToLowerInvariant(); } // Get the serialization and batch size format if (TryGetHeaderValue(context.Request.Headers, "dotmim-sync-converter", out var cs)) { converter = cs.ToLowerInvariant(); } if (!TryGetHeaderValue(context.Request.Headers, "dotmim-sync-session-id", out var sessionId)) { throw new HttpHeaderMissingExceptiopn("dotmim-sync-session-id"); } if (!TryGetHeaderValue(context.Request.Headers, "dotmim-sync-step", out string iStep)) { throw new HttpHeaderMissingExceptiopn("dotmim-sync-step"); } var step = (HttpStep)Convert.ToInt32(iStep); try { // Create a web server remote orchestrator based on cache values if (this.WebServerOrchestrator == null) { this.WebServerOrchestrator = this.WebServerProperties.CreateWebServerOrchestrator(); } // try get schema from cache if (this.Cache.TryGetValue <SyncSet>(WebServerProperties.Key, out var cachedSchema)) { this.WebServerOrchestrator.Schema = cachedSchema; } // try get session cache from current sessionId if (!this.Cache.TryGetValue <SessionCache>(sessionId, out var sessionCache)) { sessionCache = new SessionCache(); } // action from user if available action?.Invoke(this.WebServerOrchestrator); // Get the serializer and batchsize (var clientBatchSize, var clientSerializerFactory) = GetClientSerializer(serAndsizeString, this.WebServerOrchestrator); // Get converter used by client // Can be null var clientConverter = GetClientConverter(converter, this.WebServerOrchestrator); this.WebServerOrchestrator.ClientConverter = clientConverter; byte[] binaryData = null; switch (step) { case HttpStep.EnsureScopes: var m1 = clientSerializerFactory.GetSerializer <HttpMessageEnsureScopesRequest>().Deserialize(streamArray); var s1 = await this.WebServerOrchestrator.EnsureScopeAsync(m1, sessionCache, cancellationToken).ConfigureAwait(false); binaryData = clientSerializerFactory.GetSerializer <HttpMessageEnsureScopesResponse>().Serialize(s1); await this.WebServerOrchestrator.Provider.InterceptAsync(new HttpMessageEnsureScopesResponseArgs(binaryData)).ConfigureAwait(false); break; case HttpStep.SendChanges: var m2 = clientSerializerFactory.GetSerializer <HttpMessageSendChangesRequest>().Deserialize(streamArray); var s2 = await this.WebServerOrchestrator.ApplyThenGetChangesAsync(m2, sessionCache, clientBatchSize, cancellationToken).ConfigureAwait(false); binaryData = clientSerializerFactory.GetSerializer <HttpMessageSendChangesResponse>().Serialize(s2); if (s2.Changes != null && s2.Changes.HasRows) { await this.WebServerOrchestrator.Provider.InterceptAsync(new HttpMessageSendChangesResponseArgs(binaryData)).ConfigureAwait(false); } break; case HttpStep.GetChanges: var m3 = clientSerializerFactory.GetSerializer <HttpMessageGetMoreChangesRequest>().Deserialize(streamArray); var s3 = this.WebServerOrchestrator.GetMoreChanges(m3, sessionCache, cancellationToken); binaryData = clientSerializerFactory.GetSerializer <HttpMessageSendChangesResponse>().Serialize(s3); if (s3.Changes != null && s3.Changes.HasRows) { await this.WebServerOrchestrator.Provider.InterceptAsync(new HttpMessageSendChangesResponseArgs(binaryData)).ConfigureAwait(false); } break; case HttpStep.GetSnapshot: var m4 = clientSerializerFactory.GetSerializer <HttpMessageSendChangesRequest>().Deserialize(streamArray); var s4 = await this.WebServerOrchestrator.GetSnapshotAsync(m4, sessionCache, cancellationToken); binaryData = clientSerializerFactory.GetSerializer <HttpMessageSendChangesResponse>().Serialize(s4); if (s4.Changes != null && s4.Changes.HasRows) { await this.WebServerOrchestrator.Provider.InterceptAsync(new HttpMessageSendChangesResponseArgs(binaryData)).ConfigureAwait(false); } break; } // Save schema to cache with a sliding expiration this.Cache.Set(WebServerProperties.Key, this.WebServerOrchestrator.Schema, this.WebServerProperties.Options.GetServerCacheOptions()); // Save session client to cache with a sliding expiration this.Cache.Set(sessionId, sessionCache, this.WebServerProperties.Options.GetClientCacheOptions()); // Adding the serialization format used and session id httpResponse.Headers.Add("dotmim-sync-session-id", sessionId.ToString()); httpResponse.Headers.Add("dotmim-sync-serialization-format", clientSerializerFactory.Key); // data to send back, as the response byte[] data = this.EnsureCompression(httpRequest, httpResponse, binaryData); await this.GetBody(httpResponse).WriteAsync(data, 0, data.Length).ConfigureAwait(false); } catch (Exception ex) { await WriteExceptionAsync(httpResponse, ex); } finally { //if (httpMessage != null && httpMessage.Step == HttpStep.EndSession) // Cleanup(context.RequestServices.GetService(typeof(IMemoryCache)), syncSessionId); } }