| /* |
| * |
| * Copyright 2018 gRPC authors. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| */ |
| |
| #include <grpc/support/port_platform.h> |
| |
| #include "src/core/lib/iomgr/port.h" |
| |
| #ifdef GRPC_CFSTREAM_ENDPOINT |
| |
| #import <CoreFoundation/CoreFoundation.h> |
| #import "src/core/lib/iomgr/endpoint_cfstream.h" |
| |
| #include <grpc/slice_buffer.h> |
| #include <grpc/support/alloc.h> |
| #include <grpc/support/string_util.h> |
| |
| #include "src/core/lib/gpr/string.h" |
| #include "src/core/lib/iomgr/cfstream_handle.h" |
| #include "src/core/lib/iomgr/closure.h" |
| #include "src/core/lib/iomgr/endpoint.h" |
| #include "src/core/lib/iomgr/error_cfstream.h" |
| #include "src/core/lib/slice/slice_internal.h" |
| #include "src/core/lib/slice/slice_string_helpers.h" |
| |
| extern grpc_core::TraceFlag grpc_tcp_trace; |
| |
| typedef struct { |
| grpc_endpoint base; |
| gpr_refcount refcount; |
| |
| CFReadStreamRef read_stream; |
| CFWriteStreamRef write_stream; |
| CFStreamHandle* stream_sync; |
| |
| grpc_closure* read_cb; |
| grpc_closure* write_cb; |
| grpc_slice_buffer* read_slices; |
| grpc_slice_buffer* write_slices; |
| |
| grpc_closure read_action; |
| grpc_closure write_action; |
| |
| char* peer_string; |
| grpc_resource_user* resource_user; |
| grpc_resource_user_slice_allocator slice_allocator; |
| } CFStreamEndpoint; |
| |
| static void CFStreamFree(CFStreamEndpoint* ep) { |
| grpc_resource_user_unref(ep->resource_user); |
| CFRelease(ep->read_stream); |
| CFRelease(ep->write_stream); |
| CFSTREAM_HANDLE_UNREF(ep->stream_sync, "free"); |
| gpr_free(ep->peer_string); |
| gpr_free(ep); |
| } |
| |
| #ifndef NDEBUG |
| #define EP_REF(ep, reason) CFStreamRef((ep), (reason), __FILE__, __LINE__) |
| #define EP_UNREF(ep, reason) CFStreamUnref((ep), (reason), __FILE__, __LINE__) |
| static void CFStreamUnref(CFStreamEndpoint* ep, const char* reason, |
| const char* file, int line) { |
| if (grpc_tcp_trace.enabled()) { |
| gpr_atm val = gpr_atm_no_barrier_load(&ep->refcount.count); |
| gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, |
| "CFStream endpoint unref %p : %s %" PRIdPTR " -> %" PRIdPTR, ep, |
| reason, val, val - 1); |
| } |
| if (gpr_unref(&ep->refcount)) { |
| CFStreamFree(ep); |
| } |
| } |
| static void CFStreamRef(CFStreamEndpoint* ep, const char* reason, |
| const char* file, int line) { |
| if (grpc_tcp_trace.enabled()) { |
| gpr_atm val = gpr_atm_no_barrier_load(&ep->refcount.count); |
| gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, |
| "CFStream endpoint ref %p : %s %" PRIdPTR " -> %" PRIdPTR, ep, |
| reason, val, val + 1); |
| } |
| gpr_ref(&ep->refcount); |
| } |
| #else |
| #define EP_REF(ep, reason) CFStreamRef((ep)) |
| #define EP_UNREF(ep, reason) CFStreamUnref((ep)) |
| static void CFStreamUnref(CFStreamEndpoint* ep) { |
| if (gpr_unref(&ep->refcount)) { |
| CFStreamFree(ep); |
| } |
| } |
| static void CFStreamRef(CFStreamEndpoint* ep) { gpr_ref(&ep->refcount); } |
| #endif |
| |
| static grpc_error* CFStreamAnnotateError(grpc_error* src_error, |
| CFStreamEndpoint* ep) { |
| return grpc_error_set_str( |
| grpc_error_set_int(src_error, GRPC_ERROR_INT_GRPC_STATUS, |
| GRPC_STATUS_UNAVAILABLE), |
| GRPC_ERROR_STR_TARGET_ADDRESS, |
| grpc_slice_from_copied_string(ep->peer_string)); |
| } |
| |
| static void CallReadCb(CFStreamEndpoint* ep, grpc_error* error) { |
| if (grpc_tcp_trace.enabled()) { |
| gpr_log(GPR_DEBUG, "CFStream endpoint:%p call_read_cb %p %p:%p", ep, |
| ep->read_cb, ep->read_cb->cb, ep->read_cb->cb_arg); |
| size_t i; |
| const char* str = grpc_error_string(error); |
| gpr_log(GPR_DEBUG, "read: error=%s", str); |
| |
| for (i = 0; i < ep->read_slices->count; i++) { |
| char* dump = grpc_dump_slice(ep->read_slices->slices[i], |
| GPR_DUMP_HEX | GPR_DUMP_ASCII); |
| gpr_log(GPR_DEBUG, "READ %p (peer=%s): %s", ep, ep->peer_string, dump); |
| gpr_free(dump); |
| } |
| } |
| grpc_closure* cb = ep->read_cb; |
| ep->read_cb = nullptr; |
| ep->read_slices = nullptr; |
| GRPC_CLOSURE_SCHED(cb, error); |
| } |
| |
| static void CallWriteCb(CFStreamEndpoint* ep, grpc_error* error) { |
| if (grpc_tcp_trace.enabled()) { |
| gpr_log(GPR_DEBUG, "CFStream endpoint:%p call_write_cb %p %p:%p", ep, |
| ep->write_cb, ep->write_cb->cb, ep->write_cb->cb_arg); |
| const char* str = grpc_error_string(error); |
| gpr_log(GPR_DEBUG, "write: error=%s", str); |
| } |
| grpc_closure* cb = ep->write_cb; |
| ep->write_cb = nullptr; |
| ep->write_slices = nullptr; |
| GRPC_CLOSURE_SCHED(cb, error); |
| } |
| |
| static void ReadAction(void* arg, grpc_error* error) { |
| CFStreamEndpoint* ep = static_cast<CFStreamEndpoint*>(arg); |
| GPR_ASSERT(ep->read_cb != nullptr); |
| if (error) { |
| grpc_slice_buffer_reset_and_unref_internal(ep->read_slices); |
| CallReadCb(ep, GRPC_ERROR_REF(error)); |
| EP_UNREF(ep, "read"); |
| return; |
| } |
| |
| GPR_ASSERT(ep->read_slices->count == 1); |
| grpc_slice slice = ep->read_slices->slices[0]; |
| size_t len = GRPC_SLICE_LENGTH(slice); |
| CFIndex read_size = |
| CFReadStreamRead(ep->read_stream, GRPC_SLICE_START_PTR(slice), len); |
| if (read_size == -1) { |
| grpc_slice_buffer_reset_and_unref_internal(ep->read_slices); |
| CFErrorRef stream_error = CFReadStreamCopyError(ep->read_stream); |
| if (stream_error != nullptr) { |
| error = CFStreamAnnotateError( |
| GRPC_ERROR_CREATE_FROM_CFERROR(stream_error, "Read error"), ep); |
| CFRelease(stream_error); |
| } else { |
| error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Read error"); |
| } |
| CallReadCb(ep, error); |
| EP_UNREF(ep, "read"); |
| } else if (read_size == 0) { |
| grpc_slice_buffer_reset_and_unref_internal(ep->read_slices); |
| CallReadCb(ep, |
| CFStreamAnnotateError( |
| GRPC_ERROR_CREATE_FROM_STATIC_STRING("Socket closed"), ep)); |
| EP_UNREF(ep, "read"); |
| } else { |
| if (read_size < len) { |
| grpc_slice_buffer_trim_end(ep->read_slices, len - read_size, nullptr); |
| } |
| CallReadCb(ep, GRPC_ERROR_NONE); |
| EP_UNREF(ep, "read"); |
| } |
| } |
| |
| static void WriteAction(void* arg, grpc_error* error) { |
| CFStreamEndpoint* ep = static_cast<CFStreamEndpoint*>(arg); |
| GPR_ASSERT(ep->write_cb != nullptr); |
| if (error) { |
| grpc_slice_buffer_reset_and_unref_internal(ep->write_slices); |
| CallWriteCb(ep, GRPC_ERROR_REF(error)); |
| EP_UNREF(ep, "write"); |
| return; |
| } |
| |
| grpc_slice slice = grpc_slice_buffer_take_first(ep->write_slices); |
| size_t slice_len = GRPC_SLICE_LENGTH(slice); |
| CFIndex write_size = CFWriteStreamWrite( |
| ep->write_stream, GRPC_SLICE_START_PTR(slice), slice_len); |
| if (write_size == -1) { |
| grpc_slice_buffer_reset_and_unref_internal(ep->write_slices); |
| CFErrorRef stream_error = CFWriteStreamCopyError(ep->write_stream); |
| if (stream_error != nullptr) { |
| error = CFStreamAnnotateError( |
| GRPC_ERROR_CREATE_FROM_CFERROR(stream_error, "write failed."), ep); |
| CFRelease(stream_error); |
| } else { |
| error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("write failed."); |
| } |
| CallWriteCb(ep, error); |
| EP_UNREF(ep, "write"); |
| } else { |
| if (write_size < GRPC_SLICE_LENGTH(slice)) { |
| grpc_slice_buffer_undo_take_first( |
| ep->write_slices, grpc_slice_sub(slice, write_size, slice_len)); |
| } |
| if (ep->write_slices->length > 0) { |
| ep->stream_sync->NotifyOnWrite(&ep->write_action); |
| } else { |
| CallWriteCb(ep, GRPC_ERROR_NONE); |
| EP_UNREF(ep, "write"); |
| } |
| |
| if (grpc_tcp_trace.enabled()) { |
| grpc_slice trace_slice = grpc_slice_sub(slice, 0, write_size); |
| char* dump = grpc_dump_slice(trace_slice, GPR_DUMP_HEX | GPR_DUMP_ASCII); |
| gpr_log(GPR_DEBUG, "WRITE %p (peer=%s): %s", ep, ep->peer_string, dump); |
| gpr_free(dump); |
| grpc_slice_unref_internal(trace_slice); |
| } |
| } |
| grpc_slice_unref_internal(slice); |
| } |
| |
| static void CFStreamReadAllocationDone(void* arg, grpc_error* error) { |
| CFStreamEndpoint* ep = static_cast<CFStreamEndpoint*>(arg); |
| if (error == GRPC_ERROR_NONE) { |
| ep->stream_sync->NotifyOnRead(&ep->read_action); |
| } else { |
| grpc_slice_buffer_reset_and_unref_internal(ep->read_slices); |
| CallReadCb(ep, error); |
| EP_UNREF(ep, "read"); |
| } |
| } |
| |
| static void CFStreamRead(grpc_endpoint* ep, grpc_slice_buffer* slices, |
| grpc_closure* cb) { |
| CFStreamEndpoint* ep_impl = reinterpret_cast<CFStreamEndpoint*>(ep); |
| if (grpc_tcp_trace.enabled()) { |
| gpr_log(GPR_DEBUG, "CFStream endpoint:%p read (%p, %p) length:%zu", ep_impl, |
| slices, cb, slices->length); |
| } |
| GPR_ASSERT(ep_impl->read_cb == nullptr); |
| ep_impl->read_cb = cb; |
| ep_impl->read_slices = slices; |
| grpc_slice_buffer_reset_and_unref_internal(slices); |
| grpc_resource_user_alloc_slices(&ep_impl->slice_allocator, |
| GRPC_TCP_DEFAULT_READ_SLICE_SIZE, 1, |
| ep_impl->read_slices); |
| EP_REF(ep_impl, "read"); |
| } |
| |
| static void CFStreamWrite(grpc_endpoint* ep, grpc_slice_buffer* slices, |
| grpc_closure* cb) { |
| CFStreamEndpoint* ep_impl = reinterpret_cast<CFStreamEndpoint*>(ep); |
| if (grpc_tcp_trace.enabled()) { |
| gpr_log(GPR_DEBUG, "CFStream endpoint:%p write (%p, %p) length:%zu", |
| ep_impl, slices, cb, slices->length); |
| } |
| GPR_ASSERT(ep_impl->write_cb == nullptr); |
| ep_impl->write_cb = cb; |
| ep_impl->write_slices = slices; |
| EP_REF(ep_impl, "write"); |
| ep_impl->stream_sync->NotifyOnWrite(&ep_impl->write_action); |
| } |
| |
| void CFStreamShutdown(grpc_endpoint* ep, grpc_error* why) { |
| CFStreamEndpoint* ep_impl = reinterpret_cast<CFStreamEndpoint*>(ep); |
| if (grpc_tcp_trace.enabled()) { |
| gpr_log(GPR_DEBUG, "CFStream endpoint:%p shutdown (%p)", ep_impl, why); |
| } |
| CFReadStreamClose(ep_impl->read_stream); |
| CFWriteStreamClose(ep_impl->write_stream); |
| ep_impl->stream_sync->Shutdown(why); |
| grpc_resource_user_shutdown(ep_impl->resource_user); |
| if (grpc_tcp_trace.enabled()) { |
| gpr_log(GPR_DEBUG, "CFStream endpoint:%p shutdown DONE (%p)", ep_impl, why); |
| } |
| } |
| |
| void CFStreamDestroy(grpc_endpoint* ep) { |
| CFStreamEndpoint* ep_impl = reinterpret_cast<CFStreamEndpoint*>(ep); |
| if (grpc_tcp_trace.enabled()) { |
| gpr_log(GPR_DEBUG, "CFStream endpoint:%p destroy", ep_impl); |
| } |
| EP_UNREF(ep_impl, "destroy"); |
| } |
| |
| grpc_resource_user* CFStreamGetResourceUser(grpc_endpoint* ep) { |
| CFStreamEndpoint* ep_impl = reinterpret_cast<CFStreamEndpoint*>(ep); |
| return ep_impl->resource_user; |
| } |
| |
| char* CFStreamGetPeer(grpc_endpoint* ep) { |
| CFStreamEndpoint* ep_impl = reinterpret_cast<CFStreamEndpoint*>(ep); |
| return gpr_strdup(ep_impl->peer_string); |
| } |
| |
| int CFStreamGetFD(grpc_endpoint* ep) { return 0; } |
| |
| void CFStreamAddToPollset(grpc_endpoint* ep, grpc_pollset* pollset) {} |
| void CFStreamAddToPollsetSet(grpc_endpoint* ep, grpc_pollset_set* pollset) {} |
| void CFStreamDeleteFromPollsetSet(grpc_endpoint* ep, |
| grpc_pollset_set* pollset) {} |
| |
| static const grpc_endpoint_vtable vtable = {CFStreamRead, |
| CFStreamWrite, |
| CFStreamAddToPollset, |
| CFStreamAddToPollsetSet, |
| CFStreamDeleteFromPollsetSet, |
| CFStreamShutdown, |
| CFStreamDestroy, |
| CFStreamGetResourceUser, |
| CFStreamGetPeer, |
| CFStreamGetFD}; |
| |
| grpc_endpoint* grpc_cfstream_endpoint_create( |
| CFReadStreamRef read_stream, CFWriteStreamRef write_stream, |
| const char* peer_string, grpc_resource_quota* resource_quota, |
| CFStreamHandle* stream_sync) { |
| CFStreamEndpoint* ep_impl = |
| static_cast<CFStreamEndpoint*>(gpr_malloc(sizeof(CFStreamEndpoint))); |
| if (grpc_tcp_trace.enabled()) { |
| gpr_log(GPR_DEBUG, |
| "CFStream endpoint:%p create readStream:%p writeStream: %p", |
| ep_impl, read_stream, write_stream); |
| } |
| ep_impl->base.vtable = &vtable; |
| gpr_ref_init(&ep_impl->refcount, 1); |
| ep_impl->read_stream = read_stream; |
| ep_impl->write_stream = write_stream; |
| CFRetain(read_stream); |
| CFRetain(write_stream); |
| ep_impl->stream_sync = stream_sync; |
| CFSTREAM_HANDLE_REF(ep_impl->stream_sync, "endpoint create"); |
| |
| ep_impl->peer_string = gpr_strdup(peer_string); |
| ep_impl->read_cb = nil; |
| ep_impl->write_cb = nil; |
| ep_impl->read_slices = nil; |
| ep_impl->write_slices = nil; |
| GRPC_CLOSURE_INIT(&ep_impl->read_action, ReadAction, |
| static_cast<void*>(ep_impl), grpc_schedule_on_exec_ctx); |
| GRPC_CLOSURE_INIT(&ep_impl->write_action, WriteAction, |
| static_cast<void*>(ep_impl), grpc_schedule_on_exec_ctx); |
| ep_impl->resource_user = |
| grpc_resource_user_create(resource_quota, peer_string); |
| grpc_resource_user_slice_allocator_init(&ep_impl->slice_allocator, |
| ep_impl->resource_user, |
| CFStreamReadAllocationDone, ep_impl); |
| |
| return &ep_impl->base; |
| } |
| |
| #endif /* GRPC_CFSTREAM_ENDPOINT */ |