@@ -150,6 +150,87 @@ StatusOr<std::map<std::string, std::string> > ExtractParamsFromMethod(
150150 return res;
151151}
152152
153+ /* *
154+ * Cache the state required to implement `GetMetadata()`.
155+ *
156+ * The implementation of `GetMetadata()` requires substantial state, including
157+ * a gRPC server and completion queues. On Windows it is fairly expensive to
158+ * startup and shutdown these resources, we need to cache them so the tests run
159+ * fast.
160+ */
161+ class GetMetadataState {
162+ public:
163+ GetMetadataState () {
164+ // Start the generic server.
165+ grpc::ServerBuilder builder;
166+ builder.RegisterAsyncGenericService (&generic_service_);
167+ srv_cq_ = builder.AddCompletionQueue ();
168+ server_ = builder.BuildAndStart ();
169+ }
170+ ~GetMetadataState () {
171+ // Shut everything down.
172+ server_->Shutdown (std::chrono::system_clock::now ());
173+ srv_cq_->Shutdown ();
174+ cli_cq_.Shutdown ();
175+
176+ // Drain completion queues.
177+ void * placeholder;
178+ bool ok;
179+ while (srv_cq_->Next (&placeholder, &ok))
180+ ;
181+ while (cli_cq_.Next (&placeholder, &ok))
182+ ;
183+ }
184+
185+ std::multimap<std::string, std::string> GetMetadata (
186+ grpc::ClientContext& context) {
187+ // Set the deadline to far in the future. If the deadline is in the past,
188+ // gRPC doesn't send the initial metadata at all (which makes sense, given
189+ // that the context is already expired). The `context` is destroyed by this
190+ // function anyway, so we're not making things worse by changing the
191+ // deadline.
192+ context.set_deadline (std::chrono::system_clock::now () +
193+ std::chrono::hours (24 ));
194+
195+ // Send some garbage with the supplied context.
196+ grpc::GenericStub generic_stub (
197+ server_->InProcessChannel (grpc::ChannelArguments ()));
198+
199+ auto cli_stream =
200+ generic_stub.PrepareCall (&context, " made_up_method" , &cli_cq_);
201+ cli_stream->StartCall (nullptr );
202+ bool ok;
203+ void * placeholder;
204+ cli_cq_.Next (&placeholder, &ok); // actually start the client call
205+
206+ // Receive the garbage with the supplied context.
207+ grpc::GenericServerContext server_context;
208+ grpc::GenericServerAsyncReaderWriter reader_writer (&server_context);
209+ generic_service_.RequestCall (&server_context, &reader_writer, srv_cq_.get (),
210+ srv_cq_.get (), nullptr );
211+ srv_cq_->Next (&placeholder, &ok); // actually receive the data
212+
213+ // Now we've got the data - save it before cleaning up.
214+ std::multimap<std::string, std::string> res;
215+ auto const & cli_md = server_context.client_metadata ();
216+ std::transform (cli_md.begin (), cli_md.end (),
217+ std::inserter (res, res.begin ()),
218+ [](std::pair<grpc::string_ref, grpc::string_ref> const & md) {
219+ return std::make_pair (
220+ std::string (md.first .data (), md.first .length ()),
221+ std::string (md.second .data (), md.second .length ()));
222+ });
223+
224+ return res;
225+ }
226+
227+ private:
228+ grpc::CompletionQueue cli_cq_;
229+ grpc::AsyncGenericService generic_service_;
230+ std::unique_ptr<grpc::ServerCompletionQueue> srv_cq_;
231+ std::unique_ptr<grpc::Server> server_;
232+ };
233+
153234} // namespace
154235
155236/* *
@@ -226,59 +307,8 @@ Status IsContextMDValid(
226307
227308std::multimap<std::string, std::string> GetMetadata (
228309 grpc::ClientContext& context) {
229- // Set the deadline to far in the future. If the deadline is in the past, gRPC
230- // doesn't send the initial metadata at all (which makes sense, given that the
231- // context is already expired). The `context` is destroyed by this function
232- // anyway, so we're not making things worse by changing the deadline.
233- context.set_deadline (std::chrono::system_clock::now () +
234- std::chrono::hours (24 ));
235-
236- // Start the generic server.
237- grpc::ServerBuilder builder;
238- grpc::AsyncGenericService generic_service;
239- builder.RegisterAsyncGenericService (&generic_service);
240- auto srv_cq = builder.AddCompletionQueue ();
241- auto server = builder.BuildAndStart ();
242-
243- // Send some garbage with the supplied context.
244- grpc::GenericStub generic_stub (
245- server->InProcessChannel (grpc::ChannelArguments ()));
246- grpc::CompletionQueue cli_cq;
247- auto cli_stream =
248- generic_stub.PrepareCall (&context, " made_up_method" , &cli_cq);
249- cli_stream->StartCall (nullptr );
250- bool ok;
251- void * placeholder;
252- cli_cq.Next (&placeholder, &ok); // actually start the client call
253-
254- // Receive the garbage with the supplied context.
255- grpc::GenericServerContext server_context;
256- grpc::GenericServerAsyncReaderWriter reader_writer (&server_context);
257- generic_service.RequestCall (&server_context, &reader_writer, srv_cq.get (),
258- srv_cq.get (), nullptr );
259- srv_cq->Next (&placeholder, &ok); // actually receive the data
260-
261- // Now we've got the data - save it before cleaning up.
262- std::multimap<std::string, std::string> res;
263- auto const & cli_md = server_context.client_metadata ();
264- std::transform (cli_md.begin (), cli_md.end (), std::inserter (res, res.begin ()),
265- [](std::pair<grpc::string_ref, grpc::string_ref> const & md) {
266- return std::make_pair (
267- std::string (md.first .data (), md.first .length ()),
268- std::string (md.second .data (), md.second .length ()));
269- });
270-
271- // Shut everything down.
272- server->Shutdown (std::chrono::system_clock::now ());
273- srv_cq->Shutdown ();
274- cli_cq.Shutdown ();
275- // Drain completion queues.
276- while (srv_cq->Next (&placeholder, &ok))
277- ;
278- while (cli_cq.Next (&placeholder, &ok))
279- ;
280-
281- return res;
310+ static auto * const kState = new GetMetadataState;
311+ return kState ->GetMetadata (context);
282312}
283313
284314} // namespace testing_util
0 commit comments