Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle the case where appid contains at least one upperletter #1233

Merged
merged 7 commits into from
Feb 16, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/Dapr.Client/DaprClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,8 @@ public static HttpClient CreateInvokeHttpClient(string appId = null, string dapr
var handler = new InvocationHandler()
{
InnerHandler = new HttpClientHandler(),
DaprApiToken = daprApiToken
DaprApiToken = daprApiToken,
DefaultAppId = appId,
};

if (daprEndpoint is string)
Expand Down Expand Up @@ -209,7 +210,7 @@ public abstract Task PublishEventAsync(
string topicName,
Dictionary<string, string> metadata,
CancellationToken cancellationToken = default);

/// <summary>
/// // Bulk Publishes multiple events to the specified topic.
/// </summary>
Expand Down
51 changes: 49 additions & 2 deletions src/Dapr.Client/InvocationHandler.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// ------------------------------------------------------------------------
// ------------------------------------------------------------------------
// Copyright 2021 The Dapr Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand All @@ -13,7 +13,9 @@

using System;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Net.Http;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;

Expand Down Expand Up @@ -82,6 +84,12 @@
}
}

/// <summary>
/// Gets or sets the default AppId used for service invocation
/// </summary>
/// <returns>The AppId used for service invocation</returns>
public string? DefaultAppId { get; set; }

// Internal for testing
internal string? DaprApiToken
{
Expand Down Expand Up @@ -128,17 +136,56 @@
return false;
}

string host;

// It is just for optimization, to not add some overhead time
if (this.DefaultAppId is not null && uri.Host.Equals(this.DefaultAppId, StringComparison.InvariantCultureIgnoreCase))
{
host = this.DefaultAppId;
}
else
{
host = this.GetOriginalHostFromUri(uri);
}

var builder = new UriBuilder(uri)
{
Scheme = this.parsedEndpoint.Scheme,
Host = this.parsedEndpoint.Host,
Port = this.parsedEndpoint.Port,
Path = $"/v1.0/invoke/{uri.Host}/method" + uri.AbsolutePath,
Path = $"/v1.0/invoke/{host}/method" + uri.AbsolutePath,
};

rewritten = builder.Uri;
return true;
}

/// <summary>
/// Get the original host (case sensitive) from the URI (thanks to uri.OriginalString)
/// Mandatory to get the original host if the app id has at least one uppercase and the app id has not been sent to the handler
/// </summary>
/// <param name="uri">The uri</param>
/// <returns>The original hostname from the uri</returns>
/// <exception cref="ArgumentException">The original string from the uri is invalid</exception>
private string GetOriginalHostFromUri(Uri uri)
{
ArgumentNullException.ThrowIfNull(uri);

// If there is no upper character inside the original string, we can directly return the uri host
if (!uri.OriginalString.Any(char.IsUpper))
{
return uri.Host;
}

Regex regex = new Regex("^.+?://(?<host>[^:/]+)", RegexOptions.Singleline | RegexOptions.Compiled);
Match match = regex.Match(uri.OriginalString);

if (!match.Success || !match.Groups.TryGetValue("host", out Group? host))
{
throw new ArgumentException("The original string for the uri is invalid.", nameof(uri));

Check warning on line 185 in src/Dapr.Client/InvocationHandler.cs

View check run for this annotation

Codecov / codecov/patch

src/Dapr.Client/InvocationHandler.cs#L185

Added line #L185 was not covered by tests
}

return host.Value;
}
}
}
2 changes: 1 addition & 1 deletion test/Dapr.Client.Test/DaprClientTest.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// ------------------------------------------------------------------------
// ------------------------------------------------------------------------
// Copyright 2021 The Dapr Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand Down
86 changes: 72 additions & 14 deletions test/Dapr.Client.Test/InvocationHandlerTests.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// ------------------------------------------------------------------------
// ------------------------------------------------------------------------
// Copyright 2021 The Dapr Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -30,8 +30,8 @@ public class InvocationHandlerTests
public void DaprEndpoint_InvalidScheme()
{
var handler = new InvocationHandler();
var ex = Assert.Throws<ArgumentException>(() =>
{
var ex = Assert.Throws<ArgumentException>(() =>
{
handler.DaprEndpoint = "ftp://localhost:3500";
});

Expand All @@ -43,7 +43,7 @@ public void DaprEndpoint_InvalidUri()
{
var handler = new InvocationHandler();
Assert.Throws<UriFormatException>(() =>
{
{
handler.DaprEndpoint = "";
});

Expand Down Expand Up @@ -79,30 +79,63 @@ public void TryRewriteUri_FailsForRelativeUris()
}

[Theory]
[InlineData("http://bank", "https://some.host:3499/v1.0/invoke/bank/method/")]
[InlineData("http://bank:3939", "https://some.host:3499/v1.0/invoke/bank/method/")]
[InlineData("http://app-id.with.dots", "https://some.host:3499/v1.0/invoke/app-id.with.dots/method/")]
[InlineData("http://bank:3939/", "https://some.host:3499/v1.0/invoke/bank/method/")]
[InlineData("http://bank:3939/some/path", "https://some.host:3499/v1.0/invoke/bank/method/some/path")]
[InlineData("http://bank:3939/some/path?q=test&p=another#fragment", "https://some.host:3499/v1.0/invoke/bank/method/some/path?q=test&p=another#fragment")]
public void TryRewriteUri_RewritesUriToDaprInvoke(string uri, string expected)
[InlineData(null, "http://bank", "https://some.host:3499/v1.0/invoke/bank/method/")]
[InlineData("bank", "http://bank", "https://some.host:3499/v1.0/invoke/bank/method/")]
[InlineData("Bank", "http://bank", "https://some.host:3499/v1.0/invoke/Bank/method/")]
[InlineData("invalid", "http://bank", "https://some.host:3499/v1.0/invoke/bank/method/")]
[InlineData(null, "http://Bank", "https://some.host:3499/v1.0/invoke/Bank/method/")]
[InlineData("Bank", "http://Bank", "https://some.host:3499/v1.0/invoke/Bank/method/")]
[InlineData("bank", "http://Bank", "https://some.host:3499/v1.0/invoke/bank/method/")]
[InlineData("invalid", "http://Bank", "https://some.host:3499/v1.0/invoke/Bank/method/")]
[InlineData(null, "http://bank:3939", "https://some.host:3499/v1.0/invoke/bank/method/")]
[InlineData("bank", "http://bank:3939", "https://some.host:3499/v1.0/invoke/bank/method/")]
[InlineData("invalid", "http://bank:3939", "https://some.host:3499/v1.0/invoke/bank/method/")]
[InlineData(null, "http://Bank:3939", "https://some.host:3499/v1.0/invoke/Bank/method/")]
[InlineData("Bank", "http://Bank:3939", "https://some.host:3499/v1.0/invoke/Bank/method/")]
[InlineData("invalid", "http://Bank:3939", "https://some.host:3499/v1.0/invoke/Bank/method/")]
[InlineData(null, "http://app-id.with.dots", "https://some.host:3499/v1.0/invoke/app-id.with.dots/method/")]
[InlineData("app-id.with.dots", "http://app-id.with.dots", "https://some.host:3499/v1.0/invoke/app-id.with.dots/method/")]
[InlineData("invalid", "http://app-id.with.dots", "https://some.host:3499/v1.0/invoke/app-id.with.dots/method/")]
[InlineData(null, "http://App-id.with.dots", "https://some.host:3499/v1.0/invoke/App-id.with.dots/method/")]
[InlineData("App-id.with.dots", "http://App-id.with.dots", "https://some.host:3499/v1.0/invoke/App-id.with.dots/method/")]
[InlineData("invalid", "http://App-id.with.dots", "https://some.host:3499/v1.0/invoke/App-id.with.dots/method/")]
[InlineData(null, "http://bank:3939/", "https://some.host:3499/v1.0/invoke/bank/method/")]
[InlineData("bank", "http://bank:3939/", "https://some.host:3499/v1.0/invoke/bank/method/")]
[InlineData("invalid", "http://bank:3939/", "https://some.host:3499/v1.0/invoke/bank/method/")]
[InlineData(null, "http://Bank:3939/", "https://some.host:3499/v1.0/invoke/Bank/method/")]
[InlineData("Bank", "http://Bank:3939/", "https://some.host:3499/v1.0/invoke/Bank/method/")]
[InlineData("invalid", "http://Bank:3939/", "https://some.host:3499/v1.0/invoke/Bank/method/")]
[InlineData(null, "http://bank:3939/some/path", "https://some.host:3499/v1.0/invoke/bank/method/some/path")]
[InlineData("bank", "http://bank:3939/some/path", "https://some.host:3499/v1.0/invoke/bank/method/some/path")]
[InlineData("invalid", "http://bank:3939/some/path", "https://some.host:3499/v1.0/invoke/bank/method/some/path")]
[InlineData(null, "http://Bank:3939/some/path", "https://some.host:3499/v1.0/invoke/Bank/method/some/path")]
[InlineData("Bank", "http://Bank:3939/some/path", "https://some.host:3499/v1.0/invoke/Bank/method/some/path")]
[InlineData("invalid", "http://Bank:3939/some/path", "https://some.host:3499/v1.0/invoke/Bank/method/some/path")]
[InlineData(null, "http://bank:3939/some/path?q=test&p=another#fragment", "https://some.host:3499/v1.0/invoke/bank/method/some/path?q=test&p=another#fragment")]
[InlineData("bank", "http://bank:3939/some/path?q=test&p=another#fragment", "https://some.host:3499/v1.0/invoke/bank/method/some/path?q=test&p=another#fragment")]
[InlineData("invalid", "http://bank:3939/some/path?q=test&p=another#fragment", "https://some.host:3499/v1.0/invoke/bank/method/some/path?q=test&p=another#fragment")]
[InlineData(null, "http://Bank:3939/some/path?q=test&p=another#fragment", "https://some.host:3499/v1.0/invoke/Bank/method/some/path?q=test&p=another#fragment")]
[InlineData("Bank", "http://Bank:3939/some/path?q=test&p=another#fragment", "https://some.host:3499/v1.0/invoke/Bank/method/some/path?q=test&p=another#fragment")]
[InlineData("invalid", "http://Bank:3939/some/path?q=test&p=another#fragment", "https://some.host:3499/v1.0/invoke/Bank/method/some/path?q=test&p=another#fragment")]
public void TryRewriteUri_WithNoAppId_RewritesUriToDaprInvoke(string? appId, string uri, string expected)
{
var handler = new InvocationHandler()
{
DaprEndpoint = "https://some.host:3499",
DefaultAppId = appId,
};

Assert.True(handler.TryRewriteUri(new Uri(uri), out var rewritten));
Assert.Equal(expected, rewritten!.OriginalString);
}

[Fact]
public async Task SendAsync_InvalidUri_ThrowsException()
public async Task SendAsync_InvalidNotSetUri_ThrowsException()
{
var handler = new InvocationHandler();
var ex = await Assert.ThrowsAsync<ArgumentException>(async () =>
{
await CallSendAsync(handler, new HttpRequestMessage(){ }); // No URI set
await CallSendAsync(handler, new HttpRequestMessage() { }); // No URI set
});

Assert.Contains("The request URI '' is not a valid Dapr service invocation destination.", ex.Message);
Expand Down Expand Up @@ -132,6 +165,31 @@ public async Task SendAsync_RewritesUri()
Assert.False(request.Headers.TryGetValues("dapr-api-token", out _));
}

[Fact]
public async Task SendAsync_RewritesUri_AndAppId()
{
var uri = "http://bank/accounts/17?";

var capture = new CaptureHandler();
var handler = new InvocationHandler()
{
InnerHandler = capture,

DaprEndpoint = "https://localhost:5000",
DaprApiToken = null,
DefaultAppId = "bank"
};

var request = new HttpRequestMessage(HttpMethod.Post, uri);
var response = await CallSendAsync(handler, request);

Assert.Equal("https://localhost:5000/v1.0/invoke/bank/method/accounts/17?", capture.RequestUri?.OriginalString);
Assert.Null(capture.DaprApiToken);

Assert.Equal(uri, request.RequestUri?.OriginalString);
Assert.False(request.Headers.TryGetValues("dapr-api-token", out _));
}

[Fact]
public async Task SendAsync_RewritesUri_AndAddsApiToken()
{
Expand Down Expand Up @@ -164,7 +222,7 @@ private async Task<HttpResponseMessage> CallSendAsync(InvocationHandler handler,

try
{
return await (Task<HttpResponseMessage>)method!.Invoke(handler, new object[]{ message, cancellationToken, })!;
return await (Task<HttpResponseMessage>)method!.Invoke(handler, new object[] { message, cancellationToken, })!;
}
catch (TargetInvocationException tie) // reflection always adds an extra layer of exceptions.
{
Expand Down
Loading