我正在尝试访问 Lighttpd 服务器上的资源,该服务器强制完整的请求 URI 与授权请求标头中的 URI 匹配。这在RFC 7616中指定
认证服务器必须确保“uri”参数指定的资源与请求行中指定的资源相同;如果不是,服务器应该返回 400 Bad Request 错误。(由于这可能是攻击的征兆,服务器
实现者可能需要考虑记录此类错误。)
在此字段中从请求 URL 复制信息的目的是
处理中间代理可能更改
客户端请求的可能性-线。这个改变的(但可能在语义上
等价的)请求不会产生
与客户端计算的相同的摘要。
我正在使用Flurl 库(v1.4),它只是 HttpClient 的一个包装器。但是,HttpClientHandler 来自.Net。
为什么它使用基本 URI 而不是完整的 URI?它是一个错误吗?如何让它使用完整的 URI?
我想在管道中添加另一个HttpMessageHandler并使用完整的 URI 修改Authentication标头,但是HttpClientHandler不允许您设置InnerHandler。
完整的Uri 应该是:http://base/resource.cgi?my=1¶ms=2
但这是请求标头中出现的内容:
授权: Digest username="user",realm="serve",nonce="5b911545:eaf4352d2113e5e4b1ca253bd70fd90a", uri="/base/resource.cgi" ,cnonce="bf7cf40f1289bc10bd07e8bf4784159c",nc=000,00001,qop="auth"响应="cf3c731ec93f7e5928f19f880f8325ab"
这会导致 400 Bad Request响应。
我的 Flurl 代码:
HttpClient 工厂
/// <summary>
/// Custom factory to generate HttpClient and handler to use digest authentication and a continuous stream
/// </summary>
private class DigestHttpFactory : Flurl.Http.Configuration.DefaultHttpClientFactory
{
private CredentialCache CredCache { get; set; }
public DigestHttpFactory(string url, string username, string password) : base()
{
Url = url;
CredCache = new CredentialCache
{
{ new Uri(Url), "Digest", new NetworkCredential(username, password) }
};
}
private string Url { get; set; }
public override HttpClient CreateHttpClient(HttpMessageHandler handler)
{
var client = base.CreateHttpClient(handler);
client.Timeout = TimeSpan.FromMilliseconds(Timeout.Infinite); // To keep stream open indefinietly
return client;
}
public override HttpMessageHandler CreateMessageHandler()
{
var handler = new HttpClientHandler
{
Credentials = CredCache.GetCredential(new Uri(Url), "Digest")
};
return handler;
}
}
请求代码
public class MyConnection
{
public string BaseUrl => "http://base/resource.cgi";
public async Task ConnectAsync(CancellationToken cancellationToken = default(CancellationToken))
{
ConnectionCancellation = new CancellationTokenSource();
var url = BaseUrl
.SetQueryParam("my", 1)
.SetQueryParam("params", 2)
FlurlHttp.ConfigureClient(url, client =>
{
client.Configure(settings =>
{
settings.HttpClientFactory = new DigestHttpFactory(url, Username, Password);
});
});
try
{
using (var getResponse = url.GetAsync(cancellationToken, HttpCompletionOption.ResponseHeadersRead))
{
var responseMessage = await getResponse;
using (var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(ConnectionCancellation.Token, cancellationToken))
using (var stream = await responseMessage.Content.ReadAsStreamAsync())
await Process(stream, linkedCts.Token);
}
}
catch (OperationCanceledException ex)
{
throw ex;
}
catch (Exception ex)
{
throw ex;
}
}
}