1
2 /*
3 *
4 * Copyright 2018 gRPC authors.
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 *
18 */
19
20 #include <grpc/support/port_platform.h>
21
22 #include "src/core/lib/iomgr/port.h"
23
24 #ifdef GRPC_CFSTREAM_CLIENT
25
26 #include <CoreFoundation/CoreFoundation.h>
27
28 #include <string.h>
29
30 #include <grpc/support/alloc.h>
31 #include <grpc/support/log.h>
32 #include <grpc/support/sync.h>
33
34 #include <netinet/in.h>
35
36 #include "src/core/lib/channel/channel_args.h"
37 #include "src/core/lib/gprpp/host_port.h"
38 #include "src/core/lib/iomgr/cfstream_handle.h"
39 #include "src/core/lib/iomgr/closure.h"
40 #include "src/core/lib/iomgr/endpoint_cfstream.h"
41 #include "src/core/lib/iomgr/error.h"
42 #include "src/core/lib/iomgr/error_cfstream.h"
43 #include "src/core/lib/iomgr/sockaddr_utils.h"
44 #include "src/core/lib/iomgr/tcp_client.h"
45 #include "src/core/lib/iomgr/timer.h"
46
47 extern grpc_core::TraceFlag grpc_tcp_trace;
48
49 struct CFStreamConnect {
50 gpr_mu mu;
51 gpr_refcount refcount;
52
53 CFReadStreamRef read_stream;
54 CFWriteStreamRef write_stream;
55 CFStreamHandle* stream_handle;
56
57 grpc_timer alarm;
58 grpc_closure on_alarm;
59 grpc_closure on_open;
60
61 bool read_stream_open;
62 bool write_stream_open;
63 bool failed;
64
65 grpc_closure* closure;
66 grpc_endpoint** endpoint;
67 int refs;
68 std::string addr_name;
69 grpc_resource_quota* resource_quota;
70 };
71
CFStreamConnectCleanup(CFStreamConnect * connect)72 static void CFStreamConnectCleanup(CFStreamConnect* connect) {
73 grpc_resource_quota_unref_internal(connect->resource_quota);
74 CFSTREAM_HANDLE_UNREF(connect->stream_handle, "async connect clean up");
75 CFRelease(connect->read_stream);
76 CFRelease(connect->write_stream);
77 gpr_mu_destroy(&connect->mu);
78 delete connect;
79 }
80
OnAlarm(void * arg,grpc_error * error)81 static void OnAlarm(void* arg, grpc_error* error) {
82 CFStreamConnect* connect = static_cast<CFStreamConnect*>(arg);
83 if (grpc_tcp_trace.enabled()) {
84 gpr_log(GPR_DEBUG, "CLIENT_CONNECT :%p OnAlarm, error:%p", connect, error);
85 }
86 gpr_mu_lock(&connect->mu);
87 grpc_closure* closure = connect->closure;
88 connect->closure = nil;
89 const bool done = (--connect->refs == 0);
90 gpr_mu_unlock(&connect->mu);
91 // Only schedule a callback once, by either OnAlarm or OnOpen. The
92 // first one issues callback while the second one does cleanup.
93 if (done) {
94 CFStreamConnectCleanup(connect);
95 } else {
96 grpc_error* error =
97 GRPC_ERROR_CREATE_FROM_STATIC_STRING("connect() timed out");
98 grpc_core::ExecCtx::Run(DEBUG_LOCATION, closure, error);
99 }
100 }
101
OnOpen(void * arg,grpc_error * error)102 static void OnOpen(void* arg, grpc_error* error) {
103 CFStreamConnect* connect = static_cast<CFStreamConnect*>(arg);
104 if (grpc_tcp_trace.enabled()) {
105 gpr_log(GPR_DEBUG, "CLIENT_CONNECT :%p OnOpen, error:%p", connect, error);
106 }
107 gpr_mu_lock(&connect->mu);
108 grpc_timer_cancel(&connect->alarm);
109 grpc_closure* closure = connect->closure;
110 connect->closure = nil;
111
112 bool done = (--connect->refs == 0);
113 grpc_endpoint** endpoint = connect->endpoint;
114
115 // Only schedule a callback once, by either OnAlarm or OnOpen. The
116 // first one issues callback while the second one does cleanup.
117 if (done) {
118 gpr_mu_unlock(&connect->mu);
119 CFStreamConnectCleanup(connect);
120 } else {
121 if (error == GRPC_ERROR_NONE) {
122 CFErrorRef stream_error = CFReadStreamCopyError(connect->read_stream);
123 if (stream_error == NULL) {
124 stream_error = CFWriteStreamCopyError(connect->write_stream);
125 }
126 if (stream_error) {
127 error = GRPC_ERROR_CREATE_FROM_CFERROR(stream_error, "connect() error");
128 CFRelease(stream_error);
129 }
130 if (error == GRPC_ERROR_NONE) {
131 *endpoint = grpc_cfstream_endpoint_create(
132 connect->read_stream, connect->write_stream,
133 connect->addr_name.c_str(), connect->resource_quota,
134 connect->stream_handle);
135 }
136 } else {
137 GRPC_ERROR_REF(error);
138 }
139 gpr_mu_unlock(&connect->mu);
140 grpc_core::ExecCtx::Run(DEBUG_LOCATION, closure, error);
141 }
142 }
143
ParseResolvedAddress(const grpc_resolved_address * addr,CFStringRef * host,int * port)144 static void ParseResolvedAddress(const grpc_resolved_address* addr,
145 CFStringRef* host, int* port) {
146 std::string host_port = grpc_sockaddr_to_string(addr, true);
147 std::string host_string;
148 std::string port_string;
149 grpc_core::SplitHostPort(host_port, &host_string, &port_string);
150 *host = CFStringCreateWithCString(NULL, host_string.c_str(),
151 kCFStringEncodingUTF8);
152 *port = grpc_sockaddr_get_port(addr);
153 }
154
CFStreamClientConnect(grpc_closure * closure,grpc_endpoint ** ep,grpc_pollset_set * interested_parties,const grpc_channel_args * channel_args,const grpc_resolved_address * resolved_addr,grpc_millis deadline)155 static void CFStreamClientConnect(grpc_closure* closure, grpc_endpoint** ep,
156 grpc_pollset_set* interested_parties,
157 const grpc_channel_args* channel_args,
158 const grpc_resolved_address* resolved_addr,
159 grpc_millis deadline) {
160 CFStreamConnect* connect = new CFStreamConnect();
161 connect->closure = closure;
162 connect->endpoint = ep;
163 connect->addr_name = grpc_sockaddr_to_uri(resolved_addr);
164 // connect->resource_quota = resource_quota;
165 connect->refs = 2; // One for the connect operation, one for the timer.
166 gpr_ref_init(&connect->refcount, 1);
167 gpr_mu_init(&connect->mu);
168
169 if (grpc_tcp_trace.enabled()) {
170 gpr_log(GPR_DEBUG, "CLIENT_CONNECT: %p, %s: asynchronously connecting",
171 connect, connect->addr_name.c_str());
172 }
173
174 grpc_resource_quota* resource_quota = grpc_resource_quota_create(NULL);
175 if (channel_args != NULL) {
176 for (size_t i = 0; i < channel_args->num_args; i++) {
177 if (0 == strcmp(channel_args->args[i].key, GRPC_ARG_RESOURCE_QUOTA)) {
178 grpc_resource_quota_unref_internal(resource_quota);
179 resource_quota = grpc_resource_quota_ref_internal(
180 (grpc_resource_quota*)channel_args->args[i].value.pointer.p);
181 }
182 }
183 }
184 connect->resource_quota = resource_quota;
185
186 CFReadStreamRef read_stream;
187 CFWriteStreamRef write_stream;
188
189 CFStringRef host;
190 int port;
191 ParseResolvedAddress(resolved_addr, &host, &port);
192 CFStreamCreatePairWithSocketToHost(NULL, host, port, &read_stream,
193 &write_stream);
194 CFRelease(host);
195 connect->read_stream = read_stream;
196 connect->write_stream = write_stream;
197 connect->stream_handle =
198 CFStreamHandle::CreateStreamHandle(read_stream, write_stream);
199 GRPC_CLOSURE_INIT(&connect->on_open, OnOpen, static_cast<void*>(connect),
200 grpc_schedule_on_exec_ctx);
201 connect->stream_handle->NotifyOnOpen(&connect->on_open);
202 GRPC_CLOSURE_INIT(&connect->on_alarm, OnAlarm, connect,
203 grpc_schedule_on_exec_ctx);
204 gpr_mu_lock(&connect->mu);
205 CFReadStreamOpen(read_stream);
206 CFWriteStreamOpen(write_stream);
207 grpc_timer_init(&connect->alarm, deadline, &connect->on_alarm);
208 gpr_mu_unlock(&connect->mu);
209 }
210
211 grpc_tcp_client_vtable grpc_cfstream_client_vtable = {CFStreamClientConnect};
212
213 #endif /* GRPC_CFSTREAM_CLIENT */
214