Skip to content
This repository was archived by the owner on Feb 13, 2025. It is now read-only.

Commit 9711c2f

Browse files
authored
Merge pull request #19 from bacongobbler/connection-string
implement connection string support
2 parents 72497f7 + 45e087c commit 9711c2f

File tree

6 files changed

+235
-40
lines changed

6 files changed

+235
-40
lines changed

src/Bindle/BindleClient.cs

Lines changed: 29 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
using System;
2-
using System.Collections.Generic;
32
using System.Collections.Specialized;
43
using System.IO;
54
using System.Linq;
65
using System.Net;
76
using System.Net.Http;
8-
using System.Net.Http.Headers;
97
using System.Threading.Tasks;
108
using Tomlyn;
119
using Tomlyn.Syntax;
@@ -14,35 +12,41 @@ namespace Deislabs.Bindle;
1412

1513
public class BindleClient
1614
{
17-
public BindleClient(
18-
string baseUri
19-
)
20-
{
21-
_baseUri = new Uri(SlashSafe(baseUri));
22-
_httpClient = new HttpClient();
23-
}
15+
public BindleClient(string connectionString) : this(new ConnectionInfo(connectionString)) { }
2416

25-
public BindleClient(
26-
string baseUri,
27-
HttpMessageHandler messageHandler
28-
)
17+
public BindleClient(ConnectionInfo connectionInfo)
2918
{
30-
_baseUri = new Uri(SlashSafe(baseUri));
31-
_httpClient = new HttpClient(messageHandler);
19+
if (string.IsNullOrEmpty(connectionInfo.BaseUri))
20+
throw new ArgumentException("base URI cannot be empty");
21+
22+
var handler = new HttpClientHandler();
23+
24+
if (connectionInfo.SslMode == SslMode.Disable)
25+
{
26+
handler.ServerCertificateCustomValidationCallback =
27+
(httpRequestMessage, cert, cetChain, policyErrors) =>
28+
{
29+
return true;
30+
};
31+
}
32+
33+
_httpClient = new HttpClient(handler) { BaseAddress = new Uri(SlashSafe(connectionInfo.BaseUri)) };
34+
35+
if (!string.IsNullOrEmpty(connectionInfo.UserName) && !string.IsNullOrEmpty(connectionInfo.Password))
36+
{
37+
_httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", $"{connectionInfo.UserName}:{connectionInfo.Password}");
38+
}
3239
}
3340

3441
private const string INVOICE_PATH = "_i";
3542
private const string QUERY_PATH = "_q";
3643
private const string RELATIONSHIP_PATH = "_r";
37-
38-
private readonly Uri _baseUri;
3944
private readonly HttpClient _httpClient;
4045

4146
public async Task<Invoice> GetInvoice(string invoiceId, GetInvoiceOptions options = GetInvoiceOptions.None)
4247
{
4348
var query = GetInvoiceQueryString(options);
44-
var uri = new Uri(_baseUri, $"{INVOICE_PATH}/{invoiceId}{query}");
45-
var response = await _httpClient.GetAsync(uri);
49+
var response = await _httpClient.GetAsync($"{INVOICE_PATH}/{invoiceId}{query}");
4650
await ExpectResponseCode(response, HttpStatusCode.OK, HttpStatusCode.Forbidden);
4751

4852
if (response.StatusCode == HttpStatusCode.Forbidden)
@@ -74,8 +78,7 @@ public async Task<Matches> QueryInvoices(string? queryString = null,
7478
bool? yanked = null)
7579
{
7680
var query = GetDistinctInvoicesNamesQueryString(queryString, offset, limit, strict, semVer, yanked);
77-
var uri = new Uri(_baseUri, $"{QUERY_PATH}?{query}");
78-
var response = await _httpClient.GetAsync(uri);
81+
var response = await _httpClient.GetAsync($"{QUERY_PATH}?{query}");
7982
await ExpectResponseCode(response, HttpStatusCode.OK, HttpStatusCode.Forbidden);
8083

8184
if (response.StatusCode == HttpStatusCode.Forbidden)
@@ -99,9 +102,8 @@ public async Task<CreateInvoiceResult> CreateInvoice(Invoice invoice)
99102
ConvertPropertyName = name => TomlNamingHelper.PascalToCamelCase(name)
100103
});
101104

102-
var uri = new Uri(_baseUri, INVOICE_PATH);
103105
var requestContent = new StringContent(invoiceToml, null, "application/toml");
104-
var response = await _httpClient.PostAsync(uri, requestContent);
106+
var response = await _httpClient.PostAsync(INVOICE_PATH, requestContent);
105107
await ExpectResponseCode(response, HttpStatusCode.Created, HttpStatusCode.Accepted);
106108

107109
var content = await response.Content.ReadAsStringAsync();
@@ -115,15 +117,13 @@ public async Task<CreateInvoiceResult> CreateInvoice(Invoice invoice)
115117

116118
public async Task YankInvoice(string invoiceId)
117119
{
118-
var uri = new Uri(_baseUri, $"{INVOICE_PATH}/{invoiceId}");
119-
var response = await _httpClient.DeleteAsync(uri);
120+
var response = await _httpClient.DeleteAsync($"{INVOICE_PATH}/{invoiceId}");
120121
await ExpectResponseCode(response, HttpStatusCode.OK);
121122
}
122123

123124
public async Task<HttpContent> GetParcel(string invoiceId, string parcelId)
124125
{
125-
var uri = new Uri(_baseUri, $"{INVOICE_PATH}/{invoiceId}@{parcelId}");
126-
var response = await _httpClient.GetAsync(uri);
126+
var response = await _httpClient.GetAsync($"{INVOICE_PATH}/{invoiceId}@{parcelId}");
127127
await ExpectResponseCode(response, HttpStatusCode.OK);
128128

129129
return response.Content;
@@ -146,15 +146,13 @@ public async Task CreateParcel(string invoiceId, string parcelId, byte[] content
146146

147147
public async Task CreateParcel(string invoiceId, string parcelId, HttpContent content)
148148
{
149-
var uri = new Uri(_baseUri, $"{INVOICE_PATH}/{invoiceId}@{parcelId}");
150-
var response = await _httpClient.PostAsync(uri, content);
149+
var response = await _httpClient.PostAsync($"{INVOICE_PATH}/{invoiceId}@{parcelId}", content);
151150
await ExpectResponseCode(response, HttpStatusCode.OK, HttpStatusCode.Created);
152151
}
153152

154153
public async Task<MissingParcelsResponse> ListMissingParcels(string invoiceId)
155154
{
156-
var uri = new Uri(_baseUri, $"{RELATIONSHIP_PATH}/missing/{invoiceId}");
157-
var response = await _httpClient.GetAsync(uri);
155+
var response = await _httpClient.GetAsync($"{RELATIONSHIP_PATH}/missing/{invoiceId}");
158156
await ExpectResponseCode(response, HttpStatusCode.OK);
159157

160158
var content = await response.Content.ReadAsStringAsync();

src/Bindle/ConnectionInfo.cs

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
5+
namespace Deislabs.Bindle;
6+
7+
public class ConnectionInfo
8+
{
9+
static readonly string[] serverAliases = {
10+
"server",
11+
"host",
12+
"data source",
13+
"datasource",
14+
"address",
15+
"addr",
16+
"network address",
17+
};
18+
19+
static readonly string[] sslModeAliases = {
20+
"sslmode",
21+
"ssl mode"
22+
};
23+
24+
static readonly string[] usernameAliases = {
25+
"user",
26+
"username"
27+
};
28+
29+
static readonly string[] passwordAliases = {
30+
"pass",
31+
"passwd",
32+
"password"
33+
};
34+
35+
private readonly Dictionary<string, string> keyValuePairs;
36+
37+
public string BaseUri;
38+
39+
public string UserName;
40+
41+
public string Password;
42+
43+
public SslMode? SslMode;
44+
45+
public ConnectionInfo()
46+
{
47+
keyValuePairs = new Dictionary<string, string>();
48+
BaseUri = "http://localhost:8080/v1/";
49+
UserName = String.Empty;
50+
Password = String.Empty;
51+
}
52+
53+
public ConnectionInfo(string connectionString)
54+
{
55+
keyValuePairs = GetKeyValuePairs(connectionString);
56+
57+
BaseUri = GetValue(serverAliases);
58+
59+
UserName = GetValue(usernameAliases);
60+
61+
Password = GetValue(passwordAliases);
62+
63+
try
64+
{
65+
SslMode = Enum.Parse<SslMode>(GetValue(sslModeAliases), true);
66+
}
67+
catch (Exception)
68+
{
69+
SslMode = null;
70+
}
71+
}
72+
73+
private Dictionary<string, string> GetKeyValuePairs(string connectionString)
74+
{
75+
return connectionString.Split(';')
76+
.Where(kvp => kvp.Contains('='))
77+
.Select(kvp => kvp.Split(new char[] { '=' }, 2))
78+
.ToDictionary(kvp => kvp[0].Trim(),
79+
kvp => kvp[1].Trim(),
80+
StringComparer.InvariantCultureIgnoreCase);
81+
}
82+
83+
private string GetValue(string[] keyAliases)
84+
{
85+
foreach (var alias in keyAliases)
86+
{
87+
string? value;
88+
if (keyValuePairs.TryGetValue(alias, out value))
89+
return value;
90+
}
91+
return string.Empty;
92+
}
93+
}

src/Bindle/SslMode.cs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
namespace Deislabs.Bindle;
2+
3+
public enum SslMode
4+
{
5+
// I don't care about security, and I don't want to pay the overhead of encryption.
6+
Disable,
7+
// I don't care about security, but I will pay the overhead of encryption if the server insists on it.
8+
// TODO(bacongobbler): not implemented
9+
Allow,
10+
// I don't care about encryption, but I wish to pay the overhead of encryption if the server supports it.
11+
// TODO(bacongobbler): not implemented
12+
Prefer,
13+
// I want my data to be encrypted, and I accept the overhead. I trust that the network will make sure I always connect to the server I want.
14+
// TODO(bacongobbler): not implemented
15+
Require,
16+
// I want my data encrypted, and I accept the overhead. I want to be sure that I connect to a server that I trust.
17+
// TODO(bacongobbler): not implemented
18+
VerifyCA,
19+
// I want my data encrypted, and I accept the overhead. I want to be sure that I connect to a server I trust, and that it's the one I specify.
20+
// TODO(bacongobbler): not implemented
21+
VerifyFull,
22+
}

tests/Bindle.IntegrationTests/Integration.cs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ public class Integration : IClassFixture<IntegrationFixture>
2323
[Fact]
2424
public async Task CanFetchInvoice()
2525
{
26-
var client = new BindleClient(DEMO_SERVER_URL);
26+
var client = new BindleClient($"host={DEMO_SERVER_URL}");
2727
var invoice = await client.GetInvoice("your/fancy/bindle/0.3.0");
2828
Assert.Equal("1.0.0", invoice.BindleVersion);
2929
Assert.Equal("your/fancy/bindle", invoice.Bindle.Name);
@@ -37,15 +37,15 @@ public async Task CanFetchInvoice()
3737
[Fact]
3838
public async Task CanQueryInvoices()
3939
{
40-
var client = new BindleClient(DEMO_SERVER_URL);
40+
var client = new BindleClient($"host={DEMO_SERVER_URL}");
4141
var matches = await client.QueryInvoices(queryString: "my");
4242
Assert.True(matches.Invoices.All(i => i.Bindle.Name.Contains("my")));
4343
}
4444

4545
[Fact]
4646
public async Task CanFetchYankedInvoices()
4747
{
48-
var client = new BindleClient(DEMO_SERVER_URL);
48+
var client = new BindleClient($"host={DEMO_SERVER_URL}");
4949
var invoice = await client.GetInvoice("yourbindle/0.1.1", IncludeYanked);
5050
Assert.Equal("1.0.0", invoice.BindleVersion);
5151
Assert.Equal("yourbindle", invoice.Bindle.Name);
@@ -55,7 +55,7 @@ public async Task CanFetchYankedInvoices()
5555
[Fact]
5656
public async Task CanCreateInvoices()
5757
{
58-
var client = new BindleClient(DEMO_SERVER_URL);
58+
var client = new BindleClient($"host={DEMO_SERVER_URL}");
5959
var invoice = new Invoice
6060
{
6161
BindleVersion = "1.0.0",
@@ -98,7 +98,7 @@ public async Task CanCreateInvoices()
9898
[TestPriority(10)]
9999
public async Task CanYankInvoice()
100100
{
101-
var client = new BindleClient(DEMO_SERVER_URL);
101+
var client = new BindleClient($"host={DEMO_SERVER_URL}");
102102
await client.YankInvoice("your/fancy/bindle/0.3.0");
103103
await Assert.ThrowsAsync<BindleYankedException>(async () =>
104104
{
@@ -111,15 +111,15 @@ await Assert.ThrowsAsync<BindleYankedException>(async () =>
111111
[Fact]
112112
public async Task CanFetchParcel()
113113
{
114-
var client = new BindleClient(DEMO_SERVER_URL);
114+
var client = new BindleClient($"host={DEMO_SERVER_URL}");
115115
var parcel = await client.GetParcel("mybindle/0.1.0", "f7f3b33707fb76d208f5839a40e770452dcf9f348bfd7faf2c524e0fa6710ed6");
116116
Assert.Equal("Fie on you Gary", await parcel.ReadAsStringAsync());
117117
}
118118

119119
[Fact]
120120
public async Task CanCreateParcel()
121121
{
122-
var client = new BindleClient(DEMO_SERVER_URL);
122+
var client = new BindleClient($"host={DEMO_SERVER_URL}");
123123
await client.CreateParcel("mybindle/0.1.0", "460d5965e4d1909e8c7a3748a414956b7038ab5fd79937c9fcb2b214e6b0160a", "The front fell off");
124124
var fetched = await client.GetParcel("mybindle/0.1.0", "460d5965e4d1909e8c7a3748a414956b7038ab5fd79937c9fcb2b214e6b0160a");
125125
Assert.Equal("The front fell off", await fetched.ReadAsStringAsync());
@@ -128,7 +128,7 @@ public async Task CanCreateParcel()
128128
[Fact]
129129
public async Task CanListMissingParcels()
130130
{
131-
var client = new BindleClient(DEMO_SERVER_URL);
131+
var client = new BindleClient($"host={DEMO_SERVER_URL}");
132132
var resp = await client.ListMissingParcels("mybindle/0.3.0");
133133
Assert.Contains(resp.Missing, (label) => label.Sha256 == "e1706ab0a39ac88094b6d54a3f5cdba41fe5a901");
134134
Assert.DoesNotContain(resp.Missing, (label) => label.Sha256 == "f7f3b33707fb76d208f5839a40e770452dcf9f348bfd7faf2c524e0fa6710ed6");

0 commit comments

Comments
 (0)