/// <summary> /// Continue a promise chain by acting on the previous promise's result and returning a new promise /// </summary> /// <param name="promise">Previous promise</param> /// <param name="callback">Code to execute on resolution of the promise and returning a new promise</param> /// <returns>A new promise</returns> public static IPromise <S> Continue <T, S>(this IPromise <T> promise, Func <T, IPromise <S> > callback) { var result = new Promise <S>(); result.CancelTarget( promise .Progress((p, m) => result.Notify(p / 50, m)) .Done(t => { try { result.CancelTarget(callback(t) .Progress((p, m) => result.Notify(50 + p / 50, m)) .Done(s => result.Resolve(s)) .Fail(exS => result.Reject(exS))); } catch (Exception ex) { result.Reject(ex); } }).Fail(exT => result.Reject(exT))); return(result); }
private static IPromise <T> ToHttpPromise <T>(this Task <T> task, TimeoutSource timeout, LogData trace) { var result = new Promise <T>(); result.CancelTarget(timeout); task .ContinueWith(t => { if (t.IsFaulted) { Exception ex = t.Exception; while (ex != null && ex.InnerException != null && ex.GetType().Name == "AggregateException") { ex = ex.InnerException; } if (ex != null) { foreach (var kvp in trace) { ex.Data[kvp.Key] = kvp.Value; } trace.Add("exception", ex); } result.Reject(ex); } else if (t.IsCanceled) { result.Reject(new HttpTimeoutException(string.Format("A response was not received after waiting for {0:m' minutes, 's' seconds'}", TimeSpan.FromMilliseconds(timeout.TimeoutDelay)))); } else { result.Resolve(t.Result); } }); return(result); }
/// <summary> /// Asynchronously gets an HTTP connection to an innovator instance (or proxy) at the given URL /// </summary> /// <param name="preferences">Object containing preferences for the connection</param> /// <param name="async">Whether or not to return the connection asynchronously. This is important /// as an HTTP request must be issued to determine the type of connection to create</param> /// <returns>A promise to return a connection object</returns> public static IPromise <IRemoteConnection> GetConnection(ConnectionPreferences preferences, bool async) { preferences = preferences ?? new ConnectionPreferences(); var url = preferences.Url; url = (url ?? "").TrimEnd('/'); if (url.EndsWith("Server/InnovatorServer.aspx", StringComparison.OrdinalIgnoreCase)) { url = url.Substring(0, url.Length - 21); } if (!url.EndsWith("/server", StringComparison.OrdinalIgnoreCase)) { url += "/Server"; } var configUrl = url + "/mapping.xml"; var masterService = preferences.HttpService ?? DefaultService.Invoke(); var arasSerice = preferences.HttpService ?? DefaultService.Invoke(); Func <ServerMapping, IRemoteConnection> connFactory = m => { var uri = (m.Url ?? "").TrimEnd('/'); if (!uri.EndsWith("/server", StringComparison.OrdinalIgnoreCase)) { url += "/Server"; } switch (m.Type) { case ServerType.Proxy: throw new NotSupportedException(); default: return(ArasConn(arasSerice, uri, preferences)); } }; var result = new Promise <IRemoteConnection>(); var req = new HttpRequest(); req.UserAgent = preferences.Headers.UserAgent; req.SetHeader("Accept", "text/xml"); foreach (var header in preferences.Headers.NonUserAgentHeaders()) { req.SetHeader(header.Key, header.Value); } var trace = new LogData(4, "Innovator: Try to download mapping file", Factory.LogListener) { { "url", configUrl }, }; result.CancelTarget(masterService.GetPromise(new Uri(configUrl), async, trace, req) .Progress((p, m) => result.Notify(p, m)) .Done(r => { var data = r.AsString(); if (string.IsNullOrEmpty(data)) { result.Resolve(ArasConn(arasSerice, url, preferences)); } else { try { var servers = ServerMapping.FromXml(data).ToArray(); if (servers.Length < 1) { result.Resolve(ArasConn(arasSerice, url, preferences)); } else if (servers.Length == 1) { result.Resolve(connFactory(servers.Single())); } else { foreach (var server in servers) { server.Factory = connFactory; } result.Resolve(new MappedConnection(servers, preferences.AuthCallback)); } } catch (XmlException) { result.Resolve(ArasConn(arasSerice, url, preferences)); } } }).Fail(ex => { result.Resolve(ArasConn(arasSerice, url, preferences)); })).Always(trace.Dispose); if (preferences.Credentials != null) { IRemoteConnection conn = null; return(result .Continue(c => { conn = c; return c.Login(preferences.Credentials, async); }) .Convert(u => conn)); } return(result); }
public static IPromise <IAuthenticator> GetAuthenticator(Uri innovatorServerBaseUrl, HttpClient service, ICredentials creds, bool async) { var discovery = new Uri(innovatorServerBaseUrl, "OAuthServerDiscovery.aspx"); var req = new HttpRequest(); req.Headers.Add("Aras-Set-HttpSessionState-Behavior", "switch_to_initial"); var oauthBaseUri = default(Uri); var result = new Promise <IAuthenticator>(); result.CancelTarget( service.GetPromise(discovery, async, new LogData(4 , "Innovator: Get OAuth URL" , Factory.LogListener) { { "url", discovery }, }, req) .Continue(r => { if (r.StatusCode != System.Net.HttpStatusCode.OK) { throw new Exception("OAuth not found"); } var serverUrls = default(List <Uri>); using (var json = new Json.Embed.JsonTextReader(r.AsStream)) { serverUrls = json.Flatten() .Where(k => k.Key.StartsWith("$.locations[") && k.Key.EndsWith("].uri") && k.Value is string str && !string.IsNullOrEmpty(str)) .Select(k => new Uri(k.Value.ToString())) .OrderBy(u => string.Equals(u.Host, innovatorServerBaseUrl.Host, StringComparison.OrdinalIgnoreCase) ? 0 : 1) .ToList(); } if (serverUrls?.Count < 1) { throw new InvalidOperationException("OAuth server URL could not be found"); } oauthBaseUri = serverUrls[0]; var oauthUri = new Uri(oauthBaseUri, ".well-known/openid-configuration"); return(service.GetPromise(oauthUri, async, new LogData(4 , "Innovator: Get OAuth Config" , Factory.LogListener) { { "url", oauthUri }, })); }) .Progress(result.Notify) .Done(r => { var config = new OAuthConfig(r.AsStream, oauthBaseUri); if (config.ProtocolVersion >= new Version("1.0")) { result.Resolve(new OAuthAuthenticator(service, config, creds)); } else { result.Resolve(new LegacyAuthenticator(innovatorServerBaseUrl, creds)); } }) .Fail(e => { result.Resolve(new LegacyAuthenticator(innovatorServerBaseUrl, creds)); })); return(result); }
private IPromise <ICredentials> DefaultAuthCallback(INetCredentials netCred, string endpoint, bool async) { var promise = new Promise <ICredentials>(); if (string.IsNullOrEmpty(endpoint)) { promise.Resolve(netCred); } else { var handler = new SyncClientHandler() { Credentials = netCred.Credentials, PreAuthenticate = true }; var http = new SyncHttpClient(handler); var endpointUri = new Uri(endpoint + "?db=" + netCred.Database); var trace = new LogData(4, "Innovator: Authenticate user via mapping", Factory.LogListener) { { "database", netCred.Database }, { "user_name", netCred.Credentials.GetCredential(endpointUri, null).UserName }, { "url", endpointUri }, }; http.GetPromise(endpointUri, async, trace) .Done(r => { var res = r.AsXml().DescendantsAndSelf("Result").FirstOrDefault(); var user = res.Element("user").Value; var pwd = res.Element("password").Value; if (pwd.IsNullOrWhiteSpace()) { promise.Reject(new ArgumentException("Failed to authenticate with Innovator server '" + endpoint + "'. Original error: " + user, "credentials")); } var needHash = !string.Equals(res.Element("hash").Value, "false", StringComparison.OrdinalIgnoreCase); if (needHash) { promise.Resolve(new ExplicitCredentials(netCred.Database, user, pwd)); } else { promise.Resolve(new ExplicitHashCredentials(netCred.Database, user, pwd)); } }).Fail(ex => { // Only hard fail for problems which aren't time outs and not found issues. var webEx = ex as HttpException; if (webEx != null && webEx.Response.StatusCode == HttpStatusCode.NotFound) { promise.Resolve(netCred); } else if (webEx != null && webEx.Response.StatusCode == HttpStatusCode.Unauthorized) { promise.Reject(ElementFactory.Local.ServerException("Invalid username or password")); } else if (webEx != null) { try { var result = ElementFactory.Local.FromXml(webEx.Response.AsStream); if (result.Exception != null) { promise.Reject(result.Exception); } else { promise.Reject(ex); } } catch (Exception) { promise.Reject(ex); } } else if (ex is TaskCanceledException) { promise.Resolve(netCred); } else { promise.Reject(ex); } }).Always(trace.Dispose); } return(promise); }