You've already forked linux-packaging-mono
Imported Upstream version 6.0.0.172
Former-commit-id: f3cc9b82f3e5bd8f0fd3ebc098f789556b44e9cd
This commit is contained in:
parent
8016999e4d
commit
64ac736ec5
@ -66,3 +66,25 @@ using System.Runtime.InteropServices;
|
||||
|
||||
[assembly: InternalsVisibleTo ("System.Net.Http.WebRequest, PublicKey=002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293")]
|
||||
|
||||
#if MONOTOUCH
|
||||
|
||||
#if MONOTOUCH_TV
|
||||
[assembly: TypeForwardedToAttribute (typeof (System.Net.Http.NSUrlSessionHandler))]
|
||||
[assembly: TypeForwardedToAttribute (typeof (System.Net.Http.CFNetworkHandler))]
|
||||
[assembly: InternalsVisibleTo ("Xamarin.TVOS, PublicKey=0024000004800000940000000602000000240000525341310004000011000000438ac2a5acfbf16cbd2b2b47a62762f273df9cb2795ceccdf77d10bf508e69e7a362ea7a45455bbf3ac955e1f2e2814f144e5d817efc4c6502cc012df310783348304e3ae38573c6d658c234025821fda87a0be8a0d504df564e2c93b2b878925f42503e9d54dfef9f9586d9e6f38a305769587b1de01f6c0410328b2c9733db")]
|
||||
#elif MONOTOUCH_WATCH
|
||||
[assembly: TypeForwardedToAttribute (typeof (System.Net.Http.NSUrlSessionHandler))]
|
||||
[assembly: InternalsVisibleTo ("Xamarin.WatchOS, PublicKey=0024000004800000940000000602000000240000525341310004000011000000438ac2a5acfbf16cbd2b2b47a62762f273df9cb2795ceccdf77d10bf508e69e7a362ea7a45455bbf3ac955e1f2e2814f144e5d817efc4c6502cc012df310783348304e3ae38573c6d658c234025821fda87a0be8a0d504df564e2c93b2b878925f42503e9d54dfef9f9586d9e6f38a305769587b1de01f6c0410328b2c9733db")]
|
||||
#else
|
||||
[assembly: TypeForwardedToAttribute (typeof (System.Net.Http.NSUrlSessionHandler))]
|
||||
[assembly: TypeForwardedToAttribute (typeof (System.Net.Http.CFNetworkHandler))]
|
||||
[assembly: InternalsVisibleTo ("Xamarin.iOS, PublicKey=0024000004800000940000000602000000240000525341310004000011000000438ac2a5acfbf16cbd2b2b47a62762f273df9cb2795ceccdf77d10bf508e69e7a362ea7a45455bbf3ac955e1f2e2814f144e5d817efc4c6502cc012df310783348304e3ae38573c6d658c234025821fda87a0be8a0d504df564e2c93b2b878925f42503e9d54dfef9f9586d9e6f38a305769587b1de01f6c0410328b2c9733db")]
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#if XAMMAC ||Â XAMMAC_4_5
|
||||
[assembly: TypeForwardedToAttribute (typeof (Foundation.NSUrlSessionHandler))]
|
||||
[assembly: TypeForwardedToAttribute (typeof (System.Net.Http.CFNetworkHandler))]
|
||||
[assembly: InternalsVisibleTo ("Xamarin.Mac, PublicKey=0024000004800000940000000602000000240000525341310004000011000000438ac2a5acfbf16cbd2b2b47a62762f273df9cb2795ceccdf77d10bf508e69e7a362ea7a45455bbf3ac955e1f2e2814f144e5d817efc4c6502cc012df310783348304e3ae38573c6d658c234025821fda87a0be8a0d504df564e2c93b2b878925f42503e9d54dfef9f9586d9e6f38a305769587b1de01f6c0410328b2c9733db")]
|
||||
#endif
|
||||
|
21
mcs/class/System.Net.Http/FunctionalTests/Makefile
Normal file
21
mcs/class/System.Net.Http/FunctionalTests/Makefile
Normal file
@ -0,0 +1,21 @@
|
||||
thisdir = class/System.Net.Http/FunctionalTests
|
||||
SUBDIRS =
|
||||
include ../../../build/rules.make
|
||||
|
||||
ASSEMBLY = System.Net.Http.FunctionalTests
|
||||
|
||||
XTEST_LIB_REFS = System System.Core Facades/System.Threading.Tasks
|
||||
USE_XTEST_REMOTE_EXECUTOR = YES
|
||||
|
||||
XTEST_LIB_FLAGS = /resource:../TestData/testservereku.contoso.com.pfx
|
||||
|
||||
the_assembly = $(topdir)/class/lib/$(PROFILE_DIRECTORY)/System.Net.Http.dll
|
||||
|
||||
include ../../../build/tests.make
|
||||
|
||||
TEST_RUNTIME := MONO_URI_DOTNETRELATIVEORABSOLUTE=true $(TEST_RUNTIME)
|
||||
|
||||
clean-local:
|
||||
-rm -f $(tests_CLEAN_FILES) $(CLEAN_FILES)
|
||||
|
||||
dist-local: dist-default
|
18
mcs/class/System.Net.Http/HeaderUtils.Legacy.cs
Normal file
18
mcs/class/System.Net.Http/HeaderUtils.Legacy.cs
Normal file
@ -0,0 +1,18 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http.Headers;
|
||||
|
||||
namespace System.Net.Http
|
||||
{
|
||||
static class HeaderUtils
|
||||
{
|
||||
internal static bool IsContentHeader (string name)
|
||||
{
|
||||
return HttpHeaders.GetKnownHeaderKind (name) == Headers.HttpHeaderKind.Content;
|
||||
}
|
||||
|
||||
internal static string GetSingleHeaderString (string name, IEnumerable<string> values)
|
||||
{
|
||||
return HttpRequestHeaders.GetSingleHeaderString (name, values);
|
||||
}
|
||||
}
|
||||
}
|
25
mcs/class/System.Net.Http/HeaderUtils.SocketsHandler.cs
Normal file
25
mcs/class/System.Net.Http/HeaderUtils.SocketsHandler.cs
Normal file
@ -0,0 +1,25 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http.Headers;
|
||||
|
||||
namespace System.Net.Http
|
||||
{
|
||||
static class HeaderUtils
|
||||
{
|
||||
internal static bool IsContentHeader (string name)
|
||||
{
|
||||
return HeaderDescriptor.TryGet (name, out var descriptor) && descriptor.HeaderType == HttpHeaderType.Content;
|
||||
}
|
||||
|
||||
internal static string GetSingleHeaderString (string name, IEnumerable<string> values)
|
||||
{
|
||||
string separator = HttpHeaderParser.DefaultSeparator;
|
||||
if (HeaderDescriptor.TryGet (name, out var descriptor) &&
|
||||
(descriptor.Parser != null) && (descriptor.Parser.SupportsMultipleValues)) {
|
||||
separator = descriptor.Parser.Separator;
|
||||
}
|
||||
|
||||
return string.Join (separator, values);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
11
mcs/class/System.Net.Http/HttpClientHandler.Legacy.cs
Normal file
11
mcs/class/System.Net.Http/HttpClientHandler.Legacy.cs
Normal file
@ -0,0 +1,11 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace System.Net.Http
|
||||
{
|
||||
partial class HttpClientHandler : HttpMessageHandler
|
||||
{
|
||||
static IMonoHttpClientHandler CreateDefaultHandler () => new MonoWebRequestHandler ();
|
||||
}
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
namespace System.Net.Http
|
||||
{
|
||||
partial class HttpClientHandler : HttpMessageHandler
|
||||
{
|
||||
static IMonoHttpClientHandler CreateDefaultHandler () => new SocketsHttpHandler ();
|
||||
}
|
||||
}
|
194
mcs/class/System.Net.Http/HttpClientHandler.cs
Normal file
194
mcs/class/System.Net.Http/HttpClientHandler.cs
Normal file
@ -0,0 +1,194 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Security;
|
||||
using System.Security.Authentication;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace System.Net.Http
|
||||
{
|
||||
public partial class HttpClientHandler : HttpMessageHandler
|
||||
{
|
||||
readonly IMonoHttpClientHandler _delegatingHandler;
|
||||
ClientCertificateOption _clientCertificateOptions;
|
||||
|
||||
public HttpClientHandler () : this (CreateDefaultHandler ()) { }
|
||||
|
||||
internal HttpClientHandler (IMonoHttpClientHandler handler)
|
||||
{
|
||||
_delegatingHandler = handler;
|
||||
ClientCertificateOptions = ClientCertificateOption.Manual;
|
||||
}
|
||||
|
||||
protected override void Dispose (bool disposing)
|
||||
{
|
||||
if (disposing) {
|
||||
_delegatingHandler.Dispose ();
|
||||
}
|
||||
base.Dispose (disposing);
|
||||
}
|
||||
|
||||
public virtual bool SupportsAutomaticDecompression => _delegatingHandler.SupportsAutomaticDecompression;
|
||||
|
||||
public virtual bool SupportsProxy => true;
|
||||
|
||||
public virtual bool SupportsRedirectConfiguration => true;
|
||||
|
||||
public bool UseCookies {
|
||||
get => _delegatingHandler.UseCookies;
|
||||
set => _delegatingHandler.UseCookies = value;
|
||||
}
|
||||
|
||||
public CookieContainer CookieContainer {
|
||||
get => _delegatingHandler.CookieContainer;
|
||||
set => _delegatingHandler.CookieContainer = value;
|
||||
}
|
||||
|
||||
void ThrowForModifiedManagedSslOptionsIfStarted ()
|
||||
{
|
||||
// Hack to trigger an InvalidOperationException if a property that's stored on
|
||||
// SslOptions is changed, since SslOptions itself does not do any such checks.
|
||||
_delegatingHandler.SslOptions = _delegatingHandler.SslOptions;
|
||||
}
|
||||
|
||||
public ClientCertificateOption ClientCertificateOptions {
|
||||
get {
|
||||
return _clientCertificateOptions;
|
||||
}
|
||||
set {
|
||||
switch (value) {
|
||||
case ClientCertificateOption.Manual:
|
||||
ThrowForModifiedManagedSslOptionsIfStarted ();
|
||||
_clientCertificateOptions = value;
|
||||
_delegatingHandler.SslOptions.LocalCertificateSelectionCallback = (sender, targetHost, localCertificates, remoteCertificate, acceptableIssuers) => CertificateHelper.GetEligibleClientCertificate (ClientCertificates);
|
||||
break;
|
||||
|
||||
case ClientCertificateOption.Automatic:
|
||||
ThrowForModifiedManagedSslOptionsIfStarted ();
|
||||
_clientCertificateOptions = value;
|
||||
_delegatingHandler.SslOptions.LocalCertificateSelectionCallback = (sender, targetHost, localCertificates, remoteCertificate, acceptableIssuers) => CertificateHelper.GetEligibleClientCertificate ();
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException (nameof (value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public X509CertificateCollection ClientCertificates {
|
||||
get {
|
||||
if (ClientCertificateOptions != ClientCertificateOption.Manual) {
|
||||
throw new InvalidOperationException (SR.Format (SR.net_http_invalid_enable_first, nameof (ClientCertificateOptions), nameof (ClientCertificateOption.Manual)));
|
||||
}
|
||||
|
||||
return _delegatingHandler.SslOptions.ClientCertificates ??
|
||||
(_delegatingHandler.SslOptions.ClientCertificates = new X509CertificateCollection ());
|
||||
}
|
||||
}
|
||||
|
||||
public Func<HttpRequestMessage, X509Certificate2, X509Chain, SslPolicyErrors, bool> ServerCertificateCustomValidationCallback {
|
||||
get => (_delegatingHandler.SslOptions.RemoteCertificateValidationCallback?.Target as ConnectHelper.CertificateCallbackMapper)?.FromHttpClientHandler;
|
||||
set {
|
||||
ThrowForModifiedManagedSslOptionsIfStarted ();
|
||||
_delegatingHandler.SslOptions.RemoteCertificateValidationCallback = value != null ?
|
||||
new ConnectHelper.CertificateCallbackMapper (value).ForSocketsHttpHandler :
|
||||
null;
|
||||
}
|
||||
}
|
||||
|
||||
public bool CheckCertificateRevocationList {
|
||||
get => _delegatingHandler.SslOptions.CertificateRevocationCheckMode == X509RevocationMode.Online;
|
||||
set {
|
||||
ThrowForModifiedManagedSslOptionsIfStarted ();
|
||||
_delegatingHandler.SslOptions.CertificateRevocationCheckMode = value ? X509RevocationMode.Online : X509RevocationMode.NoCheck;
|
||||
}
|
||||
}
|
||||
|
||||
public SslProtocols SslProtocols {
|
||||
get => _delegatingHandler.SslOptions.EnabledSslProtocols;
|
||||
set {
|
||||
ThrowForModifiedManagedSslOptionsIfStarted ();
|
||||
_delegatingHandler.SslOptions.EnabledSslProtocols = value;
|
||||
}
|
||||
}
|
||||
|
||||
public DecompressionMethods AutomaticDecompression {
|
||||
get => _delegatingHandler.AutomaticDecompression;
|
||||
set => _delegatingHandler.AutomaticDecompression = value;
|
||||
}
|
||||
|
||||
public bool UseProxy {
|
||||
get => _delegatingHandler.UseProxy;
|
||||
set => _delegatingHandler.UseProxy = value;
|
||||
}
|
||||
|
||||
public IWebProxy Proxy {
|
||||
get => _delegatingHandler.Proxy;
|
||||
set => _delegatingHandler.Proxy = value;
|
||||
}
|
||||
|
||||
public ICredentials DefaultProxyCredentials {
|
||||
get => _delegatingHandler.DefaultProxyCredentials;
|
||||
set => _delegatingHandler.DefaultProxyCredentials = value;
|
||||
}
|
||||
|
||||
public bool PreAuthenticate {
|
||||
get => _delegatingHandler.PreAuthenticate;
|
||||
set => _delegatingHandler.PreAuthenticate = value;
|
||||
}
|
||||
|
||||
public bool UseDefaultCredentials {
|
||||
// Either read variable from curlHandler or compare .Credentials as socketsHttpHandler does not have separate prop.
|
||||
get => _delegatingHandler.Credentials == CredentialCache.DefaultCredentials;
|
||||
set {
|
||||
if (value) {
|
||||
_delegatingHandler.Credentials = CredentialCache.DefaultCredentials;
|
||||
} else {
|
||||
if (_delegatingHandler.Credentials == CredentialCache.DefaultCredentials) {
|
||||
// Only clear out the Credentials property if it was a DefaultCredentials.
|
||||
_delegatingHandler.Credentials = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ICredentials Credentials {
|
||||
get => _delegatingHandler.Credentials;
|
||||
set => _delegatingHandler.Credentials = value;
|
||||
}
|
||||
|
||||
public bool AllowAutoRedirect {
|
||||
get => _delegatingHandler.AllowAutoRedirect;
|
||||
set => _delegatingHandler.AllowAutoRedirect = value;
|
||||
}
|
||||
|
||||
public int MaxAutomaticRedirections {
|
||||
get => _delegatingHandler.MaxAutomaticRedirections;
|
||||
set => _delegatingHandler.MaxAutomaticRedirections = value;
|
||||
}
|
||||
|
||||
public int MaxConnectionsPerServer {
|
||||
get => _delegatingHandler.MaxConnectionsPerServer;
|
||||
set => _delegatingHandler.MaxConnectionsPerServer = value;
|
||||
}
|
||||
|
||||
public int MaxResponseHeadersLength {
|
||||
get => _delegatingHandler.MaxResponseHeadersLength;
|
||||
set => _delegatingHandler.MaxResponseHeadersLength = value;
|
||||
}
|
||||
|
||||
public long MaxRequestContentBufferSize {
|
||||
get => _delegatingHandler.MaxRequestContentBufferSize;
|
||||
set => _delegatingHandler.MaxRequestContentBufferSize = value;
|
||||
}
|
||||
|
||||
public IDictionary<string, object> Properties => _delegatingHandler.Properties;
|
||||
|
||||
protected internal override Task<HttpResponseMessage> SendAsync (HttpRequestMessage request, CancellationToken cancellationToken) =>
|
||||
_delegatingHandler.SendAsync (request, cancellationToken);
|
||||
}
|
||||
}
|
82
mcs/class/System.Net.Http/IMonoHttpClientHandler.cs
Normal file
82
mcs/class/System.Net.Http/IMonoHttpClientHandler.cs
Normal file
@ -0,0 +1,82 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Security;
|
||||
using System.Security.Authentication;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace System.Net.Http
|
||||
{
|
||||
interface IMonoHttpClientHandler : IDisposable
|
||||
{
|
||||
bool SupportsAutomaticDecompression {
|
||||
get;
|
||||
}
|
||||
|
||||
bool UseCookies {
|
||||
get; set;
|
||||
}
|
||||
|
||||
CookieContainer CookieContainer {
|
||||
get; set;
|
||||
}
|
||||
|
||||
SslClientAuthenticationOptions SslOptions {
|
||||
get; set;
|
||||
}
|
||||
|
||||
DecompressionMethods AutomaticDecompression {
|
||||
get; set;
|
||||
}
|
||||
|
||||
bool UseProxy {
|
||||
get; set;
|
||||
}
|
||||
|
||||
IWebProxy Proxy {
|
||||
get; set;
|
||||
}
|
||||
|
||||
ICredentials DefaultProxyCredentials {
|
||||
get; set;
|
||||
}
|
||||
|
||||
bool PreAuthenticate {
|
||||
get; set;
|
||||
}
|
||||
|
||||
ICredentials Credentials {
|
||||
get; set;
|
||||
}
|
||||
|
||||
bool AllowAutoRedirect {
|
||||
get; set;
|
||||
}
|
||||
|
||||
int MaxAutomaticRedirections {
|
||||
get; set;
|
||||
}
|
||||
|
||||
int MaxConnectionsPerServer {
|
||||
get; set;
|
||||
}
|
||||
|
||||
int MaxResponseHeadersLength {
|
||||
get; set;
|
||||
}
|
||||
|
||||
long MaxRequestContentBufferSize {
|
||||
get; set;
|
||||
}
|
||||
|
||||
IDictionary<string, object> Properties {
|
||||
get;
|
||||
}
|
||||
|
||||
Task<HttpResponseMessage> SendAsync (HttpRequestMessage request, CancellationToken cancellationToken);
|
||||
}
|
||||
}
|
@ -1,19 +1,64 @@
|
||||
thisdir = class/System.Net.Http
|
||||
SUBDIRS =
|
||||
|
||||
include ../../build/rules.make
|
||||
|
||||
LIBRARY = System.Net.Http.dll
|
||||
|
||||
LIB_REFS = System.Core System
|
||||
KEYFILE = ../msfinal.pub
|
||||
LIB_MCS_FLAGS = $(EXTRA_LIB_MCS_FLAGS)
|
||||
LIB_MCS_FLAGS = $(EXTRA_LIB_MCS_FLAGS) -unsafe -nowarn:436
|
||||
ifeq (monodroid,$(PROFILE))
|
||||
LIB_MCS_FLAGS += -d:XAMARIN_MODERN
|
||||
endif
|
||||
|
||||
ifeq (monotouch,$(PROFILE))
|
||||
API_BIN_REFS := Xamarin.iOS
|
||||
LIB_MCS_FLAGS += -d:XAMARIN_MODERN
|
||||
endif
|
||||
|
||||
ifeq (monotouch_tv,$(PROFILE))
|
||||
API_BIN_REFS := Xamarin.TVOS
|
||||
LIB_MCS_FLAGS += -d:XAMARIN_MODERN
|
||||
endif
|
||||
|
||||
ifeq (monotouch_watch,$(PROFILE))
|
||||
API_BIN_REFS := Xamarin.WatchOS
|
||||
LIB_MCS_FLAGS += -d:XAMARIN_MODERN
|
||||
endif
|
||||
|
||||
ifeq (xammac,$(PROFILE))
|
||||
API_BIN_REFS := Xamarin.Mac
|
||||
LIB_MCS_FLAGS += -d:XAMARIN_MODERN
|
||||
endif
|
||||
|
||||
ifeq (xammac_net_4_5,$(PROFILE))
|
||||
# we can't use API_BIN_REFS for xammac_net_4_5 as it inherits from net_4_x.make
|
||||
LIB_MCS_FLAGS += -r:$(topdir)/../external/binary-reference-assemblies/build/monotouch/Xamarin.Mac.dll
|
||||
LIB_MCS_FLAGS += -d:XAMARIN_MODERN
|
||||
endif
|
||||
|
||||
TEST_LIB_REFS = System System.Core
|
||||
TEST_MCS_FLAGS =
|
||||
|
||||
LIBRARY_WARN_AS_ERROR = yes
|
||||
|
||||
EXTRA_DISTFILES = \
|
||||
TestData/testservereku.contoso.com.pfx
|
||||
|
||||
ifndef SOCKETSHTTPHANDLER
|
||||
TEST_MCS_FLAGS += -d:LEGACY_HTTPCLIENT
|
||||
LIB_MCS_FLAGS += -d:LEGACY_HTTPCLIENT
|
||||
endif
|
||||
|
||||
SUBDIRS = FunctionalTests UnitTests
|
||||
|
||||
include ../../build/library.make
|
||||
|
||||
# TEST_RUNTIME := MONO_URI_DOTNETRELATIVEORABSOLUTE=true $(TEST_RUNTIME)
|
||||
|
||||
xunit-test-local: xunit-test-recursive
|
||||
|
||||
run-xunit-test-local: run-xunit-test-recursive
|
||||
|
||||
check: check-recursive
|
||||
|
||||
|
@ -1,10 +1,11 @@
|
||||
//
|
||||
// HttpClientHandler.cs
|
||||
// MonoWebRequestHandler.cs
|
||||
//
|
||||
// Authors:
|
||||
// Marek Safar <marek.safar@gmail.com>
|
||||
// Martin Baulig <mabaul@microsoft.com>
|
||||
//
|
||||
// Copyright (C) 2011 Xamarin Inc (http://www.xamarin.com)
|
||||
// Copyright (C) 2011-2018 Xamarin Inc (http://www.xamarin.com)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
@ -29,16 +30,18 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Security.Authentication;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Security.Principal;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Collections.Specialized;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Net.Cache;
|
||||
using System.Net.Security;
|
||||
using System.Linq;
|
||||
|
||||
namespace System.Net.Http
|
||||
{
|
||||
public class HttpClientHandler : HttpMessageHandler
|
||||
class MonoWebRequestHandler : IMonoHttpClientHandler
|
||||
{
|
||||
static long groupCounter;
|
||||
|
||||
@ -51,20 +54,37 @@ namespace System.Net.Http
|
||||
bool preAuthenticate;
|
||||
IWebProxy proxy;
|
||||
bool useCookies;
|
||||
bool useDefaultCredentials;
|
||||
bool useProxy;
|
||||
ClientCertificateOption certificate;
|
||||
SslClientAuthenticationOptions sslOptions;
|
||||
bool allowPipelining;
|
||||
RequestCachePolicy cachePolicy;
|
||||
AuthenticationLevel authenticationLevel;
|
||||
TimeSpan continueTimeout;
|
||||
TokenImpersonationLevel impersonationLevel;
|
||||
int maxResponseHeadersLength;
|
||||
int readWriteTimeout;
|
||||
RemoteCertificateValidationCallback serverCertificateValidationCallback;
|
||||
bool unsafeAuthenticatedConnectionSharing;
|
||||
bool sentRequest;
|
||||
string connectionGroupName;
|
||||
bool disposed;
|
||||
|
||||
public HttpClientHandler ()
|
||||
internal MonoWebRequestHandler ()
|
||||
{
|
||||
allowAutoRedirect = true;
|
||||
maxAutomaticRedirections = 50;
|
||||
maxRequestContentBufferSize = int.MaxValue;
|
||||
useCookies = true;
|
||||
useProxy = true;
|
||||
allowPipelining = true;
|
||||
authenticationLevel = AuthenticationLevel.MutualAuthRequested;
|
||||
cachePolicy = System.Net.WebRequest.DefaultCachePolicy;
|
||||
continueTimeout = TimeSpan.FromMilliseconds (350);
|
||||
impersonationLevel = TokenImpersonationLevel.Delegation;
|
||||
maxResponseHeadersLength = HttpWebRequest.DefaultMaximumResponseHeadersLength;
|
||||
readWriteTimeout = 300000;
|
||||
serverCertificateValidationCallback = null;
|
||||
unsafeAuthenticatedConnectionSharing = false;
|
||||
connectionGroupName = "HttpClientHandler" + Interlocked.Increment (ref groupCounter);
|
||||
}
|
||||
|
||||
@ -96,16 +116,6 @@ namespace System.Net.Http
|
||||
}
|
||||
}
|
||||
|
||||
public ClientCertificateOption ClientCertificateOptions {
|
||||
get {
|
||||
return certificate;
|
||||
}
|
||||
set {
|
||||
EnsureModifiability ();
|
||||
certificate = value;
|
||||
}
|
||||
}
|
||||
|
||||
public CookieContainer CookieContainer {
|
||||
get {
|
||||
return cookieContainer ?? (cookieContainer = new CookieContainer ());
|
||||
@ -203,16 +213,6 @@ namespace System.Net.Http
|
||||
}
|
||||
}
|
||||
|
||||
public bool UseDefaultCredentials {
|
||||
get {
|
||||
return useDefaultCredentials;
|
||||
}
|
||||
set {
|
||||
EnsureModifiability ();
|
||||
useDefaultCredentials = value;
|
||||
}
|
||||
}
|
||||
|
||||
public bool UseProxy {
|
||||
get {
|
||||
return useProxy;
|
||||
@ -223,14 +223,103 @@ namespace System.Net.Http
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Dispose (bool disposing)
|
||||
public bool AllowPipelining {
|
||||
get { return allowPipelining; }
|
||||
set {
|
||||
EnsureModifiability ();
|
||||
allowPipelining = value;
|
||||
}
|
||||
}
|
||||
|
||||
public RequestCachePolicy CachePolicy {
|
||||
get { return cachePolicy; }
|
||||
set {
|
||||
EnsureModifiability ();
|
||||
cachePolicy = value;
|
||||
}
|
||||
}
|
||||
|
||||
public AuthenticationLevel AuthenticationLevel {
|
||||
get { return authenticationLevel; }
|
||||
set {
|
||||
EnsureModifiability ();
|
||||
authenticationLevel = value;
|
||||
}
|
||||
}
|
||||
|
||||
[MonoTODO]
|
||||
public TimeSpan ContinueTimeout {
|
||||
get { return continueTimeout; }
|
||||
set {
|
||||
EnsureModifiability ();
|
||||
continueTimeout = value;
|
||||
}
|
||||
}
|
||||
|
||||
public TokenImpersonationLevel ImpersonationLevel {
|
||||
get { return impersonationLevel; }
|
||||
set {
|
||||
EnsureModifiability ();
|
||||
impersonationLevel = value;
|
||||
}
|
||||
}
|
||||
|
||||
public int MaxResponseHeadersLength {
|
||||
get { return maxResponseHeadersLength; }
|
||||
set {
|
||||
EnsureModifiability ();
|
||||
maxResponseHeadersLength = value;
|
||||
}
|
||||
}
|
||||
|
||||
public int ReadWriteTimeout {
|
||||
get { return readWriteTimeout; }
|
||||
set {
|
||||
EnsureModifiability ();
|
||||
readWriteTimeout = value;
|
||||
}
|
||||
}
|
||||
|
||||
public RemoteCertificateValidationCallback ServerCertificateValidationCallback {
|
||||
get { return serverCertificateValidationCallback; }
|
||||
set {
|
||||
EnsureModifiability ();
|
||||
serverCertificateValidationCallback = value;
|
||||
}
|
||||
}
|
||||
|
||||
public bool UnsafeAuthenticatedConnectionSharing {
|
||||
get { return unsafeAuthenticatedConnectionSharing; }
|
||||
set {
|
||||
EnsureModifiability ();
|
||||
unsafeAuthenticatedConnectionSharing = value;
|
||||
}
|
||||
}
|
||||
|
||||
public SslClientAuthenticationOptions SslOptions {
|
||||
get => sslOptions ?? (sslOptions = new SslClientAuthenticationOptions ());
|
||||
set {
|
||||
EnsureModifiability ();
|
||||
sslOptions = value;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose ()
|
||||
{
|
||||
Dispose (true);
|
||||
}
|
||||
|
||||
protected virtual void Dispose (bool disposing)
|
||||
{
|
||||
if (disposing && !disposed) {
|
||||
Volatile.Write (ref disposed, true);
|
||||
ServicePointManager.CloseConnectionGroup (connectionGroupName);
|
||||
}
|
||||
}
|
||||
|
||||
base.Dispose (disposing);
|
||||
bool GetConnectionKeepAlive (HttpRequestHeaders headers)
|
||||
{
|
||||
return headers.Connection.Any (l => string.Equals (l, "Keep-Alive", StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
internal virtual HttpWebRequest CreateWebRequest (HttpRequestMessage request)
|
||||
@ -239,12 +328,16 @@ namespace System.Net.Http
|
||||
wr.ThrowOnError = false;
|
||||
wr.AllowWriteStreamBuffering = false;
|
||||
|
||||
if (request.Version == HttpVersion.Version20)
|
||||
wr.ProtocolVersion = HttpVersion.Version11;
|
||||
else
|
||||
wr.ProtocolVersion = request.Version;
|
||||
|
||||
wr.ConnectionGroupName = connectionGroupName;
|
||||
wr.Method = request.Method.Method;
|
||||
wr.ProtocolVersion = request.Version;
|
||||
|
||||
if (wr.ProtocolVersion == HttpVersion.Version10) {
|
||||
wr.KeepAlive = request.Headers.ConnectionKeepAlive;
|
||||
wr.KeepAlive = GetConnectionKeepAlive (request.Headers);
|
||||
} else {
|
||||
wr.KeepAlive = request.Headers.ConnectionClose != true;
|
||||
}
|
||||
@ -264,11 +357,7 @@ namespace System.Net.Http
|
||||
wr.CookieContainer = CookieContainer;
|
||||
}
|
||||
|
||||
if (useDefaultCredentials) {
|
||||
wr.UseDefaultCredentials = true;
|
||||
} else {
|
||||
wr.Credentials = credentials;
|
||||
}
|
||||
wr.Credentials = credentials;
|
||||
|
||||
if (useProxy) {
|
||||
wr.Proxy = proxy;
|
||||
@ -298,13 +387,13 @@ namespace System.Net.Http
|
||||
values = values.Where (l => l != "chunked");
|
||||
}
|
||||
|
||||
var values_formated = HttpRequestHeaders.GetSingleHeaderString (header.Key, values);
|
||||
var values_formated = HeaderUtils.GetSingleHeaderString (header.Key, values);
|
||||
if (values_formated == null)
|
||||
continue;
|
||||
|
||||
headers.AddInternal (header.Key, values_formated);
|
||||
}
|
||||
|
||||
|
||||
return wr;
|
||||
}
|
||||
|
||||
@ -313,19 +402,23 @@ namespace System.Net.Http
|
||||
var response = new HttpResponseMessage (wr.StatusCode);
|
||||
response.RequestMessage = requestMessage;
|
||||
response.ReasonPhrase = wr.StatusDescription;
|
||||
#if LEGACY_HTTPCLIENT
|
||||
response.Content = new StreamContent (wr.GetResponseStream (), cancellationToken);
|
||||
#else
|
||||
response.Content = new StreamContent (wr.GetResponseStream ());
|
||||
#endif
|
||||
|
||||
var headers = wr.Headers;
|
||||
for (int i = 0; i < headers.Count; ++i) {
|
||||
var key = headers.GetKey(i);
|
||||
var key = headers.GetKey (i);
|
||||
var value = headers.GetValues (i);
|
||||
|
||||
HttpHeaders item_headers;
|
||||
if (HttpHeaders.GetKnownHeaderKind (key) == Headers.HttpHeaderKind.Content)
|
||||
if (HeaderUtils.IsContentHeader (key))
|
||||
item_headers = response.Content.Headers;
|
||||
else
|
||||
item_headers = response.Headers;
|
||||
|
||||
|
||||
item_headers.TryAddWithoutValidation (key, value);
|
||||
}
|
||||
|
||||
@ -348,7 +441,7 @@ namespace System.Net.Http
|
||||
}
|
||||
}
|
||||
|
||||
protected async internal override Task<HttpResponseMessage> SendAsync (HttpRequestMessage request, CancellationToken cancellationToken)
|
||||
public async Task<HttpResponseMessage> SendAsync (HttpRequestMessage request, CancellationToken cancellationToken)
|
||||
{
|
||||
if (disposed)
|
||||
throw new ObjectDisposedException (GetType ().ToString ());
|
||||
@ -413,25 +506,10 @@ namespace System.Net.Http
|
||||
cancelled.SetCanceled ();
|
||||
return await cancelled.Task;
|
||||
}
|
||||
|
||||
|
||||
return CreateResponseMessage (wresponse, request, cancellationToken);
|
||||
}
|
||||
|
||||
public bool CheckCertificateRevocationList {
|
||||
get {
|
||||
throw new NotImplementedException ();
|
||||
}
|
||||
set {
|
||||
throw new NotImplementedException ();
|
||||
}
|
||||
}
|
||||
|
||||
public X509CertificateCollection ClientCertificates {
|
||||
get {
|
||||
throw new NotImplementedException ();
|
||||
}
|
||||
}
|
||||
|
||||
public ICredentials DefaultProxyCredentials {
|
||||
get {
|
||||
throw new NotImplementedException ();
|
||||
@ -450,37 +528,10 @@ namespace System.Net.Http
|
||||
}
|
||||
}
|
||||
|
||||
public int MaxResponseHeadersLength {
|
||||
public IDictionary<string, object> Properties {
|
||||
get {
|
||||
throw new NotImplementedException ();
|
||||
}
|
||||
set {
|
||||
throw new NotImplementedException ();
|
||||
}
|
||||
}
|
||||
|
||||
public IDictionary<string,object> Properties {
|
||||
get {
|
||||
throw new NotImplementedException ();
|
||||
}
|
||||
}
|
||||
|
||||
public Func<HttpRequestMessage,X509Certificate2,X509Chain,SslPolicyErrors,bool> ServerCertificateCustomValidationCallback {
|
||||
get {
|
||||
throw new NotImplementedException ();
|
||||
}
|
||||
set {
|
||||
throw new NotImplementedException ();
|
||||
}
|
||||
}
|
||||
|
||||
public SslProtocols SslProtocols {
|
||||
get {
|
||||
throw new NotImplementedException ();
|
||||
}
|
||||
set {
|
||||
throw new NotImplementedException ();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
19
mcs/class/System.Net.Http/StreamContent.Mono.cs
Normal file
19
mcs/class/System.Net.Http/StreamContent.Mono.cs
Normal file
@ -0,0 +1,19 @@
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
|
||||
namespace System.Net.Http
|
||||
{
|
||||
partial class StreamContent
|
||||
{
|
||||
//
|
||||
// Workarounds for poor .NET API
|
||||
// Instead of having SerializeToStreamAsync with CancellationToken as public API. Only LoadIntoBufferAsync
|
||||
// called internally from the send worker can be cancelled and user cannot see/do it
|
||||
//
|
||||
[Obsolete ("FIXME: Please talk to Martin about this; see https://github.com/mono/mono/issues/12996.")]
|
||||
internal StreamContent (Stream content, CancellationToken cancellationToken)
|
||||
: this (content)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
|
||||
[assembly:System.Runtime.CompilerServices.InternalsVisibleTo ("Xamarin.Mac, PublicKey=0024000004800000940000000602000000240000525341310004000011000000438ac2a5acfbf16cbd2b2b47a62762f273df9cb2795ceccdf77d10bf508e69e7a362ea7a45455bbf3ac955e1f2e2814f144e5d817efc4c6502cc012df310783348304e3ae38573c6d658c234025821fda87a0be8a0d504df564e2c93b2b878925f42503e9d54dfef9f9586d9e6f38a305769587b1de01f6c0410328b2c9733db")]
|
||||
|
||||
namespace System.Net.Http {
|
||||
public partial class HttpClient {
|
||||
|
||||
public HttpClient ()
|
||||
: this (GetDefaultHandler (), true)
|
||||
{
|
||||
}
|
||||
|
||||
// note: the linker will re-write ObjCRuntime.RuntimeOptions.GetHttpMessageHandler to return the correct type
|
||||
// unlike, XI where this method itself gets rewritten during linking
|
||||
static HttpMessageHandler GetDefaultHandler ()
|
||||
{
|
||||
Type type = Type.GetType("ObjCRuntime.RuntimeOptions, Xamarin.Mac");
|
||||
var method = type.GetMethod ("GetHttpMessageHandler", BindingFlags.Static | BindingFlags.NonPublic);
|
||||
return (HttpMessageHandler)method.Invoke (null, null);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
|
||||
namespace System.Net.Http {
|
||||
public partial class HttpClient {
|
||||
|
||||
public HttpClient ()
|
||||
: this (CreateDefaultHandler ())
|
||||
{
|
||||
}
|
||||
|
||||
static HttpMessageHandler CreateDefaultHandler ()
|
||||
{
|
||||
return ObjCRuntime.RuntimeOptions.GetHttpMessageHandler ();
|
||||
}
|
||||
}
|
||||
}
|
@ -18,13 +18,34 @@ namespace System.Net.Http
|
||||
{
|
||||
|
||||
if (GetHttpMessageHandler == null)
|
||||
return GetFallback("No custom HttpClientHandler registered");
|
||||
{
|
||||
Type type = Type.GetType("WebAssembly.Net.Http.HttpClient.WasmHttpMessageHandler, WebAssembly.Net.Http");
|
||||
if (type == null)
|
||||
return GetFallback ("Invalid WebAssembly Module? Cannot find WebAssembly.Net.Http.HttpClient.WasmHttpMessageHandler");
|
||||
|
||||
var handler = GetHttpMessageHandler();
|
||||
if (handler == null)
|
||||
return GetFallback($"Custom HttpMessageHandler is not valid");
|
||||
MethodInfo method = type.GetMethod("GetHttpMessageHandler", BindingFlags.Static | BindingFlags.NonPublic);
|
||||
if (method == null)
|
||||
return GetFallback ("Your WebAssembly version does not support obtaining of the custom HttpClientHandler");
|
||||
|
||||
return handler;
|
||||
object ret = method.Invoke(null, null);
|
||||
if (ret == null)
|
||||
return GetFallback ("WebAssembly returned no custom HttpClientHandler");
|
||||
|
||||
var handler = ret as HttpMessageHandler;
|
||||
if (handler == null)
|
||||
return GetFallback ($"{ret?.GetType()} is not a valid HttpMessageHandler");
|
||||
|
||||
return handler;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
var handler = GetHttpMessageHandler();
|
||||
if (handler == null)
|
||||
return GetFallback($"Custom HttpMessageHandler is not valid");
|
||||
|
||||
return handler;
|
||||
}
|
||||
}
|
||||
|
||||
static HttpMessageHandler GetFallback(string message)
|
||||
|
@ -101,6 +101,11 @@ namespace System.Net.Http
|
||||
if (uri.Scheme == Uri.UriSchemeHttp || uri.Scheme == Uri.UriSchemeHttps)
|
||||
return true;
|
||||
|
||||
#if WASM
|
||||
if (uri.Scheme == "blob")
|
||||
return true;
|
||||
#endif
|
||||
|
||||
// Mono URI handling which does not distinguish between file and url absolute paths without scheme
|
||||
if (uri.Scheme == Uri.UriSchemeFile && uri.OriginalString.StartsWith ("/", StringComparison.Ordinal))
|
||||
return true;
|
||||
|
@ -1,4 +1,5 @@
|
||||
../../test-helpers/NetworkHelpers.cs
|
||||
HttpClientTestHelpers.cs
|
||||
System.Net.Http/DelegatingHandlerTest.cs
|
||||
System.Net.Http/ByteArrayContentTest.cs
|
||||
System.Net.Http/FormUrlEncodedContentTest.cs
|
||||
|
27
mcs/class/System.Net.Http/Test/HttpClientTestHelpers.cs
Normal file
27
mcs/class/System.Net.Http/Test/HttpClientTestHelpers.cs
Normal file
@ -0,0 +1,27 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Net.Http;
|
||||
|
||||
namespace MonoTests.System.Net.Http
|
||||
{
|
||||
static class HttpClientTestHelpers
|
||||
{
|
||||
#if LEGACY_HTTPCLIENT
|
||||
internal static bool UsingSocketsHandler => false;
|
||||
#else
|
||||
internal static bool UsingSocketsHandler => true;
|
||||
#endif
|
||||
|
||||
internal static bool IsSocketsHandler (HttpClientHandler handler) => UsingSocketsHandler;
|
||||
|
||||
internal static HttpClient CreateHttpClientWithHttpClientHandler ()
|
||||
{
|
||||
return new HttpClient (CreateHttpClientHandler ());
|
||||
}
|
||||
|
||||
internal static HttpClientHandler CreateHttpClientHandler ()
|
||||
{
|
||||
return new HttpClientHandler ();
|
||||
}
|
||||
}
|
||||
}
|
@ -120,7 +120,10 @@ namespace MonoTests.System.Net.Http.Headers
|
||||
headers.TryAddWithoutValidation("User-Agent", "agent2/2.0");
|
||||
|
||||
Assert.AreEqual (1, headers.UserAgent.Count, "#1");
|
||||
Assert.AreEqual ("agent2/2.0user,agent/1.0", headers.UserAgent.ToString (), "#2");
|
||||
if (HttpClientTestHelpers.UsingSocketsHandler)
|
||||
Assert.AreEqual ("agent2/2.0 user,agent/1.0", headers.UserAgent.ToString (), "#2");
|
||||
else
|
||||
Assert.AreEqual ("agent2/2.0user,agent/1.0", headers.UserAgent.ToString (), "#2");
|
||||
|
||||
Assert.AreEqual ("User-Agent: agent2/2.0 user,agent/1.0\r\n", headers.ToString (), "#3");
|
||||
|
||||
|
@ -55,7 +55,15 @@ namespace MonoTests.System.Net.Http.Headers
|
||||
{
|
||||
headers.Add ("aa", "value");
|
||||
headers.Add ("aa", "value");
|
||||
headers.Add ("Expires", (string) null);
|
||||
|
||||
try {
|
||||
headers.Add ("Expires", (string)null);
|
||||
if (HttpClientTestHelpers.UsingSocketsHandler)
|
||||
Assert.Fail ("#1");
|
||||
} catch (FormatException) {
|
||||
if (!HttpClientTestHelpers.UsingSocketsHandler)
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
@ -66,14 +66,13 @@ namespace MonoTests.System.Net.Http
|
||||
#endif
|
||||
public void Properties_Defaults ()
|
||||
{
|
||||
var h = new HttpClientHandler ();
|
||||
var h = HttpClientTestHelpers.CreateHttpClientHandler ();
|
||||
Assert.IsTrue (h.AllowAutoRedirect, "#1");
|
||||
Assert.AreEqual (DecompressionMethods.None, h.AutomaticDecompression, "#2");
|
||||
Assert.AreEqual (0, h.CookieContainer.Count, "#3");
|
||||
Assert.AreEqual (4096, h.CookieContainer.MaxCookieSize, "#3b");
|
||||
Assert.AreEqual (null, h.Credentials, "#4");
|
||||
Assert.AreEqual (50, h.MaxAutomaticRedirections, "#5");
|
||||
Assert.AreEqual (int.MaxValue, h.MaxRequestContentBufferSize, "#6");
|
||||
Assert.IsFalse (h.PreAuthenticate, "#7");
|
||||
Assert.IsNull (h.Proxy, "#8");
|
||||
Assert.IsTrue (h.SupportsAutomaticDecompression, "#9");
|
||||
@ -91,7 +90,7 @@ namespace MonoTests.System.Net.Http
|
||||
#endif
|
||||
public void Properties_Invalid ()
|
||||
{
|
||||
var h = new HttpClientHandler ();
|
||||
var h = HttpClientTestHelpers.CreateHttpClientHandler ();
|
||||
try {
|
||||
h.MaxAutomaticRedirections = 0;
|
||||
Assert.Fail ("#1");
|
||||
@ -104,6 +103,9 @@ namespace MonoTests.System.Net.Http
|
||||
} catch (ArgumentOutOfRangeException) {
|
||||
}
|
||||
|
||||
if (HttpClientTestHelpers.UsingSocketsHandler)
|
||||
Assert.Ignore ();
|
||||
|
||||
h.UseProxy = false;
|
||||
try {
|
||||
h.Proxy = new Proxy ();
|
||||
@ -118,7 +120,7 @@ namespace MonoTests.System.Net.Http
|
||||
#endif
|
||||
public void Properties_AfterClientCreation ()
|
||||
{
|
||||
var h = new HttpClientHandler ();
|
||||
var h = HttpClientTestHelpers.CreateHttpClientHandler ();
|
||||
h.AllowAutoRedirect = true;
|
||||
|
||||
// We may modify properties after creating the HttpClient.
|
||||
@ -133,7 +135,7 @@ namespace MonoTests.System.Net.Http
|
||||
#endif
|
||||
public void Disposed ()
|
||||
{
|
||||
var h = new HttpClientHandler ();
|
||||
var h = HttpClientTestHelpers.CreateHttpClientHandler ();
|
||||
h.Dispose ();
|
||||
var c = new HttpClient (h);
|
||||
try {
|
||||
@ -141,6 +143,9 @@ namespace MonoTests.System.Net.Http
|
||||
Assert.Fail ("#1");
|
||||
} catch (AggregateException e) {
|
||||
Assert.IsTrue (e.InnerException is ObjectDisposedException, "#2");
|
||||
Assert.IsFalse (HttpClientTestHelpers.IsSocketsHandler (h), "#3");
|
||||
} catch (ObjectDisposedException) {
|
||||
Assert.IsTrue (HttpClientTestHelpers.IsSocketsHandler (h), "#4");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user