SignalR HubConnectionBuilder header problem - c#

I had SignalR working with a HubConnection prior to .NET Core 6. Now the same code is generating an error when you try to start the connection that the builder is creating:
New-line characters are not allowed in header values.
I also think my other version was working with HTTP and this new project is using HTTPS. The Hub is up and working fine within the website itself. But my external app is having trouble establishing a connection using the following code:
var notifyConnection = new HubConnectionBuilder()
.WithUrl(baseUrl + "/notify", options =>
{
options.AccessTokenProvider = async () =>
{
var stringData = JsonConvert.SerializeObject(new { username = user, password = pass });
var content = new StringContent(stringData);
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
var response = await httpClient.PostAsync(baseUrl + "/api/token", content);
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
};
})
.WithAutomaticReconnect()
.Build();
notifyConnection.Closed += async (error) =>
{
if (Debug) _logger.LogInformation("Hub connection closed: " + error.Message);
await Task.Delay(new Random().Next(0, 5) * 1000);
await notifyConnection.StartAsync();
};
try
{
var task = notifyConnection.StartAsync();
task.Wait();
}
catch (Exception ex)
{
if (Debug) _logger.LogInformation("Hub connection start error: " + ex.Message);
}
The exception happens when the connection is attempted to be started asynchronously. Anyone run into a similar issue? I have verified that stringData does not have new-line characters. SignalR client is 6.0.6. I am stumped. Any help is greatly appreciated.
UPDATE:
I had trouble with the token generator. I was able to get past this error. Now it is throwing the following error:
The SSL connection could not be established, see inner exception.
The inner exception says that the certificate is invalid. That is because the certificate is for an external connection rather than the server's IP address. The HttpClient is already ignoring certificate errors with the following code:
var handler = new HttpClientHandler();
handler.ClientCertificateOptions = ClientCertificateOption.Manual;
handler.ServerCertificateCustomValidationCallback =
(httpRequestMessage, cert, cetChain, policyErrors) =>
{
return true;
};
httpClient = new HttpClient(handler);
UPDATE 2:
It is now working. I had to also tell the web sockets to ignore certificate errors. Found the solution in another question:
https://stackoverflow.com/a/63973431/2022236

Related

Unable to create a sink via the cloud logging api when on the production server

in development, I have working .net core c# code that checks for a sink and creates it if not present:
var builder = new ConfigServiceV2ClientBuilder { JsonCredentials = key, QuotaProject = billingProjectId };
var sinkClient = await builder.BuildAsync(cancellationToken);
LogSink? sink;
try
{
sink = await sinkClient.GetSinkAsync(new LogSinkName(clientProjectId, sinkName), cancellationToken);
}
catch (Exception ex)
{
if (ex.Message.Contains("NotFound"))
{
sink = new LogSink
{
Name = "sink-test",
Disabled = true,
Description = "A sink",
Destination = $"bigquery.googleapis.com/projects/{clientProjectId}/datasets/bq_logs",
Filter = #"
protoPayload.methodName=""jobservice.jobcompleted""
resource.type=""bigquery_resource""
protoPayload.serviceData.jobCompletedEvent.job.jobStatistics.totalBilledBytes!=""0""
"
};
sink = await sinkClient.CreateSinkAsync(ProjectName.FromProject(clientProjectId), sink); // fails on production server
}
}
However, when I run it in production, the get works, but the create fails:
GetSinkAsync Status(StatusCode="NotFound", Detail="Sink sink-test does not exist")
CreateSink Failure after 10028.3424ms: Status(StatusCode="Unavailable", Detail="Error starting gRPC call. HttpRequestException: The connection timed out from inactivity. (logging.googleapis.com:443) QuicException: The connection timed out from inactivity.", DebugException="System.Net.Http.HttpRequestException: The connection timed out from inactivity. (logging.googleapis.com:443)
We've tested for several days over several projects with no joy.

gRPC Net6 Client load balancing & SRV record DNS config

My service is hosted under IIS (https) and JWT is used for authentication/authorization.
The IT person configured an SRV record DNS but I'm not sure he did it correctly.
I'm getting the following error:
Status(StatusCode="Unavailable", Detail="Error getting DNS hosts for address '_http._tcp.grpcpocclientsideloadbalancing.400dev.rsvzinasti.be'. SocketException: The requested name is valid, but no data of the requested type was found.
The DNS config: here
Using nslookup returns the correct addresses: here
My code looks like this:
var credentials = CallCredentials.FromInterceptor((c, m) =>
{
m.Add("Authorization", "Bearer " + GetStsClaim());
return Task.CompletedTask;
});
var clientHandler = new SocketsHttpHandler();
clientHandler.UseProxy = false;
var handler = new SubdirectoryHandler(clientHandler, "/TestGrpc");
var channel = GrpcChannel.ForAddress("dns:///_http._tcp.grpcpocclientsideloadbalancing.400dev.rsvzinasti.be",
new GrpcChannelOptions
{
HttpHandler = handler,
Credentials = ChannelCredentials.Create(ChannelCredentials.SecureSsl, credentials),
ServiceConfig = new ServiceConfig
{
LoadBalancingConfigs = { new RoundRobinConfig() }
}
});
var client = new Greeter.GreeterClient(channel);
var request = new HelloRequest();
request.Name = "test";
var reply = await client.SayHelloAsync(request);
Calling Dns.GetHostAddresses("dns://_http._tcp.grpcpocclientsideloadbalancing.400dev.rsvzinasti.be") throws
'No such host is known.'
Could you help me find what is wrong with my config ?
Thanks in advance

HttpClient.GetByteArrayAsync(…) “deadlock” when there is no internet connection in .NET standard library calling from UWP

I am developing a UWP application for a document management system. I am trying to open documents from my application. When I click the open document, It is going to download the document and then open in the default application. But the problem is document is not downloaded if the internet is a disconnect in the middle of the process. It means when httpClient is already called. My code is as following
public async Task<DownloadFileDetail> DownloadFileAsync(int dmsFileId)
{
if (dmsFileId <= 0)
{
throw new ArgumentException("Invalid DMS File Id");
}
try
{
return await Task.Run(async () =>
{
DownloadFileDetail fileDetail = new DownloadFileDetail()
{
DocId = dmsFileId
};
string apiUrl = $"files/download/latest/{dmsFileId}";
HttpClient httpClient = new HttpClient();
httpClient.BaseAddress = new Uri(BaseApiUrl);
httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {SessionStore.Instance.AuthToken}");
var response = await httpClient.GetByteArrayAsync(apiUrl); --> gone deadlock
fileDetail.Content = response;
return fileDetail;
});
}
catch (Exception ex)
{
}
return new DownloadFileDetail()
{
DocId = dmsFileId
};
}
Download process called as UWP->.NET Standard Library (holds above code). It will be great if someone helps me to solve the problem.
Thanks
ss
Update:
The above code is working on my laptop and not working on any other laptop in dev environment
when there is no internet connection in .NET standar library calling from UWP
If the deadlock only occurs in no internet connection environment, you could check if internet is available before sending http request. Please check this NetworkHelper.
if (NetworkHelper.Instance.ConnectionInformation.IsInternetAvailable)
{
// sending the request.
}
First, remove the Task.Run(async () => ...) call:
try
{
DownloadFileDetail fileDetail = new DownloadFileDetail()
{
DocId = dmsFileId
};
string apiUrl = $"files/download/latest/{dmsFileId}";
HttpClient httpClient = new HttpClient();
httpClient.BaseAddress = new Uri(BaseApiUrl);
httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {SessionStore.Instance.AuthToken}");
var response = await httpClient.GetByteArrayAsync(apiUrl); --> gone deadlock
fileDetail.Content = response;
return fileDetail;
}

Certificate not being sent with c# http request

My question relates to the following posts:
HttpClient does not send client certificate on Windows using .NET Core
WebApi HttpClient not sending client certificate
My env:
Mac osx ( 10.15.6 )
Visual studio code for Mac ( 8.3.2 )
Goal:
I want to use self-signed certificates ( as an experiment ), to test TLS with c# and a nginx server. I can use this curl command to confirm that the crt and key allow me to access a resource:
curl --cert client.crt --key client.key -k https://localhost:443
I read the p12 keystore using this code
public X509Certificate2 readCertificates(String path, String password)
{
return new X509Certificate2(File.ReadAllBytes(path), password);
}
I use this code to make a Https request. The loaded certificate has a subject, identifier, and private key that I expect. This confirms the above step.
public async System.Threading.Tasks.Task validateRequest(X509Certificate2 certificate)
{
try
{
WebRequestHandler handler = new WebRequestHandler();
handler.ClientCertificates.Add(certificate);
handler.SslProtocols = SslProtocols.Tls12 | SslProtocols.Tls11 | SslProtocols.Tls;
ServicePointManager.ServerCertificateValidationCallback = (a, b, c, d) => { return true; };
handler.ClientCertificateOptions = ClientCertificateOption.Manual;
HttpClient client = new HttpClient(handler);
HttpRequestMessage httpContent = new HttpRequestMessage(HttpMethod.Get, new Uri("https://localhost:443"));
HttpResponseMessage response = await client.SendAsync(httpContent);
string resultContent = response.Content.ReadAsStringAsync().Result;
if (response.IsSuccessStatusCode)
{
Console.WriteLine("## we authenticated...");
}
else {
Console.WriteLine("### we have an issue");
}
response.Dispose();
client.Dispose();
httpContent.Dispose();
}
catch (Exception e) {
Console.WriteLine("##### error:: " + e);
}
}
After executing this code, my ngix server states:
client sent no required SSL certificate while reading client request header
The c# side evalutes $resultContent$ to:
400 No required SSL certificate was sent
If I replace the below lines, I get a different error:
// take this line
ServicePointManager.ServerCertificateValidationCallback = (a, b, c, d) => { return true; };
// replace it with the below
handler.ServerCertificateValidationCallback = (a, b, c, d) => { return true; };
I can observe an error on the ngix server with:
peer closed connection in SSL handshake while SSL handshaking
I can observe this error on the c# side:
Mono.Security.Interface.TlsException: CertificateUnknown
In reading the aforementioned stack posts, I was under the impression that I could read and ignore self-signed certificates by modifying ServerCertificateValidationCallback. This experiment is leading me to believe otherwise. Am I missing a key step in using self-signed certificates in making a https request?

c# webclient NetworkCredentials in Nodejs

I have a c# .net Client with this Code:
using(WebClient client = new WebClient())
{
string serialisedData = "";
serialisedData = JsonConvert.SerializeObject(myData);
client.Credentials = new NetworkCredential(config.UserData.Username, config.UserData.Password);
byte[] responsebyte = client.UploadData(config.ServerAddress, System.Text.Encoding.UTF8.GetBytes(serialisedData));
}
That Client sends data to my nodejs Server.
Nodejs Code:
var http = require('http');
var _server = http.createServer(_listener);
_server.listen(1234);
console.log( 'started' );
function _listener(req, res) {
let data = []
req.on('data', chunk => {
data.push(chunk)
})
req.on('end', () => {
data = Buffer.concat(data);
var dataString = new Buffer.from(data).toString("utf-8");
const data = JSON.parse(dataString);
// data has all the data from the c# object "myData"
res.write('response')
res.end()
})
}
But how can I access the credentials of this connection?
This is how I can Access the credentials in c#:
HttpListener listener = new HttpListener();
listener.Prefixes.Add($"https://+:{Config.Port}/");
listener.AuthenticationSchemes = AuthenticationSchemes.Basic;
listener.Start();
for (; ; )
{
Console.WriteLine("Listening...");
IAsyncResult result = listener.BeginGetContext(new AsyncCallback(DoWork), listener);
result.AsyncWaitHandle.WaitOne();
result = null;
}
private void DoWork(IAsyncResult asyncResult)
{
HttpListener listener = (HttpListener)asyncResult.AsyncState;
HttpListenerContext context = listener.EndGetContext(asyncResult);
HttpListenerBasicIdentity identity = (HttpListenerBasicIdentity)context.User.Identity;
// identity has the credentials
}
Edit: I cant change the c# Code anymore. So only nodejs solutions are needed
Edit2: The headers also have no Auth or Authentification property…
Edit3: I cant even find if other location exists except the header for credentials/authentification. But this must be possible right? I mean c# can somehow read this stuff from somewhere…
Any Idea what I can try to find the credentials?
To make your C# client to send its networkCredentials as HTTP Basic Authentication to your Nodejs server; the server should return a response whose header contains a HTTP 401 Unauthorized status and a WWW-Authenticate field if the request does not contain the Authorization header. This will cause your C# client retry the POST with Authorization header.
This process it is called Authentication challenge in case you want to search for more info.
There are serveral packages that does that for you; like http-auth or you can code it by hand (it is not very hard as it is just a matter of checking the existence of the Authorization header in the request and, if there is none or incorrect credentials, make a 401 response with a WWW-Authenticate field)
i.e. from the top of my head:
var http = require('http');
var _server = http.createServer(listener);
_server.listen(1234);
console.log('started');
function listener(req, res) {
if (!req.headers.authorization) {
res.statusCode = 401;
res.statusMessage = 'Unauthorized';
res.setHeader('WWW-Authenticate', 'Basic');
res.end();
}
}

Categories

Resources