diff --git a/.vscode/settings.json b/.vscode/settings.json
deleted file mode 100644
index 663d2eb0..00000000
--- a/.vscode/settings.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "workbench.colorCustomizations": {},
- "python.pythonPath": "C:\\Users\\shbanerj\\.conda\\envs\\py-azurefunctions\\python.exe"
-}
\ No newline at end of file
diff --git a/investigations/.gitignore b/investigations/.gitignore
new file mode 100644
index 00000000..c9021b75
--- /dev/null
+++ b/investigations/.gitignore
@@ -0,0 +1,4 @@
+# Visual Studio - C#
+**/obj
+**/bin
+*.user
diff --git a/investigations/DotNetGrpc/DotNetGrpc.sln b/investigations/DotNetGrpc/DotNetGrpc.sln
new file mode 100644
index 00000000..2ea59cbb
--- /dev/null
+++ b/investigations/DotNetGrpc/DotNetGrpc.sln
@@ -0,0 +1,25 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.29424.173
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotNetGrpcService", "DotNetGrpcService\DotNetGrpcService.csproj", "{51C71ABB-1D35-4543-898C-0D34EFF832FA}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {51C71ABB-1D35-4543-898C-0D34EFF832FA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {51C71ABB-1D35-4543-898C-0D34EFF832FA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {51C71ABB-1D35-4543-898C-0D34EFF832FA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {51C71ABB-1D35-4543-898C-0D34EFF832FA}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {BB4EB881-58FA-4A49-838A-0F0824C47920}
+ EndGlobalSection
+EndGlobal
diff --git a/investigations/DotNetGrpcClient/DotNetGrpcClient.csproj b/investigations/DotNetGrpc/DotNetGrpcClient/DotNetGrpcClient.csproj
similarity index 99%
rename from investigations/DotNetGrpcClient/DotNetGrpcClient.csproj
rename to investigations/DotNetGrpc/DotNetGrpcClient/DotNetGrpcClient.csproj
index b9261141..2cea1af5 100644
--- a/investigations/DotNetGrpcClient/DotNetGrpcClient.csproj
+++ b/investigations/DotNetGrpc/DotNetGrpcClient/DotNetGrpcClient.csproj
@@ -3,8 +3,8 @@
Exe
netcoreapp3.0
+
-
@@ -16,4 +16,5 @@
+
diff --git a/investigations/DotNetGrpcClient/Program.cs b/investigations/DotNetGrpc/DotNetGrpcClient/Program.cs
similarity index 84%
rename from investigations/DotNetGrpcClient/Program.cs
rename to investigations/DotNetGrpc/DotNetGrpcClient/Program.cs
index a8d9e08c..0bfa033b 100644
--- a/investigations/DotNetGrpcClient/Program.cs
+++ b/investigations/DotNetGrpc/DotNetGrpcClient/Program.cs
@@ -4,9 +4,9 @@
using DotNetGrpcService;
using Grpc.Net.Client;
-namespace GrpcGreeterClient
+namespace DotNetGrpcClient
{
- class Program
+ class Program
{
static async Task Main(string[] args)
{
@@ -18,9 +18,6 @@ static async Task Main(string[] args)
Console.WriteLine("Calling Python Endpoint...");
await CallEndpoint("http://localhost:50051", true);
-
- Console.WriteLine("Press any key to exit...");
- Console.ReadKey();
}
static GrpcChannel GetChannel(string grpcEndpointAddress, bool isInsecure = false)
@@ -35,8 +32,8 @@ static async Task CallEndpoint(string grpcEndpointAddress, bool isInsecure = fal
var client = new Greeter.GreeterClient(channel);
var reply = await client.SayHelloAsync(
- new HelloRequest { Name = "GreeterClient" });
- Console.WriteLine("Greeting: " + reply.Message);
+ new HelloRequest { Name = "GreeterClient-DotNet" });
+ Console.WriteLine($"Response: {reply.Message}");
}
}
-}
\ No newline at end of file
+}
diff --git a/investigations/DotNetGrpcService/Protos/greet.proto b/investigations/DotNetGrpc/DotNetGrpcClient/Protos/greet.proto
similarity index 99%
rename from investigations/DotNetGrpcService/Protos/greet.proto
rename to investigations/DotNetGrpc/DotNetGrpcClient/Protos/greet.proto
index 62060ee9..55bc6ca8 100644
--- a/investigations/DotNetGrpcService/Protos/greet.proto
+++ b/investigations/DotNetGrpc/DotNetGrpcClient/Protos/greet.proto
@@ -18,4 +18,4 @@ message HelloRequest {
// The response message containing the greetings.
message HelloReply {
string message = 1;
-}
+}
\ No newline at end of file
diff --git a/investigations/DotNetGrpc/DotNetGrpcService/DotNetGrpcService.csproj b/investigations/DotNetGrpc/DotNetGrpcService/DotNetGrpcService.csproj
new file mode 100644
index 00000000..b7fb0d20
--- /dev/null
+++ b/investigations/DotNetGrpc/DotNetGrpcService/DotNetGrpcService.csproj
@@ -0,0 +1,15 @@
+
+
+
+ netcoreapp3.0
+
+
+
+
+
+
+
+
+
+
+
diff --git a/investigations/DotNetGrpcService/Program.cs b/investigations/DotNetGrpc/DotNetGrpcService/Program.cs
similarity index 100%
rename from investigations/DotNetGrpcService/Program.cs
rename to investigations/DotNetGrpc/DotNetGrpcService/Program.cs
diff --git a/investigations/DotNetGrpcClient/Protos/greet.proto b/investigations/DotNetGrpc/DotNetGrpcService/Protos/greet.proto
similarity index 100%
rename from investigations/DotNetGrpcClient/Protos/greet.proto
rename to investigations/DotNetGrpc/DotNetGrpcService/Protos/greet.proto
diff --git a/investigations/DotNetGrpcService/Services/GreeterService.cs b/investigations/DotNetGrpc/DotNetGrpcService/Services/GreeterService.cs
similarity index 89%
rename from investigations/DotNetGrpcService/Services/GreeterService.cs
rename to investigations/DotNetGrpc/DotNetGrpcService/Services/GreeterService.cs
index 7d7fc516..79f28fea 100644
--- a/investigations/DotNetGrpcService/Services/GreeterService.cs
+++ b/investigations/DotNetGrpc/DotNetGrpcService/Services/GreeterService.cs
@@ -19,7 +19,7 @@ public override Task SayHello(HelloRequest request, ServerCallContex
{
return Task.FromResult(new HelloReply
{
- Message = "Hello " + request.Name
+ Message = $"Hello {request.Name} from .NET gRPC Server"
});
}
}
diff --git a/investigations/DotNetGrpcService/Startup.cs b/investigations/DotNetGrpc/DotNetGrpcService/Startup.cs
similarity index 98%
rename from investigations/DotNetGrpcService/Startup.cs
rename to investigations/DotNetGrpc/DotNetGrpcService/Startup.cs
index d8bf433a..95646a1c 100644
--- a/investigations/DotNetGrpcService/Startup.cs
+++ b/investigations/DotNetGrpc/DotNetGrpcService/Startup.cs
@@ -31,7 +31,6 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
app.UseEndpoints(endpoints =>
{
-
endpoints.MapGrpcService();
endpoints.MapGet("/", async context =>
diff --git a/investigations/DotNetGrpcService/DotNetGrpcService.csproj b/investigations/DotNetGrpcService/DotNetGrpcService.csproj
deleted file mode 100644
index 6dbc3bbd..00000000
--- a/investigations/DotNetGrpcService/DotNetGrpcService.csproj
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
- netcoreapp3.0
-
-
-
-
-
-
-
-
-
-
-
- all
- runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
-
-
diff --git a/investigations/DotNetGrpcService/DotNetGrpcService.sln b/investigations/DotNetGrpcService/DotNetGrpcService.sln
deleted file mode 100644
index e2331799..00000000
--- a/investigations/DotNetGrpcService/DotNetGrpcService.sln
+++ /dev/null
@@ -1,31 +0,0 @@
-
-Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio Version 16
-VisualStudioVersion = 16.0.29316.153
-MinimumVisualStudioVersion = 10.0.40219.1
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNetGrpcService", "DotNetGrpcService.csproj", "{6D265B48-69E0-4511-9E77-A8B6501AAB48}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotNetGrpcClient", "..\DotNetGrpcClient\DotNetGrpcClient.csproj", "{3E1D7598-32EE-43DC-B9E6-22C3568742FF}"
-EndProject
-Global
- GlobalSection(SolutionConfigurationPlatforms) = preSolution
- Debug|Any CPU = Debug|Any CPU
- Release|Any CPU = Release|Any CPU
- EndGlobalSection
- GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {6D265B48-69E0-4511-9E77-A8B6501AAB48}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {6D265B48-69E0-4511-9E77-A8B6501AAB48}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {6D265B48-69E0-4511-9E77-A8B6501AAB48}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {6D265B48-69E0-4511-9E77-A8B6501AAB48}.Release|Any CPU.Build.0 = Release|Any CPU
- {3E1D7598-32EE-43DC-B9E6-22C3568742FF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {3E1D7598-32EE-43DC-B9E6-22C3568742FF}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {3E1D7598-32EE-43DC-B9E6-22C3568742FF}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {3E1D7598-32EE-43DC-B9E6-22C3568742FF}.Release|Any CPU.Build.0 = Release|Any CPU
- EndGlobalSection
- GlobalSection(SolutionProperties) = preSolution
- HideSolutionNode = FALSE
- EndGlobalSection
- GlobalSection(ExtensibilityGlobals) = postSolution
- SolutionGuid = {75B6A36C-EF88-4E3A-83FD-5BD41A270F0C}
- EndGlobalSection
-EndGlobal
diff --git a/investigations/PythonGrpc/greet_client.py b/investigations/PythonGrpc/greet_client.py
index bc4780dd..fe7289c5 100644
--- a/investigations/PythonGrpc/greet_client.py
+++ b/investigations/PythonGrpc/greet_client.py
@@ -4,23 +4,25 @@
import greet_pb2
-
def getChannel(grpcEndpointAddress):
return grpc.insecure_channel(grpcEndpointAddress)
+
def sendGreetings(grpcChannel):
stub = greet_pb2_grpc.GreeterStub(grpcChannel)
- reply = stub.SayHello(greet_pb2.HelloRequest(name='Shashank'))
- return reply
+ reply = stub.SayHello(greet_pb2.HelloRequest(name='GreeterClient-Python'))
+ return reply.message
-def main():
- print ("Send greetings to C# Server...")
+
+def main():
+ print("Calling C# Endpoint...")
channel = getChannel("localhost:5000")
- print(sendGreetings(channel))
+ print(f'Response: {sendGreetings(channel)}')
- print ("Send greetings to Python Server...")
+ print("Calling Python Endpoint...")
channel = getChannel("localhost:50051")
- print(sendGreetings(channel))
+ print(f'Response: {sendGreetings(channel)}')
+
-if __name__ == '__main__':
+if __name__ == '__main__':
main()
diff --git a/investigations/PythonGrpc/greet_server.py b/investigations/PythonGrpc/greet_server.py
index 825ea02f..706858d3 100644
--- a/investigations/PythonGrpc/greet_server.py
+++ b/investigations/PythonGrpc/greet_server.py
@@ -4,12 +4,14 @@
import greet_pb2_grpc
import greet_pb2
+
class GreetServer (greet_pb2_grpc.GreeterServicer):
-
+
def SayHello(self, request, context):
print(f'Received client request from {request.name}')
- return greet_pb2.HelloReply(message='Hello ' + request.name)
-
+ return greet_pb2.HelloReply(message=f'Hello {request.name} from Python gRPC Server')
+
+
def serve():
server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
greet_pb2_grpc.add_GreeterServicer_to_server(GreetServer(), server)
@@ -20,5 +22,5 @@ def serve():
server.wait_for_termination()
-if __name__ == '__main__':
- serve()
\ No newline at end of file
+if __name__ == '__main__':
+ serve()
diff --git a/investigations/PythonGrpc/requirements.txt b/investigations/PythonGrpc/requirements.txt
new file mode 100644
index 00000000..daa9182c
--- /dev/null
+++ b/investigations/PythonGrpc/requirements.txt
@@ -0,0 +1,2 @@
+flake8
+grpcio-tools
\ No newline at end of file
diff --git a/investigations/README.md b/investigations/README.md
new file mode 100644
index 00000000..e70abfde
--- /dev/null
+++ b/investigations/README.md
@@ -0,0 +1,207 @@
+# gRPC Investigations
+
+## Introduction
+
+This folder contains code which shows communication between cross language applications - C# and Python. It is primarly meant for upskilling and understanding the basics of gRPC communication between two different language plaforms and identify tools to support development.
+
+## Tools used to build this repository
+
+This code was built on a Windows 10 PC. But the tooling can be considered cross platform.
+
+- Microsoft Visual Studio 2019
+- Microsoft Visual Studio Code
+- Python 3.6.9 on a virtual environemnt enabled via Anaconda
+
+## Structure of the code
+
+The repository consists of three folders:
+
+- **DotNetGrpc\DotNetGrpcService**: Contains the source code for the implementation of the gRPC server in C# on the .NET Core platform.
+
+- **DotNetGrpc\DotNetGrpcClient**: Contains the source code for the gRPC client implemented in C# on the .NET Core Platform.
+
+- **PythonGrpc**: Contains the source code for both the gRPC server and client and server using Python 3.6.
+
+## Client/Server Communication
+
+### gRPC Contract
+
+The implementation on both the platform conform the simplest contract, defined by the default .NET template for gRPC Server.
+
+The contract is defined in the ProtoBuf file ```greet.proto``` available in all the folders.
+
+The contract is defined as follows:
+
+- A ```Greeter``` service with a ```SayHello``` method.
+ - The ```SayHello``` method accepts a object of type - ```HelloRequest``` and responds with object of the type - ```HelloReply```.
+ - The object of type - ```HelloRequest``` contains a single field of ```string``` type called ```name```.
+ - The object of the type - ```HelloReply``` contains a single field of ```string``` type called ```message```.
+
+The code snippet from the protobuf file is shown as below:
+
+ ``` protobuf
+service Greeter {
+ // Sends a greeting
+ rpc SayHello (HelloRequest) returns (HelloReply);
+}
+
+// The request message containing the user's name.
+message HelloRequest {
+ string name = 1;
+}
+
+// The response message containing the greetings.
+message HelloReply {
+ string message = 1;
+}
+
+ ```
+
+## Communicating Across Platforms
+
+The code exibhits the ```Greet``` contract being used to communicate across platforms. When you launch the gRPC client, they try to communicate with the both the servers by sending the ```HelloRequest``` message, with a name field (GreeterClient) and the servere responds with a ```HelloReply``` containing the mssage field, which is displayed by the client.
+
+## Gotchas encountered during the investigation
+
+It was very easy to get C# client and C# server communicating over the gRPC channel. The same was for Python client and server code. The challange came when it came time for them communicate with cross platform.
+
+### Differences in startup
+
+The .NET/C# client and server are configured, by default to communicate on secure HTTPS/TLS channel. Python server and client are not. In order to make the communication work, we had to resolve the differences.
+
+### Resolving the difference - .NET/C# Server
+
+In order to make the Python client work out of the box, the .NET gRPC server was configured to run on the unenrypted HTTP port.
+
+To do this, the ```Kestrel``` section of the ```appsettings.json``` file needs to be created/modified as follows:
+
+``` json
+...
+
+ "Kestrel": {
+ "EndpointDefaults": {
+ "Url": "http://*.5000",
+ "Protocols": "Http2"
+ }
+ }
+
+...
+
+```
+
+This allows HTTP communication on port 5000. This setting to be used for **DEVELOPMENT** environments.
+
+More information on this issue can be found here on this [Github issue](https://github.com/grpc/grpc-dotnet/issues/564).
+
+### Resolving the difference - .NET/C# Client
+
+Now that unencrypted channel is also available on the .NET/C# Server, the .NET client also needs to modified to support this, when communicating with the .NET and the Python gRPC server.
+
+To do that, add the following snippet of code, prior to creating a channel:
+
+``` C#
+AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true);
+channel = GrpcChannel.ForAddress(grpcEndpointAddress);
+```
+
+More information of this and more can be found on the [Microsoft .NET gRPC Troubleshooting guide](https://docs.microsoft.com/en-US/aspnet/core/grpc/troubleshoot?view=aspnetcore-3.0).
+
+## Running the servers
+
+### .NET/C# gRPC Server
+
+1. Modify the ```appsettings.json``` file, ```investigations\DotNetGrpc\DotNetGrpcServer``` folder, based on the section above.
+2. Launch the command shell
+3. Starting from the root of the repository, navigate to ```investigations\DotNetGrpc\DotNetGrpcServer``` folder.
+4. When executing for the **first time**, on the command shell, run ```dotnet build```.
+5. Once the command is executed successfully, run ```dotnet run```.
+
+Sample Output of the .NET gRPC Server
+
+``` shell
+$ dotnet run
+info: Microsoft.Hosting.Lifetime[0]
+ Now listening on: http://localhost:5000
+info: Microsoft.Hosting.Lifetime[0]
+ Application started. Press Ctrl+C to shut down.
+info: Microsoft.Hosting.Lifetime[0]
+ Hosting environment: Development
+info: Microsoft.Hosting.Lifetime[0]
+ Content root path: C:\Projects\CSE\DurableFunctions\azure-functions-durable-python\investigations\DotNetGrpc\DotNetGrpcService
+info: Microsoft.AspNetCore.Hosting.Diagnostics[1]
+```
+
+### Python gRPC Server
+
+1. Launch the command shell
+2. Activate the virtual python environment (if applicable).
+3. Starting from the root of the repository, navigate to ```investigations\PythonGrpc``` folder.
+4. When executing for the **first time**, run ```pip install -r requirements.txt```
+5. Once the command is executed successfully, run ```python greet_server.py```.
+
+Sample Output of the .NET gRPC Server
+
+``` shell
+$ python greet_server.py
+Starting gRPC Server on port 50051
+Started. Waiting for client connections...
+```
+
+## Running the gRPC clients
+
+**Note**
+Prior to executing the gRPC clients, ensure that both the .NET/C# and Python gRPC Servers are running.
+
+### .NET/C# Client
+
+1. Launch the command shell
+2. Starting from the root of the repository, navigate to ```investigations\DotNetGrpc\DotNetGrpcClient``` folder.
+3. When executing for the **first time**, on the command shell, run ```dotnet build```.
+4. Once the command is executed successfully, run ```dotnet run```.
+
+Sample Execution of the .NET/C# client
+
+```bash
+$ dotnet run
+Calling C# Endpoint...
+Response: Hello GreeterClient-DotNet from .NET gRPC Server
+Calling Python Endpoint...
+Response: Hello GreeterClient-DotNet from Python gRPC Server
+
+```
+
+### Python Client
+
+1. Launch the command shell
+2. Activate the virtual python environment (if applicable).
+3. Starting from the root of the repository, navigate to ```investigations\PythonGrpc``` folder.
+4. When executing for the **first time**, run ```pip install -r requirements.txt```
+5. Once the command is executed successfully, run ```python greet_client.py```.
+
+Sample Execution of the Python client
+
+```bash
+
+$ python greet_client.py
+Calling C# Endpoint...
+Response: Hello GreeterClient-Python from .NET gRPC Server
+Calling Python Endpoint...
+Response: Hello GreeterClient-Python from Python gRPC Server
+
+```
+
+## Current State of client/server communications
+
+| Servers | C# Client | Python Client |
+| ---------- | :--------:| :-------------:|
+| **C# Server** | :heavy_check_mark:| :heavy_check_mark:|
+| **Python Server** | :heavy_check_mark:| :heavy_check_mark:|
+
+## Resources
+
+- [Offical gRPC site](https://grpc.io)
+- [gRPC Auth Guide](https://www.grpc.io/docs/guides/auth/)
+- [gRPC with ASP.NET Core](https://docs.microsoft.com/en-us/aspnet/core/grpc/?view=aspnetcore-3.0)
+- [Microsoft .NET gRPC Troubleshooting guide](https://docs.microsoft.com/en-US/aspnet/core/grpc/troubleshoot?view=aspnetcore-3.0)
+- [Krestel - Http2 support](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/servers/kestrel?view=aspnetcore-3.0#http2-support)
+- [PluralSight Course: Enhancing Application Communication with gRPC](https://app.pluralsight.com/library/courses/grpc-enhancing-application-communication/table-of-contents)