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/lib/gprpp/host_port.h"
22
23 #include "absl/strings/str_format.h"
24 #include "absl/strings/string_view.h"
25
26 #include <grpc/support/log.h>
27
28 namespace grpc_core {
29
JoinHostPort(absl::string_view host,int port)30 std::string JoinHostPort(absl::string_view host, int port) {
31 if (!host.empty() && host[0] != '[' && host.rfind(':') != host.npos) {
32 // IPv6 literals must be enclosed in brackets.
33 return absl::StrFormat("[%s]:%d", host, port);
34 }
35 // Ordinary non-bracketed host:port.
36 return absl::StrFormat("%s:%d", host, port);
37 }
38
39 namespace {
DoSplitHostPort(absl::string_view name,absl::string_view * host,absl::string_view * port,bool * has_port)40 bool DoSplitHostPort(absl::string_view name, absl::string_view* host,
41 absl::string_view* port, bool* has_port) {
42 *has_port = false;
43 if (!name.empty() && name[0] == '[') {
44 /* Parse a bracketed host, typically an IPv6 literal. */
45 const size_t rbracket = name.find(']', 1);
46 if (rbracket == absl::string_view::npos) {
47 /* Unmatched [ */
48 return false;
49 }
50 if (rbracket == name.size() - 1) {
51 /* ]<end> */
52 *port = absl::string_view();
53 } else if (name[rbracket + 1] == ':') {
54 /* ]:<port?> */
55 *port = name.substr(rbracket + 2, name.size() - rbracket - 2);
56 *has_port = true;
57 } else {
58 /* ]<invalid> */
59 return false;
60 }
61 *host = name.substr(1, rbracket - 1);
62 if (host->find(':') == absl::string_view::npos) {
63 /* Require all bracketed hosts to contain a colon, because a hostname or
64 IPv4 address should never use brackets. */
65 *host = absl::string_view();
66 return false;
67 }
68 } else {
69 size_t colon = name.find(':');
70 if (colon != absl::string_view::npos &&
71 name.find(':', colon + 1) == absl::string_view::npos) {
72 /* Exactly 1 colon. Split into host:port. */
73 *host = name.substr(0, colon);
74 *port = name.substr(colon + 1, name.size() - colon - 1);
75 *has_port = true;
76 } else {
77 /* 0 or 2+ colons. Bare hostname or IPv6 litearal. */
78 *host = name;
79 *port = absl::string_view();
80 }
81 }
82 return true;
83 }
84 } // namespace
85
SplitHostPort(absl::string_view name,absl::string_view * host,absl::string_view * port)86 bool SplitHostPort(absl::string_view name, absl::string_view* host,
87 absl::string_view* port) {
88 bool unused;
89 return DoSplitHostPort(name, host, port, &unused);
90 }
91
SplitHostPort(absl::string_view name,std::string * host,std::string * port)92 bool SplitHostPort(absl::string_view name, std::string* host,
93 std::string* port) {
94 GPR_DEBUG_ASSERT(host != nullptr && host->empty());
95 GPR_DEBUG_ASSERT(port != nullptr && port->empty());
96 absl::string_view host_view;
97 absl::string_view port_view;
98 bool has_port;
99 const bool ret = DoSplitHostPort(name, &host_view, &port_view, &has_port);
100 if (ret) {
101 // We always set the host, but port is set only when DoSplitHostPort find a
102 // port in the string, to remain backward compatible with the old
103 // gpr_split_host_port API.
104 *host = std::string(host_view);
105 if (has_port) {
106 *port = std::string(port_view);
107 }
108 }
109 return ret;
110 }
111
112 } // namespace grpc_core
113