diff --git a/DropNet2/Authentication/OAuthBase.cs b/DropNet2/Authentication/OAuthBase.cs
new file mode 100644
index 0000000..a3ba5cf
--- /dev/null
+++ b/DropNet2/Authentication/OAuthBase.cs
@@ -0,0 +1,365 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace DropNet2.Authentication
+{
+ public class OAuthBase
+ {
+ #region SignatureTypes enum
+
+ ///
+ /// Provides a predefined set of algorithms that are supported officially by the protocol
+ ///
+ public enum SignatureTypes
+ {
+ HMACSHA1,
+ PLAINTEXT,
+ RSASHA1
+ }
+
+ #endregion
+
+ protected const string OAuthVersion = "1.0";
+ protected const string OAuthParameterPrefix = "oauth_";
+
+ //
+ // List of know and used oauth parameters' names
+ //
+ protected const string OAuthConsumerKeyKey = "oauth_consumer_key";
+ protected const string OAuthCallbackKey = "oauth_callback";
+ protected const string OAuthVersionKey = "oauth_version";
+ protected const string OAuthSignatureMethodKey = "oauth_signature_method";
+ protected const string OAuthSignatureKey = "oauth_signature";
+ protected const string OAuthTimestampKey = "oauth_timestamp";
+ protected const string OAuthNonceKey = "oauth_nonce";
+ protected const string OAuthTokenKey = "oauth_token";
+ protected const string OAuthTokenSecretKey = "oauth_token_secret";
+
+ protected const string Hmacsha1SignatureType = "HMAC-SHA1";
+ protected const string PlainTextSignatureType = "PLAINTEXT";
+ protected const string Rsasha1SignatureType = "RSA-SHA1";
+
+ protected Random Random = new Random();
+
+ protected string UnreservedChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~";
+
+ ///
+ /// Internal function to cut out all non oauth query string parameters (all parameters not beginning with "oauth_")
+ ///
+ /// The query string part of the Url
+ /// A list of QueryParameter each containing the parameter name and value
+ private List GetQueryParameters(string parameters)
+ {
+ if (parameters.StartsWith("?"))
+ {
+ parameters = parameters.Remove(0, 1);
+ }
+
+ var result = new List();
+
+ if (!string.IsNullOrEmpty(parameters))
+ {
+ string[] p = parameters.Split('&');
+ foreach (string s in p)
+ {
+ if (!string.IsNullOrEmpty(s) && !s.StartsWith(OAuthParameterPrefix))
+ {
+ if (s.IndexOf('=') > -1)
+ {
+ string[] temp = s.Split('=');
+ result.Add(new QueryParameter(temp[0], temp[1]));
+ }
+ else
+ {
+ result.Add(new QueryParameter(s, string.Empty));
+ }
+ }
+ }
+ }
+
+ return result;
+ }
+
+ ///
+ /// This is a different Url Encode implementation since the default .NET one outputs the percent encoding in lower case.
+ /// While this is not a problem with the percent encoding spec, it is used in upper case throughout OAuth
+ ///
+ /// The value to Url encode
+ /// Returns a Url encoded string
+ protected string UrlEncode(string value)
+ {
+ var result = new StringBuilder();
+
+ foreach (char symbol in value)
+ {
+ if (UnreservedChars.IndexOf(symbol) != -1)
+ {
+ result.Append(symbol);
+ }
+ else
+ {
+ result.Append('%' + String.Format("{0:X2}", (int)symbol));
+ }
+ }
+
+ return result.ToString();
+ }
+
+ ///
+ /// Normalizes the request parameters according to the spec
+ ///
+ /// The list of parameters already sorted
+ /// a string representing the normalized parameters
+ protected string NormalizeRequestParameters(IList parameters)
+ {
+ var sb = new StringBuilder();
+ for (int i = 0; i < parameters.Count; i++)
+ {
+ QueryParameter p = parameters[i];
+ sb.AppendFormat("{0}={1}", p.Name, p.Value);
+
+ if (i < parameters.Count - 1)
+ {
+ sb.Append("&");
+ }
+ }
+
+ return sb.ToString();
+ }
+
+ ///
+ /// Generate the signature base that is used to produce the signature
+ ///
+ /// The full url that needs to be signed including its non OAuth url parameters
+ /// The consumer key
+ /// The token, if available. If not available pass null or an empty string
+ /// The token secret, if available. If not available pass null or an empty string
+ /// The http method used. Must be a valid HTTP method verb (POST,GET,PUT, etc)
+ /// TimeStamp
+ /// The nounce
+ /// The signature type. To use the default values use OAuthBase.SignatureTypes.
+ /// Normalised Url
+ /// The normalized request parameters.
+ ///
+ /// The signature base
+ ///
+ /// consumerKey
+ public string GenerateSignatureBase(Uri url, string consumerKey, string token, string tokenSecret,
+ string httpMethod, string timeStamp, string nonce, string signatureType,
+ out string normalizedUrl, out string normalizedRequestParameters)
+ {
+ if (token == null)
+ {
+ token = string.Empty;
+ }
+
+ if (tokenSecret == null)
+ {
+ tokenSecret = string.Empty;
+ }
+
+ if (string.IsNullOrEmpty(consumerKey))
+ {
+ throw new ArgumentNullException("consumerKey");
+ }
+
+ if (string.IsNullOrEmpty(httpMethod))
+ {
+ throw new ArgumentNullException("httpMethod");
+ }
+
+ if (string.IsNullOrEmpty(signatureType))
+ {
+ throw new ArgumentNullException("signatureType");
+ }
+
+ normalizedUrl = null;
+ normalizedRequestParameters = null;
+
+ List parameters = GetQueryParameters(url.Query);
+ parameters.Add(new QueryParameter(OAuthVersionKey, OAuthVersion));
+ parameters.Add(new QueryParameter(OAuthNonceKey, nonce));
+ parameters.Add(new QueryParameter(OAuthTimestampKey, timeStamp));
+ parameters.Add(new QueryParameter(OAuthSignatureMethodKey, signatureType));
+ parameters.Add(new QueryParameter(OAuthConsumerKeyKey, consumerKey));
+
+ if (!string.IsNullOrEmpty(token))
+ {
+ parameters.Add(new QueryParameter(OAuthTokenKey, token));
+ }
+
+ parameters.Sort(new QueryParameterComparer());
+
+ normalizedUrl = string.Format("{0}://{1}", url.Scheme, url.Host);
+ if (!((url.Scheme == "http" && url.Port == 80) || (url.Scheme == "https" && url.Port == 443)))
+ {
+ normalizedUrl += ":" + url.Port;
+ }
+ normalizedUrl += url.AbsolutePath;
+ normalizedRequestParameters = NormalizeRequestParameters(parameters);
+
+ var signatureBase = new StringBuilder();
+ signatureBase.AppendFormat("{0}&", httpMethod.ToUpper());
+ signatureBase.AppendFormat("{0}&", UrlEncode(normalizedUrl));
+ signatureBase.AppendFormat("{0}", UrlEncode(normalizedRequestParameters));
+
+ return signatureBase.ToString();
+ }
+
+
+ ///
+ /// Generates a signature using the HMAC-SHA1 algorithm
+ ///
+ /// The full url that needs to be signed including its non OAuth url parameters
+ /// The consumer key
+ /// The consumer seceret
+ /// The token, if available. If not available pass null or an empty string
+ /// The token secret, if available. If not available pass null or an empty string
+ /// The http method used. Must be a valid HTTP method verb (POST,GET,PUT, etc)
+ /// The time stamp.
+ /// The nonce.
+ /// The normalized URL.
+ /// The normalized request parameters.
+ /// The auth header.
+ ///
+ /// A base64 string of the hash value
+ ///
+ public string GenerateSignature(Uri url, string consumerKey, string consumerSecret, string token,
+ string tokenSecret, string httpMethod, string timeStamp, string nonce,
+ out string normalizedUrl, out string normalizedRequestParameters,
+ out string authHeader)
+ {
+ return GenerateSignature(url, consumerKey, consumerSecret, token, tokenSecret, httpMethod, timeStamp, nonce,
+ SignatureTypes.PLAINTEXT, out normalizedUrl, out normalizedRequestParameters,
+ out authHeader);
+ }
+
+ ///
+ /// Generates a signature using the specified signatureType
+ ///
+ /// The full url that needs to be signed including its non OAuth url parameters
+ /// The consumer key
+ /// The consumer seceret
+ /// The token, if available. If not available pass null or an empty string
+ /// The token secret, if available. If not available pass null or an empty string
+ /// The http method used. Must be a valid HTTP method verb (POST,GET,PUT, etc)
+ /// The time stamp.
+ /// The nonce.
+ /// The type of signature to use
+ /// The normalized URL.
+ /// The normalized request parameters.
+ /// The auth header.
+ ///
+ /// A base64 string of the hash value
+ ///
+ ///
+ /// Unknown signature type;signatureType
+ public string GenerateSignature(Uri url, string consumerKey, string consumerSecret, string token,
+ string tokenSecret, string httpMethod, string timeStamp, string nonce,
+ SignatureTypes signatureType, out string normalizedUrl,
+ out string normalizedRequestParameters, out string authHeader)
+ {
+ normalizedUrl = null;
+ normalizedRequestParameters = null;
+ authHeader = null;
+
+ switch (signatureType)
+ {
+ case SignatureTypes.PLAINTEXT:
+ var auth = new StringBuilder();
+ auth.AppendFormat("{0}=\"{1}\", ", OAuthConsumerKeyKey, UrlEncode(consumerKey));
+ auth.AppendFormat("{0}=\"{1}\", ", OAuthNonceKey, UrlEncode(nonce));
+ auth.AppendFormat("{0}=\"{1}\", ", OAuthSignatureKey, UrlEncode(string.Format("{0}&{1}", consumerSecret, tokenSecret)));
+ auth.AppendFormat("{0}=\"{1}\", ", OAuthSignatureMethodKey, "PLAINTEXT");
+ auth.AppendFormat("{0}=\"{1}\", ", OAuthTimestampKey, timeStamp);
+ if (!string.IsNullOrEmpty(token))
+ {
+ auth.AppendFormat("{0}=\"{1}\", ", OAuthTokenKey, UrlEncode(token));
+ }
+ auth.AppendFormat("{0}=\"{1}\"", OAuthVersionKey, "1.0");
+ authHeader = auth.ToString();
+ return UrlEncode(string.Format("{0}&{1}", consumerSecret, tokenSecret));
+
+ case SignatureTypes.RSASHA1:
+ throw new NotImplementedException();
+ default:
+ throw new ArgumentException("Unknown signature type", "signatureType");
+ }
+ }
+
+ ///
+ /// Generate the timestamp for the signature
+ ///
+ ///
+ public virtual string GenerateTimeStamp()
+ {
+ // Default implementation of UNIX time of the current UTC time
+ TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
+ return Convert.ToInt64(ts.TotalSeconds).ToString();
+ }
+
+ ///
+ /// Generate a nonce
+ ///
+ ///
+ public virtual string GenerateNonce()
+ {
+ // Just a simple implementation of a random number between 123400 and 9999999
+ return Random.Next(123400, 9999999).ToString();
+ }
+
+ #region Nested type: QueryParameter
+
+ ///
+ /// Provides an internal structure to sort the query parameter
+ ///
+ protected class QueryParameter
+ {
+ private readonly string _name;
+ private readonly string _value;
+
+ public QueryParameter(string name, string value)
+ {
+ _name = name;
+ _value = value;
+ }
+
+ public string Name
+ {
+ get { return _name; }
+ }
+
+ public string Value
+ {
+ get { return _value; }
+ }
+ }
+
+ #endregion
+
+ #region Nested type: QueryParameterComparer
+
+ ///
+ /// Comparer class used to perform the sorting of the query parameters
+ ///
+ protected class QueryParameterComparer : IComparer
+ {
+ #region IComparer Members
+
+ public int Compare(QueryParameter x, QueryParameter y)
+ {
+ if (x.Name == y.Name)
+ {
+ return string.Compare(x.Value, y.Value);
+ }
+
+ return string.Compare(x.Name, y.Name);
+ }
+
+ #endregion
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/DropNet2/Authentication/OAuthMessageHandler.cs b/DropNet2/Authentication/OAuthMessageHandler.cs
index dd813df..9207bc5 100644
--- a/DropNet2/Authentication/OAuthMessageHandler.cs
+++ b/DropNet2/Authentication/OAuthMessageHandler.cs
@@ -1,4 +1,5 @@
-using DropNet2.HttpHelpers;
+using System.Net.Http.Headers;
+using DropNet2.HttpHelpers;
using System;
using System.Net.Http;
using System.Text;
@@ -61,6 +62,30 @@ public HttpRequest Authenticate(HttpRequest request)
return request;
}
+ protected override System.Threading.Tasks.Task SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
+ {
+ string normalizedUri;
+ string authHeader;
+ string normalizedParameters;
+
+ var url = AuthBase.GenerateSignature(
+ request.RequestUri,
+ ApiKey,
+ ApiSecret,
+ UserToken,
+ UserSecret,
+ request.Method.Method,
+ AuthBase.GenerateTimeStamp(),
+ AuthBase.GenerateNonce(),
+ out normalizedUri,
+ out normalizedParameters,
+ out authHeader);
+
+ request.Headers.Authorization = new AuthenticationHeaderValue("OAuth", authHeader);
+
+ return base.SendAsync(request, cancellationToken);
+ }
+
private static object EncodeParameters(HttpRequest request)
{
var querystring = new StringBuilder();
@@ -91,5 +116,12 @@ private static string GenerateTimeStamp()
TimeSpan span = DateTime.UtcNow - new DateTime(0x7b2, 1, 1, 0, 0, 0, 0);
return Convert.ToInt64(span.TotalSeconds).ToString();
}
+
+ private OAuthBase AuthBase
+ {
+ get { return _authBase ?? (_authBase = new OAuthBase()); }
+ }
+
+ private OAuthBase _authBase;
}
}
diff --git a/DropNet2/Client.Files.cs b/DropNet2/Client.Files.cs
index 09a533e..f803fc4 100644
--- a/DropNet2/Client.Files.cs
+++ b/DropNet2/Client.Files.cs
@@ -32,6 +32,12 @@ public async Task GetMetaData(string path)
return response;
}
+ public async Task FilePut()
+ {
+ _httpClient.BaseAddress = new Uri("https://api-content.dropbox.com/1/");
+ var d =await _httpClient.PutAsync("dropbox/files_put/data.txt", new StreamContent(new MemoryStream()));
+ }
+
///
/// Gets a share link from a give path
///
diff --git a/DropNet2/Client.User.cs b/DropNet2/Client.User.cs
index 58251fb..85a9282 100644
--- a/DropNet2/Client.User.cs
+++ b/DropNet2/Client.User.cs
@@ -1,7 +1,12 @@
-using DropNet2.HttpHelpers;
+using System.Diagnostics;
+using System.Net;
+using DropNet2.Exceptions;
+using DropNet2.Helpers;
+using DropNet2.HttpHelpers;
using DropNet2.Models;
using System.Net.Http;
using System.Threading.Tasks;
+using Newtonsoft.Json;
namespace DropNet2
{
@@ -12,20 +17,14 @@ public partial class DropNetClient
/// Auth Step 1. Gets a Request Token which is required for the login request
///
///
- public async Task GetRequestToken()
+ public async Task GetRequestTokenAsync()
{
- var requestUrl = MakeRequestString("1/oauth/request_token", ApiType.Base);
-
- var request = new HttpRequest(HttpMethod.Get, requestUrl);
-
- _oauthHandler.Authenticate(request);
-
- var response = await _httpClient.SendAsync(request);
+ _httpClient.BaseAddress = GetBaseAddress(ApiType.Base);
+ var response = await _httpClient.GetAsync("oauth/request_token");
string responseBody = await response.Content.ReadAsStringAsync();
UserLogin = GetUserLoginFromParams(responseBody);
-
SetUserToken(UserLogin);
return UserLogin;
@@ -35,7 +34,7 @@ public async Task GetRequestToken()
/// Auth Step 3. Once a Request Token has been authorized convert it to an access token for API usage.
///
///
- public async Task GetAccessToken()
+ public async Task GetAccessTokenAsync()
{
var requestUrl = MakeRequestString("1/oauth/access_token", ApiType.Base);
@@ -59,16 +58,19 @@ public async Task GetAccessToken()
/// Gets the account info of the current logged in user
///
///
- public async Task AccountInfo()
+ public async Task AccountInfoAsync()
{
- var requestUrl = MakeRequestString("1/account/info", ApiType.Base);
-
- var request = new HttpRequest(HttpMethod.Get, requestUrl);
-
- var response = await SendAsync(request);
-
- return response;
+ _httpClient.BaseAddress = GetBaseAddress(ApiType.Base);
+
+ using (var response = await _httpClient.GetAsync("account/info"))
+ {
+ if (response.StatusCode != HttpStatusCode.OK)
+ {
+ throw new DropboxException(response);
+ }
+
+ return await response.GetResultAsync();
+ }
}
-
}
}
diff --git a/DropNet2/Client.cs b/DropNet2/Client.cs
index 6af4b41..efae941 100644
--- a/DropNet2/Client.cs
+++ b/DropNet2/Client.cs
@@ -13,8 +13,8 @@ namespace DropNet2
{
public partial class DropNetClient
{
- private const string ApiBaseUrl = "https://api.dropbox.com";
- private const string ApiContentBaseUrl = "https://api-content.dropbox.com";
+ private const string ApiBaseUrl = "https://api.dropbox.com/1/";
+ private const string ApiContentBaseUrl = "https://api-content.dropbox.com/1/";
///
/// Do not set this property directly, instead use SetUserToken
@@ -142,5 +142,10 @@ private async Task SendAsync(HttpRequest request) where T : class
return JsonConvert.DeserializeObject(responseBody);
}
+ static Uri GetBaseAddress(ApiType apiType)
+ {
+ string uri = apiType == ApiType.Base ? ApiBaseUrl : ApiContentBaseUrl;
+ return new Uri(uri);
+ }
}
}
diff --git a/DropNet2/DropNet2.csproj b/DropNet2/DropNet2.csproj
index 538f4bc..7dadc1e 100644
--- a/DropNet2/DropNet2.csproj
+++ b/DropNet2/DropNet2.csproj
@@ -39,11 +39,13 @@
+
+
diff --git a/DropNet2/Exceptions/DropboxException.cs b/DropNet2/Exceptions/DropboxException.cs
index 52f874c..443a5ad 100644
--- a/DropNet2/Exceptions/DropboxException.cs
+++ b/DropNet2/Exceptions/DropboxException.cs
@@ -27,5 +27,6 @@ public DropboxException(Exception ex)
: base("Dropbox error occurred", ex)
{
}
+
}
}
diff --git a/DropNet2/Helpers/Extensions.cs b/DropNet2/Helpers/Extensions.cs
new file mode 100644
index 0000000..6d3042d
--- /dev/null
+++ b/DropNet2/Helpers/Extensions.cs
@@ -0,0 +1,15 @@
+using System.Net.Http;
+using System.Threading.Tasks;
+using Newtonsoft.Json;
+
+namespace DropNet2.Helpers
+{
+ public static class Extensions
+ {
+ public static async Task GetResultAsync(this HttpResponseMessage response)
+ {
+ string contentString = await response.Content.ReadAsStringAsync();
+ return JsonConvert.DeserializeObject(contentString);
+ }
+ }
+}
diff --git a/DropNet2Sample/ContentsPage.xaml.cs b/DropNet2Sample/ContentsPage.xaml.cs
index a9ffa9f..236f9fc 100644
--- a/DropNet2Sample/ContentsPage.xaml.cs
+++ b/DropNet2Sample/ContentsPage.xaml.cs
@@ -58,7 +58,7 @@ private void lsbContents_SelectionChanged(object sender, System.Windows.Controls
if (selected == null) return;
- if (selected.Is_Dir)
+ if (selected.IsDirectory)
{
//navigate to the new dir
_model.LoadPath(selected.Path);
diff --git a/DropNet2Sample/MainPage.xaml.cs b/DropNet2Sample/MainPage.xaml.cs
index 12fdc62..df66fe6 100644
--- a/DropNet2Sample/MainPage.xaml.cs
+++ b/DropNet2Sample/MainPage.xaml.cs
@@ -45,7 +45,7 @@ private async void btnLogin_Click(object sender, System.Windows.RoutedEventArgs
_model.ShowBrowser = true;
//Get the request token
- var requestToken = await App.DropNetClient.GetRequestToken();
+ var requestToken = await App.DropNetClient.GetRequestTokenAsync();
var tokenUrl = App.DropNetClient.BuildAuthorizeUrl(requestToken, _tokenCallbackUrl);
//Open a browser with the URL
@@ -59,7 +59,7 @@ private async void loginBrowser_LoadCompleted(object sender, NavigationEventArgs
{
//SUCCESS!
_model.SetStatus("Getting Access Token...", true);
- var accessToken = await App.DropNetClient.GetAccessToken();
+ var accessToken = await App.DropNetClient.GetAccessTokenAsync();
//TODO - Save this token/Secret for remember me function
diff --git a/DropNet2Tests/ClientFileTests.cs b/DropNet2Tests/ClientFileTests.cs
index 6432282..04bc7b5 100644
--- a/DropNet2Tests/ClientFileTests.cs
+++ b/DropNet2Tests/ClientFileTests.cs
@@ -21,9 +21,9 @@ public void Setup()
[Test]
public async Task Given_A_Root_Path_Get_Metadata()
{
- var data = await _client.GetMetaData("/");
- Assert.NotNull(data);
- Assert.IsTrue(data.IsDirectory);
+ await _client.FilePut();
+ // Assert.NotNull(data);
+ //Assert.IsTrue(data.IsDirectory);
}
[Test]
diff --git a/DropNet2Tests/ClientUserTests.cs b/DropNet2Tests/ClientUserTests.cs
index d6c6c3b..5b6aa9d 100644
--- a/DropNet2Tests/ClientUserTests.cs
+++ b/DropNet2Tests/ClientUserTests.cs
@@ -6,13 +6,13 @@ namespace DropNet2Tests
{
[TestFixture]
public class ClientUserTests
- {
+ {
[Test]
public async Task When_Token_Requested_Then_User_Token_Is_Returned()
- {
+ {
var client = new DropNetClient(AppKey, AppSecret);
- var userToken = await client.GetRequestToken();
+ var userToken = await client.GetRequestTokenAsync();
Assert.NotNull(userToken);
}
@@ -20,8 +20,8 @@ public async Task When_Token_Requested_Then_User_Token_Is_Returned()
public async Task Given_UserToken_When_Build_Auth_Url_Then_The_Authentication_Url_Is_Returned()
{
var client = new DropNetClient(AppKey, AppSecret);
-
- var userToken = await client.GetRequestToken();
+
+ var userToken = await client.GetRequestTokenAsync();
string url = client.BuildAuthorizeUrl(userToken, "http://cloudyboxapp.com");
Assert.IsNotEmpty(url);
}
@@ -30,8 +30,7 @@ public async Task Given_UserToken_When_Build_Auth_Url_Then_The_Authentication_Ur
public async Task Given_A_Clent_Get_User_Account_Infromation()
{
var client = new DropNetClient(AppKey, AppSecret, UserToken, UserSecret);
- var accountInfromation = await client.AccountInfo();
-
+ var accountInfromation = await client.AccountInfoAsync();
Assert.NotNull(accountInfromation);
Assert.NotNull(accountInfromation.QuotaInfo);
}
@@ -42,11 +41,11 @@ public async Task Get_Access_Token_Test()
{
var client = new DropNetClient(AppKey, AppSecret);
- var userToken = await client.GetRequestToken();
-
+ var userToken = await client.GetRequestTokenAsync();
+
//Open the url in browser and login
string url = client.BuildAuthorizeUrl(userToken, "http://cloudyboxapp.com");
- var user = await client.GetAccessToken();
+ var user = await client.GetAccessTokenAsync();
Assert.NotNull(user);
}
diff --git a/DropNet2Tests/DropNet2Tests.csproj b/DropNet2Tests/DropNet2Tests.csproj
index 4fe4042..b36f4d0 100644
--- a/DropNet2Tests/DropNet2Tests.csproj
+++ b/DropNet2Tests/DropNet2Tests.csproj
@@ -40,11 +40,13 @@
-
- ..\packages\Microsoft.Net.Http.2.1.3-beta\lib\net45\System.Net.Http.Extensions.dll
+
+ False
+ ..\packages\Microsoft.Net.Http.2.1.10\lib\net45\System.Net.Http.Extensions.dll
-
- ..\packages\Microsoft.Net.Http.2.1.3-beta\lib\net45\System.Net.Http.Primitives.dll
+
+ False
+ ..\packages\Microsoft.Net.Http.2.1.10\lib\net45\System.Net.Http.Primitives.dll
@@ -71,6 +73,7 @@
+