Imported Upstream version 1.36.0
[platform/upstream/grpc.git] / src / core / ext / transport / chttp2 / server / chttp2_server.cc
1 /*
2  *
3  * Copyright 2015 gRPC authors.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  */
18
19 #include <grpc/support/port_platform.h>
20
21 #include "src/core/ext/transport/chttp2/server/chttp2_server.h"
22
23 #include <inttypes.h>
24 #include <limits.h>
25 #include <string.h>
26 #include <vector>
27
28 #include "absl/strings/match.h"
29 #include "absl/strings/str_cat.h"
30 #include "absl/strings/str_format.h"
31
32 #include <grpc/grpc.h>
33 #include <grpc/impl/codegen/grpc_types.h>
34 #include <grpc/support/alloc.h>
35 #include <grpc/support/log.h>
36 #include <grpc/support/sync.h>
37
38 #include "src/core/ext/filters/http/server/http_server_filter.h"
39 #include "src/core/ext/transport/chttp2/transport/chttp2_transport.h"
40 #include "src/core/ext/transport/chttp2/transport/internal.h"
41 #include "src/core/lib/channel/channel_args.h"
42 #include "src/core/lib/channel/handshaker.h"
43 #include "src/core/lib/channel/handshaker_registry.h"
44 #include "src/core/lib/gprpp/ref_counted.h"
45 #include "src/core/lib/gprpp/ref_counted_ptr.h"
46 #include "src/core/lib/iomgr/endpoint.h"
47 #include "src/core/lib/iomgr/resolve_address.h"
48 #include "src/core/lib/iomgr/resource_quota.h"
49 #include "src/core/lib/iomgr/sockaddr_utils.h"
50 #include "src/core/lib/iomgr/tcp_server.h"
51 #include "src/core/lib/iomgr/unix_sockets_posix.h"
52 #include "src/core/lib/slice/slice_internal.h"
53 #include "src/core/lib/surface/api_trace.h"
54 #include "src/core/lib/surface/server.h"
55
56 namespace grpc_core {
57 namespace {
58
59 const char kUnixUriPrefix[] = "unix:";
60 const char kUnixAbstractUriPrefix[] = "unix-abstract:";
61
62 class Chttp2ServerListener : public Server::ListenerInterface {
63  public:
64   static grpc_error* Create(Server* server, grpc_resolved_address* addr,
65                             grpc_channel_args* args,
66                             Chttp2ServerArgsModifier args_modifier,
67                             int* port_num);
68
69   static grpc_error* CreateWithAcceptor(Server* server, const char* name,
70                                         grpc_channel_args* args,
71                                         Chttp2ServerArgsModifier args_modifier);
72
73   // Do not instantiate directly.  Use one of the factory methods above.
74   Chttp2ServerListener(Server* server, grpc_channel_args* args,
75                        Chttp2ServerArgsModifier args_modifier);
76   ~Chttp2ServerListener() override;
77
78   void Start(Server* server,
79              const std::vector<grpc_pollset*>* pollsets) override;
80
81   channelz::ListenSocketNode* channelz_listen_socket_node() const override {
82     return channelz_listen_socket_.get();
83   }
84
85   void SetOnDestroyDone(grpc_closure* on_destroy_done) override;
86
87   void Orphan() override;
88
89  private:
90   class ConfigFetcherWatcher
91       : public grpc_server_config_fetcher::WatcherInterface {
92    public:
93     explicit ConfigFetcherWatcher(Chttp2ServerListener* listener)
94         : listener_(listener) {}
95
96     void UpdateConfig(grpc_channel_args* args) override {
97       {
98         MutexLock lock(&listener_->mu_);
99         grpc_channel_args_destroy(listener_->args_);
100         grpc_error* error = GRPC_ERROR_NONE;
101         args = listener_->args_modifier_(args, &error);
102         if (error != GRPC_ERROR_NONE) {
103           // TODO(yashykt): Set state to close down connections immediately
104           // after accepting.
105           GPR_ASSERT(0);
106         }
107         listener_->args_ = args;
108         if (!listener_->shutdown_) return;  // Already started listening.
109       }
110       int port_temp;
111       grpc_error* error = grpc_tcp_server_add_port(
112           listener_->tcp_server_, &listener_->resolved_address_, &port_temp);
113       if (error != GRPC_ERROR_NONE) {
114         GRPC_ERROR_UNREF(error);
115         gpr_log(GPR_ERROR, "Error adding port to server: %s",
116                 grpc_error_string(error));
117         // TODO(yashykt): We wouldn't need to assert here if we bound to the
118         // port earlier during AddPort.
119         GPR_ASSERT(0);
120       }
121       listener_->StartListening();
122     }
123
124    private:
125     Chttp2ServerListener* listener_;
126   };
127
128   class ConnectionState : public RefCounted<ConnectionState> {
129    public:
130     ConnectionState(Chttp2ServerListener* listener,
131                     grpc_pollset* accepting_pollset,
132                     grpc_tcp_server_acceptor* acceptor,
133                     RefCountedPtr<HandshakeManager> handshake_mgr,
134                     grpc_channel_args* args, grpc_endpoint* endpoint);
135
136     ~ConnectionState() override;
137
138    private:
139     static void OnTimeout(void* arg, grpc_error* error);
140     static void OnReceiveSettings(void* arg, grpc_error* error);
141     static void OnHandshakeDone(void* arg, grpc_error* error);
142
143     Chttp2ServerListener* const listener_;
144     grpc_pollset* const accepting_pollset_;
145     grpc_tcp_server_acceptor* const acceptor_;
146     RefCountedPtr<HandshakeManager> handshake_mgr_;
147     // State for enforcing handshake timeout on receiving HTTP/2 settings.
148     grpc_chttp2_transport* transport_ = nullptr;
149     grpc_millis deadline_;
150     grpc_timer timer_;
151     grpc_closure on_timeout_;
152     grpc_closure on_receive_settings_;
153     grpc_pollset_set* const interested_parties_;
154   };
155
156   void StartListening();
157
158   static void OnAccept(void* arg, grpc_endpoint* tcp,
159                        grpc_pollset* accepting_pollset,
160                        grpc_tcp_server_acceptor* acceptor);
161
162   RefCountedPtr<HandshakeManager> CreateHandshakeManager();
163
164   static void TcpServerShutdownComplete(void* arg, grpc_error* error);
165
166   static void DestroyListener(Server* /*server*/, void* arg,
167                               grpc_closure* destroy_done);
168
169   Server* const server_;
170   grpc_tcp_server* tcp_server_;
171   grpc_resolved_address resolved_address_;
172   Chttp2ServerArgsModifier args_modifier_;
173   Mutex mu_;
174   grpc_channel_args* args_;  // guarded by mu_
175   ConfigFetcherWatcher* config_fetcher_watcher_ = nullptr;
176   bool shutdown_ = true;
177   grpc_closure tcp_server_shutdown_complete_;
178   grpc_closure* on_destroy_done_ = nullptr;
179   HandshakeManager* pending_handshake_mgrs_ = nullptr;
180   RefCountedPtr<channelz::ListenSocketNode> channelz_listen_socket_;
181 };
182
183 //
184 // Chttp2ServerListener::ConnectionState
185 //
186
187 grpc_millis GetConnectionDeadline(const grpc_channel_args* args) {
188   int timeout_ms =
189       grpc_channel_args_find_integer(args, GRPC_ARG_SERVER_HANDSHAKE_TIMEOUT_MS,
190                                      {120 * GPR_MS_PER_SEC, 1, INT_MAX});
191   return ExecCtx::Get()->Now() + timeout_ms;
192 }
193
194 Chttp2ServerListener::ConnectionState::ConnectionState(
195     Chttp2ServerListener* listener, grpc_pollset* accepting_pollset,
196     grpc_tcp_server_acceptor* acceptor,
197     RefCountedPtr<HandshakeManager> handshake_mgr, grpc_channel_args* args,
198     grpc_endpoint* endpoint)
199     : listener_(listener),
200       accepting_pollset_(accepting_pollset),
201       acceptor_(acceptor),
202       handshake_mgr_(std::move(handshake_mgr)),
203       deadline_(GetConnectionDeadline(args)),
204       interested_parties_(grpc_pollset_set_create()) {
205   grpc_pollset_set_add_pollset(interested_parties_, accepting_pollset_);
206   HandshakerRegistry::AddHandshakers(HANDSHAKER_SERVER, args,
207                                      interested_parties_, handshake_mgr_.get());
208   handshake_mgr_->DoHandshake(endpoint, args, deadline_, acceptor_,
209                               OnHandshakeDone, this);
210 }
211
212 Chttp2ServerListener::ConnectionState::~ConnectionState() {
213   if (transport_ != nullptr) {
214     GRPC_CHTTP2_UNREF_TRANSPORT(transport_, "receive settings timeout");
215   }
216   grpc_pollset_set_del_pollset(interested_parties_, accepting_pollset_);
217   grpc_pollset_set_destroy(interested_parties_);
218 }
219
220 void Chttp2ServerListener::ConnectionState::OnTimeout(void* arg,
221                                                       grpc_error* error) {
222   ConnectionState* self = static_cast<ConnectionState*>(arg);
223   // Note that we may be called with GRPC_ERROR_NONE when the timer fires
224   // or with an error indicating that the timer system is being shut down.
225   if (error != GRPC_ERROR_CANCELLED) {
226     grpc_transport_op* op = grpc_make_transport_op(nullptr);
227     op->disconnect_with_error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
228         "Did not receive HTTP/2 settings before handshake timeout");
229     grpc_transport_perform_op(&self->transport_->base, op);
230   }
231   self->Unref();
232 }
233
234 void Chttp2ServerListener::ConnectionState::OnReceiveSettings(
235     void* arg, grpc_error* error) {
236   ConnectionState* self = static_cast<ConnectionState*>(arg);
237   if (error == GRPC_ERROR_NONE) {
238     grpc_timer_cancel(&self->timer_);
239   }
240   self->Unref();
241 }
242
243 void Chttp2ServerListener::ConnectionState::OnHandshakeDone(void* arg,
244                                                             grpc_error* error) {
245   auto* args = static_cast<HandshakerArgs*>(arg);
246   ConnectionState* self = static_cast<ConnectionState*>(args->user_data);
247   {
248     MutexLock lock(&self->listener_->mu_);
249     grpc_resource_user* resource_user =
250         self->listener_->server_->default_resource_user();
251     if (error != GRPC_ERROR_NONE || self->listener_->shutdown_) {
252       const char* error_str = grpc_error_string(error);
253       gpr_log(GPR_DEBUG, "Handshaking failed: %s", error_str);
254       if (resource_user != nullptr) {
255         grpc_resource_user_free(resource_user,
256                                 GRPC_RESOURCE_QUOTA_CHANNEL_SIZE);
257       }
258       if (error == GRPC_ERROR_NONE && args->endpoint != nullptr) {
259         // We were shut down after handshaking completed successfully, so
260         // destroy the endpoint here.
261         // TODO(ctiller): It is currently necessary to shutdown endpoints
262         // before destroying them, even if we know that there are no
263         // pending read/write callbacks.  This should be fixed, at which
264         // point this can be removed.
265         grpc_endpoint_shutdown(args->endpoint, GRPC_ERROR_NONE);
266         grpc_endpoint_destroy(args->endpoint);
267         grpc_channel_args_destroy(args->args);
268         grpc_slice_buffer_destroy_internal(args->read_buffer);
269         gpr_free(args->read_buffer);
270       }
271     } else {
272       // If the handshaking succeeded but there is no endpoint, then the
273       // handshaker may have handed off the connection to some external
274       // code, so we can just clean up here without creating a transport.
275       if (args->endpoint != nullptr) {
276         grpc_transport* transport = grpc_create_chttp2_transport(
277             args->args, args->endpoint, false, resource_user);
278         grpc_error* channel_init_err = self->listener_->server_->SetupTransport(
279             transport, self->accepting_pollset_, args->args,
280             grpc_chttp2_transport_get_socket_node(transport), resource_user);
281         if (channel_init_err == GRPC_ERROR_NONE) {
282           // Use notify_on_receive_settings callback to enforce the
283           // handshake deadline.
284           // Note: The reinterpret_cast<>s here are safe, because
285           // grpc_chttp2_transport is a C-style extension of
286           // grpc_transport, so this is morally equivalent of a
287           // static_cast<> to a derived class.
288           // TODO(roth): Change to static_cast<> when we C++-ify the
289           // transport API.
290           self->transport_ =
291               reinterpret_cast<grpc_chttp2_transport*>(transport);
292           self->Ref().release();  // Held by OnReceiveSettings().
293           GRPC_CLOSURE_INIT(&self->on_receive_settings_, OnReceiveSettings,
294                             self, grpc_schedule_on_exec_ctx);
295           grpc_chttp2_transport_start_reading(transport, args->read_buffer,
296                                               &self->on_receive_settings_);
297           grpc_channel_args_destroy(args->args);
298           self->Ref().release();  // Held by OnTimeout().
299           GRPC_CHTTP2_REF_TRANSPORT(
300               reinterpret_cast<grpc_chttp2_transport*>(transport),
301               "receive settings timeout");
302           GRPC_CLOSURE_INIT(&self->on_timeout_, OnTimeout, self,
303                             grpc_schedule_on_exec_ctx);
304           grpc_timer_init(&self->timer_, self->deadline_, &self->on_timeout_);
305         } else {
306           // Failed to create channel from transport. Clean up.
307           gpr_log(GPR_ERROR, "Failed to create channel: %s",
308                   grpc_error_string(channel_init_err));
309           GRPC_ERROR_UNREF(channel_init_err);
310           grpc_transport_destroy(transport);
311           grpc_slice_buffer_destroy_internal(args->read_buffer);
312           gpr_free(args->read_buffer);
313           if (resource_user != nullptr) {
314             grpc_resource_user_free(resource_user,
315                                     GRPC_RESOURCE_QUOTA_CHANNEL_SIZE);
316           }
317           grpc_channel_args_destroy(args->args);
318         }
319       } else {
320         if (resource_user != nullptr) {
321           grpc_resource_user_free(resource_user,
322                                   GRPC_RESOURCE_QUOTA_CHANNEL_SIZE);
323         }
324       }
325     }
326     self->handshake_mgr_->RemoveFromPendingMgrList(
327         &self->listener_->pending_handshake_mgrs_);
328   }
329   self->handshake_mgr_.reset();
330   gpr_free(self->acceptor_);
331   grpc_tcp_server_unref(self->listener_->tcp_server_);
332   self->Unref();
333 }
334
335 //
336 // Chttp2ServerListener
337 //
338
339 grpc_error* Chttp2ServerListener::Create(Server* server,
340                                          grpc_resolved_address* addr,
341                                          grpc_channel_args* args,
342                                          Chttp2ServerArgsModifier args_modifier,
343                                          int* port_num) {
344   Chttp2ServerListener* listener = nullptr;
345   // The bulk of this method is inside of a lambda to make cleanup
346   // easier without using goto.
347   grpc_error* error = [&]() {
348     // Create Chttp2ServerListener.
349     listener = new Chttp2ServerListener(server, args, args_modifier);
350     error = grpc_tcp_server_create(&listener->tcp_server_shutdown_complete_,
351                                    args, &listener->tcp_server_);
352     if (error != GRPC_ERROR_NONE) return error;
353     if (server->config_fetcher() != nullptr) {
354       listener->resolved_address_ = *addr;
355       // TODO(yashykt): Consider binding so as to be able to return the port
356       // number.
357     } else {
358       error = grpc_tcp_server_add_port(listener->tcp_server_, addr, port_num);
359       if (error != GRPC_ERROR_NONE) return error;
360     }
361     // Create channelz node.
362     if (grpc_channel_args_find_bool(args, GRPC_ARG_ENABLE_CHANNELZ,
363                                     GRPC_ENABLE_CHANNELZ_DEFAULT)) {
364       std::string string_address = grpc_sockaddr_to_string(addr, false);
365       listener->channelz_listen_socket_ =
366           MakeRefCounted<channelz::ListenSocketNode>(
367               string_address.c_str(),
368               absl::StrFormat("chttp2 listener %s", string_address.c_str()));
369     }
370     // Register with the server only upon success
371     server->AddListener(OrphanablePtr<Server::ListenerInterface>(listener));
372     return GRPC_ERROR_NONE;
373   }();
374   if (error != GRPC_ERROR_NONE) {
375     if (listener != nullptr) {
376       if (listener->tcp_server_ != nullptr) {
377         // listener is deleted when tcp_server_ is shutdown.
378         grpc_tcp_server_unref(listener->tcp_server_);
379       } else {
380         delete listener;
381       }
382     } else {
383       grpc_channel_args_destroy(args);
384     }
385   }
386   return error;
387 }
388
389 grpc_error* Chttp2ServerListener::CreateWithAcceptor(
390     Server* server, const char* name, grpc_channel_args* args,
391     Chttp2ServerArgsModifier args_modifier) {
392   Chttp2ServerListener* listener =
393       new Chttp2ServerListener(server, args, args_modifier);
394   grpc_error* error = grpc_tcp_server_create(
395       &listener->tcp_server_shutdown_complete_, args, &listener->tcp_server_);
396   if (error != GRPC_ERROR_NONE) {
397     delete listener;
398     return error;
399   }
400   // TODO(yangg) channelz
401   TcpServerFdHandler** arg_val =
402       grpc_channel_args_find_pointer<TcpServerFdHandler*>(args, name);
403   *arg_val = grpc_tcp_server_create_fd_handler(listener->tcp_server_);
404   server->AddListener(OrphanablePtr<Server::ListenerInterface>(listener));
405   return GRPC_ERROR_NONE;
406 }
407
408 Chttp2ServerListener::Chttp2ServerListener(
409     Server* server, grpc_channel_args* args,
410     Chttp2ServerArgsModifier args_modifier)
411     : server_(server), args_modifier_(args_modifier), args_(args) {
412   GRPC_CLOSURE_INIT(&tcp_server_shutdown_complete_, TcpServerShutdownComplete,
413                     this, grpc_schedule_on_exec_ctx);
414 }
415
416 Chttp2ServerListener::~Chttp2ServerListener() {
417   grpc_channel_args_destroy(args_);
418 }
419
420 /* Server callback: start listening on our ports */
421 void Chttp2ServerListener::Start(
422     Server* /*server*/, const std::vector<grpc_pollset*>* /* pollsets */) {
423   if (server_->config_fetcher() != nullptr) {
424     grpc_channel_args* args = nullptr;
425     auto watcher = absl::make_unique<ConfigFetcherWatcher>(this);
426     {
427       MutexLock lock(&mu_);
428       config_fetcher_watcher_ = watcher.get();
429       args = grpc_channel_args_copy(args_);
430     }
431     server_->config_fetcher()->StartWatch(
432         grpc_sockaddr_to_string(&resolved_address_, false), args,
433         std::move(watcher));
434   } else {
435     StartListening();
436   }
437 }
438
439 void Chttp2ServerListener::StartListening() {
440   grpc_tcp_server_start(tcp_server_, &server_->pollsets(), OnAccept, this);
441   MutexLock lock(&mu_);
442   shutdown_ = false;
443 }
444
445 void Chttp2ServerListener::SetOnDestroyDone(grpc_closure* on_destroy_done) {
446   MutexLock lock(&mu_);
447   on_destroy_done_ = on_destroy_done;
448 }
449
450 RefCountedPtr<HandshakeManager> Chttp2ServerListener::CreateHandshakeManager() {
451   MutexLock lock(&mu_);
452   if (shutdown_) return nullptr;
453   grpc_resource_user* resource_user = server_->default_resource_user();
454   if (resource_user != nullptr &&
455       !grpc_resource_user_safe_alloc(resource_user,
456                                      GRPC_RESOURCE_QUOTA_CHANNEL_SIZE)) {
457     gpr_log(GPR_ERROR,
458             "Memory quota exhausted, rejecting connection, no handshaking.");
459     return nullptr;
460   }
461   auto handshake_mgr = MakeRefCounted<HandshakeManager>();
462   handshake_mgr->AddToPendingMgrList(&pending_handshake_mgrs_);
463   grpc_tcp_server_ref(tcp_server_);  // Ref held by ConnectionState.
464   return handshake_mgr;
465 }
466
467 void Chttp2ServerListener::OnAccept(void* arg, grpc_endpoint* tcp,
468                                     grpc_pollset* accepting_pollset,
469                                     grpc_tcp_server_acceptor* acceptor) {
470   Chttp2ServerListener* self = static_cast<Chttp2ServerListener*>(arg);
471   RefCountedPtr<HandshakeManager> handshake_mgr =
472       self->CreateHandshakeManager();
473   if (handshake_mgr == nullptr) {
474     grpc_endpoint_shutdown(tcp, GRPC_ERROR_NONE);
475     grpc_endpoint_destroy(tcp);
476     gpr_free(acceptor);
477     return;
478   }
479   grpc_channel_args* args = nullptr;
480   {
481     MutexLock lock(&self->mu_);
482     args = grpc_channel_args_copy(self->args_);
483   }
484   // Deletes itself when done.
485   new ConnectionState(self, accepting_pollset, acceptor,
486                       std::move(handshake_mgr), args, tcp);
487   grpc_channel_args_destroy(args);
488 }
489
490 void Chttp2ServerListener::TcpServerShutdownComplete(void* arg,
491                                                      grpc_error* error) {
492   Chttp2ServerListener* self = static_cast<Chttp2ServerListener*>(arg);
493   /* ensure all threads have unlocked */
494   grpc_closure* destroy_done = nullptr;
495   {
496     MutexLock lock(&self->mu_);
497     destroy_done = self->on_destroy_done_;
498     GPR_ASSERT(self->shutdown_);
499     if (self->pending_handshake_mgrs_ != nullptr) {
500       self->pending_handshake_mgrs_->ShutdownAllPending(GRPC_ERROR_REF(error));
501     }
502     self->channelz_listen_socket_.reset();
503   }
504   // Flush queued work before destroying handshaker factory, since that
505   // may do a synchronous unref.
506   ExecCtx::Get()->Flush();
507   if (destroy_done != nullptr) {
508     ExecCtx::Run(DEBUG_LOCATION, destroy_done, GRPC_ERROR_REF(error));
509     ExecCtx::Get()->Flush();
510   }
511   delete self;
512 }
513
514 /* Server callback: destroy the tcp listener (so we don't generate further
515    callbacks) */
516 void Chttp2ServerListener::Orphan() {
517   // Cancel the watch before shutting down so as to avoid holding a ref to the
518   // listener in the watcher.
519   if (config_fetcher_watcher_ != nullptr) {
520     server_->config_fetcher()->CancelWatch(config_fetcher_watcher_);
521   }
522   grpc_tcp_server* tcp_server;
523   {
524     MutexLock lock(&mu_);
525     shutdown_ = true;
526     tcp_server = tcp_server_;
527   }
528   grpc_tcp_server_shutdown_listeners(tcp_server);
529   grpc_tcp_server_unref(tcp_server);
530 }
531
532 }  // namespace
533
534 //
535 // Chttp2ServerAddPort()
536 //
537
538 grpc_error* Chttp2ServerAddPort(Server* server, const char* addr,
539                                 grpc_channel_args* args,
540                                 Chttp2ServerArgsModifier args_modifier,
541                                 int* port_num) {
542   if (strncmp(addr, "external:", 9) == 0) {
543     return grpc_core::Chttp2ServerListener::CreateWithAcceptor(
544         server, addr, args, args_modifier);
545   }
546   *port_num = -1;
547   grpc_resolved_addresses* resolved = nullptr;
548   std::vector<grpc_error*> error_list;
549   // Using lambda to avoid use of goto.
550   grpc_error* error = [&]() {
551     if (absl::StartsWith(addr, kUnixUriPrefix)) {
552       error = grpc_resolve_unix_domain_address(
553           addr + sizeof(kUnixUriPrefix) - 1, &resolved);
554     } else if (absl::StartsWith(addr, kUnixAbstractUriPrefix)) {
555       error = grpc_resolve_unix_abstract_domain_address(
556           addr + sizeof(kUnixAbstractUriPrefix) - 1, &resolved);
557     } else {
558       error = grpc_blocking_resolve_address(addr, "https", &resolved);
559     }
560     if (error != GRPC_ERROR_NONE) return error;
561     // Create a listener for each resolved address.
562     for (size_t i = 0; i < resolved->naddrs; i++) {
563       // If address has a wildcard port (0), use the same port as a previous
564       // listener.
565       if (*port_num != -1 && grpc_sockaddr_get_port(&resolved->addrs[i]) == 0) {
566         grpc_sockaddr_set_port(&resolved->addrs[i], *port_num);
567       }
568       int port_temp = -1;
569       error = grpc_core::Chttp2ServerListener::Create(
570           server, &resolved->addrs[i], grpc_channel_args_copy(args),
571           args_modifier, &port_temp);
572       if (error != GRPC_ERROR_NONE) {
573         error_list.push_back(error);
574       } else {
575         if (*port_num == -1) {
576           *port_num = port_temp;
577         } else {
578           GPR_ASSERT(*port_num == port_temp);
579         }
580       }
581     }
582     if (error_list.size() == resolved->naddrs) {
583       std::string msg =
584           absl::StrFormat("No address added out of total %" PRIuPTR " resolved",
585                           resolved->naddrs);
586       return GRPC_ERROR_CREATE_REFERENCING_FROM_COPIED_STRING(
587           msg.c_str(), error_list.data(), error_list.size());
588     } else if (!error_list.empty()) {
589       std::string msg = absl::StrFormat(
590           "Only %" PRIuPTR " addresses added out of total %" PRIuPTR
591           " resolved",
592           resolved->naddrs - error_list.size(), resolved->naddrs);
593       error = GRPC_ERROR_CREATE_REFERENCING_FROM_COPIED_STRING(
594           msg.c_str(), error_list.data(), error_list.size());
595       gpr_log(GPR_INFO, "WARNING: %s", grpc_error_string(error));
596       GRPC_ERROR_UNREF(error);
597       // we managed to bind some addresses: continue without error
598     }
599     return GRPC_ERROR_NONE;
600   }();  // lambda end
601   for (grpc_error* error : error_list) {
602     GRPC_ERROR_UNREF(error);
603   }
604   grpc_channel_args_destroy(args);
605   if (resolved != nullptr) {
606     grpc_resolved_addresses_destroy(resolved);
607   }
608   if (error != GRPC_ERROR_NONE) *port_num = 0;
609   return error;
610 }
611
612 }  // namespace grpc_core