├── LICENSE ├── README.md └── src ├── GrpcLibrary ├── GrpcLibrary.csproj ├── Helloworld.cs ├── HelloworldGrpc.cs ├── NullRequest.cs └── NullRequestGrpc.cs ├── GrpcService ├── GrpcService.csproj └── Program.cs ├── LB1 ├── LB1.csproj ├── LB1.csproj.user ├── Program.cs ├── Properties │ └── launchSettings.json └── Startup.cs ├── LB2 ├── LB2.csproj ├── LB2.csproj.user ├── Program.cs ├── Properties │ └── launchSettings.json └── Startup.cs ├── Ocelot.GrpcHttpGateway.sln ├── Ocelot.GrpcHttpGateway ├── Configuration │ ├── GrpcGatewayExtensions.cs │ └── GrpcPipelineConfigurationExtensions.cs ├── GrpcHttpMiddleware.cs ├── GrpcHttpMiddlewareExtensions.cs ├── GrpcRequest │ ├── GrpcClient │ │ ├── ArgsParser.cs │ │ ├── GrpcChannelFactory.cs │ │ ├── GrpcClient.cs │ │ ├── GrpcMethod.cs │ │ └── IGrpcChannelFactory.cs │ ├── GrpcHttpContent.cs │ ├── GrpcRequest.cs │ ├── GrpcRequestBuilder.cs │ ├── IGrpcRequestBuilder.cs │ └── UnknownError.cs ├── Ocelot.GrpcHttpGateway.csproj ├── Ocelot.GrpcHttpGateway.csproj.user └── ServiceDescriptor │ ├── GrpcServiceDescriptor.cs │ └── IGrpcServiceDescriptor.cs └── WebGateway ├── DefaultServiceDescriptor.cs ├── Program.cs ├── Properties ├── PublishProfiles │ ├── FolderProfile.pubxml │ └── FolderProfile.pubxml.user └── launchSettings.json ├── Startup.cs ├── WebGateway.csproj ├── WebGateway.csproj.user ├── appsettings.json └── ocelot.json /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 yuezhishun 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ocelot.GrpcHttpGateway 2 | grpc service gateway used ocelot 3 | -------------------------------------------------------------------------------- /src/GrpcLibrary/GrpcLibrary.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/GrpcLibrary/Helloworld.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Generated by the protocol buffer compiler. DO NOT EDIT! 3 | // source: helloworld.proto 4 | // 5 | #pragma warning disable 1591, 0612, 3021 6 | #region Designer generated code 7 | 8 | using pb = global::Google.Protobuf; 9 | using pbc = global::Google.Protobuf.Collections; 10 | using pbr = global::Google.Protobuf.Reflection; 11 | using scg = global::System.Collections.Generic; 12 | namespace GrpcServerImpl { 13 | 14 | /// Holder for reflection information generated from helloworld.proto 15 | public static partial class HelloworldReflection { 16 | 17 | #region Descriptor 18 | /// File descriptor for helloworld.proto 19 | public static pbr::FileDescriptor Descriptor { 20 | get { return descriptor; } 21 | } 22 | private static pbr::FileDescriptor descriptor; 23 | 24 | static HelloworldReflection() { 25 | byte[] descriptorData = global::System.Convert.FromBase64String( 26 | string.Concat( 27 | "ChBoZWxsb3dvcmxkLnByb3RvEgpHcnBjU2VydmVyIhwKDEhlbGxvUmVxdWVz", 28 | "dBIMCgRuYW1lGAEgASgJIh0KCkhlbGxvUmVwbHkSDwoHbWVzc2FnZRgBIAEo", 29 | "CTJLCglIZWxsb0dycGMSPgoIU2F5SGVsbG8SGC5HcnBjU2VydmVyLkhlbGxv", 30 | "UmVxdWVzdBoWLkdycGNTZXJ2ZXIuSGVsbG9SZXBseSIAQhGqAg5HcnBjU2Vy", 31 | "dmVySW1wbGIGcHJvdG8z")); 32 | descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData, 33 | new pbr::FileDescriptor[] { }, 34 | new pbr::GeneratedClrTypeInfo(null, new pbr::GeneratedClrTypeInfo[] { 35 | new pbr::GeneratedClrTypeInfo(typeof(global::GrpcServerImpl.HelloRequest), global::GrpcServerImpl.HelloRequest.Parser, new[]{ "Name" }, null, null, null), 36 | new pbr::GeneratedClrTypeInfo(typeof(global::GrpcServerImpl.HelloReply), global::GrpcServerImpl.HelloReply.Parser, new[]{ "Message" }, null, null, null) 37 | })); 38 | } 39 | #endregion 40 | 41 | } 42 | #region Messages 43 | /// 44 | /// The request message containing the user's name. 45 | /// 46 | public sealed partial class HelloRequest : pb::IMessage { 47 | private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new HelloRequest()); 48 | private pb::UnknownFieldSet _unknownFields; 49 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 50 | public static pb::MessageParser Parser { get { return _parser; } } 51 | 52 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 53 | public static pbr::MessageDescriptor Descriptor { 54 | get { return global::GrpcServerImpl.HelloworldReflection.Descriptor.MessageTypes[0]; } 55 | } 56 | 57 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 58 | pbr::MessageDescriptor pb::IMessage.Descriptor { 59 | get { return Descriptor; } 60 | } 61 | 62 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 63 | public HelloRequest() { 64 | OnConstruction(); 65 | } 66 | 67 | partial void OnConstruction(); 68 | 69 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 70 | public HelloRequest(HelloRequest other) : this() { 71 | name_ = other.name_; 72 | _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields); 73 | } 74 | 75 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 76 | public HelloRequest Clone() { 77 | return new HelloRequest(this); 78 | } 79 | 80 | /// Field number for the "name" field. 81 | public const int NameFieldNumber = 1; 82 | private string name_ = ""; 83 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 84 | public string Name { 85 | get { return name_; } 86 | set { 87 | name_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); 88 | } 89 | } 90 | 91 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 92 | public override bool Equals(object other) { 93 | return Equals(other as HelloRequest); 94 | } 95 | 96 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 97 | public bool Equals(HelloRequest other) { 98 | if (ReferenceEquals(other, null)) { 99 | return false; 100 | } 101 | if (ReferenceEquals(other, this)) { 102 | return true; 103 | } 104 | if (Name != other.Name) return false; 105 | return Equals(_unknownFields, other._unknownFields); 106 | } 107 | 108 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 109 | public override int GetHashCode() { 110 | int hash = 1; 111 | if (Name.Length != 0) hash ^= Name.GetHashCode(); 112 | if (_unknownFields != null) { 113 | hash ^= _unknownFields.GetHashCode(); 114 | } 115 | return hash; 116 | } 117 | 118 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 119 | public override string ToString() { 120 | return pb::JsonFormatter.ToDiagnosticString(this); 121 | } 122 | 123 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 124 | public void WriteTo(pb::CodedOutputStream output) { 125 | if (Name.Length != 0) { 126 | output.WriteRawTag(10); 127 | output.WriteString(Name); 128 | } 129 | if (_unknownFields != null) { 130 | _unknownFields.WriteTo(output); 131 | } 132 | } 133 | 134 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 135 | public int CalculateSize() { 136 | int size = 0; 137 | if (Name.Length != 0) { 138 | size += 1 + pb::CodedOutputStream.ComputeStringSize(Name); 139 | } 140 | if (_unknownFields != null) { 141 | size += _unknownFields.CalculateSize(); 142 | } 143 | return size; 144 | } 145 | 146 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 147 | public void MergeFrom(HelloRequest other) { 148 | if (other == null) { 149 | return; 150 | } 151 | if (other.Name.Length != 0) { 152 | Name = other.Name; 153 | } 154 | _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields); 155 | } 156 | 157 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 158 | public void MergeFrom(pb::CodedInputStream input) { 159 | uint tag; 160 | while ((tag = input.ReadTag()) != 0) { 161 | switch(tag) { 162 | default: 163 | _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input); 164 | break; 165 | case 10: { 166 | Name = input.ReadString(); 167 | break; 168 | } 169 | } 170 | } 171 | } 172 | 173 | } 174 | 175 | /// 176 | /// The response message containing the greetings 177 | /// 178 | public sealed partial class HelloReply : pb::IMessage { 179 | private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new HelloReply()); 180 | private pb::UnknownFieldSet _unknownFields; 181 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 182 | public static pb::MessageParser Parser { get { return _parser; } } 183 | 184 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 185 | public static pbr::MessageDescriptor Descriptor { 186 | get { return global::GrpcServerImpl.HelloworldReflection.Descriptor.MessageTypes[1]; } 187 | } 188 | 189 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 190 | pbr::MessageDescriptor pb::IMessage.Descriptor { 191 | get { return Descriptor; } 192 | } 193 | 194 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 195 | public HelloReply() { 196 | OnConstruction(); 197 | } 198 | 199 | partial void OnConstruction(); 200 | 201 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 202 | public HelloReply(HelloReply other) : this() { 203 | message_ = other.message_; 204 | _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields); 205 | } 206 | 207 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 208 | public HelloReply Clone() { 209 | return new HelloReply(this); 210 | } 211 | 212 | /// Field number for the "message" field. 213 | public const int MessageFieldNumber = 1; 214 | private string message_ = ""; 215 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 216 | public string Message { 217 | get { return message_; } 218 | set { 219 | message_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); 220 | } 221 | } 222 | 223 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 224 | public override bool Equals(object other) { 225 | return Equals(other as HelloReply); 226 | } 227 | 228 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 229 | public bool Equals(HelloReply other) { 230 | if (ReferenceEquals(other, null)) { 231 | return false; 232 | } 233 | if (ReferenceEquals(other, this)) { 234 | return true; 235 | } 236 | if (Message != other.Message) return false; 237 | return Equals(_unknownFields, other._unknownFields); 238 | } 239 | 240 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 241 | public override int GetHashCode() { 242 | int hash = 1; 243 | if (Message.Length != 0) hash ^= Message.GetHashCode(); 244 | if (_unknownFields != null) { 245 | hash ^= _unknownFields.GetHashCode(); 246 | } 247 | return hash; 248 | } 249 | 250 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 251 | public override string ToString() { 252 | return pb::JsonFormatter.ToDiagnosticString(this); 253 | } 254 | 255 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 256 | public void WriteTo(pb::CodedOutputStream output) { 257 | if (Message.Length != 0) { 258 | output.WriteRawTag(10); 259 | output.WriteString(Message); 260 | } 261 | if (_unknownFields != null) { 262 | _unknownFields.WriteTo(output); 263 | } 264 | } 265 | 266 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 267 | public int CalculateSize() { 268 | int size = 0; 269 | if (Message.Length != 0) { 270 | size += 1 + pb::CodedOutputStream.ComputeStringSize(Message); 271 | } 272 | if (_unknownFields != null) { 273 | size += _unknownFields.CalculateSize(); 274 | } 275 | return size; 276 | } 277 | 278 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 279 | public void MergeFrom(HelloReply other) { 280 | if (other == null) { 281 | return; 282 | } 283 | if (other.Message.Length != 0) { 284 | Message = other.Message; 285 | } 286 | _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields); 287 | } 288 | 289 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 290 | public void MergeFrom(pb::CodedInputStream input) { 291 | uint tag; 292 | while ((tag = input.ReadTag()) != 0) { 293 | switch(tag) { 294 | default: 295 | _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input); 296 | break; 297 | case 10: { 298 | Message = input.ReadString(); 299 | break; 300 | } 301 | } 302 | } 303 | } 304 | 305 | } 306 | 307 | #endregion 308 | 309 | } 310 | 311 | #endregion Designer generated code 312 | -------------------------------------------------------------------------------- /src/GrpcLibrary/HelloworldGrpc.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Generated by the protocol buffer compiler. DO NOT EDIT! 3 | // source: helloworld.proto 4 | // 5 | #pragma warning disable 0414, 1591 6 | #region Designer generated code 7 | 8 | using grpc = global::Grpc.Core; 9 | 10 | namespace GrpcServerImpl { 11 | /// 12 | /// The service definition. 13 | /// 14 | public static partial class HelloGrpc 15 | { 16 | static readonly string __ServiceName = "GrpcServer.HelloGrpc"; 17 | 18 | static readonly grpc::Marshaller __Marshaller_GrpcServer_HelloRequest = grpc::Marshallers.Create((arg) => global::Google.Protobuf.MessageExtensions.ToByteArray(arg), global::GrpcServerImpl.HelloRequest.Parser.ParseFrom); 19 | static readonly grpc::Marshaller __Marshaller_GrpcServer_HelloReply = grpc::Marshallers.Create((arg) => global::Google.Protobuf.MessageExtensions.ToByteArray(arg), global::GrpcServerImpl.HelloReply.Parser.ParseFrom); 20 | 21 | static readonly grpc::Method __Method_SayHello = new grpc::Method( 22 | grpc::MethodType.Unary, 23 | __ServiceName, 24 | "SayHello", 25 | __Marshaller_GrpcServer_HelloRequest, 26 | __Marshaller_GrpcServer_HelloReply); 27 | 28 | /// Service descriptor 29 | public static global::Google.Protobuf.Reflection.ServiceDescriptor Descriptor 30 | { 31 | get { return global::GrpcServerImpl.HelloworldReflection.Descriptor.Services[0]; } 32 | } 33 | 34 | /// Base class for server-side implementations of HelloGrpc 35 | public abstract partial class HelloGrpcBase 36 | { 37 | /// 38 | /// Sends a greeting 39 | /// 40 | /// The request received from the client. 41 | /// The context of the server-side call handler being invoked. 42 | /// The response to send back to the client (wrapped by a task). 43 | public virtual global::System.Threading.Tasks.Task SayHello(global::GrpcServerImpl.HelloRequest request, grpc::ServerCallContext context) 44 | { 45 | throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, "")); 46 | } 47 | 48 | } 49 | 50 | /// Client for HelloGrpc 51 | public partial class HelloGrpcClient : grpc::ClientBase 52 | { 53 | /// Creates a new client for HelloGrpc 54 | /// The channel to use to make remote calls. 55 | public HelloGrpcClient(grpc::Channel channel) : base(channel) 56 | { 57 | } 58 | /// Creates a new client for HelloGrpc that uses a custom CallInvoker. 59 | /// The callInvoker to use to make remote calls. 60 | public HelloGrpcClient(grpc::CallInvoker callInvoker) : base(callInvoker) 61 | { 62 | } 63 | /// Protected parameterless constructor to allow creation of test doubles. 64 | protected HelloGrpcClient() : base() 65 | { 66 | } 67 | /// Protected constructor to allow creation of configured clients. 68 | /// The client configuration. 69 | protected HelloGrpcClient(ClientBaseConfiguration configuration) : base(configuration) 70 | { 71 | } 72 | 73 | /// 74 | /// Sends a greeting 75 | /// 76 | /// The request to send to the server. 77 | /// The initial metadata to send with the call. This parameter is optional. 78 | /// An optional deadline for the call. The call will be cancelled if deadline is hit. 79 | /// An optional token for canceling the call. 80 | /// The response received from the server. 81 | public virtual global::GrpcServerImpl.HelloReply SayHello(global::GrpcServerImpl.HelloRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken)) 82 | { 83 | return SayHello(request, new grpc::CallOptions(headers, deadline, cancellationToken)); 84 | } 85 | /// 86 | /// Sends a greeting 87 | /// 88 | /// The request to send to the server. 89 | /// The options for the call. 90 | /// The response received from the server. 91 | public virtual global::GrpcServerImpl.HelloReply SayHello(global::GrpcServerImpl.HelloRequest request, grpc::CallOptions options) 92 | { 93 | return CallInvoker.BlockingUnaryCall(__Method_SayHello, null, options, request); 94 | } 95 | /// 96 | /// Sends a greeting 97 | /// 98 | /// The request to send to the server. 99 | /// The initial metadata to send with the call. This parameter is optional. 100 | /// An optional deadline for the call. The call will be cancelled if deadline is hit. 101 | /// An optional token for canceling the call. 102 | /// The call object. 103 | public virtual grpc::AsyncUnaryCall SayHelloAsync(global::GrpcServerImpl.HelloRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken)) 104 | { 105 | return SayHelloAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken)); 106 | } 107 | /// 108 | /// Sends a greeting 109 | /// 110 | /// The request to send to the server. 111 | /// The options for the call. 112 | /// The call object. 113 | public virtual grpc::AsyncUnaryCall SayHelloAsync(global::GrpcServerImpl.HelloRequest request, grpc::CallOptions options) 114 | { 115 | return CallInvoker.AsyncUnaryCall(__Method_SayHello, null, options, request); 116 | } 117 | /// Creates a new instance of client from given ClientBaseConfiguration. 118 | protected override HelloGrpcClient NewInstance(ClientBaseConfiguration configuration) 119 | { 120 | return new HelloGrpcClient(configuration); 121 | } 122 | } 123 | 124 | /// Creates service definition that can be registered with a server 125 | /// An object implementing the server-side handling logic. 126 | public static grpc::ServerServiceDefinition BindService(HelloGrpcBase serviceImpl) 127 | { 128 | return grpc::ServerServiceDefinition.CreateBuilder() 129 | .AddMethod(__Method_SayHello, serviceImpl.SayHello).Build(); 130 | } 131 | 132 | } 133 | } 134 | #endregion 135 | -------------------------------------------------------------------------------- /src/GrpcLibrary/NullRequest.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Generated by the protocol buffer compiler. DO NOT EDIT! 3 | // source: NullRequest.proto 4 | // 5 | #pragma warning disable 1591, 0612, 3021 6 | #region Designer generated code 7 | 8 | using pb = global::Google.Protobuf; 9 | using pbc = global::Google.Protobuf.Collections; 10 | using pbr = global::Google.Protobuf.Reflection; 11 | using scg = global::System.Collections.Generic; 12 | namespace GrpcServerImpl { 13 | 14 | /// Holder for reflection information generated from NullRequest.proto 15 | public static partial class NullRequestReflection { 16 | 17 | #region Descriptor 18 | /// File descriptor for NullRequest.proto 19 | public static pbr::FileDescriptor Descriptor { 20 | get { return descriptor; } 21 | } 22 | private static pbr::FileDescriptor descriptor; 23 | 24 | static NullRequestReflection() { 25 | byte[] descriptorData = global::System.Convert.FromBase64String( 26 | string.Concat( 27 | "ChFOdWxsUmVxdWVzdC5wcm90bxIKR3JwY1NlcnZlciINCgtOdWxsUmVxdWVz", 28 | "dCILCglOdWxsUmVwbHkyTQoNSGVsbG9OdWxsR3JwYxI8CghTYXlIZWxsbxIX", 29 | "LkdycGNTZXJ2ZXIuTnVsbFJlcXVlc3QaFS5HcnBjU2VydmVyLk51bGxSZXBs", 30 | "eSIAQhGqAg5HcnBjU2VydmVySW1wbGIGcHJvdG8z")); 31 | descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData, 32 | new pbr::FileDescriptor[] { }, 33 | new pbr::GeneratedClrTypeInfo(null, new pbr::GeneratedClrTypeInfo[] { 34 | new pbr::GeneratedClrTypeInfo(typeof(global::GrpcServerImpl.NullRequest), global::GrpcServerImpl.NullRequest.Parser, null, null, null, null), 35 | new pbr::GeneratedClrTypeInfo(typeof(global::GrpcServerImpl.NullReply), global::GrpcServerImpl.NullReply.Parser, null, null, null, null) 36 | })); 37 | } 38 | #endregion 39 | 40 | } 41 | #region Messages 42 | /// 43 | /// The request message containing the user's name. 44 | /// 45 | public sealed partial class NullRequest : pb::IMessage { 46 | private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new NullRequest()); 47 | private pb::UnknownFieldSet _unknownFields; 48 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 49 | public static pb::MessageParser Parser { get { return _parser; } } 50 | 51 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 52 | public static pbr::MessageDescriptor Descriptor { 53 | get { return global::GrpcServerImpl.NullRequestReflection.Descriptor.MessageTypes[0]; } 54 | } 55 | 56 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 57 | pbr::MessageDescriptor pb::IMessage.Descriptor { 58 | get { return Descriptor; } 59 | } 60 | 61 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 62 | public NullRequest() { 63 | OnConstruction(); 64 | } 65 | 66 | partial void OnConstruction(); 67 | 68 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 69 | public NullRequest(NullRequest other) : this() { 70 | _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields); 71 | } 72 | 73 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 74 | public NullRequest Clone() { 75 | return new NullRequest(this); 76 | } 77 | 78 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 79 | public override bool Equals(object other) { 80 | return Equals(other as NullRequest); 81 | } 82 | 83 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 84 | public bool Equals(NullRequest other) { 85 | if (ReferenceEquals(other, null)) { 86 | return false; 87 | } 88 | if (ReferenceEquals(other, this)) { 89 | return true; 90 | } 91 | return Equals(_unknownFields, other._unknownFields); 92 | } 93 | 94 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 95 | public override int GetHashCode() { 96 | int hash = 1; 97 | if (_unknownFields != null) { 98 | hash ^= _unknownFields.GetHashCode(); 99 | } 100 | return hash; 101 | } 102 | 103 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 104 | public override string ToString() { 105 | return pb::JsonFormatter.ToDiagnosticString(this); 106 | } 107 | 108 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 109 | public void WriteTo(pb::CodedOutputStream output) { 110 | if (_unknownFields != null) { 111 | _unknownFields.WriteTo(output); 112 | } 113 | } 114 | 115 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 116 | public int CalculateSize() { 117 | int size = 0; 118 | if (_unknownFields != null) { 119 | size += _unknownFields.CalculateSize(); 120 | } 121 | return size; 122 | } 123 | 124 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 125 | public void MergeFrom(NullRequest other) { 126 | if (other == null) { 127 | return; 128 | } 129 | _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields); 130 | } 131 | 132 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 133 | public void MergeFrom(pb::CodedInputStream input) { 134 | uint tag; 135 | while ((tag = input.ReadTag()) != 0) { 136 | switch(tag) { 137 | default: 138 | _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input); 139 | break; 140 | } 141 | } 142 | } 143 | 144 | } 145 | 146 | /// 147 | /// The response message containing the greetings 148 | /// 149 | public sealed partial class NullReply : pb::IMessage { 150 | private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new NullReply()); 151 | private pb::UnknownFieldSet _unknownFields; 152 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 153 | public static pb::MessageParser Parser { get { return _parser; } } 154 | 155 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 156 | public static pbr::MessageDescriptor Descriptor { 157 | get { return global::GrpcServerImpl.NullRequestReflection.Descriptor.MessageTypes[1]; } 158 | } 159 | 160 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 161 | pbr::MessageDescriptor pb::IMessage.Descriptor { 162 | get { return Descriptor; } 163 | } 164 | 165 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 166 | public NullReply() { 167 | OnConstruction(); 168 | } 169 | 170 | partial void OnConstruction(); 171 | 172 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 173 | public NullReply(NullReply other) : this() { 174 | _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields); 175 | } 176 | 177 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 178 | public NullReply Clone() { 179 | return new NullReply(this); 180 | } 181 | 182 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 183 | public override bool Equals(object other) { 184 | return Equals(other as NullReply); 185 | } 186 | 187 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 188 | public bool Equals(NullReply other) { 189 | if (ReferenceEquals(other, null)) { 190 | return false; 191 | } 192 | if (ReferenceEquals(other, this)) { 193 | return true; 194 | } 195 | return Equals(_unknownFields, other._unknownFields); 196 | } 197 | 198 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 199 | public override int GetHashCode() { 200 | int hash = 1; 201 | if (_unknownFields != null) { 202 | hash ^= _unknownFields.GetHashCode(); 203 | } 204 | return hash; 205 | } 206 | 207 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 208 | public override string ToString() { 209 | return pb::JsonFormatter.ToDiagnosticString(this); 210 | } 211 | 212 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 213 | public void WriteTo(pb::CodedOutputStream output) { 214 | if (_unknownFields != null) { 215 | _unknownFields.WriteTo(output); 216 | } 217 | } 218 | 219 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 220 | public int CalculateSize() { 221 | int size = 0; 222 | if (_unknownFields != null) { 223 | size += _unknownFields.CalculateSize(); 224 | } 225 | return size; 226 | } 227 | 228 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 229 | public void MergeFrom(NullReply other) { 230 | if (other == null) { 231 | return; 232 | } 233 | _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields); 234 | } 235 | 236 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 237 | public void MergeFrom(pb::CodedInputStream input) { 238 | uint tag; 239 | while ((tag = input.ReadTag()) != 0) { 240 | switch(tag) { 241 | default: 242 | _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input); 243 | break; 244 | } 245 | } 246 | } 247 | 248 | } 249 | 250 | #endregion 251 | 252 | } 253 | 254 | #endregion Designer generated code 255 | -------------------------------------------------------------------------------- /src/GrpcLibrary/NullRequestGrpc.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Generated by the protocol buffer compiler. DO NOT EDIT! 3 | // source: NullRequest.proto 4 | // 5 | #pragma warning disable 0414, 1591 6 | #region Designer generated code 7 | 8 | using grpc = global::Grpc.Core; 9 | 10 | namespace GrpcServerImpl { 11 | /// 12 | /// The service definition. 13 | /// 14 | public static partial class HelloNullGrpc 15 | { 16 | static readonly string __ServiceName = "GrpcServer.HelloNullGrpc"; 17 | 18 | static readonly grpc::Marshaller __Marshaller_GrpcServer_NullRequest = grpc::Marshallers.Create((arg) => global::Google.Protobuf.MessageExtensions.ToByteArray(arg), global::GrpcServerImpl.NullRequest.Parser.ParseFrom); 19 | static readonly grpc::Marshaller __Marshaller_GrpcServer_NullReply = grpc::Marshallers.Create((arg) => global::Google.Protobuf.MessageExtensions.ToByteArray(arg), global::GrpcServerImpl.NullReply.Parser.ParseFrom); 20 | 21 | static readonly grpc::Method __Method_SayHello = new grpc::Method( 22 | grpc::MethodType.Unary, 23 | __ServiceName, 24 | "SayHello", 25 | __Marshaller_GrpcServer_NullRequest, 26 | __Marshaller_GrpcServer_NullReply); 27 | 28 | /// Service descriptor 29 | public static global::Google.Protobuf.Reflection.ServiceDescriptor Descriptor 30 | { 31 | get { return global::GrpcServerImpl.NullRequestReflection.Descriptor.Services[0]; } 32 | } 33 | 34 | /// Base class for server-side implementations of HelloNullGrpc 35 | public abstract partial class HelloNullGrpcBase 36 | { 37 | /// 38 | /// Sends a greeting 39 | /// 40 | /// The request received from the client. 41 | /// The context of the server-side call handler being invoked. 42 | /// The response to send back to the client (wrapped by a task). 43 | public virtual global::System.Threading.Tasks.Task SayHello(global::GrpcServerImpl.NullRequest request, grpc::ServerCallContext context) 44 | { 45 | throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, "")); 46 | } 47 | 48 | } 49 | 50 | /// Client for HelloNullGrpc 51 | public partial class HelloNullGrpcClient : grpc::ClientBase 52 | { 53 | /// Creates a new client for HelloNullGrpc 54 | /// The channel to use to make remote calls. 55 | public HelloNullGrpcClient(grpc::Channel channel) : base(channel) 56 | { 57 | } 58 | /// Creates a new client for HelloNullGrpc that uses a custom CallInvoker. 59 | /// The callInvoker to use to make remote calls. 60 | public HelloNullGrpcClient(grpc::CallInvoker callInvoker) : base(callInvoker) 61 | { 62 | } 63 | /// Protected parameterless constructor to allow creation of test doubles. 64 | protected HelloNullGrpcClient() : base() 65 | { 66 | } 67 | /// Protected constructor to allow creation of configured clients. 68 | /// The client configuration. 69 | protected HelloNullGrpcClient(ClientBaseConfiguration configuration) : base(configuration) 70 | { 71 | } 72 | 73 | /// 74 | /// Sends a greeting 75 | /// 76 | /// The request to send to the server. 77 | /// The initial metadata to send with the call. This parameter is optional. 78 | /// An optional deadline for the call. The call will be cancelled if deadline is hit. 79 | /// An optional token for canceling the call. 80 | /// The response received from the server. 81 | public virtual global::GrpcServerImpl.NullReply SayHello(global::GrpcServerImpl.NullRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken)) 82 | { 83 | return SayHello(request, new grpc::CallOptions(headers, deadline, cancellationToken)); 84 | } 85 | /// 86 | /// Sends a greeting 87 | /// 88 | /// The request to send to the server. 89 | /// The options for the call. 90 | /// The response received from the server. 91 | public virtual global::GrpcServerImpl.NullReply SayHello(global::GrpcServerImpl.NullRequest request, grpc::CallOptions options) 92 | { 93 | return CallInvoker.BlockingUnaryCall(__Method_SayHello, null, options, request); 94 | } 95 | /// 96 | /// Sends a greeting 97 | /// 98 | /// The request to send to the server. 99 | /// The initial metadata to send with the call. This parameter is optional. 100 | /// An optional deadline for the call. The call will be cancelled if deadline is hit. 101 | /// An optional token for canceling the call. 102 | /// The call object. 103 | public virtual grpc::AsyncUnaryCall SayHelloAsync(global::GrpcServerImpl.NullRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken)) 104 | { 105 | return SayHelloAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken)); 106 | } 107 | /// 108 | /// Sends a greeting 109 | /// 110 | /// The request to send to the server. 111 | /// The options for the call. 112 | /// The call object. 113 | public virtual grpc::AsyncUnaryCall SayHelloAsync(global::GrpcServerImpl.NullRequest request, grpc::CallOptions options) 114 | { 115 | return CallInvoker.AsyncUnaryCall(__Method_SayHello, null, options, request); 116 | } 117 | /// Creates a new instance of client from given ClientBaseConfiguration. 118 | protected override HelloNullGrpcClient NewInstance(ClientBaseConfiguration configuration) 119 | { 120 | return new HelloNullGrpcClient(configuration); 121 | } 122 | } 123 | 124 | /// Creates service definition that can be registered with a server 125 | /// An object implementing the server-side handling logic. 126 | public static grpc::ServerServiceDefinition BindService(HelloNullGrpcBase serviceImpl) 127 | { 128 | return grpc::ServerServiceDefinition.CreateBuilder() 129 | .AddMethod(__Method_SayHello, serviceImpl.SayHello).Build(); 130 | } 131 | 132 | } 133 | } 134 | #endregion 135 | -------------------------------------------------------------------------------- /src/GrpcService/GrpcService.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | netcoreapp2.1 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/GrpcService/Program.cs: -------------------------------------------------------------------------------- 1 | using Grpc.Core; 2 | using GrpcServerImpl; 3 | using System; 4 | using System.Threading.Tasks; 5 | 6 | namespace GrpcService 7 | { 8 | class Program 9 | { 10 | static void Main(string[] args) 11 | { 12 | 13 | int port1 = 50001; 14 | int port2 = 50002; 15 | var server1 = new Server 16 | { 17 | Services = { 18 | HelloGrpc.BindService(new HelloImpl()), 19 | HelloNullGrpc.BindService(new HelloNullImpl()) 20 | }, 21 | Ports = { new ServerPort("localhost", port1, ServerCredentials.Insecure) } 22 | }; 23 | 24 | server1.Start(); 25 | Console.WriteLine($"server1 listening on port:{port1}"); 26 | var server2 = new Server 27 | { 28 | Services = { 29 | HelloGrpc.BindService(new HelloImpl()), 30 | HelloNullGrpc.BindService(new HelloNullImpl()) 31 | }, 32 | Ports = { new ServerPort("localhost", port2, ServerCredentials.Insecure) } 33 | }; 34 | server2.Start(); 35 | Console.WriteLine($"server2 listening on port:{port2}"); 36 | 37 | Console.ReadLine(); 38 | server1.ShutdownAsync().Wait(); 39 | server2.ShutdownAsync().Wait(); 40 | } 41 | } 42 | class HelloImpl : HelloGrpc.HelloGrpcBase 43 | { 44 | // Server side handler of the SayHello RPC 45 | public override Task SayHello(HelloRequest request, ServerCallContext context) 46 | { 47 | return Task.FromResult(new HelloReply { Message = context.Host }); 48 | } 49 | } 50 | class HelloNullImpl : HelloNullGrpc.HelloNullGrpcBase 51 | { 52 | // Server side handler of the SayHello RPC 53 | public override Task SayHello(NullRequest request, ServerCallContext context) 54 | { 55 | return Task.FromResult(new NullReply()); 56 | } 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /src/LB1/LB1.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/LB1/LB1.csproj.user: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | ProjectDebugger 5 | 6 | 7 | LB1 8 | 9 | -------------------------------------------------------------------------------- /src/LB1/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using Microsoft.AspNetCore; 7 | using Microsoft.AspNetCore.Hosting; 8 | using Microsoft.Extensions.Configuration; 9 | using Microsoft.Extensions.Logging; 10 | 11 | namespace LB1 12 | { 13 | public class Program 14 | { 15 | public static void Main(string[] args) 16 | { 17 | CreateWebHostBuilder(args).Build().Run(); 18 | } 19 | 20 | public static IWebHostBuilder CreateWebHostBuilder(string[] args) => 21 | WebHost.CreateDefaultBuilder(args) 22 | .UseUrls("http://localhost:5001") 23 | .UseStartup(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/LB1/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:5001", 7 | "sslPort": 0 8 | } 9 | }, 10 | "profiles": { 11 | "IIS Express": { 12 | "commandName": "IISExpress", 13 | "launchBrowser": true, 14 | "environmentVariables": { 15 | "ASPNETCORE_ENVIRONMENT": "Development" 16 | } 17 | }, 18 | "LB1": { 19 | "commandName": "Project", 20 | "launchBrowser": true, 21 | "applicationUrl": "http://localhost:5001", 22 | "environmentVariables": { 23 | "ASPNETCORE_ENVIRONMENT": "Development" 24 | } 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /src/LB1/Startup.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Builder; 6 | using Microsoft.AspNetCore.Hosting; 7 | using Microsoft.AspNetCore.Http; 8 | using Microsoft.Extensions.DependencyInjection; 9 | 10 | namespace LB1 11 | { 12 | public class Startup 13 | { 14 | // This method gets called by the runtime. Use this method to add services to the container. 15 | // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 16 | public void ConfigureServices(IServiceCollection services) 17 | { 18 | } 19 | 20 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 21 | public void Configure(IApplicationBuilder app, IHostingEnvironment env) 22 | { 23 | if (env.IsDevelopment()) 24 | { 25 | app.UseDeveloperExceptionPage(); 26 | } 27 | 28 | app.Run(async (context) => 29 | { 30 | await context.Response.WriteAsync($"LB1--{context.Request.Scheme}://{context.Request.Host.Value}{context.Request.Path}"); 31 | }); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/LB2/LB2.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/LB2/LB2.csproj.user: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | ProjectDebugger 5 | 6 | 7 | LB2 8 | 9 | -------------------------------------------------------------------------------- /src/LB2/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using Microsoft.AspNetCore; 7 | using Microsoft.AspNetCore.Hosting; 8 | using Microsoft.Extensions.Configuration; 9 | using Microsoft.Extensions.Logging; 10 | 11 | namespace LB2 12 | { 13 | public class Program 14 | { 15 | public static void Main(string[] args) 16 | { 17 | CreateWebHostBuilder(args).Build().Run(); 18 | } 19 | 20 | public static IWebHostBuilder CreateWebHostBuilder(string[] args) => 21 | WebHost.CreateDefaultBuilder(args) 22 | .UseUrls("http://localhost:5002") 23 | .UseStartup(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/LB2/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:5002", 7 | "sslPort": 0 8 | } 9 | }, 10 | "profiles": { 11 | "IIS Express": { 12 | "commandName": "IISExpress", 13 | "launchBrowser": true, 14 | "environmentVariables": { 15 | "ASPNETCORE_ENVIRONMENT": "Development" 16 | } 17 | }, 18 | "LB2": { 19 | "commandName": "Project", 20 | "launchBrowser": true, 21 | "applicationUrl": "http://localhost:5002", 22 | "environmentVariables": { 23 | "ASPNETCORE_ENVIRONMENT": "Development" 24 | } 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /src/LB2/Startup.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Builder; 6 | using Microsoft.AspNetCore.Hosting; 7 | using Microsoft.AspNetCore.Http; 8 | using Microsoft.Extensions.DependencyInjection; 9 | 10 | namespace LB2 11 | { 12 | public class Startup 13 | { 14 | // This method gets called by the runtime. Use this method to add services to the container. 15 | // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 16 | public void ConfigureServices(IServiceCollection services) 17 | { 18 | } 19 | 20 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 21 | public void Configure(IApplicationBuilder app, IHostingEnvironment env) 22 | { 23 | if (env.IsDevelopment()) 24 | { 25 | app.UseDeveloperExceptionPage(); 26 | } 27 | 28 | app.Run(async (context) => 29 | { 30 | await context.Response.WriteAsync($"LB2--{context.Request.Scheme}://{context.Request.Host.Value}{context.Request.Path}"); 31 | }); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Ocelot.GrpcHttpGateway.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.27703.2047 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebGateway", "WebGateway\WebGateway.csproj", "{6C443297-1E33-4001-8DF0-B6D014FA157F}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.GrpcHttpGateway", "Ocelot.GrpcHttpGateway\Ocelot.GrpcHttpGateway.csproj", "{4EFBE030-E33C-41D2-B7DA-BF40412BE0F5}" 9 | EndProject 10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LB2", "LB2\LB2.csproj", "{B031BD0F-A4B5-4BF9-A91C-4BCE7C11B32E}" 11 | EndProject 12 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GrpcLibrary", "GrpcLibrary\GrpcLibrary.csproj", "{95F6A2BE-B7BA-4D93-8A8E-FAFF909D805D}" 13 | EndProject 14 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GrpcService", "GrpcService\GrpcService.csproj", "{E245F6F5-A66A-46D4-9350-6152AAB0DA1B}" 15 | EndProject 16 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LB1", "LB1\LB1.csproj", "{4C346307-5941-495D-B48D-CA564E51897D}" 17 | EndProject 18 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "WebTest", "WebTest", "{EAA4D121-2849-48D0-AE06-A64B580A3A89}" 19 | EndProject 20 | Global 21 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 22 | Debug|Any CPU = Debug|Any CPU 23 | Debug|x64 = Debug|x64 24 | Release|Any CPU = Release|Any CPU 25 | Release|x64 = Release|x64 26 | EndGlobalSection 27 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 28 | {6C443297-1E33-4001-8DF0-B6D014FA157F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 29 | {6C443297-1E33-4001-8DF0-B6D014FA157F}.Debug|Any CPU.Build.0 = Debug|Any CPU 30 | {6C443297-1E33-4001-8DF0-B6D014FA157F}.Debug|x64.ActiveCfg = Debug|Any CPU 31 | {6C443297-1E33-4001-8DF0-B6D014FA157F}.Debug|x64.Build.0 = Debug|Any CPU 32 | {6C443297-1E33-4001-8DF0-B6D014FA157F}.Release|Any CPU.ActiveCfg = Release|Any CPU 33 | {6C443297-1E33-4001-8DF0-B6D014FA157F}.Release|Any CPU.Build.0 = Release|Any CPU 34 | {6C443297-1E33-4001-8DF0-B6D014FA157F}.Release|x64.ActiveCfg = Release|Any CPU 35 | {6C443297-1E33-4001-8DF0-B6D014FA157F}.Release|x64.Build.0 = Release|Any CPU 36 | {4EFBE030-E33C-41D2-B7DA-BF40412BE0F5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 37 | {4EFBE030-E33C-41D2-B7DA-BF40412BE0F5}.Debug|Any CPU.Build.0 = Debug|Any CPU 38 | {4EFBE030-E33C-41D2-B7DA-BF40412BE0F5}.Debug|x64.ActiveCfg = Debug|Any CPU 39 | {4EFBE030-E33C-41D2-B7DA-BF40412BE0F5}.Debug|x64.Build.0 = Debug|Any CPU 40 | {4EFBE030-E33C-41D2-B7DA-BF40412BE0F5}.Release|Any CPU.ActiveCfg = Release|Any CPU 41 | {4EFBE030-E33C-41D2-B7DA-BF40412BE0F5}.Release|Any CPU.Build.0 = Release|Any CPU 42 | {4EFBE030-E33C-41D2-B7DA-BF40412BE0F5}.Release|x64.ActiveCfg = Release|Any CPU 43 | {4EFBE030-E33C-41D2-B7DA-BF40412BE0F5}.Release|x64.Build.0 = Release|Any CPU 44 | {B031BD0F-A4B5-4BF9-A91C-4BCE7C11B32E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 45 | {B031BD0F-A4B5-4BF9-A91C-4BCE7C11B32E}.Debug|Any CPU.Build.0 = Debug|Any CPU 46 | {B031BD0F-A4B5-4BF9-A91C-4BCE7C11B32E}.Debug|x64.ActiveCfg = Debug|Any CPU 47 | {B031BD0F-A4B5-4BF9-A91C-4BCE7C11B32E}.Debug|x64.Build.0 = Debug|Any CPU 48 | {B031BD0F-A4B5-4BF9-A91C-4BCE7C11B32E}.Release|Any CPU.ActiveCfg = Release|Any CPU 49 | {B031BD0F-A4B5-4BF9-A91C-4BCE7C11B32E}.Release|Any CPU.Build.0 = Release|Any CPU 50 | {B031BD0F-A4B5-4BF9-A91C-4BCE7C11B32E}.Release|x64.ActiveCfg = Release|Any CPU 51 | {B031BD0F-A4B5-4BF9-A91C-4BCE7C11B32E}.Release|x64.Build.0 = Release|Any CPU 52 | {95F6A2BE-B7BA-4D93-8A8E-FAFF909D805D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 53 | {95F6A2BE-B7BA-4D93-8A8E-FAFF909D805D}.Debug|Any CPU.Build.0 = Debug|Any CPU 54 | {95F6A2BE-B7BA-4D93-8A8E-FAFF909D805D}.Debug|x64.ActiveCfg = Debug|Any CPU 55 | {95F6A2BE-B7BA-4D93-8A8E-FAFF909D805D}.Debug|x64.Build.0 = Debug|Any CPU 56 | {95F6A2BE-B7BA-4D93-8A8E-FAFF909D805D}.Release|Any CPU.ActiveCfg = Release|Any CPU 57 | {95F6A2BE-B7BA-4D93-8A8E-FAFF909D805D}.Release|Any CPU.Build.0 = Release|Any CPU 58 | {95F6A2BE-B7BA-4D93-8A8E-FAFF909D805D}.Release|x64.ActiveCfg = Release|Any CPU 59 | {95F6A2BE-B7BA-4D93-8A8E-FAFF909D805D}.Release|x64.Build.0 = Release|Any CPU 60 | {E245F6F5-A66A-46D4-9350-6152AAB0DA1B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 61 | {E245F6F5-A66A-46D4-9350-6152AAB0DA1B}.Debug|Any CPU.Build.0 = Debug|Any CPU 62 | {E245F6F5-A66A-46D4-9350-6152AAB0DA1B}.Debug|x64.ActiveCfg = Debug|Any CPU 63 | {E245F6F5-A66A-46D4-9350-6152AAB0DA1B}.Debug|x64.Build.0 = Debug|Any CPU 64 | {E245F6F5-A66A-46D4-9350-6152AAB0DA1B}.Release|Any CPU.ActiveCfg = Release|Any CPU 65 | {E245F6F5-A66A-46D4-9350-6152AAB0DA1B}.Release|Any CPU.Build.0 = Release|Any CPU 66 | {E245F6F5-A66A-46D4-9350-6152AAB0DA1B}.Release|x64.ActiveCfg = Release|Any CPU 67 | {E245F6F5-A66A-46D4-9350-6152AAB0DA1B}.Release|x64.Build.0 = Release|Any CPU 68 | {4C346307-5941-495D-B48D-CA564E51897D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 69 | {4C346307-5941-495D-B48D-CA564E51897D}.Debug|Any CPU.Build.0 = Debug|Any CPU 70 | {4C346307-5941-495D-B48D-CA564E51897D}.Debug|x64.ActiveCfg = Debug|Any CPU 71 | {4C346307-5941-495D-B48D-CA564E51897D}.Debug|x64.Build.0 = Debug|Any CPU 72 | {4C346307-5941-495D-B48D-CA564E51897D}.Release|Any CPU.ActiveCfg = Release|Any CPU 73 | {4C346307-5941-495D-B48D-CA564E51897D}.Release|Any CPU.Build.0 = Release|Any CPU 74 | {4C346307-5941-495D-B48D-CA564E51897D}.Release|x64.ActiveCfg = Release|Any CPU 75 | {4C346307-5941-495D-B48D-CA564E51897D}.Release|x64.Build.0 = Release|Any CPU 76 | EndGlobalSection 77 | GlobalSection(SolutionProperties) = preSolution 78 | HideSolutionNode = FALSE 79 | EndGlobalSection 80 | GlobalSection(NestedProjects) = preSolution 81 | {B031BD0F-A4B5-4BF9-A91C-4BCE7C11B32E} = {EAA4D121-2849-48D0-AE06-A64B580A3A89} 82 | {4C346307-5941-495D-B48D-CA564E51897D} = {EAA4D121-2849-48D0-AE06-A64B580A3A89} 83 | EndGlobalSection 84 | GlobalSection(ExtensibilityGlobals) = postSolution 85 | SolutionGuid = {F5E7ED41-D33A-4368-AF8C-0B0EAA0857AD} 86 | EndGlobalSection 87 | EndGlobal 88 | -------------------------------------------------------------------------------- /src/Ocelot.GrpcHttpGateway/Configuration/GrpcGatewayExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | using Microsoft.Extensions.DependencyInjection.Extensions; 3 | using Ocelot.DependencyInjection; 4 | 5 | namespace Ocelot.GrpcHttpGateway 6 | { 7 | public static class GrpcGatewayExtensions 8 | { 9 | public static IOcelotBuilder AddGrpcHttpGateway(this IOcelotBuilder builder) 10 | { 11 | builder.Services.AddGrpcHttpGateway(); 12 | return builder; 13 | } 14 | 15 | private static IServiceCollection AddGrpcHttpGateway(this IServiceCollection services) 16 | { 17 | services.TryAddSingleton(); 18 | services.TryAddSingleton(); 19 | services.TryAddTransient(); 20 | return services; 21 | } 22 | 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Ocelot.GrpcHttpGateway/Configuration/GrpcPipelineConfigurationExtensions.cs: -------------------------------------------------------------------------------- 1 | using Ocelot.Authentication.Middleware; 2 | using Ocelot.Authorisation.Middleware; 3 | using Ocelot.Cache.Middleware; 4 | using Ocelot.Claims.Middleware; 5 | using Ocelot.DownstreamUrlCreator.Middleware; 6 | using Ocelot.Headers.Middleware; 7 | using Ocelot.LoadBalancer.Middleware; 8 | using Ocelot.Middleware; 9 | using Ocelot.Middleware.Pipeline; 10 | using Ocelot.QueryStrings.Middleware; 11 | using Ocelot.RateLimit.Middleware; 12 | using Ocelot.Request.Middleware; 13 | using Ocelot.RequestId.Middleware; 14 | using System; 15 | using System.Threading.Tasks; 16 | 17 | namespace Ocelot.GrpcHttpGateway 18 | { 19 | public static class GrpcPipelineConfigurationExtensions 20 | { 21 | 22 | public static OcelotPipelineConfiguration AddGrpcHttpGateway(this OcelotPipelineConfiguration config) 23 | { 24 | config.MapWhenOcelotPipeline.Add(builder => builder.AddOrleansHttpGateway(config)); 25 | return config; 26 | } 27 | /// 28 | /// 29 | /// 30 | private static Func AddOrleansHttpGateway(this IOcelotPipelineBuilder builder, OcelotPipelineConfiguration pipelineConfiguration) 31 | { 32 | 33 | // Now we have the ds route we can transform headers and stuff? 34 | builder.UseHttpHeadersTransformationMiddleware(); 35 | 36 | // Initialises downstream request 37 | builder.UseDownstreamRequestInitialiser(); 38 | 39 | // We check whether the request is ratelimit, and if there is no continue processing 40 | builder.UseRateLimiting(); 41 | 42 | // This adds or updates the request id (initally we try and set this based on global config in the error handling middleware) 43 | // If anything was set at global level and we have a different setting at re route level the global stuff will be overwritten 44 | // This means you can get a scenario where you have a different request id from the first piece of middleware to the request id middleware. 45 | builder.UseRequestIdMiddleware(); 46 | 47 | // Allow pre authentication logic. The idea being people might want to run something custom before what is built in. 48 | builder.UseIfNotNull(pipelineConfiguration.PreAuthenticationMiddleware); 49 | 50 | // Now we know where the client is going to go we can authenticate them. 51 | // We allow the ocelot middleware to be overriden by whatever the 52 | // user wants 53 | if (pipelineConfiguration.AuthenticationMiddleware == null) 54 | { 55 | builder.UseAuthenticationMiddleware(); 56 | } 57 | else 58 | { 59 | builder.Use(pipelineConfiguration.AuthenticationMiddleware); 60 | } 61 | 62 | // The next thing we do is look at any claims transforms in case this is important for authorisation 63 | builder.UseClaimsBuilderMiddleware(); 64 | 65 | // Allow pre authorisation logic. The idea being people might want to run something custom before what is built in. 66 | builder.UseIfNotNull(pipelineConfiguration.PreAuthorisationMiddleware); 67 | 68 | // Now we have authenticated and done any claims transformation we 69 | // can authorise the request 70 | // We allow the ocelot middleware to be overriden by whatever the 71 | // user wants 72 | if (pipelineConfiguration.AuthorisationMiddleware == null) 73 | { 74 | builder.UseAuthorisationMiddleware(); 75 | } 76 | else 77 | { 78 | builder.Use(pipelineConfiguration.AuthorisationMiddleware); 79 | } 80 | 81 | // Now we can run any header transformation logic 82 | builder.UseHttpRequestHeadersBuilderMiddleware(); 83 | 84 | // Allow the user to implement their own query string manipulation logic 85 | builder.UseIfNotNull(pipelineConfiguration.PreQueryStringBuilderMiddleware); 86 | 87 | // Now we can run any query string transformation logic 88 | builder.UseQueryStringBuilderMiddleware(); 89 | // Get the load balancer for this request 90 | builder.UseLoadBalancingMiddleware(); 91 | 92 | // This takes the downstream route we retrieved earlier and replaces any placeholders with the variables that should be used 93 | builder.UseDownstreamUrlCreatorMiddleware(); 94 | 95 | // Not sure if this is the best place for this but we use the downstream url 96 | // as the basis for our cache key. 97 | builder.UseOutputCacheMiddleware(); 98 | 99 | 100 | builder.UseGrpcHttpMiddleware(); 101 | 102 | //builder.UseHttpRequesterMiddleware(); 103 | 104 | 105 | 106 | return (context) => 107 | { 108 | return context.DownstreamReRoute.DownstreamScheme.Equals("grpc", StringComparison.OrdinalIgnoreCase); 109 | }; 110 | } 111 | 112 | private static void UseIfNotNull(this IOcelotPipelineBuilder builder, 113 | Func, Task> middleware) 114 | { 115 | if (middleware != null) 116 | { 117 | builder.Use(middleware); 118 | } 119 | } 120 | } 121 | 122 | } 123 | -------------------------------------------------------------------------------- /src/Ocelot.GrpcHttpGateway/GrpcHttpMiddleware.cs: -------------------------------------------------------------------------------- 1 | using Grpc.Core; 2 | using Microsoft.AspNetCore.Http; 3 | using Microsoft.Extensions.Options; 4 | using Newtonsoft.Json; 5 | using Ocelot.Errors; 6 | using Ocelot.Logging; 7 | using Ocelot.Middleware; 8 | using Ocelot.Responses; 9 | using System; 10 | using System.Net; 11 | using System.Net.Http; 12 | using System.Threading; 13 | using System.Threading.Tasks; 14 | 15 | namespace Ocelot.GrpcHttpGateway 16 | { 17 | public class GrpcHttpMiddleware : OcelotMiddleware 18 | { 19 | private readonly OcelotRequestDelegate next; 20 | private readonly IGrpcChannelFactory grpcChannelFactory; 21 | private readonly IGrpcServiceDescriptor grpcServiceDescriptor; 22 | private readonly IGrpcRequestBuilder grpcRequestBuilder; 23 | 24 | public GrpcHttpMiddleware(OcelotRequestDelegate next, 25 | IGrpcChannelFactory grpcChannelFactory, 26 | IGrpcServiceDescriptor grpcServiceDescriptor, 27 | IGrpcRequestBuilder grpcRequestBuilder, 28 | IOcelotLoggerFactory factory) : base(factory.CreateLogger()) 29 | { 30 | this.next = next; 31 | this.grpcChannelFactory = grpcChannelFactory; 32 | this.grpcServiceDescriptor = grpcServiceDescriptor; 33 | this.grpcRequestBuilder = grpcRequestBuilder; 34 | } 35 | 36 | public async Task Invoke(DownstreamContext context) 37 | { 38 | string resultMessage = string.Empty; 39 | var httpStatusCode = HttpStatusCode.OK; 40 | var buildRequest = grpcRequestBuilder.BuildRequest(context); 41 | if (buildRequest.IsError) 42 | { 43 | resultMessage = "bad request"; 44 | httpStatusCode = HttpStatusCode.BadRequest; 45 | Logger.LogDebug(resultMessage); 46 | //SetPipelineError(context, buildRequest.Errors); 47 | } 48 | else 49 | { 50 | try 51 | { 52 | //缓存连接应该使用服务发现或执行健康检查,不如会等太久 53 | var channel = grpcChannelFactory.GetGrpcChannel(context.DownstreamRequest.Host, context.DownstreamRequest.Port); 54 | var client = new GrpcClient(channel); 55 | 56 | resultMessage = await client.InvokeMethodAsync(buildRequest.Data.GrpcMethod, buildRequest.Data.RequestMessage); 57 | } 58 | catch (RpcException ex) 59 | { 60 | httpStatusCode = HttpStatusCode.InternalServerError; 61 | resultMessage = $"rpc exception."; 62 | Logger.LogError($"{ex.StatusCode}--{ex.Message}", ex); 63 | } 64 | catch (Exception ex) 65 | { 66 | httpStatusCode = HttpStatusCode.ServiceUnavailable; 67 | resultMessage = $"error in request grpc service."; 68 | //SetPipelineError(context, new UnknownError(error)); 69 | Logger.LogError($"{resultMessage}--{context.DownstreamRequest.ToUri()}", ex); 70 | } 71 | } 72 | OkResponse httpResponse = new OkResponse(new GrpcHttpContent(resultMessage)); 73 | context.HttpContext.Response.ContentType = "application/json"; 74 | context.DownstreamResponse = new DownstreamResponse(httpResponse.Data, httpStatusCode, httpResponse.Data.Headers); 75 | } 76 | 77 | 78 | 79 | } 80 | 81 | } -------------------------------------------------------------------------------- /src/Ocelot.GrpcHttpGateway/GrpcHttpMiddlewareExtensions.cs: -------------------------------------------------------------------------------- 1 | using Ocelot.Middleware.Pipeline; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace Ocelot.GrpcHttpGateway 7 | { 8 | public static class GrpcHttpMiddlewareExtensions 9 | { 10 | public static IOcelotPipelineBuilder UseGrpcHttpMiddleware(this IOcelotPipelineBuilder builder) 11 | { 12 | return builder.UseMiddleware(); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/Ocelot.GrpcHttpGateway/GrpcRequest/GrpcClient/ArgsParser.cs: -------------------------------------------------------------------------------- 1 | using Google.Protobuf; 2 | using Grpc.Core; 3 | using System; 4 | 5 | namespace Ocelot.GrpcHttpGateway 6 | { 7 | public static class ArgsParser where T : class, IMessage 8 | { 9 | public static MessageParser Parser = new MessageParser(() => Activator.CreateInstance()); 10 | public static Marshaller Marshaller = Marshallers.Create((arg) => MessageExtensions.ToByteArray(arg), Parser.ParseFrom); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/Ocelot.GrpcHttpGateway/GrpcRequest/GrpcClient/GrpcChannelFactory.cs: -------------------------------------------------------------------------------- 1 | using Grpc.Core; 2 | using System; 3 | using System.Collections.Concurrent; 4 | using System.Collections.Generic; 5 | using System.Text; 6 | 7 | namespace Ocelot.GrpcHttpGateway 8 | { 9 | public class GrpcChannelFactory : IGrpcChannelFactory 10 | { 11 | private readonly object _syncObj = new object(); 12 | private ConcurrentDictionary grpcServices; 13 | 14 | public GrpcChannelFactory() 15 | { 16 | grpcServices = new ConcurrentDictionary(); 17 | } 18 | public Channel GetGrpcChannel(string address, int port) 19 | { 20 | string key = $"{address}:{port}"; 21 | if (grpcServices.TryGetValue(key, out Channel channel)) 22 | return channel; 23 | channel = new Channel(key, ChannelCredentials.Insecure); 24 | return grpcServices.GetOrAdd(key, channel); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Ocelot.GrpcHttpGateway/GrpcRequest/GrpcClient/GrpcClient.cs: -------------------------------------------------------------------------------- 1 | using Google.Protobuf; 2 | using Google.Protobuf.Reflection; 3 | using Grpc.Core; 4 | using System; 5 | using System.Linq; 6 | using System.Reflection; 7 | using System.Threading; 8 | using System.Threading.Tasks; 9 | 10 | namespace Ocelot.GrpcHttpGateway 11 | { 12 | public class GrpcClient : ClientBase 13 | { 14 | public GrpcClient(Channel channel) : base(channel) { } 15 | public GrpcClient(CallInvoker callInvoker) : base(callInvoker) { } 16 | public GrpcClient() : base() { } 17 | protected GrpcClient(ClientBaseConfiguration configuration) : base(configuration) { } 18 | 19 | public K Invoke(MethodDescriptor method, T request) where T : class, IMessage where K : class, IMessage 20 | { 21 | var _method = GrpcMethod.GetMethod(method); 22 | return CallInvoker.BlockingUnaryCall(_method, null, GetDefaultCallOptions(), request); 23 | } 24 | public AsyncUnaryCall InvokeAsync(MethodDescriptor method, T request) where T : class, IMessage where K : class, IMessage 25 | { 26 | var _method = GrpcMethod.GetMethod(method); 27 | return CallInvoker.AsyncUnaryCall(_method, null, GetDefaultCallOptions(), request); 28 | } 29 | public virtual string Invoke(MethodDescriptor method, string message) where T : class, IMessage where K : class, IMessage 30 | { 31 | var req = ArgsParser.Parser.ParseJson(message); 32 | var _method = GrpcMethod.GetMethod(method); 33 | return CallInvoker.BlockingUnaryCall(_method, null, GetDefaultCallOptions(), req).ToString(); 34 | } 35 | public virtual async Task InvokeAsync(MethodDescriptor method, string message) 36 | where T : class, IMessage where K : class, IMessage 37 | { 38 | return await InvokeAsync(method, message, GetDefaultCallOptions()); 39 | } 40 | public virtual async Task InvokeAsync(MethodDescriptor method, string message, CallOptions callOptions) 41 | where T : class, IMessage where K : class, IMessage 42 | { 43 | return await InvokeAsync(method, message, GetDefaultCallOptions(), null); 44 | } 45 | public virtual async Task InvokeAsync(MethodDescriptor methodDescript, string message, CallOptions callOptions, string host) 46 | where T : class, IMessage where K : class, IMessage 47 | { 48 | var request = ArgsParser.Parser.ParseJson(message); 49 | var method = GrpcMethod.GetMethod(methodDescript); 50 | K response = await CallInvoker.AsyncUnaryCall(method, host, callOptions, request); 51 | return response.ToString(); 52 | } 53 | 54 | protected override GrpcClient NewInstance(ClientBaseConfiguration configuration) 55 | { 56 | return new GrpcClient(configuration); 57 | } 58 | ///需要添加CallOptions参数,为请求添加更多header 59 | public object InvokeMethod(MethodDescriptor method, string message) 60 | { 61 | object[] obj = { method, message }; 62 | string name = "Invoke"; 63 | Type[] parameterType = { typeof(MethodDescriptor), typeof(string) }; 64 | return InvokeMethod(this, method, parameterType, name, obj); 65 | } 66 | public async Task InvokeMethodAsync(MethodDescriptor method, string message) 67 | { 68 | return await InvokeMethodAsync(method, message, GetDefaultCallOptions()); 69 | } 70 | public async Task InvokeMethodAsync(MethodDescriptor method, string message, CallOptions callOptions) 71 | { 72 | return await InvokeMethodAsync(method, message, GetDefaultCallOptions(), null); 73 | 74 | //object[] obj = { method, message, callOptions }; 75 | //string name = "InvokeAsync"; 76 | //Type[] parameterType = { typeof(MethodDescriptor), typeof(string),typeof(CallOptions) }; 77 | //Type[] templateTypeSet = { method.InputType.ClrType, method.OutputType.ClrType }; 78 | 79 | //var task = InvokeMethodAsync(typeof(GrpcClient), this, templateTypeSet, parameterType, name, obj); 80 | //return await (task as Task); 81 | } 82 | public async Task InvokeMethodAsync(MethodDescriptor method, string message, CallOptions callOptions, string host) 83 | { 84 | object[] obj = { method, message, callOptions, host }; 85 | string name = "InvokeAsync"; 86 | Type[] parameterType = { typeof(MethodDescriptor), typeof(string), typeof(CallOptions), typeof(string) }; 87 | Type[] templateTypeSet = { method.InputType.ClrType, method.OutputType.ClrType }; 88 | 89 | var task = InvokeMethodAsync(typeof(GrpcClient), this, templateTypeSet, parameterType, name, obj); 90 | return await (task as Task); 91 | } 92 | 93 | static object InvokeMethod(GrpcClient grpcClient, MethodDescriptor method, Type[] parameterType, string name, params object[] args) 94 | { 95 | Type clsType = typeof(GrpcClient); 96 | Type[] templateTypeSet = { method.InputType.ClrType, method.OutputType.ClrType }; 97 | MethodInfo methodInfo = clsType.GetMethod(name, parameterType); 98 | methodInfo = methodInfo.MakeGenericMethod(templateTypeSet); 99 | return methodInfo.Invoke(grpcClient, args); 100 | } 101 | [Obsolete("暂不使用")] 102 | static object InvokeMethod(Type clsType, object classInstance, Type[] templateTypeSet, Type[] parameterType, string name, params object[] args) 103 | { 104 | MethodInfo methodInfo = clsType.GetMethod(name, parameterType); 105 | methodInfo = methodInfo.MakeGenericMethod(templateTypeSet); 106 | return methodInfo.Invoke(classInstance, args); 107 | } 108 | /// 109 | /// 不转task了 110 | /// 111 | static object InvokeMethodAsync(Type clsType, object classInstance, Type[] templateTypeSet, Type[] parameterType, string name, params object[] args) 112 | { 113 | MethodInfo method = clsType.GetMethod(name, parameterType); 114 | method = method.MakeGenericMethod(templateTypeSet); 115 | return method.Invoke(classInstance, args); 116 | } 117 | private CallOptions GetDefaultCallOptions(int second = 30) 118 | { 119 | return new CallOptions(null, DateTime.UtcNow.AddSeconds(second), default(CancellationToken)); 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/Ocelot.GrpcHttpGateway/GrpcRequest/GrpcClient/GrpcMethod.cs: -------------------------------------------------------------------------------- 1 | using Google.Protobuf; 2 | using Google.Protobuf.Reflection; 3 | using Grpc.Core; 4 | using System; 5 | using System.Collections.Concurrent; 6 | using System.Collections.Generic; 7 | 8 | namespace Ocelot.GrpcHttpGateway 9 | { 10 | /// 11 | /// TODO:用SingletonDictionary进行改造 12 | /// 13 | /// 14 | /// 15 | public class GrpcMethod where TRequest : class, IMessage where KResult : class, IMessage 16 | { 17 | private static ConcurrentDictionary> methods 18 | = new ConcurrentDictionary>(); 19 | 20 | public static Method GetMethod(MethodDescriptor methodDescriptor) 21 | { 22 | Method method; 23 | if (methods.TryGetValue(methodDescriptor, out method)) 24 | return method; 25 | 26 | int mtype = 0; 27 | if (methodDescriptor.IsClientStreaming) 28 | mtype = 1; 29 | if (methodDescriptor.IsServerStreaming) 30 | mtype += 2; 31 | var methodType = (MethodType)Enum.ToObject(typeof(MethodType), mtype); 32 | 33 | var _method = new Method(methodType, methodDescriptor.Service.FullName 34 | , methodDescriptor.Name, ArgsParser.Marshaller, ArgsParser.Marshaller); 35 | 36 | methods.TryAdd(methodDescriptor, _method); 37 | 38 | return _method; 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Ocelot.GrpcHttpGateway/GrpcRequest/GrpcClient/IGrpcChannelFactory.cs: -------------------------------------------------------------------------------- 1 | using Grpc.Core; 2 | 3 | namespace Ocelot.GrpcHttpGateway 4 | { 5 | public interface IGrpcChannelFactory 6 | { 7 | Channel GetGrpcChannel(string address, int port); 8 | } 9 | } -------------------------------------------------------------------------------- /src/Ocelot.GrpcHttpGateway/GrpcRequest/GrpcHttpContent.cs: -------------------------------------------------------------------------------- 1 | using Google.Protobuf; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Net; 6 | using System.Net.Http; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace Ocelot.GrpcHttpGateway 11 | { 12 | public class GrpcHttpContent: HttpContent 13 | { 14 | private string result; 15 | 16 | public GrpcHttpContent(string result) 17 | { 18 | this.result = result; 19 | } 20 | 21 | protected override async Task SerializeToStreamAsync(Stream stream, TransportContext context) 22 | { 23 | var writer = new StreamWriter(stream); 24 | await writer.WriteAsync(result); 25 | await writer.FlushAsync(); 26 | } 27 | 28 | protected override bool TryComputeLength(out long length) 29 | { 30 | length = result.Length; 31 | return true; 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Ocelot.GrpcHttpGateway/GrpcRequest/GrpcRequest.cs: -------------------------------------------------------------------------------- 1 | using Google.Protobuf; 2 | using Google.Protobuf.Reflection; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Text; 6 | 7 | namespace Ocelot.GrpcHttpGateway 8 | { 9 | public class GrpcRequest 10 | { 11 | public MethodDescriptor GrpcMethod { get; set; } 12 | 13 | public string RequestMessage { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/Ocelot.GrpcHttpGateway/GrpcRequest/GrpcRequestBuilder.cs: -------------------------------------------------------------------------------- 1 | using Google.Protobuf; 2 | using Microsoft.AspNetCore.Http; 3 | using Ocelot.Logging; 4 | using Ocelot.Middleware; 5 | using Ocelot.Request.Mapper; 6 | using Ocelot.Responses; 7 | using System; 8 | using System.Collections.Generic; 9 | using System.IO; 10 | using System.Text; 11 | 12 | namespace Ocelot.GrpcHttpGateway 13 | { 14 | public class GrpcRequestBuilder : IGrpcRequestBuilder 15 | { 16 | private readonly IOcelotLogger logger; 17 | private IGrpcServiceDescriptor grpcServiceDescriptor; 18 | 19 | public GrpcRequestBuilder(IOcelotLoggerFactory factory, IGrpcServiceDescriptor grpcServiceDescriptor) 20 | { 21 | this.logger = factory.CreateLogger(); 22 | this.grpcServiceDescriptor = grpcServiceDescriptor; 23 | } 24 | 25 | public Response BuildRequest(DownstreamContext context) 26 | { 27 | var route = context.DownstreamRequest.AbsolutePath.Trim('/').Split('/'); 28 | if (route.Length !=2) 29 | { 30 | return SetError($"error request:{route},must do like this:http://domain:port/grpc/ServiceName/MethordName/"); 31 | } 32 | string svcName = route[0].ToUpper(); 33 | string methodName = route[1].ToUpper(); 34 | //判断是否存在对应grpc服务、方法 35 | var grpcDescript = grpcServiceDescriptor.GetGrpcDescript(); 36 | if (!grpcDescript.ContainsKey(svcName)) 37 | { 38 | return SetError($"service name is not defined.{svcName}"); 39 | } 40 | if (!grpcDescript[svcName].ContainsKey(methodName)) 41 | { 42 | return SetError($"method name is not defined.{methodName}"); 43 | } 44 | GrpcRequest grpcRequest = new GrpcRequest 45 | { 46 | GrpcMethod = grpcDescript[svcName][methodName] 47 | }; 48 | try 49 | { 50 | //需要替换Scheme 51 | context.DownstreamRequest.Scheme = "http"; 52 | var requestMessage = context.DownstreamRequest.ToHttpRequestMessage(); 53 | var stream = requestMessage.Content.ReadAsStreamAsync().Result; 54 | var encoding = context.HttpContext.Request.GetTypedHeaders().ContentType?.Encoding ?? Encoding.UTF8; 55 | using (var reader = new StreamReader(stream,encoding)) 56 | { 57 | grpcRequest.RequestMessage = reader.ReadToEnd(); 58 | } 59 | //兼容请求参数为空 60 | if (string.IsNullOrEmpty(grpcRequest.RequestMessage)) 61 | { 62 | grpcRequest.RequestMessage = "{}"; 63 | } 64 | 65 | } 66 | catch(Exception) 67 | { 68 | return SetError("request parameter error"); 69 | } 70 | context.DownstreamRequest.Scheme = "grpc"; 71 | return new OkResponse(grpcRequest); 72 | } 73 | 74 | ErrorResponse SetError(Exception exception) 75 | { 76 | return new ErrorResponse(new UnmappableRequestError(exception)); 77 | } 78 | ErrorResponse SetError(string message) 79 | { 80 | var exception = new Exception(message); 81 | return SetError(exception); 82 | } 83 | 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/Ocelot.GrpcHttpGateway/GrpcRequest/IGrpcRequestBuilder.cs: -------------------------------------------------------------------------------- 1 | using Ocelot.Middleware; 2 | using Ocelot.Responses; 3 | 4 | namespace Ocelot.GrpcHttpGateway 5 | { 6 | public interface IGrpcRequestBuilder 7 | { 8 | Response BuildRequest(DownstreamContext context); 9 | } 10 | } -------------------------------------------------------------------------------- /src/Ocelot.GrpcHttpGateway/GrpcRequest/UnknownError.cs: -------------------------------------------------------------------------------- 1 | using Ocelot.Errors; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace Ocelot.GrpcHttpGateway 7 | { 8 | public class UnknownError : Error 9 | { 10 | public UnknownError(string message) : base(message, OcelotErrorCode.UnknownError) 11 | { 12 | } 13 | 14 | public UnknownError(string message,Exception exception) : base(message, OcelotErrorCode.UnknownError) 15 | { 16 | } 17 | 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Ocelot.GrpcHttpGateway/Ocelot.GrpcHttpGateway.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/Ocelot.GrpcHttpGateway/Ocelot.GrpcHttpGateway.csproj.user: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | false 5 | 6 | -------------------------------------------------------------------------------- /src/Ocelot.GrpcHttpGateway/ServiceDescriptor/GrpcServiceDescriptor.cs: -------------------------------------------------------------------------------- 1 | using Google.Protobuf; 2 | using Google.Protobuf.Reflection; 3 | using System; 4 | using System.Collections.Concurrent; 5 | using System.Collections.Generic; 6 | using System.IO; 7 | using System.Linq; 8 | using System.Reflection; 9 | using System.Text; 10 | using System.Text.RegularExpressions; 11 | 12 | namespace Ocelot.GrpcHttpGateway 13 | { 14 | public class GrpcServiceDescriptor : IGrpcServiceDescriptor 15 | { 16 | protected ConcurrentDictionary> CurrentService = new ConcurrentDictionary>(); 17 | protected string pattern = "^System|^mscorlib|^Microsoft|^Autofac|^EntityFramework|^Fluent|^Newtonsoft|^netstandard|^Npgsql|^NLog|^MySql|^Anonymously|^DynamicProxyGenAssembly"; 18 | 19 | public GrpcServiceDescriptor() 20 | { 21 | LoadGrpcAssmbly(); 22 | } 23 | 24 | public virtual ConcurrentDictionary> GetGrpcDescript() 25 | { 26 | return CurrentService; 27 | } 28 | 29 | protected virtual void LoadGrpcAssmbly(string path = "") 30 | { 31 | if (string.IsNullOrEmpty(path)) 32 | path = AppContext.BaseDirectory; 33 | var assemblies = AppDomain.CurrentDomain.GetAssemblies().ToList(); 34 | foreach (var file in Directory.GetFiles(path, "*.dll")) 35 | { 36 | assemblies.Add(Assembly.LoadFile(file)); 37 | } 38 | foreach (var assembly in assemblies) 39 | { 40 | if (Regex.IsMatch(assembly.FullName, pattern, RegexOptions.IgnoreCase | RegexOptions.Compiled)) 41 | continue; 42 | GetGrpcDescript(assembly.GetTypes()); 43 | } 44 | 45 | } 46 | 47 | protected virtual void GetGrpcDescript(Type[] types) 48 | { 49 | //根据类名特点查找FileDescriptor 50 | var fileTypes = types.Where(type => type.Name.Contains("Reflection")); 51 | foreach (var type in fileTypes) 52 | { 53 | BindingFlags flag = BindingFlags.Static | BindingFlags.Public; 54 | var property = type.GetProperties(flag).FirstOrDefault(); 55 | if (property is null) 56 | continue; 57 | var fileDescriptor = property.GetValue(null) as FileDescriptor; 58 | if (fileDescriptor is null) 59 | continue; 60 | 61 | foreach (var svr in fileDescriptor.Services) 62 | { 63 | if (CurrentService.ContainsKey(svr.Name.ToUpper())) 64 | continue; 65 | if (CurrentService.TryAdd(svr.Name.ToUpper(), new ConcurrentDictionary())) 66 | foreach (var method in svr.Methods) 67 | { 68 | CurrentService[svr.Name.ToUpper()].TryAdd(method.Name.ToUpper(), method); 69 | } 70 | } 71 | } 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/Ocelot.GrpcHttpGateway/ServiceDescriptor/IGrpcServiceDescriptor.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Concurrent; 2 | using System.Collections.Generic; 3 | using Google.Protobuf.Reflection; 4 | 5 | namespace Ocelot.GrpcHttpGateway 6 | { 7 | public interface IGrpcServiceDescriptor 8 | { 9 | ConcurrentDictionary> GetGrpcDescript(); 10 | } 11 | } -------------------------------------------------------------------------------- /src/WebGateway/DefaultServiceDescriptor.cs: -------------------------------------------------------------------------------- 1 | using Google.Protobuf.Reflection; 2 | using Ocelot.GrpcHttpGateway; 3 | using System; 4 | using System.Collections.Concurrent; 5 | using System.Collections.Generic; 6 | using System.IO; 7 | using System.Linq; 8 | using System.Reflection; 9 | using System.Text.RegularExpressions; 10 | using System.Threading.Tasks; 11 | 12 | namespace WebGateway 13 | { 14 | public class DefaultServiceDescriptor : GrpcServiceDescriptor, IGrpcServiceDescriptor 15 | { 16 | 17 | public override ConcurrentDictionary> GetGrpcDescript() 18 | { 19 | return CurrentService; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/WebGateway/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using Microsoft.AspNetCore; 7 | using Microsoft.AspNetCore.Hosting; 8 | using Microsoft.Extensions.Configuration; 9 | using Microsoft.Extensions.Logging; 10 | 11 | namespace WebGateway 12 | { 13 | public class Program 14 | { 15 | static readonly string IP = "127.0.0.1"; 16 | static readonly string Port = "5000"; 17 | public static void Main(string[] args) 18 | { 19 | CreateWebHostBuilder(args).Build().Run(); 20 | } 21 | 22 | public static IWebHostBuilder CreateWebHostBuilder(string[] args) => 23 | WebHost.CreateDefaultBuilder(args) 24 | .UseStartup() 25 | .UseKestrel() 26 | .UseUrls($"http://{IP}:{Port}") 27 | .ConfigureAppConfiguration((hostingContext, builder) => 28 | { 29 | builder.AddJsonFile("ocelot.json", false, true); 30 | }); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/WebGateway/Properties/PublishProfiles/FolderProfile.pubxml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | FileSystem 9 | FileSystem 10 | Release 11 | Any CPU 12 | 13 | True 14 | False 15 | 6c443297-1e33-4001-8df0-b6d014fa157f 16 | bin\Debug\netcoreapp2.1\publish\ 17 | False 18 | netcoreapp2.1 19 | false 20 | <_IsPortable>true 21 | 22 | -------------------------------------------------------------------------------- /src/WebGateway/Properties/PublishProfiles/FolderProfile.pubxml.user: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | <_PublishTargetUrl>D:\source\Projects\GrpcProject\src\WaterIot\Test\WebGateway\bin\Debug\netcoreapp2.1\publish\ 10 | 11 | -------------------------------------------------------------------------------- /src/WebGateway/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:5000", 7 | "sslPort": 0 8 | } 9 | }, 10 | "profiles": { 11 | "IIS Express": { 12 | "commandName": "IISExpress", 13 | "launchBrowser": true, 14 | "environmentVariables": { 15 | "ASPNETCORE_ENVIRONMENT": "Development" 16 | } 17 | }, 18 | "WebGateway": { 19 | "commandName": "Project", 20 | "launchBrowser": true, 21 | "applicationUrl": "http://localhost:5000", 22 | "environmentVariables": { 23 | "ASPNETCORE_ENVIRONMENT": "Development" 24 | } 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /src/WebGateway/Startup.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Builder; 6 | using Microsoft.AspNetCore.Hosting; 7 | using Microsoft.AspNetCore.Http; 8 | using Microsoft.Extensions.Configuration; 9 | using Microsoft.Extensions.DependencyInjection; 10 | using Ocelot.DependencyInjection; 11 | using Ocelot.Middleware; 12 | using Ocelot.GrpcHttpGateway; 13 | using Microsoft.Extensions.DependencyInjection.Extensions; 14 | 15 | namespace WebGateway 16 | { 17 | public class Startup 18 | { 19 | public Startup(IConfiguration configuration) 20 | { 21 | Configuration = configuration; 22 | } 23 | 24 | public IConfiguration Configuration { get; } 25 | // This method gets called by the runtime. Use this method to add services to the container. 26 | // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 27 | public void ConfigureServices(IServiceCollection services) 28 | { 29 | 30 | services.AddOcelot(Configuration).AddGrpcHttpGateway(); 31 | services.AddSingleton(); 32 | } 33 | 34 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 35 | public void Configure(IApplicationBuilder app, IHostingEnvironment env) 36 | { 37 | if (env.IsDevelopment()) 38 | { 39 | app.UseDeveloperExceptionPage(); 40 | } 41 | 42 | app.UseOcelot(config => 43 | { 44 | config.AddGrpcHttpGateway(); 45 | }).Wait(); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/WebGateway/WebGateway.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netcoreapp2.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | PreserveNewest 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /src/WebGateway/WebGateway.csproj.user: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | ProjectDebugger 5 | 6 | 7 | WebGateway 8 | FolderProfile 9 | 10 | -------------------------------------------------------------------------------- /src/WebGateway/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "ConnectionStrings": { 3 | "DefaultConnection": "Server=(localdb)\\MSSQLLocalDB;Database=_CHANGE_ME;Trusted_Connection=True;MultipleActiveResultSets=true" 4 | }, 5 | "Logging": { 6 | "LogLevel": { 7 | "Default": "Warning" 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /src/WebGateway/ocelot.json: -------------------------------------------------------------------------------- 1 | { 2 | "ReRoutes": [ 3 | { 4 | "DownstreamPathTemplate": "/{url}", 5 | "DownstreamScheme": "http", 6 | "DownstreamHostAndPorts": [ 7 | { 8 | "Host": "127.0.0.1", 9 | "Port": "5001" 10 | } 11 | ], 12 | "UpstreamPathTemplate": "/lb1/{url}", 13 | "UpstreamHttpMethod": [ "Get", "Post" ] 14 | }, 15 | { 16 | "DownstreamPathTemplate": "/{url}", 17 | "DownstreamScheme": "http", 18 | "DownstreamHostAndPorts": [ 19 | { 20 | "Host": "127.0.0.1", 21 | "Port": "5002" 22 | } 23 | ], 24 | "UpstreamPathTemplate": "/lb2/{url}", 25 | "UpstreamHttpMethod": [ "Get", "Post" ] 26 | }, 27 | { 28 | "DownstreamPathTemplate": "/{url}", 29 | "DownstreamScheme": "http", 30 | "DownstreamHostAndPorts": [ 31 | { 32 | "Host": "127.0.0.1", 33 | "Port": 5001 34 | }, 35 | { 36 | "Host": "127.0.0.1", 37 | "Port": 5002 38 | } 39 | 40 | ], 41 | "UpstreamPathTemplate": "/lb/{url}", 42 | "LoadBalancerOptions": { 43 | "Type": "RoundRobin" 44 | }, 45 | "UpstreamHttpMethod": [ "Get", "Post" ] 46 | }, 47 | 48 | { 49 | "DownstreamPathTemplate": "/{url}", 50 | "DownstreamScheme": "grpc", 51 | "DownstreamHostAndPorts": [ 52 | { 53 | "Host": "127.0.0.1", 54 | "Port": 50001 55 | }, 56 | { 57 | "Host": "127.0.0.1", 58 | "Port": 50002 59 | } 60 | 61 | ], 62 | "UpstreamPathTemplate": "/grpc/{url}", 63 | "LoadBalancerOptions": { 64 | "Type": "RoundRobin" //LeastConnection,RoundRobin,NoLoadBalance(无或者服务发现) 65 | }, 66 | "UpstreamHttpMethod": [ "Get", "Post" ] 67 | } 68 | ] 69 | } --------------------------------------------------------------------------------