diff --git a/Src/Support/Google.Apis.Auth/ExistingDependencies/ApplicationContext.cs b/Src/Support/Google.Apis.Auth/ExistingDependencies/ApplicationContext.cs new file mode 100644 index 00000000000..d316446bae7 --- /dev/null +++ b/Src/Support/Google.Apis.Auth/ExistingDependencies/ApplicationContext.cs @@ -0,0 +1,53 @@ +/* +Copyright 2011 Google Inc + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +using Google.Apis.Logging; +using System; + +namespace Google +{ + /// Defines the context in which this library runs. It allows setting up custom loggers. + public static class ApplicationContext + { + private static ILogger logger; + + // For testing + internal static void Reset() => logger = null; + + /// Returns the logger used within this application context. + /// It creates a if no logger was registered previously + public static ILogger Logger + { + get + { + // Register the default null-logger if no other one was set. + return logger ?? (logger = new NullLogger()); + } + } + + /// Registers a logger with this application context. + /// Thrown if a logger was already registered. + public static void RegisterLogger(ILogger loggerToRegister) + { + // TODO(peleyal): Reconsider why the library should contain only one logger. Also consider using Tracing! + if (logger != null && !(logger is NullLogger)) + { + throw new InvalidOperationException("A logger was already registered with this context."); + } + logger = loggerToRegister; + } + } +} diff --git a/Src/Support/Google.Apis.Auth/ExistingDependencies/AuthHttpClientFactory.cs b/Src/Support/Google.Apis.Auth/ExistingDependencies/AuthHttpClientFactory.cs new file mode 100644 index 00000000000..a2f25c28735 --- /dev/null +++ b/Src/Support/Google.Apis.Auth/ExistingDependencies/AuthHttpClientFactory.cs @@ -0,0 +1,16 @@ +using Google.Apis.Http; +using System; + +namespace Google.Apis.Auth.ExistingDependencies; + +/// +/// Default implementation of IHttpClientFactory just for auth. +/// Avoids us requiring the full HttpClientFactory. +/// +internal class AuthHttpClientFactory : IHttpClientFactory +{ + public ConfigurableHttpClient CreateHttpClient(CreateHttpClientArgs args) + { + throw new NotImplementedException(); + } +} diff --git a/Src/Support/Google.Apis.Auth/ExistingDependencies/BackOffHandler.cs b/Src/Support/Google.Apis.Auth/ExistingDependencies/BackOffHandler.cs new file mode 100644 index 00000000000..dc12abfa7dd --- /dev/null +++ b/Src/Support/Google.Apis.Auth/ExistingDependencies/BackOffHandler.cs @@ -0,0 +1,186 @@ +/* +Copyright 2013 Google Inc + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +using System; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; + +using Google.Apis.Logging; +using Google.Apis.Util; + +namespace Google.Apis.Http +{ + /// + /// A thread-safe back-off handler which handles an abnormal HTTP response or an exception with + /// . + /// + public class BackOffHandler : IHttpUnsuccessfulResponseHandler, IHttpExceptionHandler + { + private static readonly ILogger Logger = ApplicationContext.Logger.ForType(); + + /// An initializer class to initialize a back-off handler. + public class Initializer + { + /// Gets the back-off policy used by this back-off handler. + public IBackOff BackOff { get; private set; } + + /// + /// Gets or sets the maximum time span to wait. If the back-off instance returns a greater time span than + /// this value, this handler returns false to both HandleExceptionAsync and + /// HandleResponseAsync. Default value is 16 seconds per a retry request. + /// + public TimeSpan MaxTimeSpan { get; set; } + + /// + /// Gets or sets a delegate function which indicates whether this back-off handler should handle an + /// abnormal HTTP response. The default is . + /// + public Func HandleUnsuccessfulResponseFunc { get; set; } + + /// + /// Gets or sets a delegate function which indicates whether this back-off handler should handle an + /// exception. The default is . + /// + public Func HandleExceptionFunc { get; set; } + + /// Default function which handles server errors (503). + public static readonly Func DefaultHandleUnsuccessfulResponseFunc = + (r) => (int)r.StatusCode == 503; + + /// + /// Default function which handles exception which aren't + /// or + /// . Those exceptions represent a task or an operation + /// which was canceled and shouldn't be retried. + /// + public static readonly Func DefaultHandleExceptionFunc = + (ex) => !(ex is TaskCanceledException || ex is OperationCanceledException); + + /// Constructs a new initializer by the given back-off. + public Initializer(IBackOff backOff) + { + BackOff = backOff; + HandleExceptionFunc = DefaultHandleExceptionFunc; + HandleUnsuccessfulResponseFunc = DefaultHandleUnsuccessfulResponseFunc; + MaxTimeSpan = TimeSpan.FromSeconds(16); + } + } + + /// Gets the back-off policy used by this back-off handler. + public IBackOff BackOff { get; private set; } + + /// + /// Gets the maximum time span to wait. If the back-off instance returns a greater time span, the handle method + /// returns false. Default value is 16 seconds per a retry request. + /// + public TimeSpan MaxTimeSpan { get; private set; } + + /// + /// Gets a delegate function which indicates whether this back-off handler should handle an abnormal HTTP + /// response. The default is . + /// + public Func HandleUnsuccessfulResponseFunc { get; private set; } + + /// + /// Gets a delegate function which indicates whether this back-off handler should handle an exception. The + /// default is . + /// + public Func HandleExceptionFunc { get; private set; } + + /// Constructs a new back-off handler with the given back-off. + /// The back-off policy. + public BackOffHandler(IBackOff backOff) + : this(new Initializer(backOff)) + { + } + + /// Constructs a new back-off handler with the given initializer. + public BackOffHandler(Initializer initializer) + { + BackOff = initializer.BackOff; + MaxTimeSpan = initializer.MaxTimeSpan; + HandleExceptionFunc = initializer.HandleExceptionFunc; + HandleUnsuccessfulResponseFunc = initializer.HandleUnsuccessfulResponseFunc; + } + + #region IHttpUnsuccessfulResponseHandler + + /// + public virtual async Task HandleResponseAsync(HandleUnsuccessfulResponseArgs args) + { + // if the func returns true try to handle this current failed try + if (HandleUnsuccessfulResponseFunc != null && HandleUnsuccessfulResponseFunc(args.Response)) + { + return await HandleAsync(args.SupportsRetry, args.CurrentFailedTry, args.CancellationToken) + .ConfigureAwait(false); + } + return false; + } + + #endregion + + #region IHttpExceptionHandler + + /// + public virtual async Task HandleExceptionAsync(HandleExceptionArgs args) + { + // if the func returns true try to handle this current failed try + if (HandleExceptionFunc != null && HandleExceptionFunc(args.Exception)) + { + return await HandleAsync(args.SupportsRetry, args.CurrentFailedTry, args.CancellationToken) + .ConfigureAwait(false); + } + return false; + } + + #endregion + + /// + /// Handles back-off. In case the request doesn't support retry or the back-off time span is greater than the + /// maximum time span allowed for a request, the handler returns false. Otherwise the current thread + /// will block for x milliseconds (x is defined by the instance), and this handler + /// returns true. + /// + private async Task HandleAsync(bool supportsRetry, int currentFailedTry, + CancellationToken cancellationToken) + { + if (!supportsRetry || BackOff.MaxNumOfRetries < currentFailedTry) + { + return false; + } + + TimeSpan ts = BackOff.GetNextBackOff(currentFailedTry); + if (ts > MaxTimeSpan || ts < TimeSpan.Zero) + { + return false; + } + + await Wait(ts, cancellationToken).ConfigureAwait(false); + Logger.Debug("Back-Off handled the error. Waited {0}ms before next retry...", ts.TotalMilliseconds); + return true; + } + + /// Waits the given time span. Overriding this method is recommended for mocking purposes. + /// TimeSpan to wait (and block the current thread). + /// The cancellation token in case the user wants to cancel the operation in + /// the middle. + protected virtual async Task Wait(TimeSpan ts, CancellationToken cancellationToken) + { + await Task.Delay(ts, cancellationToken).ConfigureAwait(false); + } + } +} diff --git a/Src/Support/Google.Apis.Auth/ExistingDependencies/ConfigurableHttpClient.cs b/Src/Support/Google.Apis.Auth/ExistingDependencies/ConfigurableHttpClient.cs new file mode 100644 index 00000000000..ed84068e722 --- /dev/null +++ b/Src/Support/Google.Apis.Auth/ExistingDependencies/ConfigurableHttpClient.cs @@ -0,0 +1,50 @@ +/* +Copyright 2013 Google Inc + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +using System.Net.Http; + +namespace Google.Apis.Http +{ + /// + /// Configurable HTTP client inherits from and contains a reference to + /// . + /// + public class ConfigurableHttpClient : HttpClient + { + /// Gets the configurable message handler. + public ConfigurableMessageHandler MessageHandler { get; private set; } + + /// Constructs a new HTTP client. + /// This is equivalent to calling ConfigurableHttpClient(handler, true) + public ConfigurableHttpClient(ConfigurableMessageHandler handler) + : this(handler, true) + { + } + + /// + /// Constructs a new HTTP client. + /// + /// The handler for this client to use. + /// Whether the created + /// should dispose of the internal message handler or not when it iself is disposed. + public ConfigurableHttpClient(ConfigurableMessageHandler handler, bool disposeHandler) + : base (handler, disposeHandler) + { + MessageHandler = handler; + DefaultRequestHeaders.ExpectContinue = false; + } + } +} diff --git a/Src/Support/Google.Apis.Auth/ExistingDependencies/ConfigurableMessageHandler.cs b/Src/Support/Google.Apis.Auth/ExistingDependencies/ConfigurableMessageHandler.cs new file mode 100644 index 00000000000..cc12532d571 --- /dev/null +++ b/Src/Support/Google.Apis.Auth/ExistingDependencies/ConfigurableMessageHandler.cs @@ -0,0 +1,713 @@ +/* +Copyright 2013 Google Inc + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +using Google.Apis.Logging; +using Google.Apis.Testing; +using System.Net.Http.Headers; + +namespace Google.Apis.Http +{ + /// + /// A message handler which contains the main logic of our HTTP requests. It contains a list of + /// s for handling abnormal responses, a list of + /// s for handling exception in a request and a list of + /// s for intercepting a request before it has been sent to the server. + /// It also contains important properties like number of tries, follow redirect, etc. + /// + public class ConfigurableMessageHandler : DelegatingHandler + { + private const string QuotaProjectHeaderName = "x-goog-user-project"; + + /// The class logger. + private static readonly ILogger Logger = ApplicationContext.Logger.ForType(); + + /// Maximum allowed number of tries. + [VisibleForTestOnly] + public const int MaxAllowedNumTries = 20; + + /// + /// Key for unsuccessful response handlers in an properties. + /// + public const string UnsuccessfulResponseHandlerKey = "__UnsuccessfulResponseHandlerKey"; + + /// + /// Key for exception handlers in an properties. + /// + public const string ExceptionHandlerKey = "__ExceptionHandlerKey"; + + /// + /// Key for execute handlers in an properties. + /// + public const string ExecuteInterceptorKey = "__ExecuteInterceptorKey"; + + /// + /// Key for a stream response interceptor provider in an properties. + /// + public const string ResponseStreamInterceptorProviderKey = "__ResponseStreamInterceptorProviderKey"; + + /// + /// Key for a credential in a properties. + /// + public const string CredentialKey = "__CredentialKey"; + + /// + /// Key for request specific max retries. + /// + public const string MaxRetriesKey = "__MaxRetriesKey"; + + /// The current API version of this client library. + private static readonly string ApiVersion = Google.Apis.Util.Utilities.GetLibraryVersion(); + + /// The User-Agent suffix header which contains the . + private static readonly string UserAgentSuffix = "google-api-dotnet-client/" + ApiVersion + " (gzip)"; + + #region IHttpUnsuccessfulResponseHandler, IHttpExceptionHandler and IHttpExecuteInterceptor lists + + #region Lock objects + + // The following lock objects are used to lock the list of handlers and interceptors in order to be able to + // iterate over them from several threads and to keep this class thread-safe. + private readonly object unsuccessfulResponseHandlersLock = new object(); + private readonly object exceptionHandlersLock = new object(); + private readonly object executeInterceptorsLock = new object(); + + #endregion + + /// A list of . + private readonly IList unsuccessfulResponseHandlers = + new List(); + + /// A list of . + private readonly IList exceptionHandlers = + new List(); + + /// A list of . + private readonly IList executeInterceptors = + new List(); + + /// + /// Gets a list of s. + /// + /// Since version 1.10, and + /// were added in order to keep this class thread-safe. + /// More information is available on + /// #592. + /// + /// + [Obsolete("Use AddUnsuccessfulResponseHandler or RemoveUnsuccessfulResponseHandler instead.")] + public IList UnsuccessfulResponseHandlers + { + get { return unsuccessfulResponseHandlers; } + } + + /// Adds the specified handler to the list of unsuccessful response handlers. + public void AddUnsuccessfulResponseHandler(IHttpUnsuccessfulResponseHandler handler) + { + lock (unsuccessfulResponseHandlersLock) + { + unsuccessfulResponseHandlers.Add(handler); + } + } + + /// Removes the specified handler from the list of unsuccessful response handlers. + public void RemoveUnsuccessfulResponseHandler(IHttpUnsuccessfulResponseHandler handler) + { + lock (unsuccessfulResponseHandlersLock) + { + unsuccessfulResponseHandlers.Remove(handler); + } + } + + /// + /// Gets a list of s. + /// + /// Since version 1.10, and were added + /// in order to keep this class thread-safe. More information is available on + /// #592. + /// + /// + [Obsolete("Use AddExceptionHandler or RemoveExceptionHandler instead.")] + public IList ExceptionHandlers + { + get { return exceptionHandlers; } + } + + /// Adds the specified handler to the list of exception handlers. + public void AddExceptionHandler(IHttpExceptionHandler handler) + { + lock (exceptionHandlersLock) + { + exceptionHandlers.Add(handler); + } + } + + /// Removes the specified handler from the list of exception handlers. + public void RemoveExceptionHandler(IHttpExceptionHandler handler) + { + lock (exceptionHandlersLock) + { + exceptionHandlers.Remove(handler); + } + } + + /// + /// Gets a list of s. + /// + /// Since version 1.10, and were + /// added in order to keep this class thread-safe. More information is available on + /// #592. + /// + /// + [Obsolete("Use AddExecuteInterceptor or RemoveExecuteInterceptor instead.")] + public IList ExecuteInterceptors + { + get { return executeInterceptors; } + } + + /// Adds the specified interceptor to the list of execute interceptors. + public void AddExecuteInterceptor(IHttpExecuteInterceptor interceptor) + { + lock (executeInterceptorsLock) + { + executeInterceptors.Add(interceptor); + } + } + + /// Removes the specified interceptor from the list of execute interceptors. + public void RemoveExecuteInterceptor(IHttpExecuteInterceptor interceptor) + { + lock (executeInterceptorsLock) + { + executeInterceptors.Remove(interceptor); + } + } + + #endregion + + private int _loggingRequestId = 0; + + private ILogger _instanceLogger = Logger; + + /// + /// For testing only. + /// This defaults to the static , but can be overridden for fine-grain testing. + /// + internal ILogger InstanceLogger + { + get { return _instanceLogger; } + set { _instanceLogger = value.ForType(); } + } + + /// Number of tries. Default is 3. + private int numTries = 3; + + /// + /// Gets or sets the number of tries that will be allowed to execute. Retries occur as a result of either + /// or which handles the + /// abnormal HTTP response or exception before being terminated. + /// Set 1 for not retrying requests. The default value is 3. + /// + /// The number of allowed redirects (3xx) is defined by . This property defines + /// only the allowed tries for >=400 responses, or when an exception is thrown. For example if you set + /// to 1 and to 5, the library will send up to five redirect + /// requests, but will not send any retry requests due to an error HTTP status code. + /// + /// + public int NumTries + { + get { return numTries; } + set + { + if (value > MaxAllowedNumTries || value < 1) + { + throw new ArgumentOutOfRangeException("NumTries"); + } + numTries = value; + } + } + + /// Number of redirects allowed. Default is 10. + private int numRedirects = 10; + + /// + /// Gets or sets the number of redirects that will be allowed to execute. The default value is 10. + /// See for more information. + /// + public int NumRedirects + { + get { return numRedirects; } + set + { + if (value > MaxAllowedNumTries || value < 1) + { + throw new ArgumentOutOfRangeException("NumRedirects"); + } + numRedirects = value; + } + } + + /// + /// Gets or sets whether the handler should follow a redirect when a redirect response is received. Default + /// value is true. + /// + public bool FollowRedirect { get; set; } + + /// Gets or sets whether logging is enabled. Default value is true. + public bool IsLoggingEnabled { get; set; } + + /// + /// Specifies the type(s) of request/response events to log. + /// + [Flags] + public enum LogEventType + { + /// + /// Log no request/response information. + /// + None = 0, + + /// + /// Log the request URI. + /// + RequestUri = 1, + + /// + /// Log the request headers. + /// + RequestHeaders = 2, + + /// + /// Log the request body. The body is assumed to be ASCII, and non-printable charaters are replaced by '.'. + /// Warning: This causes the body content to be buffered in memory, so use with care for large requests. + /// + RequestBody = 4, + + /// + /// Log the response status. + /// + ResponseStatus = 8, + + /// + /// Log the response headers. + /// + ResponseHeaders = 16, + + /// + /// Log the response body. The body is assumed to be ASCII, and non-printable characters are replaced by '.'. + /// Warning: This causes the body content to be buffered in memory, so use with care for large responses. + /// + ResponseBody = 32, + + /// + /// Log abnormal response messages. + /// + ResponseAbnormal = 64, + } + + /// + /// The request/response types to log. + /// + public LogEventType LogEvents { get; set; } + + /// Gets or sets the application name which will be used on the User-Agent header. + public string ApplicationName { get; set; } + + /// Gets or sets the value set for the x-goog-api-client header. + public string GoogleApiClientHeader { get; set; } + + /// + /// The credential to apply to all requests made with this client, + /// unless theres a specific call credential set. + /// If implements + /// then it will also be included as a handler of an unsuccessful response. + /// + public IHttpExecuteInterceptor Credential { get; set; } + + /// Constructs a new configurable message handler. + public ConfigurableMessageHandler(HttpMessageHandler httpMessageHandler) + : base(httpMessageHandler) + { + // set default values + FollowRedirect = true; + IsLoggingEnabled = true; + LogEvents = LogEventType.RequestUri | LogEventType.ResponseStatus | LogEventType.ResponseAbnormal; + } + + private void LogHeaders(string initialText, HttpHeaders headers1, HttpHeaders headers2) + { + var headers = (headers1 ?? Enumerable.Empty>>()) + .Concat(headers2 ?? Enumerable.Empty>>()).ToList(); + var args = new object[headers.Count * 2]; + var fmt = new StringBuilder(headers.Count * 32); + fmt.Append(initialText); + var argBuilder = new StringBuilder(); + for (int i = 0; i < headers.Count; i++) + { + fmt.Append($"\n [{{{i * 2}}}] '{{{1 + i * 2}}}'"); + args[i * 2] = headers[i].Key; + argBuilder.Clear(); + args[1 + i * 2] = string.Join("; ", headers[i].Value); + } + InstanceLogger.Debug(fmt.ToString(), args); + } + + private async Task LogBody(string fmtText, HttpContent content) + { + // This buffers the body content within the HttpContent if required. + var bodyBytes = content != null ? await content.ReadAsByteArrayAsync().ConfigureAwait(false) : new byte[0]; + char[] bodyChars = new char[bodyBytes.Length]; + for (int i = 0; i < bodyBytes.Length; i++) + { + var b = bodyBytes[i]; + bodyChars[i] = b >= 32 && b <= 126 ? (char)b : '.'; + } + InstanceLogger.Debug(fmtText, new string(bodyChars)); + } + + /// + /// The main logic of sending a request to the server. This send method adds the User-Agent header to a request + /// with and the library version. It also calls interceptors before each attempt, + /// and unsuccessful response handler or exception handlers when abnormal response or exception occurred. + /// + protected override async Task SendAsync(HttpRequestMessage request, + CancellationToken cancellationToken) + { + var loggable = IsLoggingEnabled && InstanceLogger.IsDebugEnabled; + string loggingRequestId = ""; + if (loggable) + { + loggingRequestId = Interlocked.Increment(ref _loggingRequestId).ToString("X8"); + } + + int maxRetries = GetEffectiveMaxRetries(request); + int triesRemaining = maxRetries; + int redirectRemaining = NumRedirects; + + Exception lastException = null; + + // Set User-Agent header. + var userAgent = (ApplicationName == null ? "" : ApplicationName + " ") + UserAgentSuffix; + // TODO: setting the User-Agent won't work on Silverlight. We may need to create a special callback here to + // set it correctly. + request.Headers.Add("User-Agent", userAgent); + var apiClientHeader = GoogleApiClientHeader; + if (apiClientHeader != null) + { + request.Headers.Add("x-goog-api-client", apiClientHeader); + } + + HttpResponseMessage response = null; + do // While (triesRemaining > 0) + { + response?.Dispose(); + response = null; + lastException = null; + + // Check after previous response (if any) has been disposed of. + cancellationToken.ThrowIfCancellationRequested(); + + // We keep a local list of the interceptors, since we can't call await inside lock. + List interceptors; + lock (executeInterceptorsLock) + { + interceptors = executeInterceptors.ToList(); + } + if (request.Properties.TryGetValue(ExecuteInterceptorKey, out var interceptorsValue) && + interceptorsValue is List perCallinterceptors) + { + interceptors.AddRange(perCallinterceptors); + } + + // Intercept the request. + foreach (var interceptor in interceptors) + { + await interceptor.InterceptAsync(request, cancellationToken).ConfigureAwait(false); + } + + // Before having the credential intercept the call, check that quota project hasn't + // been added as a header. Quota project cannot be added except through the credential. + CheckValidAfterInterceptors(request); + + await CredentialInterceptAsync(request, cancellationToken).ConfigureAwait(false); + + if (loggable) + { + if ((LogEvents & LogEventType.RequestUri) != 0) + { + InstanceLogger.Debug("Request[{0}] (triesRemaining={1}) URI: '{2}'", loggingRequestId, triesRemaining, request.RequestUri); + } + if ((LogEvents & LogEventType.RequestHeaders) != 0) + { + LogHeaders($"Request[{loggingRequestId}] Headers:", request.Headers, request.Content?.Headers); + } + if ((LogEvents & LogEventType.RequestBody) != 0) + { + await LogBody($"Request[{loggingRequestId}] Body: '{{0}}'", request.Content).ConfigureAwait(false); + } + } + try + { + // Send the request! + response = await base.SendAsync(request, cancellationToken).ConfigureAwait(false); + } + catch (Exception ex) + { + lastException = ex; + } + + // Decrease the number of retries. + if (response == null || ((int)response.StatusCode >= 400 || (int)response.StatusCode < 200)) + { + triesRemaining--; + } + + // Exception was thrown, try to handle it. + if (response == null) + { + var exceptionHandled = false; + + // We keep a local list of the handlers, since we can't call await inside lock. + List handlers; + lock (exceptionHandlersLock) + { + handlers = exceptionHandlers.ToList(); + } + if (request.Properties.TryGetValue(ExceptionHandlerKey, out var handlersValue) && + handlersValue is List perCallHandlers) + { + handlers.AddRange(perCallHandlers); + } + + // Try to handle the exception with each handler. + foreach (var handler in handlers) + { + exceptionHandled |= await handler.HandleExceptionAsync(new HandleExceptionArgs + { + Request = request, + Exception = lastException, + TotalTries = maxRetries, + CurrentFailedTry = maxRetries - triesRemaining, + CancellationToken = cancellationToken + }).ConfigureAwait(false); + } + + if (!exceptionHandled) + { + InstanceLogger.Error(lastException, + "Response[{0}] Exception was thrown while executing a HTTP request and it wasn't handled", loggingRequestId); + throw lastException; + } + else if (loggable && (LogEvents & LogEventType.ResponseAbnormal) != 0) + { + InstanceLogger.Debug("Response[{0}] Exception {1} was thrown, but it was handled by an exception handler", + loggingRequestId, lastException.Message); + } + } + else + { + if (loggable) + { + if ((LogEvents & LogEventType.ResponseStatus) != 0) + { + InstanceLogger.Debug("Response[{0}] Response status: {1} '{2}'", loggingRequestId, response.StatusCode, response.ReasonPhrase); + } + if ((LogEvents & LogEventType.ResponseHeaders) != 0) + { + LogHeaders($"Response[{loggingRequestId}] Headers:", response.Headers, response.Content?.Headers); + } + if ((LogEvents & LogEventType.ResponseBody) != 0) + { + await LogBody($"Response[{loggingRequestId}] Body: '{{0}}'", response.Content).ConfigureAwait(false); + } + } + if (response.IsSuccessStatusCode) + { + // No need to retry, the response was successful. + triesRemaining = 0; + } + else + { + bool errorHandled = false; + + // We keep a local list of the handlers, since we can't call await inside lock. + List handlers; + lock (unsuccessfulResponseHandlersLock) + { + handlers = unsuccessfulResponseHandlers.ToList(); + } + if (request.Properties.TryGetValue(UnsuccessfulResponseHandlerKey, out var handlersValue) && + handlersValue is List perCallHandlers) + { + handlers.AddRange(perCallHandlers); + } + + var handlerArgs = new HandleUnsuccessfulResponseArgs + { + Request = request, + Response = response, + TotalTries = maxRetries, + CurrentFailedTry = maxRetries - triesRemaining, + CancellationToken = cancellationToken + }; + + // Try to handle the abnormal HTTP response with each handler. + foreach (var handler in handlers) + { + try + { + errorHandled |= await handler.HandleResponseAsync(handlerArgs).ConfigureAwait(false); + } + catch when (DisposeAndReturnFalse(response)) { } + + bool DisposeAndReturnFalse(IDisposable disposable) + { + disposable.Dispose(); + return false; + } + } + + errorHandled |= await CredentialHandleResponseAsync(handlerArgs).ConfigureAwait(false); + + if (!errorHandled) + { + if (FollowRedirect && HandleRedirect(response)) + { + if (redirectRemaining-- == 0) + { + triesRemaining = 0; + } + + errorHandled = true; + if (loggable && (LogEvents & LogEventType.ResponseAbnormal) != 0) + { + InstanceLogger.Debug("Response[{0}] Redirect response was handled successfully. Redirect to {1}", + loggingRequestId, response.Headers.Location); + } + } + else + { + if (loggable && (LogEvents & LogEventType.ResponseAbnormal) != 0) + { + InstanceLogger.Debug("Response[{0}] An abnormal response wasn't handled. Status code is {1}", + loggingRequestId, response.StatusCode); + } + + // No need to retry, because no handler handled the abnormal response. + triesRemaining = 0; + } + } + else if (loggable && (LogEvents & LogEventType.ResponseAbnormal) != 0) + { + InstanceLogger.Debug("Response[{0}] An abnormal response was handled by an unsuccessful response handler. " + + "Status Code is {1}", loggingRequestId, response.StatusCode); + } + } + } + } while (triesRemaining > 0); // Not a successful status code but it was handled. + + // If the response is null, we should throw the last exception. + if (response == null) + { + InstanceLogger.Error(lastException, "Request[{0}] Exception was thrown while executing a HTTP request", loggingRequestId); + throw lastException; + } + else if (!response.IsSuccessStatusCode && loggable && (LogEvents & LogEventType.ResponseAbnormal) != 0) + { + InstanceLogger.Debug("Response[{0}] Abnormal response is being returned. Status Code is {1}", loggingRequestId, response.StatusCode); + } + + return response; + } + + private void CheckValidAfterInterceptors(HttpRequestMessage request) + { + if (request.Headers.Contains(QuotaProjectHeaderName)) + { + throw new InvalidOperationException($"{QuotaProjectHeaderName} header can only be added through the credential or through the ClientBuilder."); + } + } + + private async Task CredentialInterceptAsync(HttpRequestMessage request, CancellationToken cancellationToken) + { + var effectiveCredential = GetEffectiveCredential(request); + if (effectiveCredential != null) + { + await effectiveCredential.InterceptAsync(request, cancellationToken).ConfigureAwait(false); + } + } + + private async Task CredentialHandleResponseAsync(HandleUnsuccessfulResponseArgs args) + { + var effectiveCredential = GetEffectiveCredential(args.Request); + if (effectiveCredential is IHttpUnsuccessfulResponseHandler handler) + { + return await handler.HandleResponseAsync(args).ConfigureAwait(false); + } + + return false; + } + + private IHttpExecuteInterceptor GetEffectiveCredential(HttpRequestMessage request) => + (request.Properties.TryGetValue(CredentialKey, out var cred) && cred is IHttpExecuteInterceptor callCredential) + ? callCredential : Credential; + + private int GetEffectiveMaxRetries(HttpRequestMessage request) => + (request.Properties.TryGetValue(MaxRetriesKey, out var maxRetries) && maxRetries is int perRequestMaxRetries) + ? perRequestMaxRetries : NumTries; + + /// + /// Handles redirect if the response's status code is redirect, redirects are turned on, and the header has + /// a location. + /// When the status code is 303 the method on the request is changed to a GET as per the RFC2616 + /// specification. On a redirect, it also removes the Authorization and all If-* request headers. + /// + /// Whether this method changed the request and handled redirect successfully. + private bool HandleRedirect(HttpResponseMessage message) + { + // TODO(peleyal): think if it's better to move that code to RedirectUnsucessfulResponseHandler + var uri = message.Headers.Location; + if (!message.IsRedirectStatusCode() || uri == null) + { + return false; + } + + var request = message.RequestMessage; + request.RequestUri = new Uri(request.RequestUri, uri); + // Status code for a resource that has moved to a new URI and should be retrieved using GET. + if (message.StatusCode == HttpStatusCode.SeeOther) + { + request.Method = HttpMethod.Get; + } + // Clear Authorization and If-* headers. + request.Headers.Remove("Authorization"); + request.Headers.IfMatch.Clear(); + request.Headers.IfNoneMatch.Clear(); + request.Headers.IfModifiedSince = null; + request.Headers.IfUnmodifiedSince = null; + request.Headers.Remove("If-Range"); + return true; + } + } +} diff --git a/Src/Support/Google.Apis.Auth/ExistingDependencies/ExponentialBackOff.cs b/Src/Support/Google.Apis.Auth/ExistingDependencies/ExponentialBackOff.cs new file mode 100644 index 00000000000..7160fdf608f --- /dev/null +++ b/Src/Support/Google.Apis.Auth/ExistingDependencies/ExponentialBackOff.cs @@ -0,0 +1,99 @@ +/* +Copyright 2013 Google Inc + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +using System; + +namespace Google.Apis.Util +{ + /// + /// Implementation of that increases the back-off period for each retry attempt using a + /// randomization function that grows exponentially. In addition, it also adds a randomize number of milliseconds + /// for each attempt. + /// + public class ExponentialBackOff : IBackOff + { + /// The maximum allowed number of retries. + private const int MaxAllowedNumRetries = 20; + + private readonly TimeSpan deltaBackOff; + /// + /// Gets the delta time span used to generate a random milliseconds to add to the next back-off. + /// If the value is then the generated back-off will be exactly 1, 2, 4, + /// 8, 16, etc. seconds. A valid value is between zero and one second. The default value is 250ms, which means + /// that the generated back-off will be [0.75-1.25]sec, [1.75-2.25]sec, [3.75-4.25]sec, and so on. + /// + public TimeSpan DeltaBackOff + { + get { return deltaBackOff; } + } + + private readonly int maxNumOfRetries; + /// Gets the maximum number of retries. Default value is 10. + public int MaxNumOfRetries + { + get { return maxNumOfRetries; } + } + + /// The random instance which generates a random number to add the to next back-off. + private Random random = new Random(); + + /// Constructs a new exponential back-off with default values. + public ExponentialBackOff() + : this(TimeSpan.FromMilliseconds(250)) + { + } + + /// Constructs a new exponential back-off with the given delta and maximum retries. + public ExponentialBackOff(TimeSpan deltaBackOff, int maximumNumOfRetries = 10) + { + if (deltaBackOff < TimeSpan.Zero || deltaBackOff > TimeSpan.FromSeconds(1)) + { + throw new ArgumentOutOfRangeException("deltaBackOff"); + } + if (maximumNumOfRetries < 0 || maximumNumOfRetries > MaxAllowedNumRetries) + { + throw new ArgumentOutOfRangeException("deltaBackOff"); + } + + this.deltaBackOff = deltaBackOff; + this.maxNumOfRetries = maximumNumOfRetries; + } + + #region IBackOff Members + + /// + public TimeSpan GetNextBackOff(int currentRetry) + { + if (currentRetry <= 0) + { + throw new ArgumentOutOfRangeException("currentRetry"); + } + if (currentRetry > MaxNumOfRetries) + { + return TimeSpan.MinValue; + } + + // Generate a random number of milliseconds and add it to the current exponential number. + var randomMilli = (double)random.Next( + (int)(DeltaBackOff.TotalMilliseconds * -1), + (int)(DeltaBackOff.TotalMilliseconds * 1)); + int backOffMilli = (int)(Math.Pow(2.0, (double)currentRetry - 1) * 1000 + randomMilli); + return TimeSpan.FromMilliseconds(backOffMilli); + } + + #endregion + } +} diff --git a/Src/Support/Google.Apis.Auth/ExistingDependencies/ExponentialBackOffInitializer.cs b/Src/Support/Google.Apis.Auth/ExistingDependencies/ExponentialBackOffInitializer.cs new file mode 100644 index 00000000000..4d6fff2aa8d --- /dev/null +++ b/Src/Support/Google.Apis.Auth/ExistingDependencies/ExponentialBackOffInitializer.cs @@ -0,0 +1,80 @@ +/* +Copyright 2013 Google Inc + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +using System; + +// ExponentialBackOffInitializer could be internal to Google.Apis.Auth (with code duplication). +// We add it to the AuthorizationCodeFlow and ServiceCredential, but we don't expose it. +// ExponentialBackOffPolicy is exposed via the DefaultExponentialBackOffPolicy propert in AuthorizationCodeFlow and ExternalAccountCredential. + +namespace Google.Apis.Http +{ + /// + /// Indicates if exponential back-off is used automatically on exceptions in a service requests and \ or when 503 + /// responses is returned form the server. + /// + [Flags] + public enum ExponentialBackOffPolicy + { + /// Exponential back-off is disabled. + None = 0, + /// Exponential back-off is enabled only for exceptions. + Exception = 1, + /// Exponential back-off is enabled only for 503 HTTP Status code. + UnsuccessfulResponse503 = 2 + } + + /// + /// An initializer which adds exponential back-off as exception handler and \ or unsuccessful response handler by + /// the given . + /// + public class ExponentialBackOffInitializer : IConfigurableHttpClientInitializer + { + /// Gets or sets the used back-off policy. + private ExponentialBackOffPolicy Policy { get; set; } + + /// Gets or sets the back-off handler creation function. + private Func CreateBackOff { get; set; } + + /// + /// Constructs a new back-off initializer with the given policy and back-off handler create function. + /// + public ExponentialBackOffInitializer(ExponentialBackOffPolicy policy, Func createBackOff) + { + Policy = policy; + CreateBackOff = createBackOff; + } + + /// + public void Initialize(ConfigurableHttpClient httpClient) + { + var backOff = CreateBackOff(); + + // Add exception handler and \ or unsuccessful response handler. + if ((Policy & ExponentialBackOffPolicy.Exception) == ExponentialBackOffPolicy.Exception) + { + httpClient.MessageHandler.AddExceptionHandler(backOff); + } + + if ((Policy & ExponentialBackOffPolicy.UnsuccessfulResponse503) == + ExponentialBackOffPolicy.UnsuccessfulResponse503) + { + httpClient.MessageHandler.AddUnsuccessfulResponseHandler(backOff); + } + } + } + +} diff --git a/Src/Support/Google.Apis.Auth/ExistingDependencies/GoogleApiException.cs b/Src/Support/Google.Apis.Auth/ExistingDependencies/GoogleApiException.cs new file mode 100644 index 00000000000..7e3c3e064bd --- /dev/null +++ b/Src/Support/Google.Apis.Auth/ExistingDependencies/GoogleApiException.cs @@ -0,0 +1,103 @@ +/* +Copyright 2010 Google Inc + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Note: the only reference to this is in ServiceAccountCredential. If we throw a different exception, +// e.g. InvalidOperationException, we could remove GoogleApiException, SingleError and RequestError. + +using System; +using System.Net; + +using Google.Apis.Requests; +using Google.Apis.Util; + +namespace Google +{ + /// Represents an exception thrown by an API Service. + public class GoogleApiException : Exception + { + private readonly bool _hasMessage; + + /// Gets the service name which related to this exception. + public string ServiceName { get; } + + /// Creates an API Service exception. + public GoogleApiException(string serviceName, string message, Exception inner) + : base(message, inner) + { + ServiceName = serviceName.ThrowIfNull(nameof(serviceName)); + _hasMessage = message is object; + } + + /// Creates an API Service exception. + public GoogleApiException(string serviceName, string message) : this(serviceName, message, null) + { } + + /// + /// Creates an API Service exception with no message. + /// + /// + /// may still contain useful information if the + /// and/or properties are set. + /// + public GoogleApiException(string serviceName) : this(serviceName, null, null) + { } + + /// The Error which was returned from the server, or null if unavailable. + public RequestError Error { get; set; } + + private string ErrorMessage => + Error?.Message ?? "No error message was specified."; + + private string ContentMessage => + Error is null + ? "No error details were specified." + : (Error.IsOnlyRawContent + ? $"No JSON error details were specified.{Environment.NewLine}" + + (string.IsNullOrWhiteSpace(Error.ErrorResponseContent) + ? "Raw error details are empty or white spaces only." + : $"Raw error details are: {Error.ErrorResponseContent}") + : $"{Error}"); + + /// The HTTP status code which was returned along with this error, or 0 if unavailable. + public HttpStatusCode HttpStatusCode { get; set; } + + private string HttpStatusCodeMessage => + HttpStatusCode > 0 + ? $"HttpStatusCode is {HttpStatusCode}." + : "No HttpStatusCode was specified."; + + private string ServiceNameMessage => $"The service {ServiceName} has thrown an exception."; + + private string CombinedMessage => $"{ServiceNameMessage} {HttpStatusCodeMessage} {ErrorMessage}"; + + /// + public override string Message => + // We just override the default message which is very generic in Exception + _hasMessage ? base.Message : CombinedMessage; + + /// + /// Returns a summary of this exception. + /// + /// A summary of this exception. + public override string ToString() => + // base.ToString() prints Message, and if we have overwritten Message with DefaultMessage + // then ToString() will contain some duplicate information. But that's OK. + $"{ServiceNameMessage}{Environment.NewLine}" + + $"{HttpStatusCodeMessage}{Environment.NewLine}" + + $"{ContentMessage}{Environment.NewLine}" + + $"{base.ToString()}"; + } +} \ No newline at end of file diff --git a/Src/Support/Google.Apis.Auth/ExistingDependencies/HttpExtenstions.cs b/Src/Support/Google.Apis.Auth/ExistingDependencies/HttpExtenstions.cs new file mode 100644 index 00000000000..461f2ba1e62 --- /dev/null +++ b/Src/Support/Google.Apis.Auth/ExistingDependencies/HttpExtenstions.cs @@ -0,0 +1,43 @@ +/* +Copyright 2013 Google Inc + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +using System.Net; +using System.Net.Http; + +namespace Google.Apis.Http +{ + /// + /// Extension methods to and + /// . + /// + internal static class HttpExtensions + { + /// Returns true if the response contains one of the redirect status codes. + internal static bool IsRedirectStatusCode(this HttpResponseMessage message) + { + switch (message.StatusCode) + { + case HttpStatusCode.Moved: + case HttpStatusCode.Redirect: + case HttpStatusCode.RedirectMethod: + case HttpStatusCode.TemporaryRedirect: + return true; + default: + return false; + } + } + } +} diff --git a/Src/Support/Google.Apis.Auth/ExistingDependencies/IBackOff.cs b/Src/Support/Google.Apis.Auth/ExistingDependencies/IBackOff.cs new file mode 100644 index 00000000000..32528c4bae4 --- /dev/null +++ b/Src/Support/Google.Apis.Auth/ExistingDependencies/IBackOff.cs @@ -0,0 +1,33 @@ +/* +Copyright 2013 Google Inc + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +using System; + +namespace Google.Apis.Util +{ + /// Strategy interface to control back-off between retry attempts. + public interface IBackOff + { + /// + /// Gets the a time span to wait before next retry. If the current retry reached the maximum number of retries, + /// the returned value is . + /// + TimeSpan GetNextBackOff(int currentRetry); + + /// Gets the maximum number of retries. + int MaxNumOfRetries { get; } + } +} diff --git a/Src/Support/Google.Apis.Auth/ExistingDependencies/IClock.cs b/Src/Support/Google.Apis.Auth/ExistingDependencies/IClock.cs new file mode 100644 index 00000000000..6848b0c1514 --- /dev/null +++ b/Src/Support/Google.Apis.Auth/ExistingDependencies/IClock.cs @@ -0,0 +1,56 @@ +/* +Copyright 2013 Google Inc + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +using System; + +namespace Google.Apis.Util +{ + /// Clock wrapper for getting the current time. + public interface IClock + { + /// + /// Gets a object that is set to the current date and time on this computer, + /// expressed as the local time. + /// + [Obsolete("System local time is almost always inappropriate to use. If you really need this, call UtcNow and then call ToLocalTime on the result")] + DateTime Now { get; } + + /// + /// Gets a object that is set to the current date and time on this computer, + /// expressed as UTC time. + /// + DateTime UtcNow { get; } + } + + /// + /// A default clock implementation that wraps the + /// and properties. + /// + public class SystemClock : IClock + { + /// Constructs a new system clock. + protected SystemClock() { } + + /// The default instance. + public static readonly IClock Default = new SystemClock(); + + /// + public DateTime Now => DateTime.Now; + + /// + public DateTime UtcNow => DateTime.UtcNow; + } +} diff --git a/Src/Support/Google.Apis.Auth/ExistingDependencies/IConfigurableHttpClientInitializer.cs b/Src/Support/Google.Apis.Auth/ExistingDependencies/IConfigurableHttpClientInitializer.cs new file mode 100644 index 00000000000..96eb729beed --- /dev/null +++ b/Src/Support/Google.Apis.Auth/ExistingDependencies/IConfigurableHttpClientInitializer.cs @@ -0,0 +1,33 @@ +/* +Copyright 2013 Google Inc + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Google.Apis.Http +{ + /// + /// HTTP client initializer for changing the default behavior of HTTP client. + /// Use this initializer to change default values like timeout and number of tries. + /// + public interface IConfigurableHttpClientInitializer + { + /// Initializes a HTTP client after it was created. + void Initialize(ConfigurableHttpClient httpClient); + } +} diff --git a/Src/Support/Google.Apis.Auth/ExistingDependencies/ICredentialedRequest.cs b/Src/Support/Google.Apis.Auth/ExistingDependencies/ICredentialedRequest.cs new file mode 100644 index 00000000000..67c58f8f7d9 --- /dev/null +++ b/Src/Support/Google.Apis.Auth/ExistingDependencies/ICredentialedRequest.cs @@ -0,0 +1,17 @@ +using Google.Apis.Http; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Google.Apis.Auth.ExistingDependencies; + +/// +/// TODO: Document this. +/// +public interface ICredentialedRequest +{ + /// + /// Credential to use for this request. + /// + public IHttpExecuteInterceptor Credential { get; set; } +} diff --git a/Src/Support/Google.Apis.Auth/ExistingDependencies/IDataStore.cs b/Src/Support/Google.Apis.Auth/ExistingDependencies/IDataStore.cs new file mode 100644 index 00000000000..6e440bd2923 --- /dev/null +++ b/Src/Support/Google.Apis.Auth/ExistingDependencies/IDataStore.cs @@ -0,0 +1,53 @@ +/* +Copyright 2013 Google Inc + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +using System; +using System.Threading.Tasks; + +namespace Google.Apis.Util.Store +{ + /// + /// Stores and manages data objects, where the key is a string and the value is an object. + /// + /// null keys are not allowed. + /// + /// + public interface IDataStore + { + /// Asynchronously stores the given value for the given key (replacing any existing value). + /// The type to store in the data store. + /// The key. + /// The value to store. + Task StoreAsync(string key, T value); + + /// + /// Asynchronously deletes the given key. The type is provided here as well because the "real" saved key should + /// contain type information as well, so the data store will be able to store the same key for different types. + /// + /// The type to delete from the data store. + /// The key to delete. + Task DeleteAsync(string key); + + /// Asynchronously returns the stored value for the given key or null if not found. + /// The type to retrieve from the data store. + /// The key to retrieve its value. + /// The stored object. + Task GetAsync(string key); + + /// Asynchronously clears all values in the data store. + Task ClearAsync(); + } +} \ No newline at end of file diff --git a/Src/Support/Google.Apis.Auth/ExistingDependencies/IHttpClientFactory.cs b/Src/Support/Google.Apis.Auth/ExistingDependencies/IHttpClientFactory.cs new file mode 100644 index 00000000000..f1cad0dc81a --- /dev/null +++ b/Src/Support/Google.Apis.Auth/ExistingDependencies/IHttpClientFactory.cs @@ -0,0 +1,53 @@ +/* +Copyright 2013 Google Inc + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +using System.Collections.Generic; + +// TODO: Remove entirely for the sake of + +namespace Google.Apis.Http +{ + /// Arguments for creating a HTTP client. + public class CreateHttpClientArgs + { + /// Gets or sets whether GZip is enabled. + public bool GZipEnabled { get; set; } + + /// Gets or sets the application name that is sent in the User-Agent header. + public string ApplicationName { get; set; } + + /// Gets a list of initializers to initialize the HTTP client instance. + public IList Initializers { get; private set; } + + /// Gets or sets the value for the x-goog-api-client header + public string GoogleApiClientHeader { get; set; } + + /// Constructs a new argument instance. + public CreateHttpClientArgs() + { + Initializers = new List(); + } + } + + /// + /// HTTP client factory creates configurable HTTP clients. A unique HTTP client should be created for each service. + /// + public interface IHttpClientFactory + { + /// Creates a new configurable HTTP client. + ConfigurableHttpClient CreateHttpClient(CreateHttpClientArgs args); + } +} diff --git a/Src/Support/Google.Apis.Auth/ExistingDependencies/IHttpExceptionHandler.cs b/Src/Support/Google.Apis.Auth/ExistingDependencies/IHttpExceptionHandler.cs new file mode 100644 index 00000000000..2ad9cf11502 --- /dev/null +++ b/Src/Support/Google.Apis.Auth/ExistingDependencies/IHttpExceptionHandler.cs @@ -0,0 +1,63 @@ +/* +Copyright 2013 Google Inc + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +using System; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; + +namespace Google.Apis.Http +{ + /// Argument class to . + public class HandleExceptionArgs + { + /// Gets or sets the sent request. + public HttpRequestMessage Request { get; set; } + + /// Gets or sets the exception which occurred during sending the request. + public Exception Exception { get; set; } + + /// Gets or sets the total number of tries to send the request. + public int TotalTries { get; set; } + + /// Gets or sets the current failed try. + public int CurrentFailedTry { get; set; } + + /// Gets an indication whether a retry will occur if the handler returns true. + public bool SupportsRetry + { + get { return TotalTries - CurrentFailedTry > 0; } + } + + /// Gets or sets the request's cancellation token. + public CancellationToken CancellationToken { get; set; } + } + + /// Exception handler is invoked when an exception is thrown during a HTTP request. + public interface IHttpExceptionHandler + { + /// + /// Handles an exception thrown when sending a HTTP request. + /// A simple rule must be followed, if you modify the request object in a way that the exception can be + /// resolved, you must return true. + /// + /// + /// Handle exception argument which properties such as the request, exception, current failed try. + /// + /// Whether this handler has made a change that requires the request to be resent. + Task HandleExceptionAsync(HandleExceptionArgs args); + } +} diff --git a/Src/Support/Google.Apis.Auth/ExistingDependencies/IHttpExecuteInterceptor.cs b/Src/Support/Google.Apis.Auth/ExistingDependencies/IHttpExecuteInterceptor.cs new file mode 100644 index 00000000000..3ffcbaa4080 --- /dev/null +++ b/Src/Support/Google.Apis.Auth/ExistingDependencies/IHttpExecuteInterceptor.cs @@ -0,0 +1,36 @@ +/* +Copyright 2013 Google Inc + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; + +namespace Google.Apis.Http +{ + /// + /// HTTP request execute interceptor to intercept a before it has + /// been sent. Sample usage is attaching "Authorization" header to a request. + /// + public interface IHttpExecuteInterceptor + { + /// + /// Invoked before the request is being sent. + /// + /// The HTTP request message. + /// Cancellation token to cancel the operation. + Task InterceptAsync(HttpRequestMessage request, CancellationToken cancellationToken); + } +} diff --git a/Src/Support/Google.Apis.Auth/ExistingDependencies/IHttpUnsuccessfulResponseHandler.cs b/Src/Support/Google.Apis.Auth/ExistingDependencies/IHttpUnsuccessfulResponseHandler.cs new file mode 100644 index 00000000000..3c0b9a11347 --- /dev/null +++ b/Src/Support/Google.Apis.Auth/ExistingDependencies/IHttpUnsuccessfulResponseHandler.cs @@ -0,0 +1,65 @@ +/* +Copyright 2013 Google Inc + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; + +namespace Google.Apis.Http +{ + /// Argument class to . + public class HandleUnsuccessfulResponseArgs + { + /// Gets or sets the sent request. + public HttpRequestMessage Request { get; set; } + + /// Gets or sets the abnormal response. + public HttpResponseMessage Response { get; set; } + + /// Gets or sets the total number of tries to send the request. + public int TotalTries { get; set; } + + /// Gets or sets the current failed try. + public int CurrentFailedTry { get; set; } + + /// Gets an indication whether a retry will occur if the handler returns true. + public bool SupportsRetry + { + get { return TotalTries - CurrentFailedTry > 0; } + } + + /// Gets or sets the request's cancellation token. + public CancellationToken CancellationToken { get; set; } + } + + /// + /// Unsuccessful response handler which is invoked when an abnormal HTTP response is returned when sending a HTTP + /// request. + /// + public interface IHttpUnsuccessfulResponseHandler + { + /// + /// Handles an abnormal response when sending a HTTP request. + /// A simple rule must be followed, if you modify the request object in a way that the abnormal response can + /// be resolved, you must return true. + /// + /// + /// Handle response argument which contains properties such as the request, response, current failed try. + /// + /// Whether this handler has made a change that requires the request to be resent. + Task HandleResponseAsync(HandleUnsuccessfulResponseArgs args); + } +} diff --git a/Src/Support/Google.Apis.Auth/ExistingDependencies/ILogger.cs b/Src/Support/Google.Apis.Auth/ExistingDependencies/ILogger.cs new file mode 100644 index 00000000000..dbdbbd2d8e5 --- /dev/null +++ b/Src/Support/Google.Apis.Auth/ExistingDependencies/ILogger.cs @@ -0,0 +1,62 @@ +/* +Copyright 2011 Google Inc + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +using System; + +namespace Google.Apis.Logging +{ + /// Describes a logging interface which is used for outputting messages. + public interface ILogger + { + /// Gets an indication whether debug output is logged or not. + bool IsDebugEnabled { get; } + + /// Returns a logger which will be associated with the specified type. + /// Type to which this logger belongs. + /// A type-associated logger. + ILogger ForType(Type type); + + /// Returns a logger which will be associated with the specified type. + /// A type-associated logger. + ILogger ForType(); + + /// Logs a debug message. + /// The message to log. + /// String.Format arguments (if applicable). + void Debug(string message, params object[] formatArgs); + + /// Logs an info message. + /// The message to log. + /// String.Format arguments (if applicable). + void Info(string message, params object[] formatArgs); + + /// Logs a warning. + /// The message to log. + /// String.Format arguments (if applicable). + void Warning(string message, params object[] formatArgs); + + /// Logs an error message resulting from an exception. + /// + /// The message to log. + /// String.Format arguments (if applicable). + void Error(Exception exception, string message, params object[] formatArgs); + + /// Logs an error message. + /// The message to log. + /// String.Format arguments (if applicable). + void Error(string message, params object[] formatArgs); + } +} diff --git a/Src/Support/Google.Apis.Auth/ExistingDependencies/NullLogger.cs b/Src/Support/Google.Apis.Auth/ExistingDependencies/NullLogger.cs new file mode 100644 index 00000000000..79ba38475f8 --- /dev/null +++ b/Src/Support/Google.Apis.Auth/ExistingDependencies/NullLogger.cs @@ -0,0 +1,59 @@ +/* +Copyright 2011 Google Inc + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +using System; + +namespace Google.Apis.Logging +{ + /// + /// Represents a NullLogger which does not do any logging. + /// + public class NullLogger : ILogger + { + /// + public bool IsDebugEnabled + { + get { return false; } + } + + /// + public ILogger ForType(Type type) + { + return new NullLogger(); + } + + /// + public ILogger ForType() + { + return new NullLogger(); + } + + /// + public void Info(string message, params object[] formatArgs) {} + + /// + public void Warning(string message, params object[] formatArgs) {} + + /// + public void Debug(string message, params object[] formatArgs) {} + + /// + public void Error(Exception exception, string message, params object[] formatArgs) {} + + /// + public void Error(string message, params object[] formatArgs) {} + } +} \ No newline at end of file diff --git a/Src/Support/Google.Apis.Auth/ExistingDependencies/ReplacementSerializer.cs b/Src/Support/Google.Apis.Auth/ExistingDependencies/ReplacementSerializer.cs new file mode 100644 index 00000000000..d01e0d3f126 --- /dev/null +++ b/Src/Support/Google.Apis.Auth/ExistingDependencies/ReplacementSerializer.cs @@ -0,0 +1,19 @@ +using Google.Apis.Auth.OAuth2; +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace Google.Apis.Auth.ExistingDependencies; + +// The replacement for NewtonsoftJsonSerializer +internal class ReplacementSerializer +{ + internal static string Serialize(T value) => default; + + internal static T Deserialize(string json) => default; + + public static Task DeserializeAsync(Stream stream, CancellationToken cancellationToken) => default; + + public static T Deserialize(Stream stream) => default; +} diff --git a/Src/Support/Google.Apis.Auth/ExistingDependencies/RequestError.cs b/Src/Support/Google.Apis.Auth/ExistingDependencies/RequestError.cs new file mode 100644 index 00000000000..eca7f1480d6 --- /dev/null +++ b/Src/Support/Google.Apis.Auth/ExistingDependencies/RequestError.cs @@ -0,0 +1,95 @@ +/* +Copyright 2011 Google Inc + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +using System.Collections.Generic; +using System.Text; + +using Google.Apis.Util; + +namespace Google.Apis.Requests +{ + /// + /// Collection of server errors + /// + public class RequestError + { + /// + /// Enumeration of known error codes which may occur during a request. + /// + public enum ErrorCodes + { + /// + /// The ETag condition specified caused the ETag verification to fail. + /// Depending on the ETagAction of the request this either means that a change to the object has been + /// made on the server, or that the object in question is still the same and has not been changed. + /// + ETagConditionFailed = 412 + } + + /// + /// Contains a list of all errors + /// + public IList Errors { get; set; } + + /// + /// The error code returned + /// + public int Code { get; set; } + + /// + /// The error message returned + /// + public string Message { get; set; } + + /// + /// The full content of the error response that + /// this instance was created from. + /// + /// + /// The response may contain custom information that is not represented + /// by any of the properties in . + /// + public string ErrorResponseContent { get; set; } + + internal bool IsOnlyRawContent => + Message is null && Code == 0 && Errors.IsNullOrEmpty(); + + /// + /// Returns a string summary of this error + /// + /// A string summary of this error + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + sb.AppendLine(GetType().FullName).Append(Message).AppendFormat(" [{0}]", Code).AppendLine(); + if (Errors.IsNullOrEmpty()) + { + sb.AppendLine("No individual errors"); + } + else + { + sb.AppendLine("Errors ["); + foreach (SingleError err in Errors) + { + sb.Append('\t').AppendLine(err.ToString()); + } + sb.AppendLine("]"); + } + + return sb.ToString(); + } + } +} diff --git a/Src/Support/Google.Apis.Auth/ExistingDependencies/SingleError.cs b/Src/Support/Google.Apis.Auth/ExistingDependencies/SingleError.cs new file mode 100644 index 00000000000..d1581055430 --- /dev/null +++ b/Src/Support/Google.Apis.Auth/ExistingDependencies/SingleError.cs @@ -0,0 +1,60 @@ +/* +Copyright 2011 Google Inc + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +namespace Google.Apis.Requests +{ + /// + /// A single server error + /// + public class SingleError + { + /// + /// The domain in which the error occured + /// + public string Domain { get; set; } + + /// + /// The reason the error was thrown + /// + public string Reason { get; set; } + + /// + /// The error message + /// + public string Message { get; set; } + + /// + /// Type of the location + /// + public string LocationType { get; set; } + + /// + /// Location where the error was thrown + /// + public string Location { get; set; } + + /// + /// Returns a string summary of this error + /// + /// A string summary of this error + public override string ToString() + { + return string.Format( + "Message[{0}] Location[{1} - {2}] Reason[{3}] Domain[{4}]", Message, Location, LocationType, Reason, + Domain); + } + } +} diff --git a/Src/Support/Google.Apis.Auth/ExistingDependencies/TaskExtensions.cs b/Src/Support/Google.Apis.Auth/ExistingDependencies/TaskExtensions.cs new file mode 100644 index 00000000000..dd456ca2bb0 --- /dev/null +++ b/Src/Support/Google.Apis.Auth/ExistingDependencies/TaskExtensions.cs @@ -0,0 +1,51 @@ +/* +Copyright 2020 Google Inc + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +using System.Threading; +using System.Threading.Tasks; + +namespace Google.Apis.Util +{ + // Note: this is duplicated between Google.Apis.Auth and Google.Apis.Core so it can stay internal. Please + // change both at the same time. + internal static class TaskExtensions + { + /// + /// Returns a task which can be cancelled by the given cancellation token, but otherwise observes the original + /// task's state. This does *not* cancel any work that the original task was doing, and should be used carefully. + /// + internal static Task WithCancellationToken(this Task task, CancellationToken cancellationToken) + { + if (!cancellationToken.CanBeCanceled) + { + return task; + } + + return ImplAsync(); + + // Separate async method to allow the above optimization to avoid creating any new state machines etc. + async Task ImplAsync() + { + var cts = new TaskCompletionSource(); + using (cancellationToken.Register(() => cts.TrySetCanceled())) + { + var completedTask = await Task.WhenAny(task, cts.Task).ConfigureAwait(false); + return await completedTask.ConfigureAwait(false); + } + } + } + } +} diff --git a/Src/Support/Google.Apis.Auth/ExistingDependencies/Utilities.cs b/Src/Support/Google.Apis.Auth/ExistingDependencies/Utilities.cs new file mode 100644 index 00000000000..71ff29894a8 --- /dev/null +++ b/Src/Support/Google.Apis.Auth/ExistingDependencies/Utilities.cs @@ -0,0 +1,102 @@ +/* +Copyright 2010 Google Inc + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.RegularExpressions; +using System.Reflection; + +namespace Google.Apis.Util +{ + /// A utility class which contains helper methods and extension methods. + internal static class Utilities + { + /// Returns the version of the core library. + public static string GetLibraryVersion() + { + return Regex.Match(typeof(Utilities).GetTypeInfo().Assembly.FullName, "Version=([\\d\\.]+)").Groups[1].ToString(); + } + + /// + /// A Google.Apis utility method for throwing an if the object is + /// null. + /// + public static T ThrowIfNull(this T obj, string paramName) + { + if (obj == null) + { + throw new ArgumentNullException(paramName); + } + + return obj; + } + + /// + /// A Google.Apis utility method for throwing an if the string is + /// null or empty. + /// + /// The original string. + public static string ThrowIfNullOrEmpty(this string str, string paramName) + { + if (string.IsNullOrEmpty(str)) + { + throw new ArgumentException("Parameter was empty", paramName); + } + return str; + } + + /// Returns true in case the enumerable is null or empty. + internal static bool IsNullOrEmpty(this IEnumerable coll) + { + return coll == null || coll.Count() == 0; + } + + /// + /// Checks that the given value is in fact defined in the enum used as the type argument of the method. + /// + /// The enum type to check the value within. + /// The value to check. + /// The name of the parameter whose value is being tested. + /// if it was a defined value + public static T CheckEnumValue(T value, string paramName) where T : struct + { + CheckArgument( + Enum.IsDefined(typeof(T), value), + paramName, + "Value {0} not defined in enum {1}", value, typeof(T).Name); + return value; + } + + /// + /// Checks that given argument-based condition is met, throwing an otherwise. + /// + /// The (already evaluated) condition to check. + /// The name of the parameter whose value is being tested. + /// The format string to use to create the exception message if the + /// condition is not met. + /// The first argument to the format string. + /// The second argument to the format string. + public static void CheckArgument(bool condition, string paramName, string format, T1 arg0, T2 arg1) + { + if (!condition) + { + throw new ArgumentException(string.Format(format, arg0, arg1), paramName); + } + } + + } +} diff --git a/Src/Support/Google.Apis.Auth/ExistingDependencies/VisibleForTestOnly.cs b/Src/Support/Google.Apis.Auth/ExistingDependencies/VisibleForTestOnly.cs new file mode 100644 index 00000000000..b45d2d68915 --- /dev/null +++ b/Src/Support/Google.Apis.Auth/ExistingDependencies/VisibleForTestOnly.cs @@ -0,0 +1,29 @@ +/* +Copyright 2010 Google Inc + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +using System; + +namespace Google.Apis.Testing +{ + /// + /// Marker Attribute to indicate a Method/Class/Property has been made more visible for purpose of testing. + /// Mark the member as internal and make the testing assembly a friend using + /// [assembly: InternalsVisibleTo("Full.Name.Of.Testing.Assembly")] + /// + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Property | + AttributeTargets.Field)] + public class VisibleForTestOnly : Attribute { } +} \ No newline at end of file diff --git a/Src/Support/Google.Apis.Auth/Google.Apis.Auth.csproj b/Src/Support/Google.Apis.Auth/Google.Apis.Auth.csproj index 75b2cc61ff0..c26b87d1053 100644 --- a/Src/Support/Google.Apis.Auth/Google.Apis.Auth.csproj +++ b/Src/Support/Google.Apis.Auth/Google.Apis.Auth.csproj @@ -26,9 +26,9 @@ Supported Platforms: - - - + + + @@ -37,6 +37,8 @@ Supported Platforms: + + diff --git a/Src/Support/Google.Apis.Auth/JsonWebSignature.cs b/Src/Support/Google.Apis.Auth/JsonWebSignature.cs index 3c767624413..c7a5bf526c0 100644 --- a/Src/Support/Google.Apis.Auth/JsonWebSignature.cs +++ b/Src/Support/Google.Apis.Auth/JsonWebSignature.cs @@ -14,7 +14,6 @@ You may obtain a copy of the License at limitations under the License. */ -using Google.Apis.Json; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; @@ -51,7 +50,7 @@ public static Task VerifySignedTokenAsync( /// If the token is invalid or expired. /// The type of the payload to return, so user code can validate /// additional claims. Should extend . Payload information will be deserialized - /// using . + /// using the default serializer. public async static Task VerifySignedTokenAsync( string signedJwt, SignedTokenVerificationOptions options = null, CancellationToken cancellationToken = default) where TPayload : Payload diff --git a/Src/Support/Google.Apis.Auth/OAuth2/AwsExternalAccountCredential.AwsSecurityCredential.cs b/Src/Support/Google.Apis.Auth/OAuth2/AwsExternalAccountCredential.AwsSecurityCredential.cs index 1e541ede3df..2319880ee0a 100644 --- a/Src/Support/Google.Apis.Auth/OAuth2/AwsExternalAccountCredential.AwsSecurityCredential.cs +++ b/Src/Support/Google.Apis.Auth/OAuth2/AwsExternalAccountCredential.AwsSecurityCredential.cs @@ -14,7 +14,7 @@ You may obtain a copy of the License at limitations under the License. */ -using Google.Apis.Json; +using Google.Apis.Auth.ExistingDependencies; using Newtonsoft.Json; using System; using System.IO; @@ -97,7 +97,7 @@ internal static async Task MaybeFromMetadataAsync(AwsMet && credentialJson != "") { // Deserialize the credentials - var deserializedCredentials = NewtonsoftJsonSerializer.Instance.Deserialize(credentialJson); + var deserializedCredentials = ReplacementSerializer.Deserialize(credentialJson); if (deserializedCredentials.IsSuccess && !string.IsNullOrEmpty(deserializedCredentials.AccessKeyId) diff --git a/Src/Support/Google.Apis.Auth/OAuth2/AwsExternalAccountCredential.cs b/Src/Support/Google.Apis.Auth/OAuth2/AwsExternalAccountCredential.cs index aa2cba3e6eb..d65feb37549 100644 --- a/Src/Support/Google.Apis.Auth/OAuth2/AwsExternalAccountCredential.cs +++ b/Src/Support/Google.Apis.Auth/OAuth2/AwsExternalAccountCredential.cs @@ -14,8 +14,8 @@ You may obtain a copy of the License at limitations under the License. */ +using Google.Apis.Auth.ExistingDependencies; using Google.Apis.Http; -using Google.Apis.Json; using Google.Apis.Util; using System; using System.Collections.Generic; @@ -140,7 +140,7 @@ protected async override Task GetSubjectTokenAsyncImpl(CancellationToken var subjectToken = AwsSignedSubjectToken.Create(awsSecurityCredentials, awsRegion, new Uri(regionalizedVerificationUrl), Audience, Clock); - return Uri.EscapeDataString(NewtonsoftJsonSerializer.Instance.Serialize(subjectToken)); + return Uri.EscapeDataString(ReplacementSerializer.Serialize(subjectToken)); } /// diff --git a/Src/Support/Google.Apis.Auth/OAuth2/DefaultCredentialProvider.cs b/Src/Support/Google.Apis.Auth/OAuth2/DefaultCredentialProvider.cs index 55f0e8417bd..eec81cd8efd 100644 --- a/Src/Support/Google.Apis.Auth/OAuth2/DefaultCredentialProvider.cs +++ b/Src/Support/Google.Apis.Auth/OAuth2/DefaultCredentialProvider.cs @@ -14,16 +14,15 @@ You may obtain a copy of the License at limitations under the License. */ -using System; -using System.IO; -using System.Threading; -using System.Threading.Tasks; - +using Google.Apis.Auth.ExistingDependencies; using Google.Apis.Auth.OAuth2.Flows; using Google.Apis.Auth.OAuth2.Responses; -using Google.Apis.Json; using Google.Apis.Logging; using Google.Apis.Util; +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; namespace Google.Apis.Auth.OAuth2 { @@ -165,7 +164,7 @@ internal GoogleCredential CreateDefaultCredentialFromStream(Stream stream) JsonCredentialParameters credentialParameters; try { - credentialParameters = NewtonsoftJsonSerializer.Instance.Deserialize(stream); + credentialParameters = ReplacementSerializer.Deserialize(stream); } catch (Exception e) { @@ -180,8 +179,7 @@ internal async Task CreateDefaultCredentialFromStreamAsync(Str JsonCredentialParameters credentialParameters; try { - credentialParameters = await NewtonsoftJsonSerializer.Instance - .DeserializeAsync(stream, cancellationToken) + credentialParameters = await ReplacementSerializer.DeserializeAsync(stream, cancellationToken) .ConfigureAwait(false); } catch (Exception e) @@ -197,7 +195,7 @@ internal GoogleCredential CreateDefaultCredentialFromJson(string json) JsonCredentialParameters credentialParameters; try { - credentialParameters = NewtonsoftJsonSerializer.Instance.Deserialize(json); + credentialParameters = ReplacementSerializer.Deserialize(json); } catch (Exception e) { diff --git a/Src/Support/Google.Apis.Auth/OAuth2/FileSourcedExternalAccountCredential.cs b/Src/Support/Google.Apis.Auth/OAuth2/FileSourcedExternalAccountCredential.cs index cd12981387b..0ccae405dcc 100644 --- a/Src/Support/Google.Apis.Auth/OAuth2/FileSourcedExternalAccountCredential.cs +++ b/Src/Support/Google.Apis.Auth/OAuth2/FileSourcedExternalAccountCredential.cs @@ -14,8 +14,8 @@ You may obtain a copy of the License at limitations under the License. */ +using Google.Apis.Auth.ExistingDependencies; using Google.Apis.Http; -using Google.Apis.Json; using System.Collections.Generic; using System.IO; using System.Threading; @@ -98,7 +98,7 @@ protected override async Task GetSubjectTokenAsyncImpl(CancellationToken } else { - var jsonResponse = NewtonsoftJsonSerializer.Instance.Deserialize>(fileContent); + var jsonResponse = ReplacementSerializer.Deserialize>(fileContent); subjectToken = jsonResponse[SubjectTokenJsonFieldName]; } diff --git a/Src/Support/Google.Apis.Auth/OAuth2/Flows/AuthorizationCodeFlow.cs b/Src/Support/Google.Apis.Auth/OAuth2/Flows/AuthorizationCodeFlow.cs index 1f2ed326249..e80a3f06895 100644 --- a/Src/Support/Google.Apis.Auth/OAuth2/Flows/AuthorizationCodeFlow.cs +++ b/Src/Support/Google.Apis.Auth/OAuth2/Flows/AuthorizationCodeFlow.cs @@ -14,6 +14,7 @@ You may obtain a copy of the License at limitations under the License. */ +using Google.Apis.Auth.ExistingDependencies; using Google.Apis.Auth.OAuth2.Requests; using Google.Apis.Auth.OAuth2.Responses; using Google.Apis.Http; @@ -203,7 +204,7 @@ public AuthorizationCodeFlow(Initializer initializer) // Set the HTTP client. DefaultExponentialBackOffPolicy = initializer.DefaultExponentialBackOffPolicy; - HttpClientFactory = initializer.HttpClientFactory ?? new HttpClientFactory(); + HttpClientFactory = initializer.HttpClientFactory ?? new AuthHttpClientFactory(); var httpArgs = new CreateHttpClientArgs(); // Add exponential back-off initializer if necessary. diff --git a/Src/Support/Google.Apis.Auth/OAuth2/Flows/GoogleAuthorizationCodeFlow.cs b/Src/Support/Google.Apis.Auth/OAuth2/Flows/GoogleAuthorizationCodeFlow.cs index 09e5b89f416..e3be9f277f0 100644 --- a/Src/Support/Google.Apis.Auth/OAuth2/Flows/GoogleAuthorizationCodeFlow.cs +++ b/Src/Support/Google.Apis.Auth/OAuth2/Flows/GoogleAuthorizationCodeFlow.cs @@ -14,11 +14,10 @@ You may obtain a copy of the License at limitations under the License. */ +using Google.Apis.Auth.ExistingDependencies; using Google.Apis.Auth.OAuth2.Requests; using Google.Apis.Auth.OAuth2.Responses; using Google.Apis.Http; -using Google.Apis.Json; -using Google.Apis.Util; using System; using System.Collections.Generic; using System.Net.Http; @@ -120,7 +119,7 @@ public override async Task RevokeTokenAsync(string userId, string token, if (!response.IsSuccessStatusCode) { var content = await response.Content.ReadAsStringAsync().ConfigureAwait(false); - var error = NewtonsoftJsonSerializer.Instance.Deserialize(content); + var error = ReplacementSerializer.Deserialize(content); throw new TokenResponseException(error, response.StatusCode); } diff --git a/Src/Support/Google.Apis.Auth/OAuth2/Flows/IHttpAuthorizationFlow.cs b/Src/Support/Google.Apis.Auth/OAuth2/Flows/IHttpAuthorizationFlow.cs index b08ff294c8f..c26d7fdfced 100644 --- a/Src/Support/Google.Apis.Auth/OAuth2/Flows/IHttpAuthorizationFlow.cs +++ b/Src/Support/Google.Apis.Auth/OAuth2/Flows/IHttpAuthorizationFlow.cs @@ -27,7 +27,7 @@ internal interface IHttpAuthorizationFlow : IAuthorizationCodeFlow /// given HTTP client factory. /// /// The http client factory to be used by the new instance. - /// May be null, in which case the default will be used. + /// May be null, in which case the default will be used. /// A new instance with the same type as this but that will use /// to obtain an to be used for token related operations. IHttpAuthorizationFlow WithHttpClientFactory(IHttpClientFactory httpClientFactory); diff --git a/Src/Support/Google.Apis.Auth/OAuth2/GoogleClientSecrets.cs b/Src/Support/Google.Apis.Auth/OAuth2/GoogleClientSecrets.cs index 105e41cd649..4ca2f41672d 100644 --- a/Src/Support/Google.Apis.Auth/OAuth2/GoogleClientSecrets.cs +++ b/Src/Support/Google.Apis.Auth/OAuth2/GoogleClientSecrets.cs @@ -14,7 +14,7 @@ You may obtain a copy of the License at limitations under the License. */ -using Google.Apis.Json; +using Google.Apis.Auth.ExistingDependencies; using System; using System.IO; using System.Threading; @@ -58,11 +58,11 @@ public static GoogleClientSecrets Load(Stream stream) => /// Loads the Google client secret from the input stream. public static GoogleClientSecrets FromStream(Stream stream) => - NewtonsoftJsonSerializer.Instance.Deserialize(stream); + ReplacementSerializer.Deserialize(stream); /// Asynchronously loads the Google client secret from the input stream. public static Task FromStreamAsync(Stream stream, CancellationToken cancellationToken = default) => - NewtonsoftJsonSerializer.Instance.DeserializeAsync(stream, cancellationToken); + ReplacementSerializer.DeserializeAsync(stream, cancellationToken); /// Loads the Google client secret from a JSON file. public static GoogleClientSecrets FromFile(string clientSecretsFilePath) diff --git a/Src/Support/Google.Apis.Auth/OAuth2/GoogleCredential.cs b/Src/Support/Google.Apis.Auth/OAuth2/GoogleCredential.cs index 1a53e28fcdc..b2af0fe26c9 100644 --- a/Src/Support/Google.Apis.Auth/OAuth2/GoogleCredential.cs +++ b/Src/Support/Google.Apis.Auth/OAuth2/GoogleCredential.cs @@ -327,7 +327,7 @@ GoogleAuthConsts.EnvironmentQuotaProject is string environmentQuotaProject /// Creates a copy of this credential with the specified HTTP client factory. /// /// The HTTP client factory to be used by the new credential. - /// May be null, in which case the default will be used. + /// May be null, in which case the default will be used. public virtual GoogleCredential CreateWithHttpClientFactory(IHttpClientFactory factory) => new GoogleCredential(credential.WithHttpClientFactory(factory)); diff --git a/Src/Support/Google.Apis.Auth/OAuth2/GoogleWebAuthorizationBroker.cs b/Src/Support/Google.Apis.Auth/OAuth2/GoogleWebAuthorizationBroker.cs index 0d795f151fc..ccf86f93fa9 100644 --- a/Src/Support/Google.Apis.Auth/OAuth2/GoogleWebAuthorizationBroker.cs +++ b/Src/Support/Google.Apis.Auth/OAuth2/GoogleWebAuthorizationBroker.cs @@ -21,6 +21,7 @@ limitations under the License. using System.Threading.Tasks; using Google.Apis.Auth.OAuth2.Flows; +using Google.Apis.Util; using Google.Apis.Util.Store; namespace Google.Apis.Auth.OAuth2 @@ -34,22 +35,10 @@ namespace Google.Apis.Auth.OAuth2 /// public class GoogleWebAuthorizationBroker { - // It's unforunate this is a public field. But it cannot be changed due to backward compatibility. - /// The folder which is used by the . - /// - /// The reason that this is not 'private const' is that a user can change it and store the credentials in a - /// different location. - /// - public static string Folder = "Google.Apis.Auth"; - /// /// Asynchronously authorizes the specified user. /// Requires user interaction; see remarks for more details. /// - /// - /// In case no data store is specified, will be used by - /// default. - /// /// The client secrets. /// /// The scopes which indicate the Google API access your application is requesting. @@ -75,10 +64,6 @@ public static async Task AuthorizeAsync(ClientSecrets clientSecr /// Asynchronously authorizes the specified user. /// Requires user interaction; see remarks for more details. /// - /// - /// In case no data store is specified, will be used by - /// default. - /// /// /// The client secrets stream. The authorization code flow constructor is responsible for disposing the stream. /// @@ -87,12 +72,12 @@ public static async Task AuthorizeAsync(ClientSecrets clientSecr /// /// The user to authorize. /// Cancellation token to cancel an operation. - /// The data store, if not specified a file data store will be used. + /// The data store. /// The code receiver, if not specified a local server code receiver will be used. /// User credential. public static async Task AuthorizeAsync(Stream clientSecretsStream, IEnumerable scopes, string user, CancellationToken taskCancellationToken, - IDataStore dataStore = null, ICodeReceiver codeReceiver = null) + IDataStore dataStore, ICodeReceiver codeReceiver = null) { var initializer = new GoogleAuthorizationCodeFlow.Initializer { @@ -136,16 +121,15 @@ public static async Task ReauthorizeAsync(UserCredential userCredential, /// /// The user to authorize. /// Cancellation token to cancel an operation. - /// The data store, if not specified a file data store will be used. + /// The data store. Must not be null. /// The code receiver, if not specified a local server code receiver will be used. /// User credential. public static async Task AuthorizeAsync( GoogleAuthorizationCodeFlow.Initializer initializer, IEnumerable scopes, string user, - CancellationToken taskCancellationToken, IDataStore dataStore = null, - ICodeReceiver codeReceiver = null) + CancellationToken taskCancellationToken, IDataStore dataStore, ICodeReceiver codeReceiver = null) { initializer.Scopes = scopes; - initializer.DataStore = dataStore ?? new FileDataStore(Folder); + initializer.DataStore = dataStore.ThrowIfNull(nameof(dataStore)); var flow = new GoogleAuthorizationCodeFlow(initializer); codeReceiver = codeReceiver ?? new LocalServerCodeReceiver(); diff --git a/Src/Support/Google.Apis.Auth/OAuth2/IGoogleCredential.cs b/Src/Support/Google.Apis.Auth/OAuth2/IGoogleCredential.cs index 9b86708e62b..d4191854548 100644 --- a/Src/Support/Google.Apis.Auth/OAuth2/IGoogleCredential.cs +++ b/Src/Support/Google.Apis.Auth/OAuth2/IGoogleCredential.cs @@ -76,7 +76,7 @@ internal interface IGoogleCredential : ICredential, ITokenAccessWithHeaders /// given HTTP client factory. /// /// The http client factory to be used by the new instance. - /// May be null in which case the default will be used. + /// May be null in which case the default will be used. /// A new instance with the same type as this but that will use /// to obtain an to be used for token and other operations. IGoogleCredential WithHttpClientFactory(IHttpClientFactory httpClientFactory); diff --git a/Src/Support/Google.Apis.Auth/OAuth2/ITokenAccess.cs b/Src/Support/Google.Apis.Auth/OAuth2/ITokenAccess.cs index e66b4279745..5ecdb538a3e 100644 --- a/Src/Support/Google.Apis.Auth/OAuth2/ITokenAccess.cs +++ b/Src/Support/Google.Apis.Auth/OAuth2/ITokenAccess.cs @@ -17,14 +17,12 @@ limitations under the License. using System.Threading; using System.Threading.Tasks; -using Google.Apis.Auth.OAuth2.Responses; - namespace Google.Apis.Auth.OAuth2 { /// /// Allows direct retrieval of access tokens to authenticate requests. /// This is necessary for workflows where you don't want to use - /// to access the API. + /// BaseClientService to access the API. /// (e.g. gRPC that implemenents the entire HTTP2 stack internally). /// public interface ITokenAccess diff --git a/Src/Support/Google.Apis.Auth/OAuth2/RequestExtensions.cs b/Src/Support/Google.Apis.Auth/OAuth2/RequestExtensions.cs index 6b1dc8c06e3..4841e8c33af 100644 --- a/Src/Support/Google.Apis.Auth/OAuth2/RequestExtensions.cs +++ b/Src/Support/Google.Apis.Auth/OAuth2/RequestExtensions.cs @@ -14,8 +14,8 @@ You may obtain a copy of the License at limitations under the License. */ +using Google.Apis.Auth.ExistingDependencies; using Google.Apis.Http; -using Google.Apis.Requests; using Google.Apis.Util; using System; @@ -36,7 +36,7 @@ public static class RequestExtensions /// The request which requires a credential. Must not be null. /// The credential to use for this request only. Must not be null. /// - public static T AddCredential(this T request, ICredential credential) where T : ClientServiceRequest + public static T AddCredential(this T request, ICredential credential) where T : ICredentialedRequest { request.ThrowIfNull(nameof(request)); credential.ThrowIfNull(nameof(credential)); diff --git a/Src/Support/Google.Apis.Auth/OAuth2/Requests/AuthorizationCodeRequestUrl.cs b/Src/Support/Google.Apis.Auth/OAuth2/Requests/AuthorizationCodeRequestUrl.cs index d608f991981..997bc1c3b31 100644 --- a/Src/Support/Google.Apis.Auth/OAuth2/Requests/AuthorizationCodeRequestUrl.cs +++ b/Src/Support/Google.Apis.Auth/OAuth2/Requests/AuthorizationCodeRequestUrl.cs @@ -14,13 +14,8 @@ You may obtain a copy of the License at limitations under the License. */ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - using Google.Apis.Requests; -using Google.Apis.Requests.Parameters; +using System; namespace Google.Apis.Auth.OAuth2.Requests { @@ -43,12 +38,8 @@ public AuthorizationCodeRequestUrl(Uri authorizationServerUrl) /// Creates a which is used to request the authorization code. public Uri Build() { - var builder = new RequestBuilder() - { - BaseUri = AuthorizationServerUrl - }; - ParameterUtils.InitParameters(builder, this); - return builder.BuildUri(); + // FIXME: Implement the code without using common RequestBuilder stuff. + return null; } } } diff --git a/Src/Support/Google.Apis.Auth/OAuth2/Requests/AuthorizationCodeTokenRequest.cs b/Src/Support/Google.Apis.Auth/OAuth2/Requests/AuthorizationCodeTokenRequest.cs index 2adaa41cd7e..82aa1f6765a 100644 --- a/Src/Support/Google.Apis.Auth/OAuth2/Requests/AuthorizationCodeTokenRequest.cs +++ b/Src/Support/Google.Apis.Auth/OAuth2/Requests/AuthorizationCodeTokenRequest.cs @@ -14,7 +14,7 @@ You may obtain a copy of the License at limitations under the License. */ -using System; +// FIXME: Write code to create an HttpRequestMessage for this. namespace Google.Apis.Auth.OAuth2.Requests { @@ -25,13 +25,11 @@ namespace Google.Apis.Auth.OAuth2.Requests public class AuthorizationCodeTokenRequest : TokenRequest { /// Gets or sets the authorization code received from the authorization server. - [Google.Apis.Util.RequestParameterAttribute("code")] public string Code { get; set; } /// /// Gets or sets the redirect URI parameter matching the redirect URI parameter in the authorization request. /// - [Google.Apis.Util.RequestParameterAttribute("redirect_uri")] public string RedirectUri { get; set; } /// diff --git a/Src/Support/Google.Apis.Auth/OAuth2/Requests/AuthorizationRequestUrl.cs b/Src/Support/Google.Apis.Auth/OAuth2/Requests/AuthorizationRequestUrl.cs index 712797f1c2e..a20e6799c13 100644 --- a/Src/Support/Google.Apis.Auth/OAuth2/Requests/AuthorizationRequestUrl.cs +++ b/Src/Support/Google.Apis.Auth/OAuth2/Requests/AuthorizationRequestUrl.cs @@ -29,11 +29,9 @@ public class AuthorizationRequestUrl /// token for requesting an access token (implicit grant), or space separated registered extension /// values. See http://tools.ietf.org/html/rfc6749#section-3.1.1 for more details /// - [Google.Apis.Util.RequestParameterAttribute("response_type", Google.Apis.Util.RequestParameterType.Query)] public string ResponseType { get; set; } /// Gets or sets the client identifier. - [Google.Apis.Util.RequestParameterAttribute("client_id", Google.Apis.Util.RequestParameterType.Query)] public string ClientId { get; set; } /// @@ -41,21 +39,18 @@ public class AuthorizationRequestUrl /// client after a successful authorization grant, as specified in /// http://tools.ietf.org/html/rfc6749#section-3.1.2 or null for none. /// - [Google.Apis.Util.RequestParameterAttribute("redirect_uri", Google.Apis.Util.RequestParameterType.Query)] public string RedirectUri { get; set; } /// /// Gets or sets space-separated list of scopes, as specified in http://tools.ietf.org/html/rfc6749#section-3.3 /// or null for none. /// - [Google.Apis.Util.RequestParameterAttribute("scope", Google.Apis.Util.RequestParameterType.Query)] public string Scope { get; set; } /// /// Gets or sets the state (an opaque value used by the client to maintain state between the request and /// callback, as mentioned in http://tools.ietf.org/html/rfc6749#section-3.1.2.2 or null for none. /// - [Google.Apis.Util.RequestParameterAttribute("state", Google.Apis.Util.RequestParameterType.Query)] public string State { get; set; } private readonly Uri authorizationServerUrl; diff --git a/Src/Support/Google.Apis.Auth/OAuth2/Requests/GoogleAssertionTokenRequest.cs b/Src/Support/Google.Apis.Auth/OAuth2/Requests/GoogleAssertionTokenRequest.cs index 57154e6da4f..3b38b66b4be 100644 --- a/Src/Support/Google.Apis.Auth/OAuth2/Requests/GoogleAssertionTokenRequest.cs +++ b/Src/Support/Google.Apis.Auth/OAuth2/Requests/GoogleAssertionTokenRequest.cs @@ -15,6 +15,8 @@ limitations under the License. */ +// FIXME: Write code to create an HttpRequestMessage for this. + namespace Google.Apis.Auth.OAuth2.Requests { /// @@ -24,7 +26,6 @@ namespace Google.Apis.Auth.OAuth2.Requests public class GoogleAssertionTokenRequest : TokenRequest { /// Gets or sets the JWT (including signature). - [Google.Apis.Util.RequestParameterAttribute("assertion")] public string Assertion { get; set; } /// diff --git a/Src/Support/Google.Apis.Auth/OAuth2/Requests/GoogleAuthorizationCodeRequestUrl.cs b/Src/Support/Google.Apis.Auth/OAuth2/Requests/GoogleAuthorizationCodeRequestUrl.cs index 54fbcf62df1..8fdd4a29260 100644 --- a/Src/Support/Google.Apis.Auth/OAuth2/Requests/GoogleAuthorizationCodeRequestUrl.cs +++ b/Src/Support/Google.Apis.Auth/OAuth2/Requests/GoogleAuthorizationCodeRequestUrl.cs @@ -17,6 +17,8 @@ limitations under the License. using System; using System.Collections.Generic; +// FIXME: Write code to create an HttpRequestMessage for this. + namespace Google.Apis.Auth.OAuth2.Requests { /// @@ -30,7 +32,6 @@ public class GoogleAuthorizationCodeRequestUrl : AuthorizationCodeRequestUrl /// Gets or sets the access type. Set online to request on-line access or offline to request /// off-line access or null for the default behavior. The default value is offline. /// - [Google.Apis.Util.RequestParameterAttribute("access_type", Google.Apis.Util.RequestParameterType.Query)] public string AccessType { get; set; } /// @@ -39,14 +40,12 @@ public class GoogleAuthorizationCodeRequestUrl : AuthorizationCodeRequestUrl /// See OpenIDConnect documentation /// for details. /// - [Google.Apis.Util.RequestParameterAttribute("prompt", Google.Apis.Util.RequestParameterType.Query)] public string Prompt { get; set; } /// /// Gets or sets prompt for consent behavior auto to request auto-approval orforce to force the /// approval UI to show, or null for the default behavior. /// - [Google.Apis.Util.RequestParameterAttribute("approval_prompt", Google.Apis.Util.RequestParameterType.Query)] [Obsolete("Unused for Google OpenID; use the 'Prompt' property instead.")] public string ApprovalPrompt { get; set; } @@ -56,7 +55,6 @@ public class GoogleAuthorizationCodeRequestUrl : AuthorizationCodeRequestUrl /// hint to the Authentication Server. Passing this hint will either pre-fill the email box on the sign-in form /// or select the proper multi-login session, thereby simplifying the login flow. /// - [Google.Apis.Util.RequestParameterAttribute("login_hint", Google.Apis.Util.RequestParameterType.Query)] public string LoginHint { get; set; } /// @@ -66,8 +64,6 @@ public class GoogleAuthorizationCodeRequestUrl : AuthorizationCodeRequestUrl /// authorizations granted to this user/application combination for other scopes. /// /// Currently unsupported for installed apps. - [Google.Apis.Util.RequestParameterAttribute("include_granted_scopes", - Google.Apis.Util.RequestParameterType.Query)] public string IncludeGrantedScopes { get; set; } /// @@ -75,7 +71,6 @@ public class GoogleAuthorizationCodeRequestUrl : AuthorizationCodeRequestUrl /// a random value generated by your app that enables replay protection. /// See https://developers.google.com/identity/protocols/OpenIDConnect for more details. /// - [Google.Apis.Util.RequestParameterAttribute("nonce", Google.Apis.Util.RequestParameterType.Query)] public string Nonce { get; set; } /// @@ -86,8 +81,6 @@ public class GoogleAuthorizationCodeRequestUrl : AuthorizationCodeRequestUrl /// The name of this parameter is used only for the constructor and will not end up in the resultant query /// string. /// - [Google.Apis.Util.RequestParameterAttribute("user_defined_query_params", - Google.Apis.Util.RequestParameterType.UserDefinedQueries)] public IEnumerable> UserDefinedQueryParams { get; set; } /// diff --git a/Src/Support/Google.Apis.Auth/OAuth2/Requests/GoogleRevokeTokenRequest.cs b/Src/Support/Google.Apis.Auth/OAuth2/Requests/GoogleRevokeTokenRequest.cs index a6be5caedf9..c1ce461d379 100644 --- a/Src/Support/Google.Apis.Auth/OAuth2/Requests/GoogleRevokeTokenRequest.cs +++ b/Src/Support/Google.Apis.Auth/OAuth2/Requests/GoogleRevokeTokenRequest.cs @@ -16,8 +16,7 @@ limitations under the License. using System; -using Google.Apis.Requests; -using Google.Apis.Requests.Parameters; +// FIXME: Write code to create an HttpRequestMessage for this. namespace Google.Apis.Auth.OAuth2.Requests { @@ -35,7 +34,6 @@ public Uri RevokeTokenUrl } /// Gets or sets the token to revoke. - [Google.Apis.Util.RequestParameterAttribute("token")] public string Token { get; set; } public GoogleRevokeTokenRequest(Uri revokeTokenUrl) @@ -46,12 +44,8 @@ public GoogleRevokeTokenRequest(Uri revokeTokenUrl) /// Creates a which is used to request the authorization code. public Uri Build() { - var builder = new RequestBuilder() - { - BaseUri = revokeTokenUrl - }; - ParameterUtils.InitParameters(builder, this); - return builder.BuildUri(); + // FIXME: Implement the code without using common RequestBuilder stuff. + return null; } } } diff --git a/Src/Support/Google.Apis.Auth/OAuth2/Requests/IamSignBlobRequest.cs b/Src/Support/Google.Apis.Auth/OAuth2/Requests/IamSignBlobRequest.cs index 5a916aaefb8..ac9fc5d60ee 100644 --- a/Src/Support/Google.Apis.Auth/OAuth2/Requests/IamSignBlobRequest.cs +++ b/Src/Support/Google.Apis.Auth/OAuth2/Requests/IamSignBlobRequest.cs @@ -17,6 +17,8 @@ limitations under the License. using Newtonsoft.Json; using System.Collections.Generic; +// FIXME: Write code to create an HttpRequestMessage for this. + namespace Google.Apis.Auth.OAuth2.Requests { internal class IamSignBlobRequest diff --git a/Src/Support/Google.Apis.Auth/OAuth2/Requests/ImpersonationAccessTokenRequest.cs b/Src/Support/Google.Apis.Auth/OAuth2/Requests/ImpersonationAccessTokenRequest.cs index 14f167ff271..c5219ea9a49 100644 --- a/Src/Support/Google.Apis.Auth/OAuth2/Requests/ImpersonationAccessTokenRequest.cs +++ b/Src/Support/Google.Apis.Auth/OAuth2/Requests/ImpersonationAccessTokenRequest.cs @@ -17,6 +17,8 @@ limitations under the License. using Newtonsoft.Json; using System.Collections.Generic; +// FIXME: Write code to create an HttpRequestMessage for this. + namespace Google.Apis.Auth.OAuth2.Requests { /// diff --git a/Src/Support/Google.Apis.Auth/OAuth2/Requests/ImpersonationOIdCTokenRequest.cs b/Src/Support/Google.Apis.Auth/OAuth2/Requests/ImpersonationOIdCTokenRequest.cs index 1b3293d779b..43a74e26c6e 100644 --- a/Src/Support/Google.Apis.Auth/OAuth2/Requests/ImpersonationOIdCTokenRequest.cs +++ b/Src/Support/Google.Apis.Auth/OAuth2/Requests/ImpersonationOIdCTokenRequest.cs @@ -16,6 +16,8 @@ limitations under the License. using Newtonsoft.Json; +// FIXME: Write code to create an HttpRequestMessage for this. + namespace Google.Apis.Auth.OAuth2.Requests { /// diff --git a/Src/Support/Google.Apis.Auth/OAuth2/Requests/ImpersonationRequest.cs b/Src/Support/Google.Apis.Auth/OAuth2/Requests/ImpersonationRequest.cs index 9b7714e2733..539f4224424 100644 --- a/Src/Support/Google.Apis.Auth/OAuth2/Requests/ImpersonationRequest.cs +++ b/Src/Support/Google.Apis.Auth/OAuth2/Requests/ImpersonationRequest.cs @@ -17,6 +17,8 @@ limitations under the License. using Newtonsoft.Json; using System.Collections.Generic; +// FIXME: Write code to create an HttpRequestMessage for this. + namespace Google.Apis.Auth.OAuth2.Requests { internal abstract class ImpersonationRequest diff --git a/Src/Support/Google.Apis.Auth/OAuth2/Requests/RefreshTokenRequest.cs b/Src/Support/Google.Apis.Auth/OAuth2/Requests/RefreshTokenRequest.cs index 6456406400a..6fb27334d6a 100644 --- a/Src/Support/Google.Apis.Auth/OAuth2/Requests/RefreshTokenRequest.cs +++ b/Src/Support/Google.Apis.Auth/OAuth2/Requests/RefreshTokenRequest.cs @@ -14,6 +14,8 @@ You may obtain a copy of the License at limitations under the License. */ +// FIXME: Write code to create an HttpRequestMessage for this. + namespace Google.Apis.Auth.OAuth2.Requests { /// @@ -23,7 +25,6 @@ namespace Google.Apis.Auth.OAuth2.Requests public class RefreshTokenRequest : TokenRequest { /// Gets or sets the Refresh token issued to the client. - [Google.Apis.Util.RequestParameterAttribute("refresh_token")] public string RefreshToken { get; set; } /// diff --git a/Src/Support/Google.Apis.Auth/OAuth2/Requests/RequestExtensions.cs b/Src/Support/Google.Apis.Auth/OAuth2/Requests/RequestExtensions.cs index dbf3f1b37ad..c57bec59077 100644 --- a/Src/Support/Google.Apis.Auth/OAuth2/Requests/RequestExtensions.cs +++ b/Src/Support/Google.Apis.Auth/OAuth2/Requests/RequestExtensions.cs @@ -14,10 +14,9 @@ You may obtain a copy of the License at limitations under the License. */ +using Google.Apis.Auth.ExistingDependencies; using Google.Apis.Auth.OAuth2.Responses; -using Google.Apis.Json; using Google.Apis.Logging; -using Google.Apis.Requests.Parameters; using Google.Apis.Util; using System.Net.Http; using System.Net.Http.Headers; @@ -37,7 +36,7 @@ internal static async Task PostJsonAsync( { var httpRequest = new HttpRequestMessage(HttpMethod.Post, url) { - Content = new StringContent(NewtonsoftJsonSerializer.Instance.Serialize(request), Encoding.UTF8, "application/json") + Content = new StringContent(ReplacementSerializer.Serialize(request), Encoding.UTF8, "application/json") }; return await httpClient.SendAsync(httpRequest, cancellationToken).ConfigureAwait(false); @@ -52,7 +51,7 @@ internal static async Task PostJsonAsync( { var response = await request.PostJsonAsync(httpClient, url, cancellationToken).ConfigureAwait(false); response.EnsureSuccessStatusCode(); - return await NewtonsoftJsonSerializer.Instance.DeserializeAsync( + return await ReplacementSerializer.DeserializeAsync( await response.Content.ReadAsStreamAsync().ConfigureAwait(false), cancellationToken).ConfigureAwait(false); } @@ -82,9 +81,10 @@ internal static async Task PostFormAsync(this object request, Htt string url, AuthenticationHeaderValue authenticationHeaderValue, IClock clock, ILogger logger, CancellationToken taskCancellationToken) { + // TODO: Reimplement without the common parameter code we had before. var httpRequest = new HttpRequestMessage(HttpMethod.Post, url) { - Content = ParameterUtils.CreateFormUrlEncodedContent(request) + Content = new StringContent("FIXME") }; if (authenticationHeaderValue is not null) { diff --git a/Src/Support/Google.Apis.Auth/OAuth2/Requests/StsRequestTokenBuilder.cs b/Src/Support/Google.Apis.Auth/OAuth2/Requests/StsRequestTokenBuilder.cs index e30b5914055..d36b4809891 100644 --- a/Src/Support/Google.Apis.Auth/OAuth2/Requests/StsRequestTokenBuilder.cs +++ b/Src/Support/Google.Apis.Auth/OAuth2/Requests/StsRequestTokenBuilder.cs @@ -14,7 +14,7 @@ You may obtain a copy of the License at limitations under the License. */ -using Google.Apis.Json; +using Google.Apis.Auth.ExistingDependencies; using System; using System.Collections.Generic; using System.Linq; @@ -88,7 +88,7 @@ public StsTokenRequest Build() : null; var options = (authenticationHeader is null && WorkforcePoolUserProject is string) - ? NewtonsoftJsonSerializer.Instance.Serialize(new { userProject = WorkforcePoolUserProject }) + ? ReplacementSerializer.Serialize(new { userProject = WorkforcePoolUserProject }) : null; var scope = Scopes?.Any() == true ? string.Join(" ", Scopes) : null; diff --git a/Src/Support/Google.Apis.Auth/OAuth2/Requests/StsTokenRequest.cs b/Src/Support/Google.Apis.Auth/OAuth2/Requests/StsTokenRequest.cs index f941a5c688b..5617eb01486 100644 --- a/Src/Support/Google.Apis.Auth/OAuth2/Requests/StsTokenRequest.cs +++ b/Src/Support/Google.Apis.Auth/OAuth2/Requests/StsTokenRequest.cs @@ -14,7 +14,6 @@ You may obtain a copy of the License at limitations under the License. */ -using Google.Apis.Util; using System.Net.Http.Headers; namespace Google.Apis.Auth.OAuth2.Requests @@ -45,47 +44,40 @@ internal StsTokenRequest( /// Gets the grant type for this request. /// Only urn:ietf:params:oauth:grant-type:token-exchange is currently supported. /// - [RequestParameter("grant_type")] public string GrantType { get; } /// /// The audience for which the requested token is intended. For instance: /// "//iam.googleapis.com/projects/my-project-id/locations/global/workloadIdentityPools/my-pool-id/providers/my-provider-id" /// - [RequestParameter("audience")] public string Audience { get; } /// /// The space-delimited list of desired scopes for the requested token as defined in /// http://tools.ietf.org/html/rfc6749#section-3.3. /// - [RequestParameter("scope")] public string Scope { get; } /// /// The type of the requested security token. /// Only urn:ietf:params:oauth:token-type:access_token is currently supported. /// - [RequestParameter("requested_token_type")] public string RequestedTokenType { get; } /// /// In terms of Google 3PI support, this is the 3PI credential. /// - [RequestParameter("subject_token")] public string SubjectToken { get; } /// /// The subject token type. /// - [RequestParameter("subject_token_type")] public string SubjectTokenType { get; } /// /// Google specific STS token request options. /// May be null. /// - [RequestParameter("options")] public string GoogleOptions { get; } /// @@ -93,5 +85,7 @@ internal StsTokenRequest( /// May be null. /// public AuthenticationHeaderValue AuthenticationHeader { get; } + + // FIXME: Probably hand-write code to create an appropriate HttpRequestMessage. } } diff --git a/Src/Support/Google.Apis.Auth/OAuth2/Requests/TokenRequest.cs b/Src/Support/Google.Apis.Auth/OAuth2/Requests/TokenRequest.cs index f18c97420ff..2f4e3167b90 100644 --- a/Src/Support/Google.Apis.Auth/OAuth2/Requests/TokenRequest.cs +++ b/Src/Support/Google.Apis.Auth/OAuth2/Requests/TokenRequest.cs @@ -14,13 +14,10 @@ You may obtain a copy of the License at limitations under the License. */ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - namespace Google.Apis.Auth.OAuth2.Requests { + // FIXME: Write code to create an HttpRequestMessage for this. + /// /// OAuth 2.0 request for an access token as specified in http://tools.ietf.org/html/rfc6749#section-4. /// @@ -29,22 +26,18 @@ public class TokenRequest /// /// Gets or sets space-separated list of scopes as specified in http://tools.ietf.org/html/rfc6749#section-3.3. /// - [Google.Apis.Util.RequestParameterAttribute("scope")] public string Scope { get; set; } /// /// Gets or sets the Grant type. Sets authorization_code or password or client_credentials /// or refresh_token or absolute URI of the extension grant type. /// - [Google.Apis.Util.RequestParameterAttribute("grant_type")] public string GrantType { get; set; } /// Gets or sets the client Identifier. - [Google.Apis.Util.RequestParameterAttribute("client_id")] public string ClientId { get; set; } /// Gets or sets the client Secret. - [Google.Apis.Util.RequestParameterAttribute("client_secret")] public string ClientSecret { get; set; } } } diff --git a/Src/Support/Google.Apis.Auth/OAuth2/Requests/TokenRequestExtenstions.cs b/Src/Support/Google.Apis.Auth/OAuth2/Requests/TokenRequestExtenstions.cs index 90b089c0456..64356552081 100644 --- a/Src/Support/Google.Apis.Auth/OAuth2/Requests/TokenRequestExtenstions.cs +++ b/Src/Support/Google.Apis.Auth/OAuth2/Requests/TokenRequestExtenstions.cs @@ -22,8 +22,10 @@ limitations under the License. namespace Google.Apis.Auth.OAuth2.Requests { + // TODO: Only use this if we really need to... + /// Extension methods to . - public static class TokenRequestExtenstions + public static class TokenRequestExtensions { /// /// Executes the token request in order to receive a diff --git a/Src/Support/Google.Apis.Auth/OAuth2/Responses/TokenResponse.cs b/Src/Support/Google.Apis.Auth/OAuth2/Responses/TokenResponse.cs index 0ff798b4376..b2e81cf3e92 100644 --- a/Src/Support/Google.Apis.Auth/OAuth2/Responses/TokenResponse.cs +++ b/Src/Support/Google.Apis.Auth/OAuth2/Responses/TokenResponse.cs @@ -14,7 +14,7 @@ You may obtain a copy of the License at limitations under the License. */ -using Google.Apis.Json; +using Google.Apis.Auth.ExistingDependencies; using Google.Apis.Logging; using Google.Apis.Util; using System; @@ -159,7 +159,7 @@ public static async Task FromHttpResponseAsync(HttpResponseMessag // "{"error": {"code": 404, "message": "...", "errors": [{"message": "...", ...}], "status": "NOT_FOUND"}}" var error = response.RequestMessage?.RequestUri?.AbsoluteUri.StartsWith(GoogleAuthConsts.IamServiceAccountEndpointCommonPrefix) == true ? new TokenErrorResponse { Error = content } : - NewtonsoftJsonSerializer.Instance.Deserialize(content); + ReplacementSerializer.Deserialize(content); throw new TokenResponseException(error, response.StatusCode); } @@ -176,7 +176,7 @@ public static async Task FromHttpResponseAsync(HttpResponseMessag else { typeName = nameof(TokenResponse); - newToken = NewtonsoftJsonSerializer.Instance.Deserialize(content); + newToken = ReplacementSerializer.Deserialize(content); } // We make some modifications to the token before returning, to guarantee consistency // for our code across endpoint usage. diff --git a/Src/Support/Google.Apis.Auth/OAuth2/ServiceAccountCredential.cs b/Src/Support/Google.Apis.Auth/OAuth2/ServiceAccountCredential.cs index 362f7baf926..61c657d7d7b 100644 --- a/Src/Support/Google.Apis.Auth/OAuth2/ServiceAccountCredential.cs +++ b/Src/Support/Google.Apis.Auth/OAuth2/ServiceAccountCredential.cs @@ -23,9 +23,9 @@ limitations under the License. using System.Threading; using System.Threading.Tasks; using Google.Apis.Auth.OAuth2.Requests; -using Google.Apis.Json; using Google.Apis.Util; using Google.Apis.Http; +using Google.Apis.Auth.ExistingDependencies; #if NETSTANDARD1_3 || NETSTANDARD2_0 || NET461 using RsaKey = System.Security.Cryptography.RSA; @@ -439,7 +439,7 @@ private string CreateJwtAccessTokenForOidc(OidcTokenOptions options, DateTime is private string CreateAssertionFromPayload(JsonWebSignature.Payload payload) { string serializedHeader = CreateSerializedHeader(); - string serializedPayload = NewtonsoftJsonSerializer.Instance.Serialize(payload); + string serializedPayload = ReplacementSerializer.Serialize(payload); var assertion = new StringBuilder(); assertion.Append(TokenEncodingHelpers.UrlSafeBase64Encode(serializedHeader)) @@ -490,7 +490,7 @@ private string CreateSerializedHeader() KeyId = KeyId }; - return NewtonsoftJsonSerializer.Instance.Serialize(header); + return ReplacementSerializer.Serialize(header); } /// diff --git a/Src/Support/Google.Apis.Auth/OAuth2/ServiceCredential.cs b/Src/Support/Google.Apis.Auth/OAuth2/ServiceCredential.cs index bd0d8a925e6..fb0c2ab8783 100644 --- a/Src/Support/Google.Apis.Auth/OAuth2/ServiceCredential.cs +++ b/Src/Support/Google.Apis.Auth/OAuth2/ServiceCredential.cs @@ -14,6 +14,7 @@ You may obtain a copy of the License at limitations under the License. */ +using Google.Apis.Auth.ExistingDependencies; using Google.Apis.Auth.OAuth2.Responses; using Google.Apis.Http; using Google.Apis.Logging; @@ -215,7 +216,7 @@ public ServiceCredential(Initializer initializer) DefaultExponentialBackOffPolicy = initializer.DefaultExponentialBackOffPolicy; HttpClientInitializers = new List(initializer.HttpClientInitializers).AsReadOnly(); - HttpClientFactory = initializer.HttpClientFactory ?? new HttpClientFactory(); + HttpClientFactory = initializer.HttpClientFactory ?? new AuthHttpClientFactory(); HttpClient = HttpClientFactory.CreateHttpClient(BuildCreateHttpClientArgs()); _refreshManager = new TokenRefreshManager(RequestAccessTokenAsync, Clock, Logger); diff --git a/Src/Support/Google.Apis.Auth/OAuth2/UrlSourcedExternalAccountCredential.cs b/Src/Support/Google.Apis.Auth/OAuth2/UrlSourcedExternalAccountCredential.cs index cce70374742..b0add0b4396 100644 --- a/Src/Support/Google.Apis.Auth/OAuth2/UrlSourcedExternalAccountCredential.cs +++ b/Src/Support/Google.Apis.Auth/OAuth2/UrlSourcedExternalAccountCredential.cs @@ -14,8 +14,8 @@ You may obtain a copy of the License at limitations under the License. */ +using Google.Apis.Auth.ExistingDependencies; using Google.Apis.Http; -using Google.Apis.Json; using System; using System.Collections.Generic; using System.Collections.ObjectModel; @@ -124,7 +124,7 @@ protected async override Task GetSubjectTokenAsyncImpl(CancellationToken return responseText; } - var jsonResponse = NewtonsoftJsonSerializer.Instance.Deserialize>(responseText); + var jsonResponse = ReplacementSerializer.Deserialize>(responseText); return jsonResponse[SubjectTokenJsonFieldName]; } diff --git a/Src/Support/Google.Apis.Auth/SignedToken.cs b/Src/Support/Google.Apis.Auth/SignedToken.cs index e0c85bcc545..d9a239e9f12 100644 --- a/Src/Support/Google.Apis.Auth/SignedToken.cs +++ b/Src/Support/Google.Apis.Auth/SignedToken.cs @@ -14,7 +14,7 @@ You may obtain a copy of the License at limitations under the License. */ -using Google.Apis.Json; +using Google.Apis.Auth.ExistingDependencies; using Google.Apis.Util; using System; using System.Security.Cryptography; @@ -76,8 +76,8 @@ internal static SignedToken FromSignedToken(string sign var encodedPayload = parts[1]; // Decode the three parts of the JWT: header.payload.signature - var headerValue = NewtonsoftJsonSerializer.Instance.Deserialize(TokenEncodingHelpers.Base64UrlToString(encodedHeader)); - var payloadValue = NewtonsoftJsonSerializer.Instance.Deserialize(TokenEncodingHelpers.Base64UrlToString(encodedPayload)); + var headerValue = ReplacementSerializer.Deserialize(TokenEncodingHelpers.Base64UrlToString(encodedHeader)); + var payloadValue = ReplacementSerializer.Deserialize(TokenEncodingHelpers.Base64UrlToString(encodedPayload)); var signature = TokenEncodingHelpers.Base64UrlDecode(parts[2]); return new SignedToken(encodedHeader, encodedPayload, headerValue, payloadValue, signature); diff --git a/Src/Support/GoogleApisClient.sln b/Src/Support/GoogleApisClient.sln index e1f2f1c9901..418f74e47d9 100644 --- a/Src/Support/GoogleApisClient.sln +++ b/Src/Support/GoogleApisClient.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.29613.14 +# Visual Studio Version 17 +VisualStudioVersion = 17.5.33530.505 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Google.Apis.Core", "Google.Apis.Core\Google.Apis.Core.csproj", "{556BEB88-1F3B-4EFA-B912-828C90AC3BEB}" EndProject