Google Api
This commit is contained in:
parent
e307811d89
commit
83f9fc1bd8
123 changed files with 12961 additions and 60 deletions
|
|
@ -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
|
||||
{
|
||||
/// <summary>Defines the context in which this library runs. It allows setting up custom loggers.</summary>
|
||||
public static class ApplicationContext
|
||||
{
|
||||
private static ILogger logger;
|
||||
|
||||
// For testing
|
||||
internal static void Reset() => logger = null;
|
||||
|
||||
/// <summary>Returns the logger used within this application context.</summary>
|
||||
/// <remarks>It creates a <see cref="NullLogger"/> if no logger was registered previously</remarks>
|
||||
public static ILogger Logger
|
||||
{
|
||||
get
|
||||
{
|
||||
// Register the default null-logger if no other one was set.
|
||||
return logger ?? (logger = new NullLogger());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Registers a logger with this application context.</summary>
|
||||
/// <exception cref="InvalidOperationException">Thrown if a logger was already registered.</exception>
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
namespace Google.Apis.Discovery
|
||||
{
|
||||
/// <summary>An enumeration of all supported discovery versions.</summary>
|
||||
public enum DiscoveryVersion
|
||||
{
|
||||
/// <summary>Discovery version 1.0.</summary>
|
||||
Version_1_0,
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
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.Util;
|
||||
|
||||
namespace Google.Apis.Discovery
|
||||
{
|
||||
/// <summary>
|
||||
/// Specifies a list of features which can be defined within the discovery document of a service.
|
||||
/// </summary>
|
||||
public enum Features
|
||||
{
|
||||
/// <summary>
|
||||
/// If this feature is specified, then the data of a response is encapsulated within a "data" resource.
|
||||
/// </summary>
|
||||
[StringValue("dataWrapper")]
|
||||
LegacyDataResponse,
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
namespace Google.Apis.Discovery
|
||||
{
|
||||
/// <summary>Represents a parameter for a method.</summary>
|
||||
public interface IParameter
|
||||
{
|
||||
/// <summary>Gets the name of the parameter.</summary>
|
||||
string Name { get; }
|
||||
|
||||
/// <summary>Gets the pattern that this parameter must follow.</summary>
|
||||
string Pattern { get; }
|
||||
|
||||
/// <summary>Gets an indication whether this parameter is optional or required.</summary>
|
||||
bool IsRequired { get; }
|
||||
|
||||
/// <summary>Gets the default value of this parameter.</summary>
|
||||
string DefaultValue { get; }
|
||||
|
||||
/// <summary>Gets the type of the parameter.</summary>
|
||||
string ParameterType { get; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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.
|
||||
*/
|
||||
|
||||
namespace Google.Apis.Discovery
|
||||
{
|
||||
/// <summary>Represents a method's parameter.</summary>
|
||||
public class Parameter : IParameter
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string Pattern { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsRequired { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string ParameterType { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string DefaultValue { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
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.Net;
|
||||
|
||||
using Google.Apis.Requests;
|
||||
using Google.Apis.Util;
|
||||
|
||||
namespace Google
|
||||
{
|
||||
/// <summary>Represents an exception thrown by an API Service.</summary>
|
||||
public class GoogleApiException : Exception
|
||||
{
|
||||
private readonly string serviceName;
|
||||
|
||||
/// <summary>Gets the service name which related to this exception.</summary>
|
||||
public string ServiceName
|
||||
{
|
||||
get { return serviceName; }
|
||||
}
|
||||
|
||||
/// <summary>Creates an API Service exception.</summary>
|
||||
public GoogleApiException(string serviceName, string message, Exception inner)
|
||||
: base(message, inner)
|
||||
{
|
||||
serviceName.ThrowIfNull("serviceName");
|
||||
this.serviceName = serviceName;
|
||||
}
|
||||
|
||||
/// <summary>Creates an API Service exception.</summary>
|
||||
public GoogleApiException(string serviceName, string message) : this(serviceName, message, null) { }
|
||||
|
||||
/// <summary>The Error which was returned from the server, or <c>null</c> if unavailable.</summary>
|
||||
public RequestError Error { get; set; }
|
||||
|
||||
/// <summary>The HTTP status code which was returned along with this error, or 0 if unavailable.</summary>
|
||||
public HttpStatusCode HttpStatusCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns a summary of this exception.
|
||||
/// </summary>
|
||||
/// <returns>A summary of this exception.</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("The service {1} has thrown an exception: {0}", base.ToString(), serviceName);
|
||||
}
|
||||
}
|
||||
}
|
||||
186
Yavsc/GoogleApiSupport/Google.Apis.Core/Http/BackOffHandler.cs
Normal file
186
Yavsc/GoogleApiSupport/Google.Apis.Core/Http/BackOffHandler.cs
Normal file
|
|
@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// A thread-safe back-off handler which handles an abnormal HTTP response or an exception with
|
||||
/// <see cref="Google.Apis.Util.IBackOff"/>.
|
||||
/// </summary>
|
||||
public class BackOffHandler : IHttpUnsuccessfulResponseHandler, IHttpExceptionHandler
|
||||
{
|
||||
private static readonly ILogger Logger = ApplicationContext.Logger.ForType<BackOffHandler>();
|
||||
|
||||
/// <summary>An initializer class to initialize a back-off handler.</summary>
|
||||
public class Initializer
|
||||
{
|
||||
/// <summary>Gets the back-off policy used by this back-off handler.</summary>
|
||||
public IBackOff BackOff { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 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 <c>false</c> to both <c>HandleExceptionAsync</c> and
|
||||
/// <c>HandleResponseAsync</c>. Default value is 16 seconds per a retry request.
|
||||
/// </summary>
|
||||
public TimeSpan MaxTimeSpan { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a delegate function which indicates whether this back-off handler should handle an
|
||||
/// abnormal HTTP response. The default is <see cref="DefaultHandleUnsuccessfulResponseFunc"/>.
|
||||
/// </summary>
|
||||
public Func<HttpResponseMessage, bool> HandleUnsuccessfulResponseFunc { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a delegate function which indicates whether this back-off handler should handle an
|
||||
/// exception. The default is <see cref="DefaultHandleExceptionFunc"/>.
|
||||
/// </summary>
|
||||
public Func<Exception, bool> HandleExceptionFunc { get; set; }
|
||||
|
||||
/// <summary>Default function which handles server errors (503).</summary>
|
||||
public static readonly Func<HttpResponseMessage, bool> DefaultHandleUnsuccessfulResponseFunc =
|
||||
(r) => (int)r.StatusCode == 503;
|
||||
|
||||
/// <summary>
|
||||
/// Default function which handles exception which aren't
|
||||
/// <see cref="System.Threading.Tasks.TaskCanceledException"/> or
|
||||
/// <see cref="System.OperationCanceledException"/>. Those exceptions represent a task or an operation
|
||||
/// which was canceled and shouldn't be retried.
|
||||
/// </summary>
|
||||
public static readonly Func<Exception, bool> DefaultHandleExceptionFunc =
|
||||
(ex) => !(ex is TaskCanceledException || ex is OperationCanceledException);
|
||||
|
||||
/// <summary>Constructs a new initializer by the given back-off.</summary>
|
||||
public Initializer(IBackOff backOff)
|
||||
{
|
||||
BackOff = backOff;
|
||||
HandleExceptionFunc = DefaultHandleExceptionFunc;
|
||||
HandleUnsuccessfulResponseFunc = DefaultHandleUnsuccessfulResponseFunc;
|
||||
MaxTimeSpan = TimeSpan.FromSeconds(16);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Gets the back-off policy used by this back-off handler.</summary>
|
||||
public IBackOff BackOff { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the maximum time span to wait. If the back-off instance returns a greater time span, the handle method
|
||||
/// returns <c>false</c>. Default value is 16 seconds per a retry request.
|
||||
/// </summary>
|
||||
public TimeSpan MaxTimeSpan { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a delegate function which indicates whether this back-off handler should handle an abnormal HTTP
|
||||
/// response. The default is <see cref="Initializer.DefaultHandleUnsuccessfulResponseFunc"/>.
|
||||
/// </summary>
|
||||
public Func<HttpResponseMessage, bool> HandleUnsuccessfulResponseFunc { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a delegate function which indicates whether this back-off handler should handle an exception. The
|
||||
/// default is <see cref="Initializer.DefaultHandleExceptionFunc"/>.
|
||||
/// </summary>
|
||||
public Func<Exception, bool> HandleExceptionFunc { get; private set; }
|
||||
|
||||
/// <summary>Constructs a new back-off handler with the given back-off.</summary>
|
||||
/// <param name="backOff">The back-off policy.</param>
|
||||
public BackOffHandler(IBackOff backOff)
|
||||
: this(new Initializer(backOff))
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>Constructs a new back-off handler with the given initializer.</summary>
|
||||
public BackOffHandler(Initializer initializer)
|
||||
{
|
||||
BackOff = initializer.BackOff;
|
||||
MaxTimeSpan = initializer.MaxTimeSpan;
|
||||
HandleExceptionFunc = initializer.HandleExceptionFunc;
|
||||
HandleUnsuccessfulResponseFunc = initializer.HandleUnsuccessfulResponseFunc;
|
||||
}
|
||||
|
||||
#region IHttpUnsuccessfulResponseHandler
|
||||
|
||||
/// <inheritdoc/>
|
||||
public virtual async Task<bool> 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
|
||||
|
||||
/// <inheritdoc/>
|
||||
public virtual async Task<bool> 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
|
||||
|
||||
/// <summary>
|
||||
/// 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 <c>false</c>. Otherwise the current thread
|
||||
/// will block for x milliseconds (x is defined by the <see cref="BackOff"/> instance), and this handler
|
||||
/// returns <c>true</c>.
|
||||
/// </summary>
|
||||
private async Task<bool> 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;
|
||||
}
|
||||
|
||||
/// <summary>Waits the given time span. Overriding this method is recommended for mocking purposes.</summary>
|
||||
/// <param name="ts">TimeSpan to wait (and block the current thread).</param>
|
||||
/// <param name="cancellationToken">The cancellation token in case the user wants to cancel the operation in
|
||||
/// the middle.</param>
|
||||
protected virtual async Task Wait(TimeSpan ts, CancellationToken cancellationToken)
|
||||
{
|
||||
await Task.Delay(ts, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
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
|
||||
{
|
||||
/// <summary>
|
||||
/// Configurable HTTP client inherits from <see cref="System.Net.Http.HttpClient"/> and contains a reference to
|
||||
/// <see cref="Google.Apis.Http.ConfigurableMessageHandler"/>.
|
||||
/// </summary>
|
||||
public class ConfigurableHttpClient : HttpClient
|
||||
{
|
||||
/// <summary>Gets the configurable message handler.</summary>
|
||||
public ConfigurableMessageHandler MessageHandler { get; private set; }
|
||||
|
||||
/// <summary>Constructs a new HTTP client.</summary>
|
||||
public ConfigurableHttpClient(ConfigurableMessageHandler handler)
|
||||
: base(handler)
|
||||
{
|
||||
MessageHandler = handler;
|
||||
DefaultRequestHeaders.ExpectContinue = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,593 @@
|
|||
/*
|
||||
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
|
||||
{
|
||||
/// <summary>
|
||||
/// A message handler which contains the main logic of our HTTP requests. It contains a list of
|
||||
/// <see cref="IHttpUnsuccessfulResponseHandler"/>s for handling abnormal responses, a list of
|
||||
/// <see cref="IHttpExceptionHandler"/>s for handling exception in a request and a list of
|
||||
/// <see cref="IHttpExecuteInterceptor"/>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.
|
||||
/// </summary>
|
||||
public class ConfigurableMessageHandler : DelegatingHandler
|
||||
{
|
||||
/// <summary>The class logger.</summary>
|
||||
private static readonly ILogger Logger = ApplicationContext.Logger.ForType<ConfigurableMessageHandler>();
|
||||
|
||||
/// <summary>Maximum allowed number of tries.</summary>
|
||||
[VisibleForTestOnly]
|
||||
public const int MaxAllowedNumTries = 20;
|
||||
|
||||
/// <summary>The current API version of this client library.</summary>
|
||||
private static readonly string ApiVersion = Google.Apis.Util.Utilities.GetLibraryVersion();
|
||||
|
||||
/// <summary>The User-Agent suffix header which contains the <see cref="ApiVersion"/>.</summary>
|
||||
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
|
||||
|
||||
/// <summary>A list of <see cref="IHttpUnsuccessfulResponseHandler"/>.</summary>
|
||||
private readonly IList<IHttpUnsuccessfulResponseHandler> unsuccessfulResponseHandlers =
|
||||
new List<IHttpUnsuccessfulResponseHandler>();
|
||||
|
||||
/// <summary>A list of <see cref="IHttpExceptionHandler"/>.</summary>
|
||||
private readonly IList<IHttpExceptionHandler> exceptionHandlers =
|
||||
new List<IHttpExceptionHandler>();
|
||||
|
||||
/// <summary>A list of <see cref="IHttpExecuteInterceptor"/>.</summary>
|
||||
private readonly IList<IHttpExecuteInterceptor> executeInterceptors =
|
||||
new List<IHttpExecuteInterceptor>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of <see cref="IHttpUnsuccessfulResponseHandler"/>s.
|
||||
/// <remarks>
|
||||
/// Since version 1.10, <see cref="AddUnsuccessfulResponseHandler"/> and
|
||||
/// <see cref="RemoveUnsuccessfulResponseHandler"/> were added in order to keep this class thread-safe.
|
||||
/// More information is available on
|
||||
/// <a href="https://github.com/google/google-api-dotnet-client/issues/592">#592</a>.
|
||||
/// </remarks>
|
||||
/// </summary>
|
||||
[Obsolete("Use AddUnsuccessfulResponseHandler or RemoveUnsuccessfulResponseHandler instead.")]
|
||||
public IList<IHttpUnsuccessfulResponseHandler> UnsuccessfulResponseHandlers
|
||||
{
|
||||
get { return unsuccessfulResponseHandlers; }
|
||||
}
|
||||
|
||||
/// <summary>Adds the specified handler to the list of unsuccessful response handlers.</summary>
|
||||
public void AddUnsuccessfulResponseHandler(IHttpUnsuccessfulResponseHandler handler)
|
||||
{
|
||||
lock (unsuccessfulResponseHandlersLock)
|
||||
{
|
||||
unsuccessfulResponseHandlers.Add(handler);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Removes the specified handler from the list of unsuccessful response handlers.</summary>
|
||||
public void RemoveUnsuccessfulResponseHandler(IHttpUnsuccessfulResponseHandler handler)
|
||||
{
|
||||
lock (unsuccessfulResponseHandlersLock)
|
||||
{
|
||||
unsuccessfulResponseHandlers.Remove(handler);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of <see cref="IHttpExceptionHandler"/>s.
|
||||
/// <remarks>
|
||||
/// Since version 1.10, <see cref="AddExceptionHandler"/> and <see cref="RemoveExceptionHandler"/> were added
|
||||
/// in order to keep this class thread-safe. More information is available on
|
||||
/// <a href="https://github.com/google/google-api-dotnet-client/issues/592">#592</a>.
|
||||
/// </remarks>
|
||||
/// </summary>
|
||||
[Obsolete("Use AddExceptionHandler or RemoveExceptionHandler instead.")]
|
||||
public IList<IHttpExceptionHandler> ExceptionHandlers
|
||||
{
|
||||
get { return exceptionHandlers; }
|
||||
}
|
||||
|
||||
/// <summary>Adds the specified handler to the list of exception handlers.</summary>
|
||||
public void AddExceptionHandler(IHttpExceptionHandler handler)
|
||||
{
|
||||
lock (exceptionHandlersLock)
|
||||
{
|
||||
exceptionHandlers.Add(handler);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Removes the specified handler from the list of exception handlers.</summary>
|
||||
public void RemoveExceptionHandler(IHttpExceptionHandler handler)
|
||||
{
|
||||
lock (exceptionHandlersLock)
|
||||
{
|
||||
exceptionHandlers.Remove(handler);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of <see cref="IHttpExecuteInterceptor"/>s.
|
||||
/// <remarks>
|
||||
/// Since version 1.10, <see cref="AddExecuteInterceptor"/> and <see cref="RemoveExecuteInterceptor"/> were
|
||||
/// added in order to keep this class thread-safe. More information is available on
|
||||
/// <a href="https://github.com/google/google-api-dotnet-client/issues/592">#592</a>.
|
||||
/// </remarks>
|
||||
/// </summary>
|
||||
[Obsolete("Use AddExecuteInterceptor or RemoveExecuteInterceptor instead.")]
|
||||
public IList<IHttpExecuteInterceptor> ExecuteInterceptors
|
||||
{
|
||||
get { return executeInterceptors; }
|
||||
}
|
||||
|
||||
/// <summary>Adds the specified interceptor to the list of execute interceptors.</summary>
|
||||
public void AddExecuteInterceptor(IHttpExecuteInterceptor interceptor)
|
||||
{
|
||||
lock (executeInterceptorsLock)
|
||||
{
|
||||
executeInterceptors.Add(interceptor);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Removes the specified interceptor from the list of execute interceptors.</summary>
|
||||
public void RemoveExecuteInterceptor(IHttpExecuteInterceptor interceptor)
|
||||
{
|
||||
lock (executeInterceptorsLock)
|
||||
{
|
||||
executeInterceptors.Remove(interceptor);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private int _loggingRequestId = 0;
|
||||
|
||||
private ILogger _instanceLogger = Logger;
|
||||
|
||||
/// <summary>
|
||||
/// For testing only.
|
||||
/// This defaults to the static <see cref="Logger"/>, but can be overridden for fine-grain testing.
|
||||
/// </summary>
|
||||
internal ILogger InstanceLogger
|
||||
{
|
||||
get { return _instanceLogger; }
|
||||
set { _instanceLogger = value.ForType<ConfigurableMessageHandler>(); }
|
||||
}
|
||||
|
||||
/// <summary>Number of tries. Default is <c>3</c>.</summary>
|
||||
private int numTries = 3;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the number of tries that will be allowed to execute. Retries occur as a result of either
|
||||
/// <see cref="IHttpUnsuccessfulResponseHandler"/> or <see cref="IHttpExceptionHandler"/> which handles the
|
||||
/// abnormal HTTP response or exception before being terminated.
|
||||
/// Set <c>1</c> for not retrying requests. The default value is <c>3</c>.
|
||||
/// <remarks>
|
||||
/// The number of allowed redirects (3xx) is defined by <see cref="NumRedirects"/>. This property defines
|
||||
/// only the allowed tries for >=400 responses, or when an exception is thrown. For example if you set
|
||||
/// <see cref="NumTries"/> to 1 and <see cref="NumRedirects"/> 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.
|
||||
/// </remarks>
|
||||
/// </summary>
|
||||
public int NumTries
|
||||
{
|
||||
get { return numTries; }
|
||||
set
|
||||
{
|
||||
if (value > MaxAllowedNumTries || value < 1)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("NumTries");
|
||||
}
|
||||
numTries = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Number of redirects allowed. Default is <c>10</c>.</summary>
|
||||
private int numRedirects = 10;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the number of redirects that will be allowed to execute. The default value is <c>10</c>.
|
||||
/// See <see cref="NumTries"/> for more information.
|
||||
/// </summary>
|
||||
public int NumRedirects
|
||||
{
|
||||
get { return numRedirects; }
|
||||
set
|
||||
{
|
||||
if (value > MaxAllowedNumTries || value < 1)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("NumRedirects");
|
||||
}
|
||||
numRedirects = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether the handler should follow a redirect when a redirect response is received. Default
|
||||
/// value is <c>true</c>.
|
||||
/// </summary>
|
||||
public bool FollowRedirect { get; set; }
|
||||
|
||||
/// <summary>Gets or sets whether logging is enabled. Default value is <c>true</c>.</summary>
|
||||
public bool IsLoggingEnabled { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Specifies the type(s) of request/response events to log.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum LogEventType
|
||||
{
|
||||
/// <summary>
|
||||
/// Log no request/response information.
|
||||
/// </summary>
|
||||
None = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Log the request URI.
|
||||
/// </summary>
|
||||
RequestUri = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Log the request headers.
|
||||
/// </summary>
|
||||
RequestHeaders = 2,
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
RequestBody = 4,
|
||||
|
||||
/// <summary>
|
||||
/// Log the response status.
|
||||
/// </summary>
|
||||
ResponseStatus = 8,
|
||||
|
||||
/// <summary>
|
||||
/// Log the response headers.
|
||||
/// </summary>
|
||||
ResponseHeaders = 16,
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
ResponseBody = 32,
|
||||
|
||||
/// <summary>
|
||||
/// Log abnormal response messages.
|
||||
/// </summary>
|
||||
ResponseAbnormal = 64,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The request/response types to log.
|
||||
/// </summary>
|
||||
public LogEventType LogEvents { get; set; }
|
||||
|
||||
/// <summary>Gets or sets the application name which will be used on the User-Agent header.</summary>
|
||||
public string ApplicationName { get; set; }
|
||||
|
||||
/// <summary>Constructs a new configurable message handler.</summary>
|
||||
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<KeyValuePair<string, IEnumerable<string>>>())
|
||||
.Concat(headers2 ?? Enumerable.Empty<KeyValuePair<string, IEnumerable<string>>>()).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() : 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));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The main logic of sending a request to the server. This send method adds the User-Agent header to a request
|
||||
/// with <see cref="ApplicationName"/> and the library version. It also calls interceptors before each attempt,
|
||||
/// and unsuccessful response handler or exception handlers when abnormal response or exception occurred.
|
||||
/// </summary>
|
||||
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var loggable = IsLoggingEnabled && InstanceLogger.IsDebugEnabled;
|
||||
string loggingRequestId = "";
|
||||
if (loggable)
|
||||
{
|
||||
loggingRequestId = Interlocked.Increment(ref _loggingRequestId).ToString("X8");
|
||||
}
|
||||
|
||||
int triesRemaining = NumTries;
|
||||
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);
|
||||
|
||||
HttpResponseMessage response = null;
|
||||
do // While (triesRemaining > 0)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
if (response != null)
|
||||
{
|
||||
response.Dispose();
|
||||
response = null;
|
||||
}
|
||||
lastException = null;
|
||||
|
||||
// We keep a local list of the interceptors, since we can't call await inside lock.
|
||||
IEnumerable<IHttpExecuteInterceptor> interceptors;
|
||||
lock (executeInterceptorsLock)
|
||||
{
|
||||
interceptors = executeInterceptors.ToList();
|
||||
}
|
||||
|
||||
// Intercept the request.
|
||||
foreach (var interceptor in interceptors)
|
||||
{
|
||||
await interceptor.InterceptAsync(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);
|
||||
}
|
||||
}
|
||||
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.
|
||||
IEnumerable<IHttpExceptionHandler> handlers;
|
||||
lock (exceptionHandlersLock)
|
||||
{
|
||||
handlers = exceptionHandlers.ToList();
|
||||
}
|
||||
|
||||
// Try to handle the exception with each handler.
|
||||
foreach (var handler in handlers)
|
||||
{
|
||||
exceptionHandled |= await handler.HandleExceptionAsync(new HandleExceptionArgs
|
||||
{
|
||||
Request = request,
|
||||
Exception = lastException,
|
||||
TotalTries = NumTries,
|
||||
CurrentFailedTry = NumTries - 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);
|
||||
}
|
||||
}
|
||||
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.
|
||||
IEnumerable<IHttpUnsuccessfulResponseHandler> handlers;
|
||||
lock (unsuccessfulResponseHandlersLock)
|
||||
{
|
||||
handlers = unsuccessfulResponseHandlers.ToList();
|
||||
}
|
||||
// Try to handle the abnormal HTTP response with each handler.
|
||||
foreach (var handler in handlers)
|
||||
{
|
||||
errorHandled |= await handler.HandleResponseAsync(new HandleUnsuccessfulResponseArgs
|
||||
{
|
||||
Request = request,
|
||||
Response = response,
|
||||
TotalTries = NumTries,
|
||||
CurrentFailedTry = NumTries - triesRemaining,
|
||||
CancellationToken = cancellationToken
|
||||
}).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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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 <c>303</c> the method on the request is changed to a GET as per the RFC2616
|
||||
/// specification. On a redirect, it also removes the <c>Authorization</c> and all <c>If-*</c> request headers.
|
||||
/// </summary>
|
||||
/// <returns> Whether this method changed the request and handled redirect successfully. </returns>
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
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.Http
|
||||
{
|
||||
/// <summary>
|
||||
/// Indicates if exponential back-off is used automatically on exceptions in a service requests and \ or when 503
|
||||
/// responses is returned form the server.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum ExponentialBackOffPolicy
|
||||
{
|
||||
/// <summary>Exponential back-off is disabled.</summary>
|
||||
None = 0,
|
||||
/// <summary>Exponential back-off is enabled only for exceptions.</summary>
|
||||
Exception = 1,
|
||||
/// <summary>Exponential back-off is enabled only for 503 HTTP Status code.</summary>
|
||||
UnsuccessfulResponse503 = 2
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An initializer which adds exponential back-off as exception handler and \ or unsuccessful response handler by
|
||||
/// the given <see cref="ExponentialBackOffPolicy"/>.
|
||||
/// </summary>
|
||||
public class ExponentialBackOffInitializer : IConfigurableHttpClientInitializer
|
||||
{
|
||||
/// <summary>Gets or sets the used back-off policy.</summary>
|
||||
private ExponentialBackOffPolicy Policy { get; set; }
|
||||
|
||||
/// <summary>Gets or sets the back-off handler creation function.</summary>
|
||||
private Func<BackOffHandler> CreateBackOff { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new back-off initializer with the given policy and back-off handler create function.
|
||||
/// </summary>
|
||||
public ExponentialBackOffInitializer(ExponentialBackOffPolicy policy, Func<BackOffHandler> createBackOff)
|
||||
{
|
||||
Policy = policy;
|
||||
CreateBackOff = createBackOff;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
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 Google.Apis.Logging;
|
||||
|
||||
namespace Google.Apis.Http
|
||||
{
|
||||
/// <summary>The default implementation of the HTTP client factory.</summary>
|
||||
public class HttpClientFactory : IHttpClientFactory
|
||||
{
|
||||
/// <summary>The class logger.</summary>
|
||||
private static readonly ILogger Logger = ApplicationContext.Logger.ForType<HttpClientFactory>();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ConfigurableHttpClient CreateHttpClient(CreateHttpClientArgs args)
|
||||
{
|
||||
// Create the handler.
|
||||
var handler = CreateHandler(args);
|
||||
var configurableHandler = new ConfigurableMessageHandler(handler)
|
||||
{
|
||||
ApplicationName = args.ApplicationName
|
||||
};
|
||||
|
||||
// Create the client.
|
||||
var client = new ConfigurableHttpClient(configurableHandler);
|
||||
foreach (var initializer in args.Initializers)
|
||||
{
|
||||
initializer.Initialize(client);
|
||||
}
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
/// <summary>Creates a HTTP message handler. Override this method to mock a message handler.</summary>
|
||||
protected virtual HttpMessageHandler CreateHandler(CreateHttpClientArgs args)
|
||||
{
|
||||
var handler = new HttpClientHandler();
|
||||
|
||||
// If the framework supports redirect configuration, set it to false, because ConfigurableMessageHandler
|
||||
// handles redirect.
|
||||
if (handler.SupportsRedirectConfiguration)
|
||||
{
|
||||
handler.AllowAutoRedirect = false;
|
||||
}
|
||||
|
||||
// If the framework supports automatic decompression and GZip is enabled, set automatic decompression.
|
||||
if (handler.SupportsAutomaticDecompression && args.GZipEnabled)
|
||||
{
|
||||
handler.AutomaticDecompression = System.Net.DecompressionMethods.GZip |
|
||||
System.Net.DecompressionMethods.Deflate;
|
||||
}
|
||||
|
||||
Logger.Debug("Handler was created. SupportsRedirectConfiguration={0}, SupportsAutomaticDecompression={1}",
|
||||
handler.SupportsRedirectConfiguration, handler.SupportsAutomaticDecompression);
|
||||
|
||||
return handler;
|
||||
}
|
||||
}
|
||||
}
|
||||
37
Yavsc/GoogleApiSupport/Google.Apis.Core/Http/HttpConsts.cs
Normal file
37
Yavsc/GoogleApiSupport/Google.Apis.Core/Http/HttpConsts.cs
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
namespace Google.Apis.Http
|
||||
{
|
||||
/// <summary>HTTP constants.</summary>
|
||||
public static class HttpConsts
|
||||
{
|
||||
/// <summary>Http GET request</summary>
|
||||
public const string Get = "GET";
|
||||
|
||||
/// <summary>Http DELETE request</summary>
|
||||
public const string Delete = "DELETE";
|
||||
|
||||
/// <summary>Http PUT request</summary>
|
||||
public const string Put = "PUT";
|
||||
|
||||
/// <summary>Http POST request</summary>
|
||||
public const string Post = "POST";
|
||||
|
||||
/// <summary>Http PATCH request</summary>
|
||||
public const string Patch = "PATCH";
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
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
|
||||
{
|
||||
/// <summary>
|
||||
/// Extension methods to <see cref="System.Net.Http.HttpRequestMessage"/> and
|
||||
/// <see cref="System.Net.Http.HttpResponseMessage"/>.
|
||||
/// </summary>
|
||||
public static class HttpExtenstions
|
||||
{
|
||||
/// <summary>Returns <c>true</c> if the response contains one of the redirect status codes.</summary>
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>A Google.Apis utility method for setting an empty HTTP content.</summary>
|
||||
public static HttpContent SetEmptyContent(this HttpRequestMessage request)
|
||||
{
|
||||
request.Content = new ByteArrayContent(new byte[0]);
|
||||
request.Content.Headers.ContentLength = 0;
|
||||
return request.Content;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
namespace Google.Apis.Http
|
||||
{
|
||||
/// <summary>
|
||||
/// HTTP client initializer for changing the default behavior of HTTP client.
|
||||
/// Use this initializer to change default values like timeout and number of tries.
|
||||
/// You can also set different handlers and interceptors like <see cref="IHttpUnsuccessfulResponseHandler"/>s,
|
||||
/// <see cref="IHttpExceptionHandler"/>s and <see cref="IHttpExecuteInterceptor"/>s.
|
||||
/// </summary>
|
||||
public interface IConfigurableHttpClientInitializer
|
||||
{
|
||||
/// <summary>Initializes a HTTP client after it was created.</summary>
|
||||
void Initialize(ConfigurableHttpClient httpClient);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
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;
|
||||
|
||||
namespace Google.Apis.Http
|
||||
{
|
||||
/// <summary>Arguments for creating a HTTP client.</summary>
|
||||
public class CreateHttpClientArgs
|
||||
{
|
||||
/// <summary>Gets or sets whether GZip is enabled.</summary>
|
||||
public bool GZipEnabled { get; set; }
|
||||
|
||||
/// <summary>Gets or sets the application name that is sent in the User-Agent header.</summary>
|
||||
public string ApplicationName { get; set; }
|
||||
|
||||
/// <summary>Gets a list of initializers to initialize the HTTP client instance.</summary>
|
||||
public IList<IConfigurableHttpClientInitializer> Initializers { get; private set; }
|
||||
|
||||
/// <summary>Constructs a new argument instance.</summary>
|
||||
public CreateHttpClientArgs()
|
||||
{
|
||||
Initializers = new List<IConfigurableHttpClientInitializer>();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// HTTP client factory creates configurable HTTP clients. A unique HTTP client should be created for each service.
|
||||
/// </summary>
|
||||
public interface IHttpClientFactory
|
||||
{
|
||||
/// <summary>Creates a new configurable HTTP client.</summary>
|
||||
ConfigurableHttpClient CreateHttpClient(CreateHttpClientArgs args);
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
{
|
||||
/// <summary>Argument class to <see cref="IHttpExceptionHandler.HandleExceptionAsync"/>.</summary>
|
||||
public class HandleExceptionArgs
|
||||
{
|
||||
/// <summary>Gets or sets the sent request.</summary>
|
||||
public HttpRequestMessage Request { get; set; }
|
||||
|
||||
/// <summary>Gets or sets the exception which occurred during sending the request.</summary>
|
||||
public Exception Exception { get; set; }
|
||||
|
||||
/// <summary>Gets or sets the total number of tries to send the request.</summary>
|
||||
public int TotalTries { get; set; }
|
||||
|
||||
/// <summary>Gets or sets the current failed try.</summary>
|
||||
public int CurrentFailedTry { get; set; }
|
||||
|
||||
/// <summary>Gets an indication whether a retry will occur if the handler returns <c>true</c>.</summary>
|
||||
public bool SupportsRetry
|
||||
{
|
||||
get { return TotalTries - CurrentFailedTry > 0; }
|
||||
}
|
||||
|
||||
/// <summary>Gets or sets the request's cancellation token.</summary>
|
||||
public CancellationToken CancellationToken { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>Exception handler is invoked when an exception is thrown during a HTTP request.</summary>
|
||||
public interface IHttpExceptionHandler
|
||||
{
|
||||
/// <summary>
|
||||
/// 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 <c>true</c>.
|
||||
/// </summary>
|
||||
/// <param name="args">
|
||||
/// Handle exception argument which properties such as the request, exception, current failed try.
|
||||
/// </param>
|
||||
/// <returns>Whether this handler has made a change that requires the request to be resent.</returns>
|
||||
Task<bool> HandleExceptionAsync(HandleExceptionArgs args);
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// HTTP request execute interceptor to intercept a <see cref="System.Net.Http.HttpRequestMessage"/> before it has
|
||||
/// been sent. Sample usage is attaching "Authorization" header to a request.
|
||||
/// </summary>
|
||||
public interface IHttpExecuteInterceptor
|
||||
{
|
||||
/// <summary>
|
||||
/// <summary>Invoked before the request is being sent.</summary>
|
||||
/// </summary>
|
||||
/// <param name="request">The HTTP request message.</param>
|
||||
/// <param name="cancellationToken">Cancellation token to cancel the operation.</param>
|
||||
Task InterceptAsync(HttpRequestMessage request, CancellationToken cancellationToken);
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
{
|
||||
/// <summary>Argument class to <see cref="IHttpUnsuccessfulResponseHandler.HandleResponseAsync"/>.</summary>
|
||||
public class HandleUnsuccessfulResponseArgs
|
||||
{
|
||||
/// <summary>Gets or sets the sent request.</summary>
|
||||
public HttpRequestMessage Request { get; set; }
|
||||
|
||||
/// <summary>Gets or sets the abnormal response.</summary>
|
||||
public HttpResponseMessage Response { get; set; }
|
||||
|
||||
/// <summary>Gets or sets the total number of tries to send the request.</summary>
|
||||
public int TotalTries { get; set; }
|
||||
|
||||
/// <summary>Gets or sets the current failed try.</summary>
|
||||
public int CurrentFailedTry { get; set; }
|
||||
|
||||
/// <summary>Gets an indication whether a retry will occur if the handler returns <c>true</c>.</summary>
|
||||
public bool SupportsRetry
|
||||
{
|
||||
get { return TotalTries - CurrentFailedTry > 0; }
|
||||
}
|
||||
|
||||
/// <summary>Gets or sets the request's cancellation token.</summary>
|
||||
public CancellationToken CancellationToken { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unsuccessful response handler which is invoked when an abnormal HTTP response is returned when sending a HTTP
|
||||
/// request.
|
||||
/// </summary>
|
||||
public interface IHttpUnsuccessfulResponseHandler
|
||||
{
|
||||
/// <summary>
|
||||
/// 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 <c>true</c>.
|
||||
/// </summary>
|
||||
/// <param name="args">
|
||||
/// Handle response argument which contains properties such as the request, response, current failed try.
|
||||
/// </param>
|
||||
/// <returns>Whether this handler has made a change that requires the request to be resent.</returns>
|
||||
Task<bool> HandleResponseAsync(HandleUnsuccessfulResponseArgs args);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
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.Net.Http.Headers;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Google.Apis.Testing;
|
||||
|
||||
namespace Google.Apis.Http
|
||||
{
|
||||
/// <summary>
|
||||
/// Intercepts HTTP GET requests with a URLs longer than a specified maximum number of characters.
|
||||
/// The interceptor will change such requests as follows:
|
||||
/// <list type="bullet">
|
||||
/// <item>The request's method will be changed to POST</item>
|
||||
/// <item>A <c>X-HTTP-Method-Override</c> header will be added with the value <c>GET</c></item>
|
||||
/// <item>Any query parameters from the URI will be moved into the body of the request.</item>
|
||||
/// <item>If query parameters are moved, the content type is set to <c>application/x-www-form-urlencoded</c></item>
|
||||
/// </list>
|
||||
/// </summary>
|
||||
[VisibleForTestOnly]
|
||||
public class MaxUrlLengthInterceptor : IHttpExecuteInterceptor
|
||||
{
|
||||
private readonly uint maxUrlLength;
|
||||
|
||||
///<summary>Constructs a new Max URL length interceptor with the given max length.</summary>
|
||||
public MaxUrlLengthInterceptor(uint maxUrlLength)
|
||||
{
|
||||
this.maxUrlLength = maxUrlLength;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Task InterceptAsync(HttpRequestMessage request, CancellationToken cancellationToken)
|
||||
{
|
||||
if (request.Method != HttpMethod.Get || request.RequestUri.AbsoluteUri.Length <= maxUrlLength)
|
||||
{
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
// Change the method to POST.
|
||||
request.Method = HttpMethod.Post;
|
||||
var query = request.RequestUri.Query;
|
||||
if (!String.IsNullOrEmpty(query))
|
||||
{
|
||||
// Move query parameters to the body (without the "?").
|
||||
request.Content = new StringContent(query.Substring(1));
|
||||
request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/x-www-form-urlencoded");
|
||||
var requestString = request.RequestUri.ToString();
|
||||
// The new request URI is the old one minus the "?" and everything that follows, since we moved the
|
||||
// query params to the body. For example: "www.example.com/?q=foo" => "www.example.com/".
|
||||
request.RequestUri = new Uri(requestString.Remove(requestString.IndexOf("?")));
|
||||
}
|
||||
request.Headers.Add("X-HTTP-Method-Override", "GET");
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
43
Yavsc/GoogleApiSupport/Google.Apis.Core/ISerializer.cs
Normal file
43
Yavsc/GoogleApiSupport/Google.Apis.Core/ISerializer.cs
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
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;
|
||||
using System.IO;
|
||||
|
||||
namespace Google.Apis
|
||||
{
|
||||
/// <summary>Serialization interface that supports serialize and deserialize methods.</summary>
|
||||
public interface ISerializer
|
||||
{
|
||||
/// <summary>Gets the application format this serializer supports (e.g. "json", "xml", etc.).</summary>
|
||||
string Format { get; }
|
||||
|
||||
/// <summary>Serializes the specified object into a Stream.</summary>
|
||||
void Serialize(object obj, Stream target);
|
||||
|
||||
/// <summary>Serializes the specified object into a string.</summary>
|
||||
string Serialize(object obj);
|
||||
|
||||
/// <summary>Deserializes the string into an object.</summary>
|
||||
T Deserialize<T>(string input);
|
||||
|
||||
/// <summary>Deserializes the string into an object.</summary>
|
||||
object Deserialize(string input, Type type);
|
||||
|
||||
/// <summary>Deserializes the stream into an object.</summary>
|
||||
T Deserialize<T>(Stream input);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
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.Json
|
||||
{
|
||||
/// <summary>Represents a JSON serializer.</summary>
|
||||
public interface IJsonSerializer : ISerializer
|
||||
{
|
||||
}
|
||||
}
|
||||
100
Yavsc/GoogleApiSupport/Google.Apis.Core/Json/JsonExplicitNull.cs
Normal file
100
Yavsc/GoogleApiSupport/Google.Apis.Core/Json/JsonExplicitNull.cs
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
/*
|
||||
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;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Google.Apis.Json
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides values which are explicitly expressed as <c>null</c> when converted to JSON.
|
||||
/// </summary>
|
||||
public static class JsonExplicitNull
|
||||
{
|
||||
/// <summary>
|
||||
/// Get an <see cref="IList{T}"/> that is explicitly expressed as <c>null</c> when converted to JSON.
|
||||
/// </summary>
|
||||
/// <returns>An <see cref="IList{T}"/> that is explicitly expressed as <c>null</c> when converted to JSON.</returns>
|
||||
public static IList<T> ForIList<T>() => ExplicitNullList<T>.Instance;
|
||||
|
||||
[JsonExplicitNull]
|
||||
private sealed class ExplicitNullList<T> : IList<T>
|
||||
{
|
||||
public static ExplicitNullList<T> Instance = new ExplicitNullList<T>();
|
||||
|
||||
public T this[int index]
|
||||
{
|
||||
get { throw new NotSupportedException(); }
|
||||
set { throw new NotSupportedException(); }
|
||||
}
|
||||
|
||||
public int Count { get { throw new NotSupportedException(); } }
|
||||
|
||||
public bool IsReadOnly { get { throw new NotSupportedException(); } }
|
||||
|
||||
public void Add(T item)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public bool Contains(T item)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public void CopyTo(T[] array, int arrayIndex)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public IEnumerator<T> GetEnumerator()
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public int IndexOf(T item)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public void Insert(int index, T item)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public bool Remove(T item)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public void RemoveAt(int index)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
Copyright 2016 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.Json
|
||||
{
|
||||
/// <summary>
|
||||
/// All values of a type with this attribute are represented as a literal <c>null</c> in JSON.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public class JsonExplicitNullAttribute : Attribute { }
|
||||
}
|
||||
|
|
@ -0,0 +1,179 @@
|
|||
/*
|
||||
Copyright 2017 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.Util;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Linq;
|
||||
|
||||
namespace Google.Apis.Json
|
||||
{
|
||||
/// <summary>
|
||||
/// A JSON converter which honers RFC 3339 and the serialized date is accepted by Google services.
|
||||
/// </summary>
|
||||
public class RFC3339DateTimeConverter : JsonConverter
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override bool CanRead => false;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override object ReadJson(Newtonsoft.Json.JsonReader reader, Type objectType, object existingValue,
|
||||
JsonSerializer serializer)
|
||||
{
|
||||
throw new NotImplementedException("Unnecessary because CanRead is false.");
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool CanConvert(Type objectType) =>
|
||||
// Convert DateTime only.
|
||||
objectType == typeof(DateTime) || objectType == typeof(Nullable<DateTime>);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
||||
{
|
||||
if (value != null)
|
||||
{
|
||||
DateTime date = (DateTime)value;
|
||||
serializer.Serialize(writer, Utilities.ConvertToRFC3339(date));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A JSON converter to write <c>null</c> literals into JSON when explicitly requested.
|
||||
/// </summary>
|
||||
public class ExplicitNullConverter : JsonConverter
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override bool CanRead => false;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanConvert(Type objectType) => objectType.GetTypeInfo().GetCustomAttributes(typeof(JsonExplicitNullAttribute), false).Any();
|
||||
|
||||
/// <inheritdoc />
|
||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
||||
{
|
||||
throw new NotImplementedException("Unnecessary because CanRead is false.");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) => writer.WriteNull();
|
||||
}
|
||||
|
||||
/// <summary>Class for serialization and deserialization of JSON documents using the Newtonsoft Library.</summary>
|
||||
public class NewtonsoftJsonSerializer : IJsonSerializer
|
||||
{
|
||||
private readonly JsonSerializerSettings settings;
|
||||
private readonly JsonSerializer serializer;
|
||||
|
||||
/// <summary>The default instance of the Newtonsoft JSON Serializer, with default settings.</summary>
|
||||
public static NewtonsoftJsonSerializer Instance { get; } = new NewtonsoftJsonSerializer();
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new instance with the default serialization settings, equivalent to <see cref="Instance"/>.
|
||||
/// </summary>
|
||||
public NewtonsoftJsonSerializer() : this(CreateDefaultSettings())
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new instance with the given settings.
|
||||
/// </summary>
|
||||
/// <param name="settings">The settings to apply when serializing and deserializing. Must not be null.</param>
|
||||
public NewtonsoftJsonSerializer(JsonSerializerSettings settings)
|
||||
{
|
||||
Utilities.ThrowIfNull(settings, nameof(settings));
|
||||
this.settings = settings;
|
||||
serializer = JsonSerializer.Create(settings);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of <see cref="JsonSerializerSettings"/> with the same behavior
|
||||
/// as the ones used in <see cref="Instance"/>. This method is expected to be used to construct
|
||||
/// settings which are then passed to <see cref="NewtonsoftJsonSerializer.NewtonsoftJsonSerializer(JsonSerializerSettings)"/>.
|
||||
/// </summary>
|
||||
/// <returns>A new set of default settings.</returns>
|
||||
public static JsonSerializerSettings CreateDefaultSettings() =>
|
||||
new JsonSerializerSettings
|
||||
{
|
||||
NullValueHandling = NullValueHandling.Ignore,
|
||||
MetadataPropertyHandling = MetadataPropertyHandling.Ignore,
|
||||
Converters = { new RFC3339DateTimeConverter(), new ExplicitNullConverter() }
|
||||
};
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string Format => "json";
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Serialize(object obj, Stream target)
|
||||
{
|
||||
using (var writer = new StreamWriter(target))
|
||||
{
|
||||
if (obj == null)
|
||||
{
|
||||
obj = string.Empty;
|
||||
}
|
||||
serializer.Serialize(writer, obj);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string Serialize(object obj)
|
||||
{
|
||||
using (TextWriter tw = new StringWriter())
|
||||
{
|
||||
if (obj == null)
|
||||
{
|
||||
obj = string.Empty;
|
||||
}
|
||||
serializer.Serialize(tw, obj);
|
||||
return tw.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public T Deserialize<T>(string input)
|
||||
{
|
||||
if (string.IsNullOrEmpty(input))
|
||||
{
|
||||
return default(T);
|
||||
}
|
||||
return JsonConvert.DeserializeObject<T>(input, settings);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public object Deserialize(string input, Type type)
|
||||
{
|
||||
if (string.IsNullOrEmpty(input))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return JsonConvert.DeserializeObject(input, type, settings);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public T Deserialize<T>(Stream input)
|
||||
{
|
||||
// Convert the JSON document into an object.
|
||||
using (StreamReader streamReader = new StreamReader(input))
|
||||
{
|
||||
return (T)serializer.Deserialize(streamReader, typeof(T));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
172
Yavsc/GoogleApiSupport/Google.Apis.Core/Logging/BaseLogger.cs
Normal file
172
Yavsc/GoogleApiSupport/Google.Apis.Core/Logging/BaseLogger.cs
Normal file
|
|
@ -0,0 +1,172 @@
|
|||
/*
|
||||
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.Util;
|
||||
using System;
|
||||
using System.Globalization;
|
||||
|
||||
namespace Google.Apis.Logging
|
||||
{
|
||||
/// <summary>
|
||||
/// An abstract base logger, upon which real loggers may be built.
|
||||
/// </summary>
|
||||
public abstract class BaseLogger : ILogger
|
||||
{
|
||||
// Does not match gRPC datetime log format, which is "MMdd HH:mm:ss.ffffff"
|
||||
private const string DateTimeFormatString = "yyyy-MM-dd HH:mm:ss.ffffff";
|
||||
|
||||
/// <summary>
|
||||
/// Construct a <see cref="BaseLogger"/>.
|
||||
/// </summary>
|
||||
/// <param name="minimumLogLevel">Logging will be enabled at this level and all higher levels.</param>
|
||||
/// <param name="clock">The <see cref="IClock"/> to use to timestamp log entries.</param>
|
||||
/// <param name="forType">The type from which entries are being logged. May be <c>null</c>.</param>
|
||||
protected BaseLogger(LogLevel minimumLogLevel, IClock clock, Type forType)
|
||||
{
|
||||
MinimumLogLevel = minimumLogLevel;
|
||||
IsDebugEnabled = minimumLogLevel <= LogLevel.Debug;
|
||||
IsInfoEnabled = minimumLogLevel <= LogLevel.Info;
|
||||
IsWarningEnabled = minimumLogLevel <= LogLevel.Warning;
|
||||
IsErrorEnabled = minimumLogLevel <= LogLevel.Error;
|
||||
Clock = clock ?? SystemClock.Default;
|
||||
LoggerForType = forType;
|
||||
if (forType != null)
|
||||
{
|
||||
var namespaceStr = forType.Namespace ?? "";
|
||||
if (namespaceStr.Length > 0)
|
||||
{
|
||||
namespaceStr += ".";
|
||||
}
|
||||
_loggerForTypeString = namespaceStr + forType.Name + " ";
|
||||
}
|
||||
else
|
||||
{
|
||||
_loggerForTypeString = "";
|
||||
}
|
||||
}
|
||||
|
||||
private readonly string _loggerForTypeString;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="IClock"/> being used to timestamp log entries.
|
||||
/// </summary>
|
||||
public IClock Clock { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The type from which entries are being logged. May be <c>null</c>.
|
||||
/// </summary>
|
||||
public Type LoggerForType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Logging is enabled at this level and all higher levels.
|
||||
/// </summary>
|
||||
public LogLevel MinimumLogLevel { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Is Debug level logging enabled?
|
||||
/// </summary>
|
||||
public bool IsDebugEnabled { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Is info level logging enabled?
|
||||
/// </summary>
|
||||
public bool IsInfoEnabled { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Is warning level logging enabled?
|
||||
/// </summary>
|
||||
public bool IsWarningEnabled { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Is error level logging enabled?
|
||||
/// </summary>
|
||||
public bool IsErrorEnabled { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Build a new logger of the derived concrete type, for use to log from the specified type.
|
||||
/// </summary>
|
||||
/// <param name="type">The type from which entries are being logged.</param>
|
||||
/// <returns>A new <see cref="ILogger"/> instance, logging from the specified type.</returns>
|
||||
protected abstract ILogger BuildNewLogger(Type type);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ILogger ForType<T>() => ForType(typeof(T));
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ILogger ForType(Type type) => type == LoggerForType ? this : BuildNewLogger(type);
|
||||
|
||||
/// <summary>
|
||||
/// Perform the actual logging.
|
||||
/// </summary>
|
||||
/// <param name="logLevel">The <see cref="LogLevel"/> of this log entry.</param>
|
||||
/// <param name="formattedMessage">The fully formatted log message, ready for logging.</param>
|
||||
protected abstract void Log(LogLevel logLevel, string formattedMessage);
|
||||
|
||||
private string FormatLogEntry(string severityString, string message, params object[] formatArgs)
|
||||
{
|
||||
var msg = string.Format(message, formatArgs);
|
||||
var when = Clock.UtcNow.ToString(DateTimeFormatString, CultureInfo.InvariantCulture);
|
||||
// Matches gRPC log format
|
||||
return $"{severityString}{when} {_loggerForTypeString}{msg}";
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Debug(string message, params object[] formatArgs)
|
||||
{
|
||||
if (IsDebugEnabled)
|
||||
{
|
||||
Log(LogLevel.Debug, FormatLogEntry("D", message, formatArgs));
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Info(string message, params object[] formatArgs)
|
||||
{
|
||||
if (IsInfoEnabled)
|
||||
{
|
||||
Log(LogLevel.Info, FormatLogEntry("I", message, formatArgs));
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Warning(string message, params object[] formatArgs)
|
||||
{
|
||||
if (IsWarningEnabled)
|
||||
{
|
||||
Log(LogLevel.Warning, FormatLogEntry("W", message, formatArgs));
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Error(Exception exception, string message, params object[] formatArgs)
|
||||
{
|
||||
if (IsErrorEnabled)
|
||||
{
|
||||
Log(LogLevel.Error, $"{FormatLogEntry("E", message, formatArgs)} {exception}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Error(string message, params object[] formatArgs)
|
||||
{
|
||||
if (IsErrorEnabled)
|
||||
{
|
||||
Log(LogLevel.Error, FormatLogEntry("E", message, formatArgs));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
Copyright 2017 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.Util;
|
||||
using System;
|
||||
|
||||
namespace Google.Apis.Logging
|
||||
{
|
||||
/// <summary>
|
||||
/// A logger than logs to StdError or StdOut.
|
||||
/// </summary>
|
||||
public sealed class ConsoleLogger : BaseLogger, ILogger
|
||||
{
|
||||
/// <summary>
|
||||
/// Construct a <see cref="ConsoleLogger"/>.
|
||||
/// </summary>
|
||||
/// <param name="minimumLogLevel">Logging will be enabled at this level and all higher levels.</param>
|
||||
/// <param name="logToStdOut"><c>true</c> to log to StdOut, defaults to logging to StdError.</param>
|
||||
/// <param name="clock">Optional <see cref="IClock"/>; will use the system clock if <c>null</c>.</param>
|
||||
public ConsoleLogger(LogLevel minimumLogLevel, bool logToStdOut = false, IClock clock = null) : this(minimumLogLevel, logToStdOut, clock, null) { }
|
||||
|
||||
private ConsoleLogger(LogLevel minimumLogLevel, bool logToStdOut, IClock clock, Type forType) : base(minimumLogLevel, clock, forType)
|
||||
{
|
||||
LogToStdOut = logToStdOut;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <c>false</c> to log to StdError; <c>true</c> to log to StdOut.
|
||||
/// </summary>
|
||||
public bool LogToStdOut { get; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override ILogger BuildNewLogger(Type type) => new ConsoleLogger(MinimumLogLevel, LogToStdOut, Clock, type);
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void Log(LogLevel logLevel, string formattedMessage)
|
||||
{
|
||||
(LogToStdOut ? Console.Out : Console.Error).WriteLine(formattedMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
62
Yavsc/GoogleApiSupport/Google.Apis.Core/Logging/ILogger.cs
Normal file
62
Yavsc/GoogleApiSupport/Google.Apis.Core/Logging/ILogger.cs
Normal file
|
|
@ -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
|
||||
{
|
||||
/// <summary>Describes a logging interface which is used for outputting messages.</summary>
|
||||
public interface ILogger
|
||||
{
|
||||
/// <summary>Gets an indication whether debug output is logged or not.</summary>
|
||||
bool IsDebugEnabled { get; }
|
||||
|
||||
/// <summary>Returns a logger which will be associated with the specified type.</summary>
|
||||
/// <param name="type">Type to which this logger belongs.</param>
|
||||
/// <returns>A type-associated logger.</returns>
|
||||
ILogger ForType(Type type);
|
||||
|
||||
/// <summary>Returns a logger which will be associated with the specified type.</summary>
|
||||
/// <returns>A type-associated logger.</returns>
|
||||
ILogger ForType<T>();
|
||||
|
||||
/// <summary>Logs a debug message.</summary>
|
||||
/// <param name="message">The message to log.</param>
|
||||
/// <param name="formatArgs">String.Format arguments (if applicable).</param>
|
||||
void Debug(string message, params object[] formatArgs);
|
||||
|
||||
/// <summary>Logs an info message.</summary>
|
||||
/// <param name="message">The message to log.</param>
|
||||
/// <param name="formatArgs">String.Format arguments (if applicable).</param>
|
||||
void Info(string message, params object[] formatArgs);
|
||||
|
||||
/// <summary>Logs a warning.</summary>
|
||||
/// <param name="message">The message to log.</param>
|
||||
/// <param name="formatArgs">String.Format arguments (if applicable).</param>
|
||||
void Warning(string message, params object[] formatArgs);
|
||||
|
||||
/// <summary>Logs an error message resulting from an exception.</summary>
|
||||
/// <param name="exception"></param>
|
||||
/// <param name="message">The message to log.</param>
|
||||
/// <param name="formatArgs">String.Format arguments (if applicable).</param>
|
||||
void Error(Exception exception, string message, params object[] formatArgs);
|
||||
|
||||
/// <summary>Logs an error message.</summary>
|
||||
/// <param name="message">The message to log.</param>
|
||||
/// <param name="formatArgs">String.Format arguments (if applicable).</param>
|
||||
void Error(string message, params object[] formatArgs);
|
||||
}
|
||||
}
|
||||
54
Yavsc/GoogleApiSupport/Google.Apis.Core/Logging/LogLevel.cs
Normal file
54
Yavsc/GoogleApiSupport/Google.Apis.Core/Logging/LogLevel.cs
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
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.Logging
|
||||
{
|
||||
/// <summary>
|
||||
/// The supported logging levels.
|
||||
/// </summary>
|
||||
public enum LogLevel
|
||||
{
|
||||
/// <summary>
|
||||
/// A value lower than all logging levels.
|
||||
/// </summary>
|
||||
All = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Debug logging.
|
||||
/// </summary>
|
||||
Debug = 100,
|
||||
|
||||
/// <summary>
|
||||
/// Info logging.
|
||||
/// </summary>
|
||||
Info = 200,
|
||||
|
||||
/// <summary>
|
||||
/// Warning logging.
|
||||
/// </summary>
|
||||
Warning = 300,
|
||||
|
||||
/// <summary>
|
||||
/// Error logging.
|
||||
/// </summary>
|
||||
Error = 400,
|
||||
|
||||
/// <summary>
|
||||
/// A value higher than all logging levels.
|
||||
/// </summary>
|
||||
None = 1000,
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
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.Util;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace Google.Apis.Logging
|
||||
{
|
||||
/// <summary>
|
||||
/// A logger than logs to an in-memory buffer.
|
||||
/// Generally for use during tests.
|
||||
/// </summary>
|
||||
public sealed class MemoryLogger : BaseLogger, ILogger
|
||||
{
|
||||
/// <summary>
|
||||
/// Construct a <see cref="MemoryLogger"/>.
|
||||
/// </summary>
|
||||
/// <param name="minimumLogLevel">Logging will be enabled at this level and all higher levels.</param>
|
||||
/// <param name="maximumEntryCount">The maximum number of log entries. Further log entries will be silently discarded.</param>
|
||||
/// <param name="clock">Optional <see cref="IClock"/>; will use the system clock if <c>null</c>.</param>
|
||||
public MemoryLogger(LogLevel minimumLogLevel, int maximumEntryCount = 1000, IClock clock = null) :
|
||||
this(minimumLogLevel, maximumEntryCount, clock, new List<string>(), null) { }
|
||||
|
||||
private MemoryLogger(LogLevel minimumLogLevel, int maximumEntryCount, IClock clock, List<string> logEntries, Type forType) : base(minimumLogLevel, clock, forType)
|
||||
{
|
||||
_logEntries = logEntries;
|
||||
LogEntries = new ReadOnlyCollection<string>(_logEntries);
|
||||
_maximumEntryCount = maximumEntryCount;
|
||||
}
|
||||
|
||||
private readonly int _maximumEntryCount;
|
||||
|
||||
// This list is shared between all derived MemoryLogger instances
|
||||
private readonly List<string> _logEntries;
|
||||
|
||||
/// <summary>
|
||||
/// The list of log entries.
|
||||
/// </summary>
|
||||
public IList<string> LogEntries { get; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override ILogger BuildNewLogger(Type type) => new MemoryLogger(MinimumLogLevel, _maximumEntryCount, Clock, _logEntries, type);
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void Log(LogLevel logLevel, string formattedMessage)
|
||||
{
|
||||
lock (_logEntries)
|
||||
{
|
||||
if (_logEntries.Count < _maximumEntryCount)
|
||||
{
|
||||
_logEntries.Add(formattedMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a NullLogger which does not do any logging.
|
||||
/// </summary>
|
||||
public class NullLogger : ILogger
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public bool IsDebugEnabled
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ILogger ForType(Type type)
|
||||
{
|
||||
return new NullLogger();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ILogger ForType<T>()
|
||||
{
|
||||
return new NullLogger();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Info(string message, params object[] formatArgs) {}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Warning(string message, params object[] formatArgs) {}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Debug(string message, params object[] formatArgs) {}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Error(Exception exception, string message, params object[] formatArgs) {}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Error(string message, params object[] formatArgs) {}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,166 @@
|
|||
/*
|
||||
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;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using Google.Apis.Util;
|
||||
|
||||
namespace Google.Apis.Requests.Parameters
|
||||
{
|
||||
/// <summary>A collection of parameters (key value pairs). May contain duplicate keys.</summary>
|
||||
public class ParameterCollection : List<KeyValuePair<string, string>>
|
||||
{
|
||||
/// <summary>Constructs a new parameter collection.</summary>
|
||||
public ParameterCollection() : base() { }
|
||||
|
||||
/// <summary>Constructs a new parameter collection from the given collection.</summary>
|
||||
public ParameterCollection(IEnumerable<KeyValuePair<string, string>> collection) : base(collection) { }
|
||||
|
||||
/// <summary>Adds a single parameter to this collection.</summary>
|
||||
public void Add(string key, string value)
|
||||
{
|
||||
Add(new KeyValuePair<string, string>(key, value));
|
||||
}
|
||||
|
||||
/// <summary>Returns <c>true</c> if this parameter is set within the collection.</summary>
|
||||
public bool ContainsKey(string key)
|
||||
{
|
||||
key.ThrowIfNullOrEmpty("key");
|
||||
string value;
|
||||
return TryGetValue(key, out value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to find the a key within the specified key value collection. Returns true if the key was found.
|
||||
/// If a pair was found the out parameter value will contain the value of that pair.
|
||||
/// </summary>
|
||||
public bool TryGetValue(string key, out string value)
|
||||
{
|
||||
key.ThrowIfNullOrEmpty("key");
|
||||
|
||||
foreach (KeyValuePair<string, string> pair in this)
|
||||
{
|
||||
// Check if this pair matches the specified key name.
|
||||
if (pair.Key.Equals(key))
|
||||
{
|
||||
value = pair.Value;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// No result found.
|
||||
value = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the value of the first matching key, or throws a KeyNotFoundException if the parameter is not
|
||||
/// present within the collection.
|
||||
/// </summary>
|
||||
public string GetFirstMatch(string key)
|
||||
{
|
||||
string val;
|
||||
if (!TryGetValue(key, out val))
|
||||
{
|
||||
throw new KeyNotFoundException("Parameter with the name '" + key + "' was not found.");
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns all matches for the specified key. May return an empty enumeration if the key is not present.
|
||||
/// </summary>
|
||||
public IEnumerable<string> GetAllMatches(string key)
|
||||
{
|
||||
key.ThrowIfNullOrEmpty("key");
|
||||
|
||||
foreach (KeyValuePair<string, string> pair in this)
|
||||
{
|
||||
// Check if this pair matches the specified key name.
|
||||
if (pair.Key.Equals(key))
|
||||
{
|
||||
yield return pair.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns all matches for the specified key. May return an empty enumeration if the key is not present.
|
||||
/// </summary>
|
||||
public IEnumerable<string> this[string key]
|
||||
{
|
||||
get { return GetAllMatches(key); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a parameter collection from the specified URL encoded query string.
|
||||
/// Example:
|
||||
/// The query string "foo=bar&chocolate=cookie" would result in two parameters (foo and bar)
|
||||
/// with the values "bar" and "cookie" set.
|
||||
/// </summary>
|
||||
public static ParameterCollection FromQueryString(string qs)
|
||||
{
|
||||
var collection = new ParameterCollection();
|
||||
var qsParam = qs.Split('&');
|
||||
foreach (var param in qsParam)
|
||||
{
|
||||
// Split the parameter into key and value.
|
||||
var info = param.Split(new[] { '=' });
|
||||
if (info.Length == 2)
|
||||
{
|
||||
collection.Add(Uri.UnescapeDataString(info[0]), Uri.UnescapeDataString(info[1]));
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentException(string.Format(
|
||||
"Invalid query string [{0}]. Invalid part [{1}]", qs, param));
|
||||
}
|
||||
}
|
||||
|
||||
return collection;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a parameter collection from the specified dictionary.
|
||||
/// If the value is an enumerable, a parameter pair will be added for each value.
|
||||
/// Otherwise the value will be converted into a string using the .ToString() method.
|
||||
/// </summary>
|
||||
public static ParameterCollection FromDictionary(IDictionary<string, object> dictionary)
|
||||
{
|
||||
var collection = new ParameterCollection();
|
||||
foreach (KeyValuePair<string, object> pair in dictionary)
|
||||
{
|
||||
// Try parsing the value of the pair as an enumerable.
|
||||
var valueAsEnumerable = pair.Value as IEnumerable;
|
||||
if (!(pair.Value is string) && valueAsEnumerable != null)
|
||||
{
|
||||
foreach (var value in valueAsEnumerable)
|
||||
{
|
||||
collection.Add(pair.Key, Util.Utilities.ConvertToString(value));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Otherwise just convert it to a string.
|
||||
collection.Add(pair.Key, pair.Value == null ? null : Util.Utilities.ConvertToString(pair.Value));
|
||||
}
|
||||
}
|
||||
return collection;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,150 @@
|
|||
/*
|
||||
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.Net.Http;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
using Google.Apis.Logging;
|
||||
using Google.Apis.Util;
|
||||
|
||||
namespace Google.Apis.Requests.Parameters
|
||||
{
|
||||
/// <summary>
|
||||
/// Utility class for iterating on <see cref="RequestParameterAttribute"/> properties in a request object.
|
||||
/// </summary>
|
||||
public static class ParameterUtils
|
||||
{
|
||||
private static readonly ILogger Logger = ApplicationContext.Logger.ForType(typeof(ParameterUtils));
|
||||
|
||||
/// <summary>
|
||||
/// Creates a <see cref="System.Net.Http.FormUrlEncodedContent"/> with all the specified parameters in
|
||||
/// the input request. It uses reflection to iterate over all properties with
|
||||
/// <see cref="Google.Apis.Util.RequestParameterAttribute"/> attribute.
|
||||
/// </summary>
|
||||
/// <param name="request">
|
||||
/// A request object which contains properties with
|
||||
/// <see cref="Google.Apis.Util.RequestParameterAttribute"/> attribute. Those properties will be serialized
|
||||
/// to the returned <see cref="System.Net.Http.FormUrlEncodedContent"/>.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// A <see cref="System.Net.Http.FormUrlEncodedContent"/> which contains the all the given object required
|
||||
/// values.
|
||||
/// </returns>
|
||||
public static FormUrlEncodedContent CreateFormUrlEncodedContent(object request)
|
||||
{
|
||||
IList<KeyValuePair<string, string>> list = new List<KeyValuePair<string, string>>();
|
||||
IterateParameters(request, (type, name, value) =>
|
||||
{
|
||||
list.Add(new KeyValuePair<string, string>(name, value.ToString()));
|
||||
});
|
||||
return new FormUrlEncodedContent(list);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a parameter dictionary by using reflection to iterate over all properties with
|
||||
/// <see cref="Google.Apis.Util.RequestParameterAttribute"/> attribute.
|
||||
/// </summary>
|
||||
/// <param name="request">
|
||||
/// A request object which contains properties with
|
||||
/// <see cref="Google.Apis.Util.RequestParameterAttribute"/> attribute. Those properties will be set
|
||||
/// in the output dictionary.
|
||||
/// </param>
|
||||
public static IDictionary<string, object> CreateParameterDictionary(object request)
|
||||
{
|
||||
var dict = new Dictionary<string, object>();
|
||||
IterateParameters(request, (type, name, value) =>
|
||||
{
|
||||
dict.Add(name, value);
|
||||
});
|
||||
return dict;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets query parameters in the given builder with all all properties with the
|
||||
/// <see cref="Google.Apis.Util.RequestParameterAttribute"/> attribute.
|
||||
/// </summary>
|
||||
/// <param name="builder">The request builder</param>
|
||||
/// <param name="request">
|
||||
/// A request object which contains properties with
|
||||
/// <see cref="Google.Apis.Util.RequestParameterAttribute"/> attribute. Those properties will be set in the
|
||||
/// given request builder object
|
||||
/// </param>
|
||||
public static void InitParameters(RequestBuilder builder, object request)
|
||||
{
|
||||
IterateParameters(request, (type, name, value) =>
|
||||
{
|
||||
builder.AddParameter(type, name, value.ToString());
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Iterates over all <see cref="Google.Apis.Util.RequestParameterAttribute"/> properties in the request
|
||||
/// object and invokes the specified action for each of them.
|
||||
/// </summary>
|
||||
/// <param name="request">A request object</param>
|
||||
/// <param name="action">An action to invoke which gets the parameter type, name and its value</param>
|
||||
private static void IterateParameters(object request, Action<RequestParameterType, string, object> action)
|
||||
{
|
||||
// Use reflection to build the parameter dictionary.
|
||||
foreach (PropertyInfo property in request.GetType().GetProperties(BindingFlags.Instance |
|
||||
BindingFlags.Public))
|
||||
{
|
||||
// Retrieve the RequestParameterAttribute.
|
||||
RequestParameterAttribute attribute =
|
||||
property.GetCustomAttributes(typeof(RequestParameterAttribute), false).FirstOrDefault() as
|
||||
RequestParameterAttribute;
|
||||
if (attribute == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get the name of this parameter from the attribute, if it doesn't exist take a lower-case variant of
|
||||
// property name.
|
||||
string name = attribute.Name ?? property.Name.ToLower();
|
||||
|
||||
var propertyType = property.PropertyType;
|
||||
var value = property.GetValue(request, null);
|
||||
|
||||
// Call action with the type name and value.
|
||||
if (propertyType.GetTypeInfo().IsValueType || value != null)
|
||||
{
|
||||
if (attribute.Type == RequestParameterType.UserDefinedQueries)
|
||||
{
|
||||
if (typeof(IEnumerable<KeyValuePair<string, string>>).IsAssignableFrom(value.GetType()))
|
||||
{
|
||||
foreach (var pair in (IEnumerable<KeyValuePair<string, string>>)value)
|
||||
{
|
||||
action(RequestParameterType.Query, pair.Key, pair.Value);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Warning("Parameter marked with RequestParameterType.UserDefinedQueries attribute " +
|
||||
"was not of type IEnumerable<KeyValuePair<string, string>> and will be skipped.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
action(attribute.Type, name, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
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.Text.RegularExpressions;
|
||||
|
||||
using Google.Apis.Discovery;
|
||||
using Google.Apis.Testing;
|
||||
|
||||
namespace Google.Apis.Requests.Parameters
|
||||
{
|
||||
/// <summary>Logic for validating a parameter.</summary>
|
||||
public static class ParameterValidator
|
||||
{
|
||||
/// <summary>Validates a parameter value against the methods regex.</summary>
|
||||
[VisibleForTestOnly]
|
||||
public static bool ValidateRegex(IParameter param, string paramValue)
|
||||
{
|
||||
return string.IsNullOrEmpty(param.Pattern) || new Regex(param.Pattern).IsMatch(paramValue);
|
||||
}
|
||||
|
||||
/// <summary>Validates if a parameter is valid.</summary>
|
||||
public static bool ValidateParameter(IParameter parameter, string value)
|
||||
{
|
||||
// Fail if a required parameter is not present.
|
||||
if (String.IsNullOrEmpty(value))
|
||||
{
|
||||
return !parameter.IsRequired;
|
||||
}
|
||||
|
||||
// The parameter has value so validate the regex.
|
||||
return ValidateRegex(parameter, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,308 @@
|
|||
/*
|
||||
Copyright 2012 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.Http;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
using Google.Apis.Http;
|
||||
using Google.Apis.Logging;
|
||||
using Google.Apis.Util;
|
||||
|
||||
namespace Google.Apis.Requests
|
||||
{
|
||||
/// <summary>Utility class for building a URI using <see cref="BuildUri"/> or a HTTP request using
|
||||
/// <see cref="CreateRequest"/> from the query and path parameters of a REST call.</summary>
|
||||
public class RequestBuilder
|
||||
{
|
||||
static RequestBuilder()
|
||||
{
|
||||
UriPatcher.PatchUriQuirks();
|
||||
}
|
||||
|
||||
private static readonly ILogger Logger = ApplicationContext.Logger.ForType<RequestBuilder>();
|
||||
|
||||
/// <summary>Pattern to get the groups that are part of the path.</summary>
|
||||
private static Regex PathParametersPattern = new Regex(@"{[^{}]*}*");
|
||||
|
||||
/// <summary>Supported HTTP methods.</summary>
|
||||
private static IEnumerable<string> SupportedMethods = new List<string>
|
||||
{
|
||||
HttpConsts.Get, HttpConsts.Post, HttpConsts.Put, HttpConsts.Delete, HttpConsts.Patch
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// A dictionary containing the parameters which will be inserted into the path of the URI. These parameters
|
||||
/// will be substituted into the URI path where the path contains "{key}". See
|
||||
/// http://tools.ietf.org/html/rfc6570 for more information.
|
||||
/// </summary>
|
||||
private IDictionary<string, IList<string>> PathParameters { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A dictionary containing the parameters which will apply to the query portion of this request.
|
||||
/// </summary>
|
||||
private List<KeyValuePair<string, string>> QueryParameters { get; set; }
|
||||
|
||||
/// <summary>The base URI for this request (usually applies to the service itself).</summary>
|
||||
public Uri BaseUri { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The path portion of this request. It's appended to the <see cref="BaseUri"/> and the parameters are
|
||||
/// substituted from the <see cref="PathParameters"/> dictionary.
|
||||
/// </summary>
|
||||
public string Path { get; set; }
|
||||
|
||||
/// <summary>The HTTP method used for this request.</summary>
|
||||
private string method;
|
||||
|
||||
/// <summary>The HTTP method used for this request (such as GET, PUT, POST, etc...).</summary>
|
||||
/// <remarks>The default Value is <see cref="Google.Apis.Http.HttpConsts.Get"/>.</remarks>
|
||||
public string Method
|
||||
{
|
||||
get { return method; }
|
||||
set
|
||||
{
|
||||
if (!SupportedMethods.Contains(value))
|
||||
throw new ArgumentOutOfRangeException("Method");
|
||||
method = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Construct a new request builder.</summary>
|
||||
/// TODO(peleyal): Consider using the Factory pattern here.
|
||||
public RequestBuilder()
|
||||
{
|
||||
PathParameters = new Dictionary<string, IList<string>>();
|
||||
QueryParameters = new List<KeyValuePair<string, string>>();
|
||||
Method = HttpConsts.Get;
|
||||
}
|
||||
|
||||
/// <summary>Constructs a Uri as defined by the parts of this request builder.</summary>
|
||||
public Uri BuildUri()
|
||||
{
|
||||
var restPath = BuildRestPath();
|
||||
|
||||
if (QueryParameters.Count > 0)
|
||||
{
|
||||
// In case the path already contains '?' - we should add '&'. Otherwise add '?'.
|
||||
restPath.Append(restPath.ToString().Contains("?") ? "&" : "?");
|
||||
|
||||
// If parameter value is empty - just add the "name", otherwise "name=value"
|
||||
restPath.Append(String.Join("&", QueryParameters.Select(
|
||||
x => string.IsNullOrEmpty(x.Value) ?
|
||||
Uri.EscapeDataString(x.Key) :
|
||||
String.Format("{0}={1}", Uri.EscapeDataString(x.Key), Uri.EscapeDataString(x.Value)))
|
||||
.ToArray()));
|
||||
}
|
||||
|
||||
return new Uri(this.BaseUri, restPath.ToString());
|
||||
}
|
||||
|
||||
/// <summary>Operator list that can appear in the path argument.</summary>
|
||||
private const string OPERATORS = "+#./;?&|!@=";
|
||||
|
||||
/// <summary>
|
||||
/// Builds the REST path string builder based on <see cref="PathParameters"/> and the URI template spec
|
||||
/// http://tools.ietf.org/html/rfc6570.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private StringBuilder BuildRestPath()
|
||||
{
|
||||
if (string.IsNullOrEmpty(Path))
|
||||
{
|
||||
return new StringBuilder(string.Empty);
|
||||
}
|
||||
|
||||
var restPath = new StringBuilder(Path);
|
||||
var matches = PathParametersPattern.Matches(restPath.ToString());
|
||||
foreach (var match in matches)
|
||||
{
|
||||
var matchStr = match.ToString();
|
||||
// Strip the first and last characters: '{' and '}'.
|
||||
var content = matchStr.Substring(1, matchStr.Length - 2);
|
||||
|
||||
var op = string.Empty;
|
||||
// If the content's first character is an operator, save and remove it from the content string.
|
||||
if (OPERATORS.Contains(content[0].ToString()))
|
||||
{
|
||||
op = content[0].ToString();
|
||||
content = content.Substring(1);
|
||||
}
|
||||
|
||||
var newContent = new StringBuilder();
|
||||
|
||||
// Iterate over all possible parameters.
|
||||
var parameters = content.Split(',');
|
||||
for (var index = 0; index < parameters.Length; ++index)
|
||||
{
|
||||
var parameter = parameters[index];
|
||||
|
||||
var parameterName = parameter;
|
||||
var containStar = false;
|
||||
var numOfChars = 0;
|
||||
|
||||
// Check if it ends with '*'.
|
||||
if (parameterName[parameterName.Length - 1] == '*')
|
||||
{
|
||||
containStar = true;
|
||||
parameterName = parameterName.Substring(0, parameterName.Length - 1);
|
||||
}
|
||||
// Check if it contains :n which means we should only use the first n characters of this parameter.
|
||||
if (parameterName.Contains(":"))
|
||||
{
|
||||
if (!int.TryParse(parameterName.Substring(parameterName.IndexOf(":") + 1), out numOfChars))
|
||||
{
|
||||
throw new ArgumentException(
|
||||
string.Format("Can't parse number after ':' in Path \"{0}\". Parameter is \"{1}\"",
|
||||
Path, parameterName), Path);
|
||||
}
|
||||
parameterName = parameterName.Substring(0, parameterName.IndexOf(":"));
|
||||
}
|
||||
|
||||
// We can improve the following if statement, but for readability we will leave it like that.
|
||||
var joiner = op;
|
||||
var start = op;
|
||||
switch (op)
|
||||
{
|
||||
case "+":
|
||||
start = index == 0 ? "" : ",";
|
||||
joiner = ",";
|
||||
break;
|
||||
case ".":
|
||||
if (!containStar)
|
||||
{
|
||||
joiner = ",";
|
||||
}
|
||||
break;
|
||||
case "/":
|
||||
if (!containStar)
|
||||
{
|
||||
joiner = ",";
|
||||
}
|
||||
break;
|
||||
case "#":
|
||||
start = index == 0 ? "#" : ",";
|
||||
joiner = ",";
|
||||
break;
|
||||
|
||||
case "?":
|
||||
start = (index == 0 ? "?" : "&") + parameterName + "=";
|
||||
joiner = ",";
|
||||
if (containStar)
|
||||
{
|
||||
joiner = "&" + parameterName + "=";
|
||||
}
|
||||
break;
|
||||
case "&":
|
||||
case ";":
|
||||
start = op + parameterName + "=";
|
||||
joiner = ",";
|
||||
if (containStar)
|
||||
{
|
||||
joiner = op + parameterName + "=";
|
||||
}
|
||||
break;
|
||||
// No operator, in that case just ','.
|
||||
default:
|
||||
if (index > 0)
|
||||
{
|
||||
start = ",";
|
||||
}
|
||||
joiner = ",";
|
||||
break;
|
||||
}
|
||||
|
||||
// Check if a path parameter equals the name which appears in the REST path.
|
||||
if (PathParameters.ContainsKey(parameterName))
|
||||
{
|
||||
var value = string.Join(joiner, PathParameters[parameterName]);
|
||||
|
||||
// Check if we need to use a substring of the value.
|
||||
if (numOfChars != 0 && numOfChars < value.Length)
|
||||
{
|
||||
value = value.Substring(0, numOfChars);
|
||||
}
|
||||
|
||||
if (op != "+" && op != "#" && PathParameters[parameterName].Count == 1)
|
||||
{
|
||||
value = Uri.EscapeDataString(value);
|
||||
}
|
||||
|
||||
value = start + value;
|
||||
newContent.Append(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentException(
|
||||
string.Format("Path \"{0}\" misses a \"{1}\" parameter", Path, parameterName), Path);
|
||||
}
|
||||
}
|
||||
|
||||
if (op == ";")
|
||||
{
|
||||
if (newContent[newContent.Length - 1] == '=')
|
||||
{
|
||||
newContent = newContent.Remove(newContent.Length - 1, 1);
|
||||
}
|
||||
newContent = newContent.Replace("=;", ";");
|
||||
}
|
||||
restPath = restPath.Replace(matchStr, newContent.ToString());
|
||||
}
|
||||
return restPath;
|
||||
}
|
||||
|
||||
/// <summary>Adds a parameter value.</summary>
|
||||
/// <param name="type">Type of the parameter (must be 'Path' or 'Query').</param>
|
||||
/// <param name="name">Parameter name.</param>
|
||||
/// <param name="value">Parameter value.</param>
|
||||
public void AddParameter(RequestParameterType type, string name, string value)
|
||||
{
|
||||
name.ThrowIfNull("name");
|
||||
if (value == null)
|
||||
{
|
||||
Logger.Warning("Add parameter should not get null values. type={0}, name={1}", type, name);
|
||||
return;
|
||||
}
|
||||
switch (type)
|
||||
{
|
||||
case RequestParameterType.Path:
|
||||
if (!PathParameters.ContainsKey(name))
|
||||
{
|
||||
PathParameters[name] = new List<string> { value };
|
||||
}
|
||||
else
|
||||
{
|
||||
PathParameters[name].Add(value);
|
||||
}
|
||||
break;
|
||||
case RequestParameterType.Query:
|
||||
QueryParameters.Add(new KeyValuePair<string, string>(name, value));
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException("type");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Creates a new HTTP request message.</summary>
|
||||
public HttpRequestMessage CreateRequest()
|
||||
{
|
||||
return new HttpRequestMessage(new HttpMethod(Method), BuildUri());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
/*
|
||||
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
|
||||
{
|
||||
/// <summary>
|
||||
/// Collection of server errors
|
||||
/// </summary>
|
||||
public class RequestError
|
||||
{
|
||||
/// <summary>
|
||||
/// Enumeration of known error codes which may occur during a request.
|
||||
/// </summary>
|
||||
public enum ErrorCodes
|
||||
{
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
ETagConditionFailed = 412
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Contains a list of all errors
|
||||
/// </summary>
|
||||
public IList<SingleError> Errors { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The error code returned
|
||||
/// </summary>
|
||||
public int Code { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The error message returned
|
||||
/// </summary>
|
||||
public string Message { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns a string summary of this error
|
||||
/// </summary>
|
||||
/// <returns>A string summary of this error</returns>
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// A single server error
|
||||
/// </summary>
|
||||
public class SingleError
|
||||
{
|
||||
/// <summary>
|
||||
/// The domain in which the error occured
|
||||
/// </summary>
|
||||
public string Domain { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The reason the error was thrown
|
||||
/// </summary>
|
||||
public string Reason { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The error message
|
||||
/// </summary>
|
||||
public string Message { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Type of the location
|
||||
/// </summary>
|
||||
public string LocationType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Location where the error was thrown
|
||||
/// </summary>
|
||||
public string Location { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns a string summary of this error
|
||||
/// </summary>
|
||||
/// <returns>A string summary of this error</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format(
|
||||
"Message[{0}] Location[{1} - {2}] Reason[{3}] Domain[{4}]", Message, Location, LocationType, Reason,
|
||||
Domain);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// <code>[assembly: InternalsVisibleTo("Full.Name.Of.Testing.Assembly")]</code>
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Property |
|
||||
AttributeTargets.Field)]
|
||||
public class VisibleForTestOnly : Attribute { }
|
||||
}
|
||||
|
|
@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// Implementation of <see cref="IBackOff"/> 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.
|
||||
/// </summary>
|
||||
public class ExponentialBackOff : IBackOff
|
||||
{
|
||||
/// <summary>The maximum allowed number of retries.</summary>
|
||||
private const int MaxAllowedNumRetries = 20;
|
||||
|
||||
private readonly TimeSpan deltaBackOff;
|
||||
/// <summary>
|
||||
/// Gets the delta time span used to generate a random milliseconds to add to the next back-off.
|
||||
/// If the value is <see cref="System.TimeSpan.Zero"/> 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.
|
||||
/// </summary>
|
||||
public TimeSpan DeltaBackOff
|
||||
{
|
||||
get { return deltaBackOff; }
|
||||
}
|
||||
|
||||
private readonly int maxNumOfRetries;
|
||||
/// <summary>Gets the maximum number of retries. Default value is <c>10</c>.</summary>
|
||||
public int MaxNumOfRetries
|
||||
{
|
||||
get { return maxNumOfRetries; }
|
||||
}
|
||||
|
||||
/// <summary>The random instance which generates a random number to add the to next back-off.</summary>
|
||||
private Random random = new Random();
|
||||
|
||||
/// <summary>Constructs a new exponential back-off with default values.</summary>
|
||||
public ExponentialBackOff()
|
||||
: this(TimeSpan.FromMilliseconds(250))
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>Constructs a new exponential back-off with the given delta and maximum retries.</summary>
|
||||
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
|
||||
|
||||
/// <inheritdoc/>
|
||||
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
|
||||
}
|
||||
}
|
||||
33
Yavsc/GoogleApiSupport/Google.Apis.Core/Util/IBackOff.cs
Normal file
33
Yavsc/GoogleApiSupport/Google.Apis.Core/Util/IBackOff.cs
Normal file
|
|
@ -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
|
||||
{
|
||||
/// <summary>Strategy interface to control back-off between retry attempts.</summary>
|
||||
public interface IBackOff
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the a time span to wait before next retry. If the current retry reached the maximum number of retries,
|
||||
/// the returned value is <see cref="TimeSpan.MinValue"/>.
|
||||
/// </summary>
|
||||
TimeSpan GetNextBackOff(int currentRetry);
|
||||
|
||||
/// <summary>Gets the maximum number of retries.</summary>
|
||||
int MaxNumOfRetries { get; }
|
||||
}
|
||||
}
|
||||
56
Yavsc/GoogleApiSupport/Google.Apis.Core/Util/IClock.cs
Normal file
56
Yavsc/GoogleApiSupport/Google.Apis.Core/Util/IClock.cs
Normal file
|
|
@ -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
|
||||
{
|
||||
/// <summary>Clock wrapper for getting the current time.</summary>
|
||||
public interface IClock
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a <see cref="System.DateTime"/> object that is set to the current date and time on this computer,
|
||||
/// expressed as the local time.
|
||||
/// </summary>
|
||||
[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; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a <see cref="System.DateTime"/> object that is set to the current date and time on this computer,
|
||||
/// expressed as UTC time.
|
||||
/// </summary>
|
||||
DateTime UtcNow { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A default clock implementation that wraps the <see cref="System.DateTime.UtcNow"/>
|
||||
/// and <see cref="System.DateTime.Now"/> properties.
|
||||
/// </summary>
|
||||
public class SystemClock : IClock
|
||||
{
|
||||
/// <summary>Constructs a new system clock.</summary>
|
||||
protected SystemClock() { }
|
||||
|
||||
/// <summary>The default instance.</summary>
|
||||
public static readonly IClock Default = new SystemClock();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public DateTime Now => DateTime.Now;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public DateTime UtcNow => DateTime.UtcNow;
|
||||
}
|
||||
}
|
||||
75
Yavsc/GoogleApiSupport/Google.Apis.Core/Util/Repeatable.cs
Normal file
75
Yavsc/GoogleApiSupport/Google.Apis.Core/Util/Repeatable.cs
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
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;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace Google.Apis.Util
|
||||
{
|
||||
/// <summary>
|
||||
/// Repeatable class which allows you to both pass a single element, as well as an array, as a parameter value.
|
||||
/// </summary>
|
||||
public class Repeatable<T> : IEnumerable<T>
|
||||
{
|
||||
private readonly IList<T> values;
|
||||
|
||||
/// <summary>Creates a repeatable value.</summary>
|
||||
public Repeatable(IEnumerable<T> enumeration)
|
||||
{
|
||||
values = new ReadOnlyCollection<T>(new List<T>(enumeration));
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IEnumerator<T> GetEnumerator()
|
||||
{
|
||||
return values.GetEnumerator();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
/// <summary>Converts the single element into a repeatable.</summary>
|
||||
public static implicit operator Repeatable<T>(T elem)
|
||||
{
|
||||
if (elem == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return new Repeatable<T>(new[] { elem });
|
||||
}
|
||||
|
||||
/// <summary>Converts a number of elements into a repeatable.</summary>
|
||||
public static implicit operator Repeatable<T>(T[] elem)
|
||||
{
|
||||
if (elem.Length == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return new Repeatable<T>(elem);
|
||||
}
|
||||
|
||||
/// <summary>Converts a number of elements into a repeatable.</summary>
|
||||
public static implicit operator Repeatable<T>(List<T> elem)
|
||||
{
|
||||
return new Repeatable<T>(elem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
/*
|
||||
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.Util
|
||||
{
|
||||
/// <summary>
|
||||
/// An attribute which is used to specially mark a property for reflective purposes,
|
||||
/// assign a name to the property and indicate it's location in the request as either
|
||||
/// in the path or query portion of the request URL.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
|
||||
public class RequestParameterAttribute : Attribute
|
||||
{
|
||||
private readonly string name;
|
||||
private readonly RequestParameterType type;
|
||||
|
||||
/// <summary>Gets the name of the parameter.</summary>
|
||||
public string Name { get { return name; } }
|
||||
|
||||
/// <summary>Gets the type of the parameter, Path or Query.</summary>
|
||||
public RequestParameterType Type { get { return type; } }
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new property attribute to be a part of a REST URI.
|
||||
/// This constructor uses <see cref="RequestParameterType.Query"/> as the parameter's type.
|
||||
/// </summary>
|
||||
/// <param name="name">
|
||||
/// The name of the parameter. If the parameter is a path parameter this name will be used to substitute the
|
||||
/// string value into the path, replacing {name}. If the parameter is a query parameter, this parameter will be
|
||||
/// added to the query string, in the format "name=value".
|
||||
/// </param>
|
||||
public RequestParameterAttribute(string name)
|
||||
: this(name, RequestParameterType.Query)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>Constructs a new property attribute to be a part of a REST URI.</summary>
|
||||
/// <param name="name">
|
||||
/// The name of the parameter. If the parameter is a path parameter this name will be used to substitute the
|
||||
/// string value into the path, replacing {name}. If the parameter is a query parameter, this parameter will be
|
||||
/// added to the query string, in the format "name=value".
|
||||
/// </param>
|
||||
/// <param name="type">The type of the parameter, either Path, Query or UserDefinedQueries.</param>
|
||||
public RequestParameterAttribute(string name, RequestParameterType type)
|
||||
{
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Describe the type of this parameter (Path, Query or UserDefinedQueries).</summary>
|
||||
public enum RequestParameterType
|
||||
{
|
||||
/// <summary>A path parameter which is inserted into the path portion of the request URI.</summary>
|
||||
Path,
|
||||
|
||||
/// <summary>A query parameter which is inserted into the query portion of the request URI.</summary>
|
||||
Query,
|
||||
|
||||
/// <summary>
|
||||
/// A group of user-defined parameters that will be added in to the query portion of the request URI. If this
|
||||
/// type is being used, the name of the RequestParameterAttirbute is meaningless.
|
||||
/// </summary>
|
||||
UserDefinedQueries
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
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 Google.Apis.Requests;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Google.Apis.Util
|
||||
{
|
||||
/// <summary>
|
||||
/// Calls to Google Api return StandardResponses as Json with
|
||||
/// two properties Data, being the return type of the method called
|
||||
/// and Error, being any errors that occure.
|
||||
/// </summary>
|
||||
public sealed class StandardResponse<InnerType>
|
||||
{
|
||||
/// <summary>May be null if call failed.</summary>
|
||||
[JsonProperty("data")]
|
||||
public InnerType Data { get; set; }
|
||||
|
||||
/// <summary>May be null if call succedded.</summary>
|
||||
[JsonProperty("error")]
|
||||
public RequestError Error { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
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.Threading.Tasks;
|
||||
|
||||
namespace Google.Apis.Util.Store
|
||||
{
|
||||
/// <summary>
|
||||
/// Stores and manages data objects, where the key is a string and the value is an object.
|
||||
/// <para>
|
||||
/// <c>null</c> keys are not allowed.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public interface IDataStore
|
||||
{
|
||||
/// <summary>Asynchronously stores the given value for the given key (replacing any existing value).</summary>
|
||||
/// <typeparam name="T">The type to store in the data store.</typeparam>
|
||||
/// <param name="key">The key.</param>
|
||||
/// <param name="value">The value to store.</param>
|
||||
Task StoreAsync<T>(string key, T value);
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type to delete from the data store.</typeparam>
|
||||
/// <param name="key">The key to delete.</param>
|
||||
Task DeleteAsync<T>(string key);
|
||||
|
||||
/// <summary>Asynchronously returns the stored value for the given key or <c>null</c> if not found.</summary>
|
||||
/// <typeparam name="T">The type to retrieve from the data store.</typeparam>
|
||||
/// <param name="key">The key to retrieve its value.</param>
|
||||
/// <returns>The stored object.</returns>
|
||||
Task<T> GetAsync<T>(string key);
|
||||
|
||||
/// <summary>Asynchronously clears all values in the data store.</summary>
|
||||
Task ClearAsync();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
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.Util
|
||||
{
|
||||
/// <summary>Defines an attribute containing a string representation of the member.</summary>
|
||||
[AttributeUsage(AttributeTargets.Field, AllowMultiple = false)]
|
||||
public class StringValueAttribute : Attribute
|
||||
{
|
||||
private readonly string text;
|
||||
/// <summary>The text which belongs to this member.</summary>
|
||||
public string Text { get { return text; } }
|
||||
|
||||
/// <summary>Creates a new string value attribute with the specified text.</summary>
|
||||
public StringValueAttribute(string text)
|
||||
{
|
||||
text.ThrowIfNull("text");
|
||||
this.text = text;
|
||||
}
|
||||
}
|
||||
}
|
||||
122
Yavsc/GoogleApiSupport/Google.Apis.Core/Util/UriPatcher.cs
Normal file
122
Yavsc/GoogleApiSupport/Google.Apis.Core/Util/UriPatcher.cs
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
/*
|
||||
Copyright 2016 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.Reflection;
|
||||
|
||||
namespace Google.Apis.Util
|
||||
{
|
||||
/// <summary>
|
||||
/// Workarounds for some unfortunate behaviors in the .NET Framework's
|
||||
/// implementation of System.Uri
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// UriPatcher lets us work around some unfortunate behaviors in the .NET Framework's
|
||||
/// implementation of System.Uri.
|
||||
///
|
||||
/// == Problem 1: Slashes and dots
|
||||
///
|
||||
/// Prior to .NET 4.5, System.Uri would always unescape "%2f" ("/") and "%5c" ("\\").
|
||||
/// Relative path components were also compressed.
|
||||
///
|
||||
/// As a result, this: "http://www.example.com/.%2f.%5c./"
|
||||
/// ... turned into this: "http://www.example.com/"
|
||||
///
|
||||
/// This breaks API requests where slashes or dots appear in path parameters. Such requests
|
||||
/// arise, for example, when these characters appear in the name of a GCS object.
|
||||
///
|
||||
/// == Problem 2: Fewer unreserved characters
|
||||
///
|
||||
/// Unless IDN/IRI parsing is enabled -- which it is not, by default, prior to .NET 4.5 --
|
||||
/// Uri.EscapeDataString uses the set of "unreserved" characters from RFC 2396 instead of the
|
||||
/// newer, *smaller* list from RFC 3986. We build requests using URI templating as described
|
||||
/// by RFC 6570, which specifies that the latter definition (RFC 3986) should be used.
|
||||
///
|
||||
/// This breaks API requests with parameters including any of: !*'()
|
||||
///
|
||||
/// == Solutions
|
||||
///
|
||||
/// Though the default behaviors changed in .NET 4.5, these "quirks" remain for compatibility
|
||||
/// unless the application explicitly targets the new runtime. Usually, that means adding a
|
||||
/// TargetFrameworkAttribute to the entry assembly.
|
||||
///
|
||||
/// Applications running on .NET 4.0 or later can also set "DontUnescapePathDotsAndSlashes"
|
||||
/// and enable IDN/IRI parsing using app.config or web.config.
|
||||
///
|
||||
/// As a class library, we can't control app.config or the entry assembly, so we can't take
|
||||
/// either approach. Instead, we resort to reflection trickery to try to solve these problems
|
||||
/// if we detect they exist. Sorry.
|
||||
/// </remarks>
|
||||
public static class UriPatcher
|
||||
{
|
||||
/// <summary>
|
||||
/// Patch URI quirks in System.Uri. See class summary for details.
|
||||
/// </summary>
|
||||
public static void PatchUriQuirks()
|
||||
{
|
||||
var uriParser = typeof(System.Uri).GetTypeInfo().Assembly.GetType("System.UriParser");
|
||||
if (uriParser == null) { return; }
|
||||
|
||||
// Is "%2f" unescaped for http: or https: URIs?
|
||||
if (new Uri("http://example.com/%2f").AbsolutePath == "//" ||
|
||||
new Uri("https://example.com/%2f").AbsolutePath == "//")
|
||||
{
|
||||
// Call System.UriParser.Http[s]Uri.SetUpdatableFlags(UriSyntaxFlags.None)
|
||||
// https://github.com/Microsoft/referencesource/blob/d925d870f3cb3f6a/System/net/System/_UriSyntax.cs#L87
|
||||
// https://github.com/Microsoft/referencesource/blob/d925d870f3cb3f6a/System/net/System/_UriSyntax.cs#L77
|
||||
// https://github.com/Microsoft/referencesource/blob/d925d870f3cb3f6a/System/net/System/_UriSyntax.cs#L352
|
||||
|
||||
var setUpdatableFlagsMethod = uriParser.GetMethod("SetUpdatableFlags",
|
||||
BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
if (setUpdatableFlagsMethod != null)
|
||||
{
|
||||
Action<string> setUriParserUpdatableFlags = (fieldName) =>
|
||||
{
|
||||
var parserField = uriParser.GetField(fieldName,
|
||||
BindingFlags.Static | BindingFlags.NonPublic);
|
||||
if (parserField == null) { return; }
|
||||
var parserInstance = parserField.GetValue(null);
|
||||
if (parserInstance == null) { return; }
|
||||
setUpdatableFlagsMethod.Invoke(parserInstance, new object[] { 0 });
|
||||
};
|
||||
|
||||
// Make the change for the http: and https: URI parsers.
|
||||
setUriParserUpdatableFlags("HttpUri");
|
||||
setUriParserUpdatableFlags("HttpsUri");
|
||||
}
|
||||
}
|
||||
|
||||
// Is "*" considered "unreserved"?
|
||||
if (Uri.EscapeDataString("*") == "*")
|
||||
{
|
||||
// Set UriParser.s_QuirksVersion to at least UriQuirksVersion.V3
|
||||
// https://github.com/Microsoft/referencesource/blob/d925d870f3cb3f6a/System/net/System/_UriSyntax.cs#L114
|
||||
// https://github.com/Microsoft/referencesource/blob/d925d870f3cb3f6a/System/net/System/UriHelper.cs#L701
|
||||
|
||||
var quirksField = uriParser.GetField("s_QuirksVersion",
|
||||
BindingFlags.Static | BindingFlags.NonPublic);
|
||||
if (quirksField != null)
|
||||
{
|
||||
int quirksVersion = (int)quirksField.GetValue(null);
|
||||
if (quirksVersion <= 2)
|
||||
{
|
||||
quirksField.SetValue(null, 3);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
175
Yavsc/GoogleApiSupport/Google.Apis.Core/Util/Utilities.cs
Normal file
175
Yavsc/GoogleApiSupport/Google.Apis.Core/Util/Utilities.cs
Normal file
|
|
@ -0,0 +1,175 @@
|
|||
/*
|
||||
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 Google.Apis.Testing;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Google.Apis.Util
|
||||
{
|
||||
/// <summary>A utility class which contains helper methods and extension methods.</summary>
|
||||
public static class Utilities
|
||||
{
|
||||
/// <summary>Returns the version of the core library.</summary>
|
||||
[VisibleForTestOnly]
|
||||
public static string GetLibraryVersion()
|
||||
{
|
||||
return Regex.Match(typeof(Utilities).GetTypeInfo().Assembly.FullName, "Version=([\\d\\.]+)").Groups[1].ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A Google.Apis utility method for throwing an <see cref="System.ArgumentNullException"/> if the object is
|
||||
/// <c>null</c>.
|
||||
/// </summary>
|
||||
public static T ThrowIfNull<T>(this T obj, string paramName)
|
||||
{
|
||||
if (obj == null)
|
||||
{
|
||||
throw new ArgumentNullException(paramName);
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A Google.Apis utility method for throwing an <see cref="System.ArgumentNullException"/> if the string is
|
||||
/// <c>null</c> or empty.
|
||||
/// </summary>
|
||||
/// <returns>The original string.</returns>
|
||||
public static string ThrowIfNullOrEmpty(this string str, string paramName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(str))
|
||||
{
|
||||
throw new ArgumentException("Parameter was empty", paramName);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
/// <summary>Returns <c>true</c> in case the enumerable is <c>null</c> or empty.</summary>
|
||||
internal static bool IsNullOrEmpty<T>(this IEnumerable<T> coll)
|
||||
{
|
||||
return coll == null || coll.Count() == 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A Google.Apis utility method for returning the first matching custom attribute (or <c>null</c>) of the specified member.
|
||||
/// </summary>
|
||||
public static T GetCustomAttribute<T>(this MemberInfo info) where T : Attribute
|
||||
{
|
||||
object[] results = info.GetCustomAttributes(typeof(T), false).ToArray();
|
||||
return results.Length == 0 ? null : (T)results[0];
|
||||
}
|
||||
|
||||
/// <summary>Returns the defined string value of an Enum.</summary>
|
||||
internal static string GetStringValue(this Enum value)
|
||||
{
|
||||
FieldInfo entry = value.GetType().GetField(value.ToString());
|
||||
entry.ThrowIfNull("value");
|
||||
|
||||
// If set, return the value.
|
||||
var attribute = entry.GetCustomAttribute<StringValueAttribute>();
|
||||
if (attribute != null)
|
||||
{
|
||||
return attribute.Text;
|
||||
}
|
||||
|
||||
// Otherwise, throw an exception.
|
||||
throw new ArgumentException(
|
||||
string.Format("Enum value '{0}' does not contain a StringValue attribute", entry), "value");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the defined string value of an Enum. Use for test purposes or in other Google.Apis projects.
|
||||
/// </summary>
|
||||
public static string GetEnumStringValue(Enum value)
|
||||
{
|
||||
return value.GetStringValue();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to convert the specified object to a string. Uses custom type converters if available.
|
||||
/// Returns null for a null object.
|
||||
/// </summary>
|
||||
[VisibleForTestOnly]
|
||||
public static string ConvertToString(object o)
|
||||
{
|
||||
if (o == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (o.GetType().GetTypeInfo().IsEnum)
|
||||
{
|
||||
// Try to convert the Enum value using the StringValue attribute.
|
||||
var enumType = o.GetType();
|
||||
FieldInfo field = enumType.GetField(o.ToString());
|
||||
StringValueAttribute attribute = field.GetCustomAttribute<StringValueAttribute>();
|
||||
return attribute != null ? attribute.Text : o.ToString();
|
||||
}
|
||||
|
||||
if (o is DateTime)
|
||||
{
|
||||
// Honor RFC3339.
|
||||
return ConvertToRFC3339((DateTime)o);
|
||||
}
|
||||
|
||||
if (o is bool)
|
||||
{
|
||||
return o.ToString().ToLowerInvariant();
|
||||
}
|
||||
|
||||
return o.ToString();
|
||||
}
|
||||
|
||||
/// <summary>Converts the input date into a RFC3339 string (http://www.ietf.org/rfc/rfc3339.txt).</summary>
|
||||
internal static string ConvertToRFC3339(DateTime date)
|
||||
{
|
||||
if (date.Kind == DateTimeKind.Unspecified)
|
||||
{
|
||||
date = date.ToUniversalTime();
|
||||
}
|
||||
return date.ToString("yyyy-MM-dd'T'HH:mm:ss.fffK", DateTimeFormatInfo.InvariantInfo);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses the input string and returns <see cref="System.DateTime"/> if the input is a valid
|
||||
/// representation of a date. Otherwise it returns <c>null</c>.
|
||||
/// </summary>
|
||||
public static DateTime? GetDateTimeFromString(string raw)
|
||||
{
|
||||
DateTime result;
|
||||
if (!DateTime.TryParse(raw, out result))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>Returns a string (by RFC3339) form the input <see cref="DateTime"/> instance.</summary>
|
||||
public static string GetStringFromDateTime(DateTime? date)
|
||||
{
|
||||
if (!date.HasValue)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return ConvertToRFC3339(date.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue