How to make GrpcChannel use HTTP/2 on MonoAndroid? - c#

I am trying to make request to my server by MagicOnion protocol (it uses transport from gRPC, but deffrent serialization protocol, message pack instead of protobuf).
An simple test client app, working under net5.0 is executing code like this:
AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true);
var address = $"http://{ServerUrl.Host}:5002";
using var channel = GrpcChannel.ForAddress(address);
var myServiceClient = MagicOnionClient.Create<IMyService>(channel);
var result = await myServiceClient.GetMyData();
...and recieves response succesfully. But if I try to execute the same code on Android app, I am seeing this exception message on server logs:
Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2.Http2ConnectionErrorException: HTTP/2 connection error (PROTOCOL_ERROR): Invalid HTTP/2 connection preface.
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2.Http2Connection.ParsePreface(ReadOnlySequence`1& buffer, SequencePosition& consumed, SequencePosition& examined)
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2.Http2Connection.TryReadPrefaceAsync()
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2.Http2Connection.ProcessRequestsAsync[TContext](IHttpApplication`1 application)
With logs and traffic dump I can see that the client on .Net 5 uses HTTP/2, but on Android - HTTP/1.1. As I can see, this is the only deifference between requests.
So, how can I make Android (API30, monoandroid10.0) client use HTTP/2?

The resolution is to use another gRPCC implementation lib - Grpc.Core. It provides GrpcChannel class wich is compatible with MagicOnion.
In my case, the library didn't work immediately, throwing the error about libgrpc_csharp_ext loading trouble. To solve this, you also have to add pacakge Grpc.Core.Xamarin.
The usage example:
var channel = new Grpc.Core.Channel(ServerUrl.Host, 5002, Grpc.Core.ChannelCredentials.Insecure);
var myServiceClient = MagicOnionClient.Create<IMyService>(channel);
var result = await myServiceClient.GetMyData();

Related

DotNetTor Cannot Make Requests

Recently I want to get a .json content from an onion site
(e.g. http://takedownmi4lfjhv.onion/root.json, this is an existing link)
So I installed the C# library DotNetTor (Nuget package is here)
I copy & pasted the DotNetTor QuickStart Example Project sample code and ran it with my C# project.
var requestUri = "http://icanhazip.com/";
// 1. Get real IP
using (var httpClient = new HttpClient())
{
var message = httpClient.GetAsync(requestUri).Result;
var content = message.Content.ReadAsStringAsync().Result;
Console.WriteLine($"Your real IP: \t\t{content}");
}
// 2. Get TOR IP
using (var httpClient = new HttpClient(new SocksPortHandler("127.0.0.1", socksPort: 9050)))
{
var message = httpClient.GetAsync(requestUri).Result; // GOT ERROR HERE // GOT ERROR HERE
var content = message.Content.ReadAsStringAsync().Result;
Console.WriteLine($"Your TOR IP: \t\t{content}");
// 3. Change TOR IP
var controlPortClient = new DotNetTor.ControlPort.Client("127.0.0.1", controlPort: 9051, password: "ILoveBitcoin21");
controlPortClient.ChangeCircuitAsync().Wait();
// 4. Get changed TOR IP
message = httpClient.GetAsync(requestUri).Result;
content = message.Content.ReadAsStringAsync().Result;
Console.WriteLine($"Your other TOR IP: \t{content}");
}
However, at the line I marked // GOT ERROR HERE, it poped up an error
System.AggregateException
Inner Exception 1:
TorException: Failed to send the request
Inner Exception 2:
SocketException: No connection could be made because the target machine actively refused it 127.0.0.1:9050
Why failed to send the request? How to resolve that?
The code above is simply a Socks Proxy handler and does not really contain anything about Tor.
new SocksPortHandler("127.0.0.1", socksPort: 9050)
See this link of code. This is nothing more than connecting via a SocksPortHandler 127.0.0.1:9050
But this port is not yet configured to Tor at all.
So before this, you should configure the port.
You can download the Tor Expert Bundle from here. This can help you configure that port for Tor.

Why doesn't Microsoft.Azure.ServiceBus's QueueClient drop back to HTTPS? [duplicate]

I have created a very simple console application that connects to Azure ServiceBus and sends one message. I tried the latest library from Microsoft (Microsoft.Azure.ServiceBus) but no matter what I do I just get this error:
No connection could be made because the target machine actively
refused it ErrorCode: ConnectionRefused
I have tried exactly the same connection string in Service Bus Explorer and it does work just fine. Moreover I connected without problems using the older library from Microsoft (WindowsAzure.ServiceBus).
var sender = new MessageSender("endpoint", "topicName");
sender.SendAsync(new Message(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject("test"))));
I tried with .NET Framework 4.6.2 and Core, same exception. I suspect there may be some differences in the default protocol that these libraries use, but I could not figure out that for sure.
P.S. Have tried the example from Microsoft docs but result is still the same exception
The old client supported ConnectivityMode using TCP, HTTP, HTTPS, and AutoDetect. ServiceBus Explorer is using AutoDetect, trying TCP first and then failing over to HTTPS, regardless of the TransportMode you were using (SBMP or AMQP).
With the new client this has changed. TransportMode now combines both options and offers Amqp (AMQP over TCP) or AmqpWebSockets (AMQP over WebSockets). There's no AutoDetect mode. You will have to create your clients and specify TransportType as AmqpWebSockets to bypass blocked TCP port 5671 and instead use port 443.
It seems that the documentation is lacking a lot on how to connect using HTTPS (Amqp over WebSockets) but after some help from Sean Feldman in the accepted answer I managed to connect. Here is the code that I used if someone is interested:
var tokenProvider = TokenProvider.CreateSharedAccessSignatureTokenProvider(
"RootManageSharedAccessKey", // SharedAccessKeyName
"SomeToken");
var sender = new MessageSender(
"sb://mydomain.servicebus.windows.net/",
"topicName",
tokenProvider,
TransportType.AmqpWebSockets);
Or a variant that let's you have the whole connection string in one piece
var builder = new ServiceBusConnectionStringBuilder("YouConnectionString");
var tokenProvider = TokenProvider.CreateSharedAccessSignatureTokenProvider(
builder.SasKeyName,
builder.SasKey);
var sender = new MessageSender(
builder.Endpoint,
"TopicName",
tokenProvider,
TransportType.AmqpWebSockets);
It is actually possible to use ConnectionString directly but then it has to be augmented to use the right protocol.
var sender = new MessageSender("TransportType=AmqpWebSockets;Endpoint=...", "TopicName")
Or the version that allows to embed EntityPath into the ConnectionString
var connectionBuilder = new ServiceBusConnectionStringBuilder("EntityPath=MyTopic;TransportType=AmqpWebSockets;Endpoint=...")
var sender = new MessageSender(connectionBuilder);
I was having the same issue but his worked for me
var clientOptions = new ServiceBusClientOptions();
clientOptions.TransportType = ServiceBusTransportType.AmqpWebSockets;
client = new ServiceBusClient(connectionString, clientOptions);
sender = client.CreateSender(topicName);
// create a batch
using ServiceBusMessageBatch messageBatch = await sender.CreateMessageBatchAsync();

Can amqplite act as a server to a WindowsAzure.ServiceBus client?

I want to do black-box testing of a messaging client library that uses the WindowsAzure.ServiceBus nuget package nuget package (docs) to send and receive messages to/from Microsoft ServiceBus queues.
My goal is to create integration tests for the messaging client library, but not communicate with an actual Microsoft Service Bus server.
Looking at the amqplite library, I was hoping that I could create an AMQP host using amqplite, and configure the messaging client library to communicate with this host, instead of an actual Microsoft Service Bus.
Here is my custom AMQP host, using amqplite:
public class LinkProcessor : ILinkProcessor
{
public void Process(AttachContext attachContext)
{
Console.WriteLine("LinkProcessor.Attach!");
}
}
var containerHost = new ContainerHost(new Uri("amqp://localhost:9876/"));
containerHost.RegisterLinkProcessor(new LinkProcessor());
containerHost.Open();
I register a ILinkProcessor on the containerHost so I can see on the console any connection attempts (based on my limited understanding of AMQP/AMQPlite this is what I believe a ILinkProcessor does).
The AMQP client below can connect, the server process outputs "LinkProcessor.Attach!" on the console.
async Task Main()
{
await SendMessage("q2", "hello world");
}
async Task SendMessage(string queue, string message)
{
var connection = await Connection.Factory.CreateAsync(new Address("amqp://localhost:9876/"));
var session = new Session(connection);
var msg = new Message(message);
var sender = new SenderLink(session, "sender-link", queue);
await sender.SendAsync(msg);
await sender.CloseAsync();
await session.CloseAsync();
await connection.CloseAsync();
}
Back to the messaging client library that I want to test:
The library is configured using a connection string, a working connection string looks like this (I assume the connection string format is one defined by WindowsAzure.ServiceBus):
Endpoint=sb://xxx/yyy;StsEndpoint=https://nnn:NNNN/xxx;RuntimePort=NNNN;ManagementPort=NNNN;OAuthUsername=xxx;OAuthPassword=yyy
I changed the config string to the following:
Endpoint=amqp://localhost:9876/q2
When I run the test client, the following happens:
The client throws an exception: You have tried to create a channel to a service that does not support .Net Framing.
I see no activity on the AMQPlite host end, i.e. no "LinkProcessor.Attach!" message on the console.
Question:
Is what I'm trying to accomplish possible? I.e. is there a way to configure a AMQPlite host so that it can accept connections from a WindowsAzure.ServiceBus client ?

Edit WCF message before it is sent to server

We recently had to upgrade our Win 8.1 store app to Win 10. Part of that change was modifying our NetTcpBindings to instead be BasicHttpBindings for file uploads since UWP does not currently support NetTcpBindings. Our issue is that when the client calls the UploadFileMethod on the proxy class, we intercept the message before it is sent to the server so we can apply headers that are used later as follows:
public async Task UploadFileAsync(RemoteFileInfo request)
{
using (new OperationContextScope(this.InnerChannel))
{
string nameSpace = #"http://tempuri.org";
OperationContext.Current.OutgoingMessageHeaders.Add(MessageHeader.CreateHeader("FileName", nameSpace, request.FileName));
OperationContext.Current.OutgoingMessageHeaders.Add(MessageHeader.CreateHeader("Length", nameSpace,
request.Length));
await Channel.UploadFileAsync(request);
}
}
This used to work fine when we were using NetTcpBinding but since we switched to BasicHttpBinding that code is now throwing an exception on the line:
await Channel.UploadFileAsync(request);
With the exception reading:
This message cannot support the operation because it has been written.
After reading up on this exception it appears that we cannot mess with the request object at all before it is sent to the server when using BasicHttpBinding. If that is the case, how can we add OutgoingMessageHeaders to the message using properties of the request itself?
Edit: The proxy class is created as follows:
var imageProxy = new RTMImageServiceProxy(globalContext.Win10UploadBinding,
globalContext.ImageEndpointAddress);
Where Win10UploadBinding is configured as so:
BasicHttpBinding win10BasicBinding = new BasicHttpBinding();
win10BasicBinding.Security.Mode = BasicHttpSecurityMode.None;
win10BasicBinding.TransferMode = TransferMode.Streamed;
win10BasicBinding.SendTimeout = new TimeSpan(0, 0, 2, 0);
win10BasicBinding.MaxReceivedMessageSize = 2147483647;
this.win10UploadBinding = win10BasicBinding;
and globalContext is just a static class I used to store commonly-used variables.
Apparently it turns out that once written cannot be altered so create a copy with adjusted headers. Equivalent issue was brought up here.
Anyway, I encourage you to create custom message inspector: class deriving IClientMessageInspector, as far as client is concerned. It provides separation between method being invoced and headers being adjusted.

Scaling out SignalR with SQL Server - is it possible to add messages from an outside service?

I am using SQL Server as a backplane for scaling out my SignalR.
In my scenario there are some services which run on other servers and I need to integrate their working status into my SignalR hub.
I tried as a test running a simple Console application with the SQL server scaleout and publish a message like this:
var config = new SqlScaleoutConfiguration(connectionString);
GlobalHost.DependencyResolver.UseSqlServer(connectionString);
var messageBus = new SqlMessageBus(GlobalHost.DependencyResolver, config);
var message = new Message("TransactionHub", "RegisterClient","{userId:1}");
messageBus.Publish(message);
Can I use the SQLScaleout like this somehow?
If not, is there some other way to do what I am trying to do?
Edit:
I've done as halter73 suggested and it works ok, you have to notice that if you activate a client side function, the parameter you send has to match, ie if the object is "Namespace.ClassName" then you should send an object of the same type exactly.
You should try using GlobalHost.ConnectionManager.GetHubContext instead of publishing to the bus directly. Your code would look something like this:
var context = GlobalHost.ConnectionManager.GetHubContext<TransactionHub>();
context.Clients.All.registerClient(new { userId = 1 });

Categories

Resources