1GRPC Server Reflection Protocol 2=============================== 3 4This document describes server reflection as an optional extension for servers 5to assist clients in runtime construction of requests without having stub 6information precompiled into the client. 7 8The primary usecase for server reflection is to write (typically) command line 9debugging tools for talking to a grpc server. In particular, such a tool will 10take in a method and a payload (in human readable text format) send it to the 11server (typically in binary proto wire format), and then take the response and 12decode it to text to present to the user. 13 14This broadly involves two problems: determining what formats (which protobuf 15messages) a server’s method uses, and determining how to convert messages 16between human readable format and the (likely binary) wire format. 17 18## Method reflection 19 20We want to be able to answer the following queries: 21 1. What methods does a server export? 22 2. For a particular method, how do we call it? 23Specifically, what are the names of the methods, are those methods unary or 24streaming, and what are the types of the argument and result? 25 26``` 27#TODO(dklempner): link to an actual .proto later. 28package grpc.reflection.v1alpha; 29 30message ListApisRequest { 31} 32 33message ListApisResponse { 34 repeated google.protobuf.Api apis = 1; 35} 36 37message GetMethodRequest { 38 string method = 1; 39} 40message GetMethodResponse { 41 google.protobuf.Method method = 1; 42} 43 44service ServerReflection { 45 rpc ListApis (ListApisRequest) returns (ListApisResponse); 46 rpc GetMethod (GetMethodRequest) returns (GetMethodResponse); 47} 48``` 49 50Note that a server is under no obligation to return a complete list of all 51methods it supports. For example, a reverse proxy may support server reflection 52for methods implemented directly on the proxy but not enumerate all methods 53supported by its backends. 54 55 56### Open questions on method reflection 57 * Consider how to extend this protocol to support non-protobuf methods. 58 59## Argument reflection 60The second half of the problem is converting between the human readable 61input/output of a debugging tool and the binary format understood by the 62method. 63 64This is obviously dependent on protocol type. At one extreme, if both the 65server and the debugging tool accept JSON, there may be no need for such a 66conversion in the first place. At the opposite extreme, a server using a custom 67binary format has no hope of being supported by a generic system. The 68intermediate interesting common case is a server which speaks binary-proto and 69a debugging client which speaks either ascii-proto or json-proto. 70 71One approach would be to require servers directly support human readable input. 72In the future method reflection may be extended to document such support, 73should it become widespread or standardized. 74 75## Protobuf descriptors 76 77A second would be for the server to export its 78google::protobuf::DescriptorDatabase over the wire. This is very easy to 79implement in C++, and Google implementations of a similar protocol already 80exist in C++, Go, and Java. 81 82This protocol mostly returns FileDescriptorProtos, which are a proto encoding 83of a parsed .proto file. It supports four queries: 84 1. The FileDescriptorProto for a given file name 85 2. The FileDescriptorProto for the file with a given symbol 86 3. The FileDescriptorProto for the file with a given extension 87 4. The list of known extension tag numbers of a given type 88 89These directly correspond to the methods of 90google::protobuf::DescriptorDatabase. Note that this protocol includes support 91for extensions, which have been removed from proto3 but are still in widespread 92use in Google’s codebase. 93 94Because most usecases will require also requesting the transitive dependencies 95of requested files, the queries will also return all transitive dependencies of 96the returned file. Should interesting usecases for non-transitive queries turn 97up later, we can easily extend the protocol to support them. 98 99### Reverse proxy traversal 100 101One potential issue with naive reverse proxies is that, while any individual 102server will have a consistent and valid picture of the proto DB which is 103sufficient to handle incoming requests, incompatibilities will arise if the 104backend servers have a mix of builds. For example, if a given message is moved 105from foo.proto to bar.proto, and the client requests foo.proto from an old 106server and bar.proto from a new server, the resulting database will have a 107double definition. 108 109To solve this problem, the protocol is structured as a bidirectional stream, 110ensuring all related requests go to a single server. This has the additional 111benefit that overlapping recursive requests don’t require sending a lot of 112redundant information, because there is a single stream to maintain context 113between queries. 114 115``` 116package grpc.reflection.v1alpha; 117message DescriptorDatabaseRequest { 118 string host = 1; 119 oneof message_request { 120 string files_for_file_name = 3; 121 string files_for_symbol_name = 4; 122 FileContainingExtensionRequest file_containing_extension = 5; 123 string list_all_extensions_of_type = 6; 124 } 125} 126 127message FileContainingExtensionRequest { 128 string base_message = 1; 129 int64 extension_id = 2; 130} 131 132message DescriptorDatabaseResponse { 133 string valid_host = 1; 134 DescriptorDatabaseRequest original_request = 2; 135 oneof message_response { 136 // These are proto2 type google.protobuf.FileDescriptorProto, but 137 // we avoid taking a dependency on descriptor.proto, which uses 138 // proto2 only features, by making them opaque 139 // bytes instead 140 repeated bytes fd_proto = 4; 141 ListAllExtensionsResponse extensions_response = 5; 142 // Notably includes error code 5, NOT FOUND 143 int32 error_code = 6; 144 } 145} 146 147message ListAllExtensionsResponse { 148 string base_type_name; 149 repeated int64 extension_number; 150} 151 152service ProtoDescriptorDatabase { 153 rpc DescriptorDatabaseInfo(stream DescriptorDatabaseRequest) returns (stream DescriptorDatabaseResponse); 154} 155``` 156 157Any given request must either result in an error code or an answer, usually in 158the form of a series of FileDescriptorProtos with the requested file itself 159and all previously unsent transitive imports of that file. Servers may track 160which FileDescriptorProtos have been sent on a given stream, for a given value 161of valid_host, and avoid sending them repeatedly for overlapping requests. 162 163| message_request message | Result | 164| files_for_file_name | transitive closure of file name | 165| files_for_symbol_name | transitive closure file containing symbol | 166| file_containing_extension | transitive closure of file containing a given extension number of a given symbol | 167| list_all_extensions_of_type | ListAllExtensionsResponse containing all known extension numbers of a given type | 168 169At some point it would make sense to additionally also support any.proto’s 170format. Note that known any.proto messages can be queried by symbol using this 171protocol even without any such support, by parsing the url and extracting the 172symbol name from it. 173 174## Language specific implementation thoughts 175All of the information needed to implement Proto reflection is available to the 176code generator, but I’m not certain we actually generate this in every 177language. If the proto implementation in the language doesn’t have something 178like google::protobuf::DescriptorPool the grpc implementation for that language 179will need to index those FileDescriptorProtos by file and symbol and imports. 180 181One issue is that some grpc implementations are very loosely coupled with 182protobufs; in such implementations it probably makes sense to split apart these 183reflection APIs so as not to take an additional proto dependency. 184 185## Known Implementations 186 187Enabling server reflection differs language-to-language. Here are links to docs relevant to 188each language: 189 190- [Java](https://github.com/grpc/grpc-java/blob/master/documentation/server-reflection-tutorial.md#enable-server-reflection) 191- [Go](https://github.com/grpc/grpc-go/blob/master/Documentation/server-reflection-tutorial.md#enable-server-reflection) 192- [C++](https://grpc.io/grpc/cpp/md_doc_server_reflection_tutorial.html) 193- [C#](https://github.com/grpc/grpc/blob/master/doc/csharp/server_reflection.md) 194- [Python](https://github.com/grpc/grpc/blob/master/doc/python/server_reflection.md) 195- Ruby: not yet implemented [#2567](https://github.com/grpc/grpc/issues/2567) 196- Node: not yet implemented [#2568](https://github.com/grpc/grpc/issues/2568) 197