@ -0,0 +1,3 @@ |
||||
# load bazelrc from the legacy location |
||||
# as recommended in https://github.com/bazelbuild/bazel/issues/6319 |
||||
import %workspace%/tools/bazel.rc |
@ -1,6 +1,6 @@ |
||||
--- |
||||
Checks: 'modernize-use-nullptr,google-build-namespaces,google-build-explicit-make-pair,readability-function-size,performance-*' |
||||
WarningsAsErrors: 'modernize-use-nullptr,google-build-namespaces,google-build-explicit-make-pair,readability-function-size,performance-*' |
||||
Checks: 'modernize-use-nullptr,google-build-namespaces,google-build-explicit-make-pair,readability-function-size,performance-*,bugprone-*' |
||||
WarningsAsErrors: 'modernize-use-nullptr,google-build-namespaces,google-build-explicit-make-pair,readability-function-size,performance-*,bugprone-*' |
||||
CheckOptions: |
||||
- key: readability-function-size.StatementThreshold |
||||
value: '450' |
||||
|
@ -0,0 +1,2 @@ |
||||
daysUntilLock: 90 |
||||
lockComment: false |
@ -1,6 +1,6 @@ |
||||
set noparent |
||||
@nicolasnoble |
||||
@dgquintas |
||||
@jtattermusch |
||||
@a11r |
||||
@vjpai |
||||
|
||||
|
@ -1,4 +1,4 @@ |
||||
set noparent |
||||
@jtattermusch |
||||
@nicolasnoble |
||||
@mehrdada |
||||
@apolcyn |
||||
|
@ -0,0 +1,32 @@ |
||||
# Polling Engine Usage on gRPC client and Server |
||||
|
||||
_Author: Sree Kuchibhotla (@sreecha) - Sep 2018_ |
||||
|
||||
|
||||
This document talks about how polling engine is used in gRPC core (both on client and server code paths). |
||||
|
||||
## gRPC client |
||||
|
||||
### Relation between Call, Channel (sub-channels), Completion queue, `grpc_pollset` |
||||
- A gRPC Call is tied to a channel (more specifically a sub-channel) and a completion queue for the lifetime of the call. |
||||
- Once a _sub-channel_ is picked for the call, the file-descriptor (socket fd in case of TCP channels) is added to the pollset corresponding to call's completion queue. (Recall that as per [grpc-cq](grpc-cq.md), a completion queue has a pollset by default) |
||||
|
||||
![image](../images/grpc-call-channel-cq.png) |
||||
|
||||
|
||||
### Making progress on Async `connect()` on sub-channels (`grpc_pollset_set` usecase) |
||||
- A gRPC channel is created between a client and a 'target'. The 'target' may resolve in to one or more backend servers. |
||||
- A sub-channel is the 'connection' from a client to the backend server |
||||
- While establishing sub-cannels (i.e connections) to the backends, gRPC issues async [`connect()`](https://github.com/grpc/grpc/blob/v1.15.1/src/core/lib/iomgr/tcp_client_posix.cc#L296) calls which may not complete right away. When the `connect()` eventually succeeds, the socket fd is make 'writable' |
||||
- This means that the polling engine must be monitoring all these sub-channel `fd`s for writable events and we need to make sure there is a polling thread that monitors all these fds |
||||
- To accomplish this, the `grpc_pollset_set` is used the following way (see picture below) |
||||
|
||||
![image](../images/grpc-client-lb-pss.png) |
||||
|
||||
## gRPC server |
||||
|
||||
- The listening fd (i.e., the socket fd corresponding to the server listening port) is added to each of the server completion queues. Note that in gRPC we use SO_REUSEPORT option and create multiple listening fds but all of them map to the same listening port |
||||
- A new incoming channel is assigned to some server completion queue picked randomly (note that we currently [round-robin](https://github.com/grpc/grpc/blob/v1.15.1/src/core/lib/iomgr/tcp_server_posix.cc#L231) over the server completion queues) |
||||
|
||||
![image](../images/grpc-server-cq-fds.png) |
||||
|
@ -0,0 +1,64 @@ |
||||
# gRPC Completion Queue |
||||
|
||||
_Author: Sree Kuchibhotla (@sreecha) - Sep 2018_ |
||||
|
||||
Code: [completion_queue.cc](https://github.com/grpc/grpc/blob/v1.15.1/src/core/lib/surface/completion_queue.cc) |
||||
|
||||
This document gives an overview of completion queue architecture and focuses mainly on the interaction between completion queue and the Polling engine layer. |
||||
|
||||
## Completion queue attributes |
||||
Completion queue has two attributes |
||||
|
||||
- Completion_type: |
||||
- GRPC_CQ_NEXT: grpc_completion_queue_next() can be called (but not grpc_completion_queue_pluck()) |
||||
- GRPC_CQ_PLUCK: grpc_completion_queue_pluck() can be called (but not grpc_completion_queue_next()) |
||||
- GRPC_CQ_CALLBACK: The tags in the queue are function pointers to callbacks. Also, neither next() nor pluck() can be called on this |
||||
|
||||
- Polling_type: |
||||
- GRPC_CQ_NON_POLLING: Threads calling completion_queue_next/pluck do not do any polling |
||||
- GRPC_CQ_DEFAULT_POLLING: Threads calling completion_queue_next/pluck do polling |
||||
- GRPC_CQ_NON_LISTENING: Functionally similar to default polling except for a boolean attribute that states that the cq is non-listening. This is used by the grpc-server code to not associate any listening sockets with this completion-queue’s pollset |
||||
|
||||
|
||||
## Details |
||||
|
||||
![image](../images/grpc-cq.png) |
||||
|
||||
|
||||
### **grpc\_completion\_queue\_next()** & **grpc_completion_queue_pluck()** APIS |
||||
|
||||
|
||||
``` C++ |
||||
grpc_completion_queue_next(cq, deadline)/pluck(cq, deadline, tag) { |
||||
while(true) { |
||||
\\ 1. If an event is queued in the completion queue, dequeue and return |
||||
\\ (in case of pluck() dequeue only if the tag is the one we are interested in) |
||||
|
||||
\\ 2. If completion queue shutdown return |
||||
|
||||
\\ 3. In case of pluck, add (tag, worker) pair to the tag<->worker map on the cq |
||||
|
||||
\\ 4. Call grpc_pollset_work(cq’s-pollset, deadline) to do polling |
||||
\\ Note that if this function found some fds to be readable/writable/error, |
||||
\\ it would have scheduled those closures (which may queue completion events |
||||
\\ on SOME completion queue - not necessarily this one) |
||||
} |
||||
} |
||||
``` |
||||
|
||||
### Queuing a completion event (i.e., "tag") |
||||
|
||||
``` C++ |
||||
grpc_cq_end_op(cq, tag) { |
||||
\\ 1. Queue the tag in the event queue |
||||
|
||||
\\ 2. Find the pollset corresponding to the completion queue |
||||
\\ (i) If the cq is of type GRPC_CQ_NEXT, then KICK ANY worker |
||||
\\ i.e., call grpc_pollset_kick(pollset, nullptr) |
||||
\\ (ii) If the cq is of type GRPC_CQ_PLUCK, then search the tag<->worker |
||||
\\ map on the completion queue to find the worker. Then specifically |
||||
\\ kick that worker i.e call grpc_pollset_kick(pollset, worker) |
||||
} |
||||
|
||||
``` |
||||
|
@ -0,0 +1,154 @@ |
||||
# Polling Engines |
||||
|
||||
_Author: Sree Kuchibhotla (@sreecha) - Sep 2018_ |
||||
|
||||
|
||||
## Why do we need a 'polling engine' ? |
||||
|
||||
Polling engine component was created for the following reasons: |
||||
|
||||
- gRPC code deals with a bunch of file descriptors on which events like descriptor being readable/writable/error have to be monitored |
||||
- gRPC code knows the actions to perform when such events happen |
||||
- For example: |
||||
- `grpc_endpoint` code calls `recvmsg` call when the fd is readable and `sendmsg` call when the fd is writable |
||||
- ` tcp_client` connect code issues async `connect` and finishes creating the client once the fd is writable (i.e when the `connect` actually finished) |
||||
- gRPC needed some component that can "efficiently" do the above operations __using the threads provided by the applications (i.e., not create any new threads)__. Also by "efficiently" we mean optimized for latency and throughput |
||||
|
||||
|
||||
## Polling Engine Implementations in gRPC |
||||
There are multiple polling engine implementations depending on the OS and the OS version. Fortunately all of them expose the same interface |
||||
|
||||
- Linux: |
||||
|
||||
- **`epollex`** (default but requires kernel version >= 4.5), |
||||
- `epoll1` (If `epollex` is not available and glibc version >= 2.9) |
||||
- `poll` (If kernel does not have epoll support) |
||||
- `poll-cv` (If explicitly configured) |
||||
- Mac: **`poll`** (default), `poll-cv` (If explicitly configured) |
||||
- Windows: (no name) |
||||
- One-off polling engines: |
||||
- AppEngine platform: **`poll-cv`** (default) |
||||
- NodeJS : `libuv` polling engine implementation (requires different compile `#define`s) |
||||
|
||||
## Polling Engine Interface |
||||
|
||||
### Opaque Structures exposed by the polling engine |
||||
The following are the **Opaque** structures exposed by Polling Engine interface (NOTE: Different polling engine implementations have different definitions of these structures) |
||||
|
||||
- **grpc_fd:** Structure representing a file descriptor |
||||
- **grpc_pollset:** A set of one or more grpc_fds that are ‘polled’ for readable/writable/error events. One grpc_fd can be in multiple `grpc_pollset`s |
||||
- **grpc_pollset_worker:** Structure representing a ‘polling thread’ - more specifically, the thread that calls `grpc_pollset_work()` API |
||||
- **grpc_pollset_set:** A group of `grpc_fds`, `grpc_pollsets` and `grpc_pollset_sets` (yes, a `grpc_pollset_set` can contain other `grpc_pollset_sets`) |
||||
|
||||
### Polling engine API |
||||
|
||||
#### grpc_fd |
||||
- **grpc\_fd\_notify\_on\_[read|write|error]** |
||||
- Signature: `grpc_fd_notify_on_(grpc_fd* fd, grpc_closure* closure)` |
||||
- Register a [closure](https://github.com/grpc/grpc/blob/v1.15.1/src/core/lib/iomgr/closure.h#L67) to be called when the fd becomes readable/writable or has an error (In grpc parlance, we refer to this act as “arming the fd”) |
||||
- The closure is called exactly once per event. I.e once the fd becomes readable (or writable or error), the closure is fired and the fd is ‘unarmed’. To be notified again, the fd has to be armed again. |
||||
|
||||
- **grpc_fd_shutdown** |
||||
- Signature: `grpc_fd_shutdown(grpc_fd* fd)` |
||||
- Any current (or future) closures registered for readable/writable/error events are scheduled immediately with an error |
||||
|
||||
- **grpc_fd_orphan** |
||||
- Signature: `grpc_fd_orphan(grpc_fd* fd, grpc_closure* on_done, int* release_fd, char* reason)` |
||||
- Release the `grpc_fd` structure and call `on_done` closure when the operation is complete |
||||
- If `release_fd` is set to `nullptr`, then `close()` the underlying fd as well. If not, put the underlying fd in `release_fd` (and do not call `close()`) |
||||
- `release_fd` set to non-null in cases where the underlying fd is NOT owned by grpc core (like for example the fds used by C-Ares DNS resolver ) |
||||
|
||||
#### grpc_pollset |
||||
|
||||
- **grpc_pollset_add_fd ** |
||||
- Signature: `grpc_pollset_add_fd(grpc_pollset* ps, grpc_fd *fd)` |
||||
- Add fd to pollset |
||||
> **NOTE**: There is no `grpc_pollset_remove_fd`. This is because calling `grpc_fd_orphan()` will effectively remove the fd from all the pollsets it’s a part of |
||||
|
||||
- ** grpc_pollset_work ** |
||||
- Signature: `grpc_pollset_work(grpc_pollset* ps, grpc_pollset_worker** worker, grpc_millis deadline)` |
||||
> **NOTE**: `grpc_pollset_work()` requires the pollset mutex to be locked before calling it. Shortly after calling `grpc_pollset_work()`, the function populates the `*worker` pointer (among other things) and releases the mutex. Once `grpc_pollset_work()` returns, the `*worker` pointer is **invalid** and should not be used anymore. See the code in `completion_queue.cc` to see how this is used. |
||||
- Poll the fds in the pollset for events AND return when ANY of the following is true: |
||||
- Deadline expired |
||||
- Some fds in the pollset were found to be readable/writable/error and those associated closures were ‘scheduled’ (but not necessarily executed) |
||||
- worker is “kicked” (see `grpc_pollset_kick` for more details) |
||||
|
||||
- **grpc_pollset_kick** |
||||
- Signature: `grpc_pollset_kick(grpc_pollset* ps, grpc_pollset_worker* worker)` |
||||
- “Kick the worker” i.e Force the worker to return from grpc_pollset_work() |
||||
- If `worker == nullptr`, kick ANY worker active on that pollset |
||||
|
||||
#### grpc_pollset_set |
||||
|
||||
- **grpc\_pollset\_set\_[add|del]\_fd** |
||||
- Signature: `grpc_pollset_set_[add|del]_fd(grpc_pollset_set* pss, grpc_fd *fd)` |
||||
Add/Remove fd to the `grpc_pollset_set` |
||||
|
||||
- **grpc\_pollset\_set_[add|del]\_pollset** |
||||
- Signature: `grpc_pollset_set_[add|del]_pollset(grpc_pollset_set* pss, grpc_pollset* ps)` |
||||
- What does adding a pollset to a pollset_set mean ? |
||||
- It means that calling `grpc_pollset_work()` on the pollset will also poll all the fds in the pollset_set i.e semantically, it is similar to adding all the fds inside pollset_set to the pollset. |
||||
- This guarantee is no longer true once the pollset is removed from the pollset_set |
||||
|
||||
- **grpc\_pollset\_set_[add|del]\_pollset\_set** |
||||
- Signature: `grpc_pollset_set_[add|del]_pollset_set(grpc_pollset_set* bag, grpc_pollset_set* item)` |
||||
- Semantically, this is similar to adding all the fds in the ‘bag’ pollset_set to the ‘item’ pollset_set |
||||
|
||||
|
||||
#### Recap: |
||||
|
||||
__Relation between grpc_pollset_worker, grpc_pollset and grpc_fd:__ |
||||
|
||||
![image](../images/grpc-ps-pss-fd.png) |
||||
|
||||
__grpc_pollset_set__ |
||||
|
||||
![image](../images/grpc-pss.png) |
||||
|
||||
|
||||
## Polling Engine Implementations |
||||
|
||||
### epoll1 |
||||
|
||||
![image](../images/grpc-epoll1.png) |
||||
|
||||
Code at `src/core/lib/iomgr/ev_epoll1_posix.cc` |
||||
|
||||
- The logic to choose a designated poller is quite complicated. Pollsets are internally sharded into what are called `pollset_neighborhood` (a structure internal to `epoll1` polling engine implementation). `grpc_pollset_workers` that call `grpc_pollset_work` on a given pollset are all queued in a linked-list against the `grpc_pollset`. The head of the linked list is called "root worker" |
||||
|
||||
- There are as many neighborhoods as the number of cores. A pollset is put in a neighborhood based on the CPU core of the root worker thread. When picking the next designated poller, we always try to find another worker on the current pollset. If there are no more workers in the current pollset, a `pollset_neighborhood` listed is scanned to pick the next pollset and worker that could be the new designated poller. |
||||
- NOTE: There is room to tune this implementation. All we really need is good way to maintain a list of `grpc_pollset_workers` with a way to group them per-pollset (needed to implement `grpc_pollset_kick` semantics) and a way randomly select a new designated poller |
||||
|
||||
- See [`begin_worker()`](https://github.com/grpc/grpc/blob/v1.15.1/src/core/lib/iomgr/ev_epoll1_linux.cc#L729) function to see how a designated poller is chosen. Similarly [`end_worker()`](https://github.com/grpc/grpc/blob/v1.15.1/src/core/lib/iomgr/ev_epoll1_linux.cc#L916) function is called by the worker that was just out of `epoll_wait()` and will have to choose a new designated poller) |
||||
|
||||
|
||||
### epollex |
||||
|
||||
![image](../images/grpc-epollex.png) |
||||
|
||||
Code at `src/core/lib/iomgr/ev_epollex_posix.cc` |
||||
|
||||
- FDs are added to multiple epollsets with EPOLLEXCLUSIVE flag. This prevents multiple worker threads from waking up from polling whenever the fd is readable/writable |
||||
|
||||
- A few observations: |
||||
|
||||
- If multiple pollsets are pointing to the same `Pollable`, then the `pollable` MUST be either empty or of type `PO_FD` (i.e single-fd) |
||||
- A multi-pollable has one-and-only-one incoming link from a pollset |
||||
- The same FD can be in multiple `Pollable`s (even if one of the `Pollable`s is of type PO_FD) |
||||
- There cannot be two `Pollable`s of type PO_FD for the same fd |
||||
|
||||
- Why do we need `Pollable` of type PO_FD and PO_EMTPY ? |
||||
- The main reason is the Sync client API |
||||
- We create one new completion queue per call. If we didn’t have PO_EMPTY and PO_FD type pollables, then every call on a given channel will effectively have to create a `Pollable` and hence an epollset. This is because every completion queue automatically creates a pollset and the channel fd will have to be put in that pollset. This clearly requires an epollset to put that fd. Creating an epollset per call (even if we delete the epollset once the call is completed) would mean a lot of sys calls to create/delete epoll fds. This is clearly not a good idea. |
||||
- With these new types of `Pollable`s, all pollsets (corresponding to the new per-call completion queue) will initially point to PO_EMPTY global epollset. Then once the channel fd is added to the pollset, the pollset will point to the `Pollable` of type PO_FD containing just that fd (i.e it will reuse the existing `Pollable`). This way, the epoll fd creation/deletion churn is avoided. |
||||
|
||||
|
||||
### Other polling engine implementations (poll and windows polling engine) |
||||
- **poll** polling engine: gRPC's `poll` polling engine is quite complicated. It uses the `poll()` function to do the polling (and hence it is for platforms like osx where epoll is not available) |
||||
- The implementation is further complicated by the fact that poll() is level triggered (just keep this in mind in case you wonder why the code at `src/core/lib/iomgr/ev_poll_posix.cc` is written a certain/seemingly complicated way :)) |
||||
|
||||
- **Polling engine on Windows**: Windows polling engine looks nothing like other polling engines |
||||
- Unlike the grpc polling engines for Unix systems (epollex, epoll1 and poll) Windows endpoint implementation and polling engine implementations are very closely tied together |
||||
- Windows endpoint read/write API implementations use the Windows IO API which require specifying an [I/O completion port](https://docs.microsoft.com/en-us/windows/desktop/fileio/i-o-completion-ports) |
||||
- In Windows polling engine’s grpc_pollset_work() implementation, ONE of the threads is chosen to wait on the I/O completion port while other threads wait on a condition variable (much like the turnstile polling in epollex/epoll1) |
||||
|
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 52 KiB |
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 44 KiB |
After Width: | Height: | Size: 45 KiB |
After Width: | Height: | Size: 55 KiB |
After Width: | Height: | Size: 41 KiB |
After Width: | Height: | Size: 35 KiB |
After Width: | Height: | Size: 51 KiB |
After Width: | Height: | Size: 24 KiB |
After Width: | Height: | Size: 31 KiB |
After Width: | Height: | Size: 41 KiB |
@ -0,0 +1,102 @@ |
||||
# Copyright 2018 The 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. |
||||
|
||||
# -- Path setup -------------------------------------------------------------- |
||||
|
||||
import os |
||||
import sys |
||||
PYTHON_FOLDER = os.path.join(os.path.dirname(os.path.realpath(__file__)), |
||||
'..', '..', '..', 'src', 'python') |
||||
sys.path.insert(0, os.path.join(PYTHON_FOLDER, 'grpcio')) |
||||
sys.path.insert(0, os.path.join(PYTHON_FOLDER, 'grpcio_health_checking')) |
||||
sys.path.insert(0, os.path.join(PYTHON_FOLDER, 'grpcio_reflection')) |
||||
sys.path.insert(0, os.path.join(PYTHON_FOLDER, 'grpcio_testing')) |
||||
|
||||
# -- Project information ----------------------------------------------------- |
||||
|
||||
project = 'gRPC Python' |
||||
copyright = '2018, The gRPC Authors' |
||||
author = 'The gRPC Authors' |
||||
|
||||
# Import generated grpc_version after the path been modified |
||||
import grpc_version |
||||
version = ".".join(grpc_version.VERSION.split(".")[:3]) |
||||
release = grpc_version.VERSION |
||||
|
||||
# -- General configuration --------------------------------------------------- |
||||
|
||||
templates_path = ['_templates'] |
||||
source_suffix = ['.rst', '.md'] |
||||
master_doc = 'index' |
||||
language = 'en' |
||||
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] |
||||
pygments_style = None |
||||
|
||||
# --- Extensions Configuration ----------------------------------------------- |
||||
|
||||
extensions = [ |
||||
'sphinx.ext.autodoc', |
||||
'sphinx.ext.viewcode', |
||||
'sphinx.ext.todo', |
||||
'sphinx.ext.napoleon', |
||||
'sphinx.ext.coverage', |
||||
] |
||||
|
||||
napoleon_google_docstring = True |
||||
napoleon_numpy_docstring = True |
||||
napoleon_include_special_with_doc = True |
||||
|
||||
autodoc_default_options = { |
||||
'members': None, |
||||
} |
||||
|
||||
autodoc_mock_imports = [ |
||||
'grpc._cython', |
||||
'grpc_health.v1.health_pb2', |
||||
'grpc_health.v1.health_pb2_grpc', |
||||
'grpc_reflection.v1alpha.reflection_pb2', |
||||
'grpc_reflection.v1alpha.reflection_pb2_grpc', |
||||
] |
||||
|
||||
# -- HTML Configuration ------------------------------------------------- |
||||
|
||||
html_theme = 'alabaster' |
||||
html_theme_options = { |
||||
'fixed_sidebar': True, |
||||
'page_width': '1140px', |
||||
'show_related': True, |
||||
'analytics_id': 'UA-60127042-1', |
||||
'description': grpc_version.VERSION, |
||||
'show_powered_by': False, |
||||
} |
||||
|
||||
# -- Options for manual page output ------------------------------------------ |
||||
|
||||
man_pages = [(master_doc, 'grpcio', 'grpcio Documentation', [author], 1)] |
||||
|
||||
# -- Options for Texinfo output ---------------------------------------------- |
||||
|
||||
texinfo_documents = [ |
||||
(master_doc, 'grpcio', 'grpcio Documentation', author, 'grpcio', |
||||
'One line description of project.', 'Miscellaneous'), |
||||
] |
||||
|
||||
# -- Options for Epub output ------------------------------------------------- |
||||
|
||||
epub_title = project |
||||
epub_exclude_files = ['search.html'] |
||||
|
||||
# -- Options for todo extension ---------------------------------------------- |
||||
|
||||
todo_include_todos = True |
@ -0,0 +1,16 @@ |
||||
Glossary |
||||
================ |
||||
|
||||
.. glossary:: |
||||
|
||||
metadatum |
||||
A key-value pair included in the HTTP header. It is a |
||||
2-tuple where the first entry is the key and the |
||||
second is the value, i.e. (key, value). The metadata key is an ASCII str, |
||||
and must be a valid HTTP header name. The metadata value can be |
||||
either a valid HTTP ASCII str, or bytes. If bytes are provided, |
||||
the key must end with '-bin', i.e. |
||||
``('binary-metadata-bin', b'\\x00\\xFF')`` |
||||
|
||||
metadata |
||||
A sequence of metadatum. |
@ -0,0 +1,169 @@ |
||||
gRPC |
||||
============= |
||||
|
||||
.. module:: grpc |
||||
|
||||
Tutorial |
||||
-------- |
||||
|
||||
If you want to see gRPC in action first, visit the `Python Quickstart <https://grpc.io/docs/quickstart/python.html>`_. |
||||
Or, if you would like dive in with more extensive usage of gRPC Python, check `gRPC Basics - Python <https://grpc.io/docs/tutorials/basic/python.html>`_ out. |
||||
|
||||
|
||||
Example |
||||
------- |
||||
|
||||
Go to `gRPC Python Examples <https://github.com/grpc/grpc/tree/master/examples/python>`_ |
||||
|
||||
|
||||
Module Contents |
||||
--------------- |
||||
|
||||
Create Client |
||||
^^^^^^^^^^^^^ |
||||
|
||||
.. autofunction:: insecure_channel |
||||
.. autofunction:: secure_channel |
||||
.. autofunction:: intercept_channel |
||||
|
||||
|
||||
Create Client Credentials |
||||
^^^^^^^^^^^^^^^^^^^^^^^^^ |
||||
|
||||
.. autofunction:: ssl_channel_credentials |
||||
.. autofunction:: metadata_call_credentials |
||||
.. autofunction:: access_token_call_credentials |
||||
.. autofunction:: composite_call_credentials |
||||
.. autofunction:: composite_channel_credentials |
||||
|
||||
|
||||
Create Server |
||||
^^^^^^^^^^^^^ |
||||
|
||||
.. autofunction:: server |
||||
|
||||
|
||||
Create Server Credentials |
||||
^^^^^^^^^^^^^^^^^^^^^^^^^ |
||||
|
||||
.. autofunction:: ssl_server_credentials |
||||
.. autofunction:: ssl_server_certificate_configuration |
||||
.. autofunction:: dynamic_ssl_server_credentials |
||||
|
||||
|
||||
RPC Method Handlers |
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^ |
||||
|
||||
.. autofunction:: unary_unary_rpc_method_handler |
||||
.. autofunction:: unary_stream_rpc_method_handler |
||||
.. autofunction:: stream_unary_rpc_method_handler |
||||
.. autofunction:: stream_stream_rpc_method_handler |
||||
.. autofunction:: method_handlers_generic_handler |
||||
|
||||
|
||||
Channel Ready Future |
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^ |
||||
|
||||
.. autofunction:: channel_ready_future |
||||
|
||||
|
||||
Channel Connectivity |
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^ |
||||
|
||||
.. autoclass:: ChannelConnectivity |
||||
|
||||
|
||||
gRPC Status Code |
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^ |
||||
|
||||
.. autoclass:: StatusCode |
||||
|
||||
|
||||
Channel Object |
||||
^^^^^^^^^^^^^^ |
||||
|
||||
.. autoclass:: Channel |
||||
|
||||
|
||||
Server Object |
||||
^^^^^^^^^^^^^ |
||||
|
||||
.. autoclass:: Server |
||||
|
||||
|
||||
Authentication & Authorization Objects |
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
||||
|
||||
.. autoclass:: ChannelCredentials |
||||
.. autoclass:: CallCredentials |
||||
.. autoclass:: AuthMetadataContext |
||||
.. autoclass:: AuthMetadataPluginCallback |
||||
.. autoclass:: AuthMetadataPlugin |
||||
.. autoclass:: ServerCredentials |
||||
.. autoclass:: ServerCertificateConfiguration |
||||
|
||||
|
||||
gRPC Exceptions |
||||
^^^^^^^^^^^^^^^ |
||||
|
||||
.. autoexception:: RpcError |
||||
|
||||
|
||||
Shared Context |
||||
^^^^^^^^^^^^^^ |
||||
|
||||
.. autoclass:: RpcContext |
||||
|
||||
|
||||
Client-Side Context |
||||
^^^^^^^^^^^^^^^^^^^^^^^ |
||||
|
||||
.. autoclass:: Call |
||||
|
||||
|
||||
Client-Side Interceptor |
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
||||
|
||||
.. autoclass:: ClientCallDetails |
||||
.. autoclass:: UnaryUnaryClientInterceptor |
||||
.. autoclass:: UnaryStreamClientInterceptor |
||||
.. autoclass:: StreamUnaryClientInterceptor |
||||
.. autoclass:: StreamStreamClientInterceptor |
||||
|
||||
|
||||
Service-Side Context |
||||
^^^^^^^^^^^^^^^^^^^^ |
||||
|
||||
.. autoclass:: ServicerContext |
||||
|
||||
|
||||
Service-Side Handler |
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
||||
|
||||
.. autoclass:: RpcMethodHandler |
||||
.. autoclass:: HandlerCallDetails |
||||
.. autoclass:: GenericRpcHandler |
||||
.. autoclass:: ServiceRpcHandler |
||||
|
||||
|
||||
Service-Side Interceptor |
||||
^^^^^^^^^^^^^^^^^^^^^^^^ |
||||
|
||||
.. autoclass:: ServerInterceptor |
||||
|
||||
|
||||
Multi-Callable Interfaces |
||||
^^^^^^^^^^^^^^^^^^^^^^^^^ |
||||
|
||||
.. autoclass:: UnaryUnaryMultiCallable |
||||
.. autoclass:: UnaryStreamMultiCallable |
||||
.. autoclass:: StreamUnaryMultiCallable |
||||
.. autoclass:: StreamStreamMultiCallable |
||||
|
||||
|
||||
Future Interfaces |
||||
^^^^^^^^^^^^^^^^^ |
||||
|
||||
.. autoexception:: FutureTimeoutError |
||||
.. autoexception:: FutureCancelledError |
||||
.. autoclass:: Future |
@ -0,0 +1,7 @@ |
||||
gRPC Health Checking |
||||
==================== |
||||
|
||||
Module Contents |
||||
--------------- |
||||
|
||||
.. autoclass:: grpc_health.v1.health.HealthServicer |
@ -0,0 +1,19 @@ |
||||
gRPC Reflection |
||||
==================== |
||||
|
||||
What is gRPC reflection? |
||||
--------------------------------------------- |
||||
|
||||
Check this out `gRPC Python Server Reflection <https://github.com/grpc/grpc/blob/master/doc/python/server_reflection.md>`_ |
||||
|
||||
|
||||
Example |
||||
------- |
||||
|
||||
Refer to the GitHub `reflection example <https://github.com/grpc/grpc/blob/master/examples/python/helloworld/greeter_server_with_reflection.py>`_ |
||||
|
||||
|
||||
Module Contents |
||||
--------------- |
||||
|
||||
.. automodule:: grpc_reflection.v1alpha.reflection |
@ -0,0 +1,7 @@ |
||||
gRPC Testing |
||||
==================== |
||||
|
||||
Module Contents |
||||
--------------- |
||||
|
||||
.. automodule:: grpc_testing |
@ -0,0 +1,24 @@ |
||||
Welcome to gRPC Python's documentation! |
||||
======================================= |
||||
|
||||
Version: |version| Release: |release| |
||||
|
||||
API Reference |
||||
============= |
||||
|
||||
.. toctree:: |
||||
:caption: Contents: |
||||
|
||||
grpc |
||||
grpc_health_checking |
||||
grpc_reflection |
||||
grpc_testing |
||||
glossary |
||||
|
||||
|
||||
Indices and tables |
||||
================== |
||||
|
||||
* :ref:`genindex` |
||||
* :ref:`modindex` |
||||
* :ref:`search` |
@ -0,0 +1,66 @@ |
||||
Pod::Spec.new do |s| |
||||
s.name = "HelloWorld" |
||||
s.version = "0.0.1" |
||||
s.license = "Apache License, Version 2.0" |
||||
s.authors = { 'gRPC contributors' => 'grpc-io@googlegroups.com' } |
||||
s.homepage = "https://grpc.io/" |
||||
s.summary = "HelloWorld example" |
||||
s.source = { :git => 'https://github.com/grpc/grpc.git' } |
||||
|
||||
s.ios.deployment_target = "7.1" |
||||
s.osx.deployment_target = "10.9" |
||||
|
||||
# Base directory where the .proto files are. |
||||
src = "../../protos" |
||||
|
||||
# Run protoc with the Objective-C and gRPC plugins to generate protocol messages and gRPC clients. |
||||
s.dependency "!ProtoCompiler-gRPCPlugin", "~> 1.0" |
||||
|
||||
# Pods directory corresponding to this app's Podfile, relative to the location of this podspec. |
||||
pods_root = 'Pods' |
||||
|
||||
# Path where Cocoapods downloads protoc and the gRPC plugin. |
||||
protoc_dir = "#{pods_root}/!ProtoCompiler" |
||||
protoc = "#{protoc_dir}/protoc" |
||||
plugin = "#{pods_root}/!ProtoCompiler-gRPCPlugin/grpc_objective_c_plugin" |
||||
|
||||
# Directory where the generated files will be placed. |
||||
dir = "#{pods_root}/#{s.name}" |
||||
|
||||
s.prepare_command = <<-CMD |
||||
mkdir -p #{dir} |
||||
#{protoc} \ |
||||
--plugin=protoc-gen-grpc=#{plugin} \ |
||||
--objc_out=#{dir} \ |
||||
--grpc_out=#{dir} \ |
||||
-I #{src} \ |
||||
-I #{protoc_dir} \ |
||||
#{src}/helloworld.proto |
||||
CMD |
||||
|
||||
# Files generated by protoc |
||||
s.subspec "Messages" do |ms| |
||||
ms.source_files = "#{dir}/*.pbobjc.{h,m}", "#{dir}/**/*.pbobjc.{h,m}" |
||||
ms.header_mappings_dir = dir |
||||
ms.requires_arc = false |
||||
# The generated files depend on the protobuf runtime. |
||||
ms.dependency "Protobuf" |
||||
end |
||||
|
||||
# Files generated by the gRPC plugin |
||||
s.subspec "Services" do |ss| |
||||
ss.source_files = "#{dir}/*.pbrpc.{h,m}", "#{dir}/**/*.pbrpc.{h,m}" |
||||
ss.header_mappings_dir = dir |
||||
ss.requires_arc = true |
||||
# The generated files depend on the gRPC runtime, and on the files generated by protoc. |
||||
ss.dependency "gRPC-ProtoRPC" |
||||
ss.dependency "#{s.name}/Messages" |
||||
end |
||||
|
||||
s.pod_target_xcconfig = { |
||||
# This is needed by all pods that depend on Protobuf: |
||||
'GCC_PREPROCESSOR_DEFINITIONS' => '$(inherited) GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS=1', |
||||
# This is needed by all pods that depend on gRPC-RxLibrary: |
||||
'CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES' => 'YES', |
||||
} |
||||
end |
@ -0,0 +1,399 @@ |
||||
// !$*UTF8*$! |
||||
{ |
||||
archiveVersion = 1; |
||||
classes = { |
||||
}; |
||||
objectVersion = 50; |
||||
objects = { |
||||
|
||||
/* Begin PBXBuildFile section */ |
||||
5EF711A4215174880077496F /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 5EF711A3215174880077496F /* AppDelegate.m */; }; |
||||
5EF711A7215174880077496F /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 5EF711A6215174880077496F /* ViewController.m */; }; |
||||
5EF711A9215174890077496F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 5EF711A8215174890077496F /* Assets.xcassets */; }; |
||||
5EF711AC215174890077496F /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5EF711AA215174890077496F /* Main.storyboard */; }; |
||||
5EF711AF215174890077496F /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 5EF711AE215174890077496F /* main.m */; }; |
||||
827B966E84F6A63FD0F3F6BC /* libPods-HelloWorld.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 369D887F6054EBA486218C69 /* libPods-HelloWorld.a */; }; |
||||
/* End PBXBuildFile section */ |
||||
|
||||
/* Begin PBXFileReference section */ |
||||
2AAF8E8BA7DBFD2D3886AA25 /* Pods-HelloWorld.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-HelloWorld.release.xcconfig"; path = "Pods/Target Support Files/Pods-HelloWorld/Pods-HelloWorld.release.xcconfig"; sourceTree = "<group>"; }; |
||||
369D887F6054EBA486218C69 /* libPods-HelloWorld.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-HelloWorld.a"; sourceTree = BUILT_PRODUCTS_DIR; }; |
||||
5EF7119F215174870077496F /* HelloWorld.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = HelloWorld.app; sourceTree = BUILT_PRODUCTS_DIR; }; |
||||
5EF711A2215174880077496F /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; }; |
||||
5EF711A3215174880077496F /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; }; |
||||
5EF711A5215174880077496F /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = "<group>"; }; |
||||
5EF711A6215174880077496F /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = "<group>"; }; |
||||
5EF711A8215174890077496F /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; }; |
||||
5EF711AB215174890077496F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; }; |
||||
5EF711AD215174890077496F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; |
||||
5EF711AE215174890077496F /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; }; |
||||
5EF711B0215174890077496F /* HelloWorld.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = HelloWorld.entitlements; sourceTree = "<group>"; }; |
||||
CA4699782F6344C8E67C9FEE /* Pods-HelloWorld.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-HelloWorld.debug.xcconfig"; path = "Pods/Target Support Files/Pods-HelloWorld/Pods-HelloWorld.debug.xcconfig"; sourceTree = "<group>"; }; |
||||
/* End PBXFileReference section */ |
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */ |
||||
5EF7119C215174870077496F /* Frameworks */ = { |
||||
isa = PBXFrameworksBuildPhase; |
||||
buildActionMask = 2147483647; |
||||
files = ( |
||||
827B966E84F6A63FD0F3F6BC /* libPods-HelloWorld.a in Frameworks */, |
||||
); |
||||
runOnlyForDeploymentPostprocessing = 0; |
||||
}; |
||||
/* End PBXFrameworksBuildPhase section */ |
||||
|
||||
/* Begin PBXGroup section */ |
||||
5EF71196215174870077496F = { |
||||
isa = PBXGroup; |
||||
children = ( |
||||
5EF711AE215174890077496F /* main.m */, |
||||
5EF711A1215174870077496F /* HelloWorld */, |
||||
5EF711A0215174870077496F /* Products */, |
||||
8D3EFBB796129582177142C4 /* Pods */, |
||||
A986548CB5622AF6CC3ECCCE /* Frameworks */, |
||||
); |
||||
sourceTree = "<group>"; |
||||
}; |
||||
5EF711A0215174870077496F /* Products */ = { |
||||
isa = PBXGroup; |
||||
children = ( |
||||
5EF7119F215174870077496F /* HelloWorld.app */, |
||||
); |
||||
name = Products; |
||||
sourceTree = "<group>"; |
||||
}; |
||||
5EF711A1215174870077496F /* HelloWorld */ = { |
||||
isa = PBXGroup; |
||||
children = ( |
||||
5EF711A2215174880077496F /* AppDelegate.h */, |
||||
5EF711A3215174880077496F /* AppDelegate.m */, |
||||
5EF711A5215174880077496F /* ViewController.h */, |
||||
5EF711A6215174880077496F /* ViewController.m */, |
||||
5EF711A8215174890077496F /* Assets.xcassets */, |
||||
5EF711AA215174890077496F /* Main.storyboard */, |
||||
5EF711AD215174890077496F /* Info.plist */, |
||||
5EF711B0215174890077496F /* HelloWorld.entitlements */, |
||||
); |
||||
path = HelloWorld; |
||||
sourceTree = "<group>"; |
||||
}; |
||||
8D3EFBB796129582177142C4 /* Pods */ = { |
||||
isa = PBXGroup; |
||||
children = ( |
||||
CA4699782F6344C8E67C9FEE /* Pods-HelloWorld.debug.xcconfig */, |
||||
2AAF8E8BA7DBFD2D3886AA25 /* Pods-HelloWorld.release.xcconfig */, |
||||
); |
||||
name = Pods; |
||||
sourceTree = "<group>"; |
||||
}; |
||||
A986548CB5622AF6CC3ECCCE /* Frameworks */ = { |
||||
isa = PBXGroup; |
||||
children = ( |
||||
369D887F6054EBA486218C69 /* libPods-HelloWorld.a */, |
||||
); |
||||
name = Frameworks; |
||||
sourceTree = "<group>"; |
||||
}; |
||||
/* End PBXGroup section */ |
||||
|
||||
/* Begin PBXNativeTarget section */ |
||||
5EF7119E215174870077496F /* HelloWorld */ = { |
||||
isa = PBXNativeTarget; |
||||
buildConfigurationList = 5EF711B3215174890077496F /* Build configuration list for PBXNativeTarget "HelloWorld" */; |
||||
buildPhases = ( |
||||
3694AEA482289A5BDD5EA5A4 /* [CP] Check Pods Manifest.lock */, |
||||
5EF7119B215174870077496F /* Sources */, |
||||
5EF7119C215174870077496F /* Frameworks */, |
||||
5EF7119D215174870077496F /* Resources */, |
||||
DF5241368CCEAA9DC73E7EA8 /* [CP] Copy Pods Resources */, |
||||
); |
||||
buildRules = ( |
||||
); |
||||
dependencies = ( |
||||
); |
||||
name = HelloWorld; |
||||
productName = HelloWorld; |
||||
productReference = 5EF7119F215174870077496F /* HelloWorld.app */; |
||||
productType = "com.apple.product-type.application"; |
||||
}; |
||||
/* End PBXNativeTarget section */ |
||||
|
||||
/* Begin PBXProject section */ |
||||
5EF71197215174870077496F /* Project object */ = { |
||||
isa = PBXProject; |
||||
attributes = { |
||||
LastUpgradeCheck = 0930; |
||||
ORGANIZATIONNAME = gRPC; |
||||
TargetAttributes = { |
||||
5EF7119E215174870077496F = { |
||||
CreatedOnToolsVersion = 9.3; |
||||
SystemCapabilities = { |
||||
com.apple.Sandbox = { |
||||
enabled = 0; |
||||
}; |
||||
}; |
||||
}; |
||||
}; |
||||
}; |
||||
buildConfigurationList = 5EF7119A215174870077496F /* Build configuration list for PBXProject "HelloWorld" */; |
||||
compatibilityVersion = "Xcode 9.3"; |
||||
developmentRegion = en; |
||||
hasScannedForEncodings = 0; |
||||
knownRegions = ( |
||||
en, |
||||
Base, |
||||
); |
||||
mainGroup = 5EF71196215174870077496F; |
||||
productRefGroup = 5EF711A0215174870077496F /* Products */; |
||||
projectDirPath = ""; |
||||
projectRoot = ""; |
||||
targets = ( |
||||
5EF7119E215174870077496F /* HelloWorld */, |
||||
); |
||||
}; |
||||
/* End PBXProject section */ |
||||
|
||||
/* Begin PBXResourcesBuildPhase section */ |
||||
5EF7119D215174870077496F /* Resources */ = { |
||||
isa = PBXResourcesBuildPhase; |
||||
buildActionMask = 2147483647; |
||||
files = ( |
||||
5EF711A9215174890077496F /* Assets.xcassets in Resources */, |
||||
5EF711AC215174890077496F /* Main.storyboard in Resources */, |
||||
); |
||||
runOnlyForDeploymentPostprocessing = 0; |
||||
}; |
||||
/* End PBXResourcesBuildPhase section */ |
||||
|
||||
/* Begin PBXShellScriptBuildPhase section */ |
||||
3694AEA482289A5BDD5EA5A4 /* [CP] Check Pods Manifest.lock */ = { |
||||
isa = PBXShellScriptBuildPhase; |
||||
buildActionMask = 2147483647; |
||||
files = ( |
||||
); |
||||
inputPaths = ( |
||||
"${PODS_PODFILE_DIR_PATH}/Podfile.lock", |
||||
"${PODS_ROOT}/Manifest.lock", |
||||
); |
||||
name = "[CP] Check Pods Manifest.lock"; |
||||
outputPaths = ( |
||||
"$(DERIVED_FILE_DIR)/Pods-HelloWorld-checkManifestLockResult.txt", |
||||
); |
||||
runOnlyForDeploymentPostprocessing = 0; |
||||
shellPath = /bin/sh; |
||||
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; |
||||
showEnvVarsInLog = 0; |
||||
}; |
||||
DF5241368CCEAA9DC73E7EA8 /* [CP] Copy Pods Resources */ = { |
||||
isa = PBXShellScriptBuildPhase; |
||||
buildActionMask = 2147483647; |
||||
files = ( |
||||
); |
||||
inputPaths = ( |
||||
"${SRCROOT}/Pods/Target Support Files/Pods-HelloWorld/Pods-HelloWorld-resources.sh", |
||||
"${PODS_CONFIGURATION_BUILD_DIR}/gRPC/gRPCCertificates.bundle", |
||||
); |
||||
name = "[CP] Copy Pods Resources"; |
||||
outputPaths = ( |
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/gRPCCertificates.bundle", |
||||
); |
||||
runOnlyForDeploymentPostprocessing = 0; |
||||
shellPath = /bin/sh; |
||||
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-HelloWorld/Pods-HelloWorld-resources.sh\"\n"; |
||||
showEnvVarsInLog = 0; |
||||
}; |
||||
/* End PBXShellScriptBuildPhase section */ |
||||
|
||||
/* Begin PBXSourcesBuildPhase section */ |
||||
5EF7119B215174870077496F /* Sources */ = { |
||||
isa = PBXSourcesBuildPhase; |
||||
buildActionMask = 2147483647; |
||||
files = ( |
||||
5EF711A7215174880077496F /* ViewController.m in Sources */, |
||||
5EF711AF215174890077496F /* main.m in Sources */, |
||||
5EF711A4215174880077496F /* AppDelegate.m in Sources */, |
||||
); |
||||
runOnlyForDeploymentPostprocessing = 0; |
||||
}; |
||||
/* End PBXSourcesBuildPhase section */ |
||||
|
||||
/* Begin PBXVariantGroup section */ |
||||
5EF711AA215174890077496F /* Main.storyboard */ = { |
||||
isa = PBXVariantGroup; |
||||
children = ( |
||||
5EF711AB215174890077496F /* Base */, |
||||
); |
||||
name = Main.storyboard; |
||||
sourceTree = "<group>"; |
||||
}; |
||||
/* End PBXVariantGroup section */ |
||||
|
||||
/* Begin XCBuildConfiguration section */ |
||||
5EF711B1215174890077496F /* Debug */ = { |
||||
isa = XCBuildConfiguration; |
||||
buildSettings = { |
||||
ALWAYS_SEARCH_USER_PATHS = NO; |
||||
CLANG_ANALYZER_NONNULL = YES; |
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; |
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; |
||||
CLANG_CXX_LIBRARY = "libc++"; |
||||
CLANG_ENABLE_MODULES = YES; |
||||
CLANG_ENABLE_OBJC_ARC = YES; |
||||
CLANG_ENABLE_OBJC_WEAK = YES; |
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; |
||||
CLANG_WARN_BOOL_CONVERSION = YES; |
||||
CLANG_WARN_COMMA = YES; |
||||
CLANG_WARN_CONSTANT_CONVERSION = YES; |
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; |
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; |
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES; |
||||
CLANG_WARN_EMPTY_BODY = YES; |
||||
CLANG_WARN_ENUM_CONVERSION = YES; |
||||
CLANG_WARN_INFINITE_RECURSION = YES; |
||||
CLANG_WARN_INT_CONVERSION = YES; |
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; |
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; |
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; |
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; |
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; |
||||
CLANG_WARN_STRICT_PROTOTYPES = YES; |
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES; |
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; |
||||
CLANG_WARN_UNREACHABLE_CODE = YES; |
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; |
||||
CODE_SIGN_IDENTITY = "-"; |
||||
COPY_PHASE_STRIP = NO; |
||||
DEBUG_INFORMATION_FORMAT = dwarf; |
||||
ENABLE_STRICT_OBJC_MSGSEND = YES; |
||||
ENABLE_TESTABILITY = YES; |
||||
GCC_C_LANGUAGE_STANDARD = gnu11; |
||||
GCC_DYNAMIC_NO_PIC = NO; |
||||
GCC_NO_COMMON_BLOCKS = YES; |
||||
GCC_OPTIMIZATION_LEVEL = 0; |
||||
GCC_PREPROCESSOR_DEFINITIONS = ( |
||||
"DEBUG=1", |
||||
"$(inherited)", |
||||
); |
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES; |
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; |
||||
GCC_WARN_UNDECLARED_SELECTOR = YES; |
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; |
||||
GCC_WARN_UNUSED_FUNCTION = YES; |
||||
GCC_WARN_UNUSED_VARIABLE = YES; |
||||
MACOSX_DEPLOYMENT_TARGET = 10.13; |
||||
MTL_ENABLE_DEBUG_INFO = YES; |
||||
ONLY_ACTIVE_ARCH = YES; |
||||
SDKROOT = macosx; |
||||
}; |
||||
name = Debug; |
||||
}; |
||||
5EF711B2215174890077496F /* Release */ = { |
||||
isa = XCBuildConfiguration; |
||||
buildSettings = { |
||||
ALWAYS_SEARCH_USER_PATHS = NO; |
||||
CLANG_ANALYZER_NONNULL = YES; |
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; |
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; |
||||
CLANG_CXX_LIBRARY = "libc++"; |
||||
CLANG_ENABLE_MODULES = YES; |
||||
CLANG_ENABLE_OBJC_ARC = YES; |
||||
CLANG_ENABLE_OBJC_WEAK = YES; |
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; |
||||
CLANG_WARN_BOOL_CONVERSION = YES; |
||||
CLANG_WARN_COMMA = YES; |
||||
CLANG_WARN_CONSTANT_CONVERSION = YES; |
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; |
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; |
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES; |
||||
CLANG_WARN_EMPTY_BODY = YES; |
||||
CLANG_WARN_ENUM_CONVERSION = YES; |
||||
CLANG_WARN_INFINITE_RECURSION = YES; |
||||
CLANG_WARN_INT_CONVERSION = YES; |
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; |
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; |
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; |
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; |
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; |
||||
CLANG_WARN_STRICT_PROTOTYPES = YES; |
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES; |
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; |
||||
CLANG_WARN_UNREACHABLE_CODE = YES; |
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; |
||||
CODE_SIGN_IDENTITY = "-"; |
||||
COPY_PHASE_STRIP = NO; |
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; |
||||
ENABLE_NS_ASSERTIONS = NO; |
||||
ENABLE_STRICT_OBJC_MSGSEND = YES; |
||||
GCC_C_LANGUAGE_STANDARD = gnu11; |
||||
GCC_NO_COMMON_BLOCKS = YES; |
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES; |
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; |
||||
GCC_WARN_UNDECLARED_SELECTOR = YES; |
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; |
||||
GCC_WARN_UNUSED_FUNCTION = YES; |
||||
GCC_WARN_UNUSED_VARIABLE = YES; |
||||
MACOSX_DEPLOYMENT_TARGET = 10.13; |
||||
MTL_ENABLE_DEBUG_INFO = NO; |
||||
SDKROOT = macosx; |
||||
}; |
||||
name = Release; |
||||
}; |
||||
5EF711B4215174890077496F /* Debug */ = { |
||||
isa = XCBuildConfiguration; |
||||
baseConfigurationReference = CA4699782F6344C8E67C9FEE /* Pods-HelloWorld.debug.xcconfig */; |
||||
buildSettings = { |
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; |
||||
CODE_SIGN_STYLE = Automatic; |
||||
COMBINE_HIDPI_IMAGES = YES; |
||||
INFOPLIST_FILE = HelloWorld/Info.plist; |
||||
LD_RUNPATH_SEARCH_PATHS = ( |
||||
"$(inherited)", |
||||
"@executable_path/../Frameworks", |
||||
); |
||||
PRODUCT_BUNDLE_IDENTIFIER = io.grpc.HelloWorld; |
||||
PRODUCT_NAME = "$(TARGET_NAME)"; |
||||
}; |
||||
name = Debug; |
||||
}; |
||||
5EF711B5215174890077496F /* Release */ = { |
||||
isa = XCBuildConfiguration; |
||||
baseConfigurationReference = 2AAF8E8BA7DBFD2D3886AA25 /* Pods-HelloWorld.release.xcconfig */; |
||||
buildSettings = { |
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; |
||||
CODE_SIGN_STYLE = Automatic; |
||||
COMBINE_HIDPI_IMAGES = YES; |
||||
INFOPLIST_FILE = HelloWorld/Info.plist; |
||||
LD_RUNPATH_SEARCH_PATHS = ( |
||||
"$(inherited)", |
||||
"@executable_path/../Frameworks", |
||||
); |
||||
PRODUCT_BUNDLE_IDENTIFIER = io.grpc.HelloWorld; |
||||
PRODUCT_NAME = "$(TARGET_NAME)"; |
||||
}; |
||||
name = Release; |
||||
}; |
||||
/* End XCBuildConfiguration section */ |
||||
|
||||
/* Begin XCConfigurationList section */ |
||||
5EF7119A215174870077496F /* Build configuration list for PBXProject "HelloWorld" */ = { |
||||
isa = XCConfigurationList; |
||||
buildConfigurations = ( |
||||
5EF711B1215174890077496F /* Debug */, |
||||
5EF711B2215174890077496F /* Release */, |
||||
); |
||||
defaultConfigurationIsVisible = 0; |
||||
defaultConfigurationName = Release; |
||||
}; |
||||
5EF711B3215174890077496F /* Build configuration list for PBXNativeTarget "HelloWorld" */ = { |
||||
isa = XCConfigurationList; |
||||
buildConfigurations = ( |
||||
5EF711B4215174890077496F /* Debug */, |
||||
5EF711B5215174890077496F /* Release */, |
||||
); |
||||
defaultConfigurationIsVisible = 0; |
||||
defaultConfigurationName = Release; |
||||
}; |
||||
/* End XCConfigurationList section */ |
||||
}; |
||||
rootObject = 5EF71197215174870077496F /* Project object */; |
||||
} |
@ -0,0 +1,25 @@ |
||||
/*
|
||||
* |
||||
* 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. |
||||
* |
||||
*/ |
||||
|
||||
#import <Cocoa/Cocoa.h> |
||||
|
||||
@interface AppDelegate : NSObject <NSApplicationDelegate> |
||||
|
||||
|
||||
@end |
||||
|
@ -0,0 +1,37 @@ |
||||
/* |
||||
* |
||||
* 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. |
||||
* |
||||
*/ |
||||
|
||||
#import "AppDelegate.h" |
||||
|
||||
@interface AppDelegate () |
||||
|
||||
@end |
||||
|
||||
@implementation AppDelegate |
||||
|
||||
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification { |
||||
// Insert code here to initialize your application |
||||
} |
||||
|
||||
|
||||
- (void)applicationWillTerminate:(NSNotification *)aNotification { |
||||
// Insert code here to tear down your application |
||||
} |
||||
|
||||
|
||||
@end |
@ -0,0 +1,58 @@ |
||||
{ |
||||
"images" : [ |
||||
{ |
||||
"idiom" : "mac", |
||||
"size" : "16x16", |
||||
"scale" : "1x" |
||||
}, |
||||
{ |
||||
"idiom" : "mac", |
||||
"size" : "16x16", |
||||
"scale" : "2x" |
||||
}, |
||||
{ |
||||
"idiom" : "mac", |
||||
"size" : "32x32", |
||||
"scale" : "1x" |
||||
}, |
||||
{ |
||||
"idiom" : "mac", |
||||
"size" : "32x32", |
||||
"scale" : "2x" |
||||
}, |
||||
{ |
||||
"idiom" : "mac", |
||||
"size" : "128x128", |
||||
"scale" : "1x" |
||||
}, |
||||
{ |
||||
"idiom" : "mac", |
||||
"size" : "128x128", |
||||
"scale" : "2x" |
||||
}, |
||||
{ |
||||
"idiom" : "mac", |
||||
"size" : "256x256", |
||||
"scale" : "1x" |
||||
}, |
||||
{ |
||||
"idiom" : "mac", |
||||
"size" : "256x256", |
||||
"scale" : "2x" |
||||
}, |
||||
{ |
||||
"idiom" : "mac", |
||||
"size" : "512x512", |
||||
"scale" : "1x" |
||||
}, |
||||
{ |
||||
"idiom" : "mac", |
||||
"size" : "512x512", |
||||
"scale" : "2x" |
||||
} |
||||
], |
||||
"info" : { |
||||
"version" : 1, |
||||
"author" : "xcode" |
||||
} |
||||
} |
@ -0,0 +1,6 @@ |
||||
{ |
||||
"info" : { |
||||
"version" : 1, |
||||
"author" : "xcode" |
||||
} |
||||
} |
@ -0,0 +1,717 @@ |
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?> |
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="11134" systemVersion="15F34" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" initialViewController="B8D-0N-5wS"> |
||||
<dependencies> |
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="11134"/> |
||||
</dependencies> |
||||
<scenes> |
||||
<!--Application--> |
||||
<scene sceneID="JPo-4y-FX3"> |
||||
<objects> |
||||
<application id="hnw-xV-0zn" sceneMemberID="viewController"> |
||||
<menu key="mainMenu" title="Main Menu" systemMenu="main" id="AYu-sK-qS6"> |
||||
<items> |
||||
<menuItem title="HelloWorld" id="1Xt-HY-uBw"> |
||||
<modifierMask key="keyEquivalentModifierMask"/> |
||||
<menu key="submenu" title="HelloWorld" systemMenu="apple" id="uQy-DD-JDr"> |
||||
<items> |
||||
<menuItem title="About HelloWorld" id="5kV-Vb-QxS"> |
||||
<modifierMask key="keyEquivalentModifierMask"/> |
||||
<connections> |
||||
<action selector="orderFrontStandardAboutPanel:" target="Ady-hI-5gd" id="Exp-CZ-Vem"/> |
||||
</connections> |
||||
</menuItem> |
||||
<menuItem isSeparatorItem="YES" id="VOq-y0-SEH"/> |
||||
<menuItem title="Preferences…" keyEquivalent="," id="BOF-NM-1cW"/> |
||||
<menuItem isSeparatorItem="YES" id="wFC-TO-SCJ"/> |
||||
<menuItem title="Services" id="NMo-om-nkz"> |
||||
<modifierMask key="keyEquivalentModifierMask"/> |
||||
<menu key="submenu" title="Services" systemMenu="services" id="hz9-B4-Xy5"/> |
||||
</menuItem> |
||||
<menuItem isSeparatorItem="YES" id="4je-JR-u6R"/> |
||||
<menuItem title="Hide HelloWorld" keyEquivalent="h" id="Olw-nP-bQN"> |
||||
<connections> |
||||
<action selector="hide:" target="Ady-hI-5gd" id="PnN-Uc-m68"/> |
||||
</connections> |
||||
</menuItem> |
||||
<menuItem title="Hide Others" keyEquivalent="h" id="Vdr-fp-XzO"> |
||||
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/> |
||||
<connections> |
||||
<action selector="hideOtherApplications:" target="Ady-hI-5gd" id="VT4-aY-XCT"/> |
||||
</connections> |
||||
</menuItem> |
||||
<menuItem title="Show All" id="Kd2-mp-pUS"> |
||||
<modifierMask key="keyEquivalentModifierMask"/> |
||||
<connections> |
||||
<action selector="unhideAllApplications:" target="Ady-hI-5gd" id="Dhg-Le-xox"/> |
||||
</connections> |
||||
</menuItem> |
||||
<menuItem isSeparatorItem="YES" id="kCx-OE-vgT"/> |
||||
<menuItem title="Quit HelloWorld" keyEquivalent="q" id="4sb-4s-VLi"> |
||||
<connections> |
||||
<action selector="terminate:" target="Ady-hI-5gd" id="Te7-pn-YzF"/> |
||||
</connections> |
||||
</menuItem> |
||||
</items> |
||||
</menu> |
||||
</menuItem> |
||||
<menuItem title="File" id="dMs-cI-mzQ"> |
||||
<modifierMask key="keyEquivalentModifierMask"/> |
||||
<menu key="submenu" title="File" id="bib-Uj-vzu"> |
||||
<items> |
||||
<menuItem title="New" keyEquivalent="n" id="Was-JA-tGl"> |
||||
<connections> |
||||
<action selector="newDocument:" target="Ady-hI-5gd" id="4Si-XN-c54"/> |
||||
</connections> |
||||
</menuItem> |
||||
<menuItem title="Open…" keyEquivalent="o" id="IAo-SY-fd9"> |
||||
<connections> |
||||
<action selector="openDocument:" target="Ady-hI-5gd" id="bVn-NM-KNZ"/> |
||||
</connections> |
||||
</menuItem> |
||||
<menuItem title="Open Recent" id="tXI-mr-wws"> |
||||
<modifierMask key="keyEquivalentModifierMask"/> |
||||
<menu key="submenu" title="Open Recent" systemMenu="recentDocuments" id="oas-Oc-fiZ"> |
||||
<items> |
||||
<menuItem title="Clear Menu" id="vNY-rz-j42"> |
||||
<modifierMask key="keyEquivalentModifierMask"/> |
||||
<connections> |
||||
<action selector="clearRecentDocuments:" target="Ady-hI-5gd" id="Daa-9d-B3U"/> |
||||
</connections> |
||||
</menuItem> |
||||
</items> |
||||
</menu> |
||||
</menuItem> |
||||
<menuItem isSeparatorItem="YES" id="m54-Is-iLE"/> |
||||
<menuItem title="Close" keyEquivalent="w" id="DVo-aG-piG"> |
||||
<connections> |
||||
<action selector="performClose:" target="Ady-hI-5gd" id="HmO-Ls-i7Q"/> |
||||
</connections> |
||||
</menuItem> |
||||
<menuItem title="Save…" keyEquivalent="s" id="pxx-59-PXV"> |
||||
<connections> |
||||
<action selector="saveDocument:" target="Ady-hI-5gd" id="teZ-XB-qJY"/> |
||||
</connections> |
||||
</menuItem> |
||||
<menuItem title="Save As…" keyEquivalent="S" id="Bw7-FT-i3A"> |
||||
<connections> |
||||
<action selector="saveDocumentAs:" target="Ady-hI-5gd" id="mDf-zr-I0C"/> |
||||
</connections> |
||||
</menuItem> |
||||
<menuItem title="Revert to Saved" keyEquivalent="r" id="KaW-ft-85H"> |
||||
<connections> |
||||
<action selector="revertDocumentToSaved:" target="Ady-hI-5gd" id="iJ3-Pv-kwq"/> |
||||
</connections> |
||||
</menuItem> |
||||
<menuItem isSeparatorItem="YES" id="aJh-i4-bef"/> |
||||
<menuItem title="Page Setup…" keyEquivalent="P" id="qIS-W8-SiK"> |
||||
<modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/> |
||||
<connections> |
||||
<action selector="runPageLayout:" target="Ady-hI-5gd" id="Din-rz-gC5"/> |
||||
</connections> |
||||
</menuItem> |
||||
<menuItem title="Print…" keyEquivalent="p" id="aTl-1u-JFS"> |
||||
<connections> |
||||
<action selector="print:" target="Ady-hI-5gd" id="qaZ-4w-aoO"/> |
||||
</connections> |
||||
</menuItem> |
||||
</items> |
||||
</menu> |
||||
</menuItem> |
||||
<menuItem title="Edit" id="5QF-Oa-p0T"> |
||||
<modifierMask key="keyEquivalentModifierMask"/> |
||||
<menu key="submenu" title="Edit" id="W48-6f-4Dl"> |
||||
<items> |
||||
<menuItem title="Undo" keyEquivalent="z" id="dRJ-4n-Yzg"> |
||||
<connections> |
||||
<action selector="undo:" target="Ady-hI-5gd" id="M6e-cu-g7V"/> |
||||
</connections> |
||||
</menuItem> |
||||
<menuItem title="Redo" keyEquivalent="Z" id="6dh-zS-Vam"> |
||||
<connections> |
||||
<action selector="redo:" target="Ady-hI-5gd" id="oIA-Rs-6OD"/> |
||||
</connections> |
||||
</menuItem> |
||||
<menuItem isSeparatorItem="YES" id="WRV-NI-Exz"/> |
||||
<menuItem title="Cut" keyEquivalent="x" id="uRl-iY-unG"> |
||||
<connections> |
||||
<action selector="cut:" target="Ady-hI-5gd" id="YJe-68-I9s"/> |
||||
</connections> |
||||
</menuItem> |
||||
<menuItem title="Copy" keyEquivalent="c" id="x3v-GG-iWU"> |
||||
<connections> |
||||
<action selector="copy:" target="Ady-hI-5gd" id="G1f-GL-Joy"/> |
||||
</connections> |
||||
</menuItem> |
||||
<menuItem title="Paste" keyEquivalent="v" id="gVA-U4-sdL"> |
||||
<connections> |
||||
<action selector="paste:" target="Ady-hI-5gd" id="UvS-8e-Qdg"/> |
||||
</connections> |
||||
</menuItem> |
||||
<menuItem title="Paste and Match Style" keyEquivalent="V" id="WeT-3V-zwk"> |
||||
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/> |
||||
<connections> |
||||
<action selector="pasteAsPlainText:" target="Ady-hI-5gd" id="cEh-KX-wJQ"/> |
||||
</connections> |
||||
</menuItem> |
||||
<menuItem title="Delete" id="pa3-QI-u2k"> |
||||
<modifierMask key="keyEquivalentModifierMask"/> |
||||
<connections> |
||||
<action selector="delete:" target="Ady-hI-5gd" id="0Mk-Ml-PaM"/> |
||||
</connections> |
||||
</menuItem> |
||||
<menuItem title="Select All" keyEquivalent="a" id="Ruw-6m-B2m"> |
||||
<connections> |
||||
<action selector="selectAll:" target="Ady-hI-5gd" id="VNm-Mi-diN"/> |
||||
</connections> |
||||
</menuItem> |
||||
<menuItem isSeparatorItem="YES" id="uyl-h8-XO2"/> |
||||
<menuItem title="Find" id="4EN-yA-p0u"> |
||||
<modifierMask key="keyEquivalentModifierMask"/> |
||||
<menu key="submenu" title="Find" id="1b7-l0-nxx"> |
||||
<items> |
||||
<menuItem title="Find…" tag="1" keyEquivalent="f" id="Xz5-n4-O0W"> |
||||
<connections> |
||||
<action selector="performFindPanelAction:" target="Ady-hI-5gd" id="cD7-Qs-BN4"/> |
||||
</connections> |
||||
</menuItem> |
||||
<menuItem title="Find and Replace…" tag="12" keyEquivalent="f" id="YEy-JH-Tfz"> |
||||
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/> |
||||
<connections> |
||||
<action selector="performFindPanelAction:" target="Ady-hI-5gd" id="WD3-Gg-5AJ"/> |
||||
</connections> |
||||
</menuItem> |
||||
<menuItem title="Find Next" tag="2" keyEquivalent="g" id="q09-fT-Sye"> |
||||
<connections> |
||||
<action selector="performFindPanelAction:" target="Ady-hI-5gd" id="NDo-RZ-v9R"/> |
||||
</connections> |
||||
</menuItem> |
||||
<menuItem title="Find Previous" tag="3" keyEquivalent="G" id="OwM-mh-QMV"> |
||||
<connections> |
||||
<action selector="performFindPanelAction:" target="Ady-hI-5gd" id="HOh-sY-3ay"/> |
||||
</connections> |
||||
</menuItem> |
||||
<menuItem title="Use Selection for Find" tag="7" keyEquivalent="e" id="buJ-ug-pKt"> |
||||
<connections> |
||||
<action selector="performFindPanelAction:" target="Ady-hI-5gd" id="U76-nv-p5D"/> |
||||
</connections> |
||||
</menuItem> |
||||
<menuItem title="Jump to Selection" keyEquivalent="j" id="S0p-oC-mLd"> |
||||
<connections> |
||||
<action selector="centerSelectionInVisibleArea:" target="Ady-hI-5gd" id="IOG-6D-g5B"/> |
||||
</connections> |
||||
</menuItem> |
||||
</items> |
||||
</menu> |
||||
</menuItem> |
||||
<menuItem title="Spelling and Grammar" id="Dv1-io-Yv7"> |
||||
<modifierMask key="keyEquivalentModifierMask"/> |
||||
<menu key="submenu" title="Spelling" id="3IN-sU-3Bg"> |
||||
<items> |
||||
<menuItem title="Show Spelling and Grammar" keyEquivalent=":" id="HFo-cy-zxI"> |
||||
<connections> |
||||
<action selector="showGuessPanel:" target="Ady-hI-5gd" id="vFj-Ks-hy3"/> |
||||
</connections> |
||||
</menuItem> |
||||
<menuItem title="Check Document Now" keyEquivalent=";" id="hz2-CU-CR7"> |
||||
<connections> |
||||
<action selector="checkSpelling:" target="Ady-hI-5gd" id="fz7-VC-reM"/> |
||||
</connections> |
||||
</menuItem> |
||||
<menuItem isSeparatorItem="YES" id="bNw-od-mp5"/> |
||||
<menuItem title="Check Spelling While Typing" id="rbD-Rh-wIN"> |
||||
<modifierMask key="keyEquivalentModifierMask"/> |
||||
<connections> |
||||
<action selector="toggleContinuousSpellChecking:" target="Ady-hI-5gd" id="7w6-Qz-0kB"/> |
||||
</connections> |
||||
</menuItem> |
||||
<menuItem title="Check Grammar With Spelling" id="mK6-2p-4JG"> |
||||
<modifierMask key="keyEquivalentModifierMask"/> |
||||
<connections> |
||||
<action selector="toggleGrammarChecking:" target="Ady-hI-5gd" id="muD-Qn-j4w"/> |
||||
</connections> |
||||
</menuItem> |
||||
<menuItem title="Correct Spelling Automatically" id="78Y-hA-62v"> |
||||
<modifierMask key="keyEquivalentModifierMask"/> |
||||
<connections> |
||||
<action selector="toggleAutomaticSpellingCorrection:" target="Ady-hI-5gd" id="2lM-Qi-WAP"/> |
||||
</connections> |
||||
</menuItem> |
||||
</items> |
||||
</menu> |
||||
</menuItem> |
||||
<menuItem title="Substitutions" id="9ic-FL-obx"> |
||||
<modifierMask key="keyEquivalentModifierMask"/> |
||||
<menu key="submenu" title="Substitutions" id="FeM-D8-WVr"> |
||||
<items> |
||||
<menuItem title="Show Substitutions" id="z6F-FW-3nz"> |
||||
<modifierMask key="keyEquivalentModifierMask"/> |
||||
<connections> |
||||
<action selector="orderFrontSubstitutionsPanel:" target="Ady-hI-5gd" id="oku-mr-iSq"/> |
||||
</connections> |
||||
</menuItem> |
||||
<menuItem isSeparatorItem="YES" id="gPx-C9-uUO"/> |
||||
<menuItem title="Smart Copy/Paste" id="9yt-4B-nSM"> |
||||
<modifierMask key="keyEquivalentModifierMask"/> |
||||
<connections> |
||||
<action selector="toggleSmartInsertDelete:" target="Ady-hI-5gd" id="3IJ-Se-DZD"/> |
||||
</connections> |
||||
</menuItem> |
||||
<menuItem title="Smart Quotes" id="hQb-2v-fYv"> |
||||
<modifierMask key="keyEquivalentModifierMask"/> |
||||
<connections> |
||||
<action selector="toggleAutomaticQuoteSubstitution:" target="Ady-hI-5gd" id="ptq-xd-QOA"/> |
||||
</connections> |
||||
</menuItem> |
||||
<menuItem title="Smart Dashes" id="rgM-f4-ycn"> |
||||
<modifierMask key="keyEquivalentModifierMask"/> |
||||
<connections> |
||||
<action selector="toggleAutomaticDashSubstitution:" target="Ady-hI-5gd" id="oCt-pO-9gS"/> |
||||
</connections> |
||||
</menuItem> |
||||
<menuItem title="Smart Links" id="cwL-P1-jid"> |
||||
<modifierMask key="keyEquivalentModifierMask"/> |
||||
<connections> |
||||
<action selector="toggleAutomaticLinkDetection:" target="Ady-hI-5gd" id="Gip-E3-Fov"/> |
||||
</connections> |
||||
</menuItem> |
||||
<menuItem title="Data Detectors" id="tRr-pd-1PS"> |
||||
<modifierMask key="keyEquivalentModifierMask"/> |
||||
<connections> |
||||
<action selector="toggleAutomaticDataDetection:" target="Ady-hI-5gd" id="R1I-Nq-Kbl"/> |
||||
</connections> |
||||
</menuItem> |
||||
<menuItem title="Text Replacement" id="HFQ-gK-NFA"> |
||||
<modifierMask key="keyEquivalentModifierMask"/> |
||||
<connections> |
||||
<action selector="toggleAutomaticTextReplacement:" target="Ady-hI-5gd" id="DvP-Fe-Py6"/> |
||||
</connections> |
||||
</menuItem> |
||||
</items> |
||||
</menu> |
||||
</menuItem> |
||||
<menuItem title="Transformations" id="2oI-Rn-ZJC"> |
||||
<modifierMask key="keyEquivalentModifierMask"/> |
||||
<menu key="submenu" title="Transformations" id="c8a-y6-VQd"> |
||||
<items> |
||||
<menuItem title="Make Upper Case" id="vmV-6d-7jI"> |
||||
<modifierMask key="keyEquivalentModifierMask"/> |
||||
<connections> |
||||
<action selector="uppercaseWord:" target="Ady-hI-5gd" id="sPh-Tk-edu"/> |
||||
</connections> |
||||
</menuItem> |
||||
<menuItem title="Make Lower Case" id="d9M-CD-aMd"> |
||||
<modifierMask key="keyEquivalentModifierMask"/> |
||||
<connections> |
||||
<action selector="lowercaseWord:" target="Ady-hI-5gd" id="iUZ-b5-hil"/> |
||||
</connections> |
||||
</menuItem> |
||||
<menuItem title="Capitalize" id="UEZ-Bs-lqG"> |
||||
<modifierMask key="keyEquivalentModifierMask"/> |
||||
<connections> |
||||
<action selector="capitalizeWord:" target="Ady-hI-5gd" id="26H-TL-nsh"/> |
||||
</connections> |
||||
</menuItem> |
||||
</items> |
||||
</menu> |
||||
</menuItem> |
||||
<menuItem title="Speech" id="xrE-MZ-jX0"> |
||||
<modifierMask key="keyEquivalentModifierMask"/> |
||||
<menu key="submenu" title="Speech" id="3rS-ZA-NoH"> |
||||
<items> |
||||
<menuItem title="Start Speaking" id="Ynk-f8-cLZ"> |
||||
<modifierMask key="keyEquivalentModifierMask"/> |
||||
<connections> |
||||
<action selector="startSpeaking:" target="Ady-hI-5gd" id="654-Ng-kyl"/> |
||||
</connections> |
||||
</menuItem> |
||||
<menuItem title="Stop Speaking" id="Oyz-dy-DGm"> |
||||
<modifierMask key="keyEquivalentModifierMask"/> |
||||
<connections> |
||||
<action selector="stopSpeaking:" target="Ady-hI-5gd" id="dX8-6p-jy9"/> |
||||
</connections> |
||||
</menuItem> |
||||
</items> |
||||
</menu> |
||||
</menuItem> |
||||
</items> |
||||
</menu> |
||||
</menuItem> |
||||
<menuItem title="Format" id="jxT-CU-nIS"> |
||||
<modifierMask key="keyEquivalentModifierMask"/> |
||||
<menu key="submenu" title="Format" id="GEO-Iw-cKr"> |
||||
<items> |
||||
<menuItem title="Font" id="Gi5-1S-RQB"> |
||||
<modifierMask key="keyEquivalentModifierMask"/> |
||||
<menu key="submenu" title="Font" systemMenu="font" id="aXa-aM-Jaq"> |
||||
<items> |
||||
<menuItem title="Show Fonts" keyEquivalent="t" id="Q5e-8K-NDq"> |
||||
<connections> |
||||
<action selector="orderFrontFontPanel:" target="YLy-65-1bz" id="WHr-nq-2xA"/> |
||||
</connections> |
||||
</menuItem> |
||||
<menuItem title="Bold" tag="2" keyEquivalent="b" id="GB9-OM-e27"> |
||||
<connections> |
||||
<action selector="addFontTrait:" target="YLy-65-1bz" id="hqk-hr-sYV"/> |
||||
</connections> |
||||
</menuItem> |
||||
<menuItem title="Italic" tag="1" keyEquivalent="i" id="Vjx-xi-njq"> |
||||
<connections> |
||||
<action selector="addFontTrait:" target="YLy-65-1bz" id="IHV-OB-c03"/> |
||||
</connections> |
||||
</menuItem> |
||||
<menuItem title="Underline" keyEquivalent="u" id="WRG-CD-K1S"> |
||||
<connections> |
||||
<action selector="underline:" target="Ady-hI-5gd" id="FYS-2b-JAY"/> |
||||
</connections> |
||||
</menuItem> |
||||
<menuItem isSeparatorItem="YES" id="5gT-KC-WSO"/> |
||||
<menuItem title="Bigger" tag="3" keyEquivalent="+" id="Ptp-SP-VEL"> |
||||
<connections> |
||||
<action selector="modifyFont:" target="YLy-65-1bz" id="Uc7-di-UnL"/> |
||||
</connections> |
||||
</menuItem> |
||||
<menuItem title="Smaller" tag="4" keyEquivalent="-" id="i1d-Er-qST"> |
||||
<connections> |
||||
<action selector="modifyFont:" target="YLy-65-1bz" id="HcX-Lf-eNd"/> |
||||
</connections> |
||||
</menuItem> |
||||
<menuItem isSeparatorItem="YES" id="kx3-Dk-x3B"/> |
||||
<menuItem title="Kern" id="jBQ-r6-VK2"> |
||||
<modifierMask key="keyEquivalentModifierMask"/> |
||||
<menu key="submenu" title="Kern" id="tlD-Oa-oAM"> |
||||
<items> |
||||
<menuItem title="Use Default" id="GUa-eO-cwY"> |
||||
<modifierMask key="keyEquivalentModifierMask"/> |
||||
<connections> |
||||
<action selector="useStandardKerning:" target="Ady-hI-5gd" id="6dk-9l-Ckg"/> |
||||
</connections> |
||||
</menuItem> |
||||
<menuItem title="Use None" id="cDB-IK-hbR"> |
||||
<modifierMask key="keyEquivalentModifierMask"/> |
||||
<connections> |
||||
<action selector="turnOffKerning:" target="Ady-hI-5gd" id="U8a-gz-Maa"/> |
||||
</connections> |
||||
</menuItem> |
||||
<menuItem title="Tighten" id="46P-cB-AYj"> |
||||
<modifierMask key="keyEquivalentModifierMask"/> |
||||
<connections> |
||||
<action selector="tightenKerning:" target="Ady-hI-5gd" id="hr7-Nz-8ro"/> |
||||
</connections> |
||||
</menuItem> |
||||
<menuItem title="Loosen" id="ogc-rX-tC1"> |
||||
<modifierMask key="keyEquivalentModifierMask"/> |
||||
<connections> |
||||
<action selector="loosenKerning:" target="Ady-hI-5gd" id="8i4-f9-FKE"/> |
||||
</connections> |
||||
</menuItem> |
||||
</items> |
||||
</menu> |
||||
</menuItem> |
||||
<menuItem title="Ligatures" id="o6e-r0-MWq"> |
||||
<modifierMask key="keyEquivalentModifierMask"/> |
||||
<menu key="submenu" title="Ligatures" id="w0m-vy-SC9"> |
||||
<items> |
||||
<menuItem title="Use Default" id="agt-UL-0e3"> |
||||
<modifierMask key="keyEquivalentModifierMask"/> |
||||
<connections> |
||||
<action selector="useStandardLigatures:" target="Ady-hI-5gd" id="7uR-wd-Dx6"/> |
||||
</connections> |
||||
</menuItem> |
||||
<menuItem title="Use None" id="J7y-lM-qPV"> |
||||
<modifierMask key="keyEquivalentModifierMask"/> |
||||
<connections> |
||||
<action selector="turnOffLigatures:" target="Ady-hI-5gd" id="iX2-gA-Ilz"/> |
||||
</connections> |
||||
</menuItem> |
||||
<menuItem title="Use All" id="xQD-1f-W4t"> |
||||
<modifierMask key="keyEquivalentModifierMask"/> |
||||
<connections> |
||||
<action selector="useAllLigatures:" target="Ady-hI-5gd" id="KcB-kA-TuK"/> |
||||
</connections> |
||||
</menuItem> |
||||
</items> |
||||
</menu> |
||||
</menuItem> |
||||
<menuItem title="Baseline" id="OaQ-X3-Vso"> |
||||
<modifierMask key="keyEquivalentModifierMask"/> |
||||
<menu key="submenu" title="Baseline" id="ijk-EB-dga"> |
||||
<items> |
||||
<menuItem title="Use Default" id="3Om-Ey-2VK"> |
||||
<modifierMask key="keyEquivalentModifierMask"/> |
||||
<connections> |
||||
<action selector="unscript:" target="Ady-hI-5gd" id="0vZ-95-Ywn"/> |
||||
</connections> |
||||
</menuItem> |
||||
<menuItem title="Superscript" id="Rqc-34-cIF"> |
||||
<modifierMask key="keyEquivalentModifierMask"/> |
||||
<connections> |
||||
<action selector="superscript:" target="Ady-hI-5gd" id="3qV-fo-wpU"/> |
||||
</connections> |
||||
</menuItem> |
||||
<menuItem title="Subscript" id="I0S-gh-46l"> |
||||
<modifierMask key="keyEquivalentModifierMask"/> |
||||
<connections> |
||||
<action selector="subscript:" target="Ady-hI-5gd" id="Q6W-4W-IGz"/> |
||||
</connections> |
||||
</menuItem> |
||||
<menuItem title="Raise" id="2h7-ER-AoG"> |
||||
<modifierMask key="keyEquivalentModifierMask"/> |
||||
<connections> |
||||
<action selector="raiseBaseline:" target="Ady-hI-5gd" id="4sk-31-7Q9"/> |
||||
</connections> |
||||
</menuItem> |
||||
<menuItem title="Lower" id="1tx-W0-xDw"> |
||||
<modifierMask key="keyEquivalentModifierMask"/> |
||||
<connections> |
||||
<action selector="lowerBaseline:" target="Ady-hI-5gd" id="OF1-bc-KW4"/> |
||||
</connections> |
||||
</menuItem> |
||||
</items> |
||||
</menu> |
||||
</menuItem> |
||||
<menuItem isSeparatorItem="YES" id="Ndw-q3-faq"/> |
||||
<menuItem title="Show Colors" keyEquivalent="C" id="bgn-CT-cEk"> |
||||
<connections> |
||||
<action selector="orderFrontColorPanel:" target="Ady-hI-5gd" id="mSX-Xz-DV3"/> |
||||
</connections> |
||||
</menuItem> |
||||
<menuItem isSeparatorItem="YES" id="iMs-zA-UFJ"/> |
||||
<menuItem title="Copy Style" keyEquivalent="c" id="5Vv-lz-BsD"> |
||||
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/> |
||||
<connections> |
||||
<action selector="copyFont:" target="Ady-hI-5gd" id="GJO-xA-L4q"/> |
||||
</connections> |
||||
</menuItem> |
||||
<menuItem title="Paste Style" keyEquivalent="v" id="vKC-jM-MkH"> |
||||
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/> |
||||
<connections> |
||||
<action selector="pasteFont:" target="Ady-hI-5gd" id="JfD-CL-leO"/> |
||||
</connections> |
||||
</menuItem> |
||||
</items> |
||||
</menu> |
||||
</menuItem> |
||||
<menuItem title="Text" id="Fal-I4-PZk"> |
||||
<modifierMask key="keyEquivalentModifierMask"/> |
||||
<menu key="submenu" title="Text" id="d9c-me-L2H"> |
||||
<items> |
||||
<menuItem title="Align Left" keyEquivalent="{" id="ZM1-6Q-yy1"> |
||||
<connections> |
||||
<action selector="alignLeft:" target="Ady-hI-5gd" id="zUv-R1-uAa"/> |
||||
</connections> |
||||
</menuItem> |
||||
<menuItem title="Center" keyEquivalent="|" id="VIY-Ag-zcb"> |
||||
<connections> |
||||
<action selector="alignCenter:" target="Ady-hI-5gd" id="spX-mk-kcS"/> |
||||
</connections> |
||||
</menuItem> |
||||
<menuItem title="Justify" id="J5U-5w-g23"> |
||||
<modifierMask key="keyEquivalentModifierMask"/> |
||||
<connections> |
||||
<action selector="alignJustified:" target="Ady-hI-5gd" id="ljL-7U-jND"/> |
||||
</connections> |
||||
</menuItem> |
||||
<menuItem title="Align Right" keyEquivalent="}" id="wb2-vD-lq4"> |
||||
<connections> |
||||
<action selector="alignRight:" target="Ady-hI-5gd" id="r48-bG-YeY"/> |
||||
</connections> |
||||
</menuItem> |
||||
<menuItem isSeparatorItem="YES" id="4s2-GY-VfK"/> |
||||
<menuItem title="Writing Direction" id="H1b-Si-o9J"> |
||||
<modifierMask key="keyEquivalentModifierMask"/> |
||||
<menu key="submenu" title="Writing Direction" id="8mr-sm-Yjd"> |
||||
<items> |
||||
<menuItem title="Paragraph" enabled="NO" id="ZvO-Gk-QUH"> |
||||
<modifierMask key="keyEquivalentModifierMask"/> |
||||
</menuItem> |
||||
<menuItem id="YGs-j5-SAR"> |
||||
<string key="title"> Default</string> |
||||
<modifierMask key="keyEquivalentModifierMask"/> |
||||
<connections> |
||||
<action selector="makeBaseWritingDirectionNatural:" target="Ady-hI-5gd" id="qtV-5e-UBP"/> |
||||
</connections> |
||||
</menuItem> |
||||
<menuItem id="Lbh-J2-qVU"> |
||||
<string key="title"> Left to Right</string> |
||||
<modifierMask key="keyEquivalentModifierMask"/> |
||||
<connections> |
||||
<action selector="makeBaseWritingDirectionLeftToRight:" target="Ady-hI-5gd" id="S0X-9S-QSf"/> |
||||
</connections> |
||||
</menuItem> |
||||
<menuItem id="jFq-tB-4Kx"> |
||||
<string key="title"> Right to Left</string> |
||||
<modifierMask key="keyEquivalentModifierMask"/> |
||||
<connections> |
||||
<action selector="makeBaseWritingDirectionRightToLeft:" target="Ady-hI-5gd" id="5fk-qB-AqJ"/> |
||||
</connections> |
||||
</menuItem> |
||||
<menuItem isSeparatorItem="YES" id="swp-gr-a21"/> |
||||
<menuItem title="Selection" enabled="NO" id="cqv-fj-IhA"> |
||||
<modifierMask key="keyEquivalentModifierMask"/> |
||||
</menuItem> |
||||
<menuItem id="Nop-cj-93Q"> |
||||
<string key="title"> Default</string> |
||||
<modifierMask key="keyEquivalentModifierMask"/> |
||||
<connections> |
||||
<action selector="makeTextWritingDirectionNatural:" target="Ady-hI-5gd" id="lPI-Se-ZHp"/> |
||||
</connections> |
||||
</menuItem> |
||||
<menuItem id="BgM-ve-c93"> |
||||
<string key="title"> Left to Right</string> |
||||
<modifierMask key="keyEquivalentModifierMask"/> |
||||
<connections> |
||||
<action selector="makeTextWritingDirectionLeftToRight:" target="Ady-hI-5gd" id="caW-Bv-w94"/> |
||||
</connections> |
||||
</menuItem> |
||||
<menuItem id="RB4-Sm-HuC"> |
||||
<string key="title"> Right to Left</string> |
||||
<modifierMask key="keyEquivalentModifierMask"/> |
||||
<connections> |
||||
<action selector="makeTextWritingDirectionRightToLeft:" target="Ady-hI-5gd" id="EXD-6r-ZUu"/> |
||||
</connections> |
||||
</menuItem> |
||||
</items> |
||||
</menu> |
||||
</menuItem> |
||||
<menuItem isSeparatorItem="YES" id="fKy-g9-1gm"/> |
||||
<menuItem title="Show Ruler" id="vLm-3I-IUL"> |
||||
<modifierMask key="keyEquivalentModifierMask"/> |
||||
<connections> |
||||
<action selector="toggleRuler:" target="Ady-hI-5gd" id="FOx-HJ-KwY"/> |
||||
</connections> |
||||
</menuItem> |
||||
<menuItem title="Copy Ruler" keyEquivalent="c" id="MkV-Pr-PK5"> |
||||
<modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/> |
||||
<connections> |
||||
<action selector="copyRuler:" target="Ady-hI-5gd" id="71i-fW-3W2"/> |
||||
</connections> |
||||
</menuItem> |
||||
<menuItem title="Paste Ruler" keyEquivalent="v" id="LVM-kO-fVI"> |
||||
<modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/> |
||||
<connections> |
||||
<action selector="pasteRuler:" target="Ady-hI-5gd" id="cSh-wd-qM2"/> |
||||
</connections> |
||||
</menuItem> |
||||
</items> |
||||
</menu> |
||||
</menuItem> |
||||
</items> |
||||
</menu> |
||||
</menuItem> |
||||
<menuItem title="View" id="H8h-7b-M4v"> |
||||
<modifierMask key="keyEquivalentModifierMask"/> |
||||
<menu key="submenu" title="View" id="HyV-fh-RgO"> |
||||
<items> |
||||
<menuItem title="Show Toolbar" keyEquivalent="t" id="snW-S8-Cw5"> |
||||
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/> |
||||
<connections> |
||||
<action selector="toggleToolbarShown:" target="Ady-hI-5gd" id="BXY-wc-z0C"/> |
||||
</connections> |
||||
</menuItem> |
||||
<menuItem title="Customize Toolbar…" id="1UK-8n-QPP"> |
||||
<modifierMask key="keyEquivalentModifierMask"/> |
||||
<connections> |
||||
<action selector="runToolbarCustomizationPalette:" target="Ady-hI-5gd" id="pQI-g3-MTW"/> |
||||
</connections> |
||||
</menuItem> |
||||
<menuItem isSeparatorItem="YES" id="hB3-LF-h0Y"/> |
||||
<menuItem title="Show Sidebar" keyEquivalent="s" id="kIP-vf-haE"> |
||||
<modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/> |
||||
<connections> |
||||
<action selector="toggleSourceList:" target="Ady-hI-5gd" id="iwa-gc-5KM"/> |
||||
</connections> |
||||
</menuItem> |
||||
<menuItem title="Enter Full Screen" keyEquivalent="f" id="4J7-dP-txa"> |
||||
<modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/> |
||||
<connections> |
||||
<action selector="toggleFullScreen:" target="Ady-hI-5gd" id="dU3-MA-1Rq"/> |
||||
</connections> |
||||
</menuItem> |
||||
</items> |
||||
</menu> |
||||
</menuItem> |
||||
<menuItem title="Window" id="aUF-d1-5bR"> |
||||
<modifierMask key="keyEquivalentModifierMask"/> |
||||
<menu key="submenu" title="Window" systemMenu="window" id="Td7-aD-5lo"> |
||||
<items> |
||||
<menuItem title="Minimize" keyEquivalent="m" id="OY7-WF-poV"> |
||||
<connections> |
||||
<action selector="performMiniaturize:" target="Ady-hI-5gd" id="VwT-WD-YPe"/> |
||||
</connections> |
||||
</menuItem> |
||||
<menuItem title="Zoom" id="R4o-n2-Eq4"> |
||||
<modifierMask key="keyEquivalentModifierMask"/> |
||||
<connections> |
||||
<action selector="performZoom:" target="Ady-hI-5gd" id="DIl-cC-cCs"/> |
||||
</connections> |
||||
</menuItem> |
||||
<menuItem isSeparatorItem="YES" id="eu3-7i-yIM"/> |
||||
<menuItem title="Bring All to Front" id="LE2-aR-0XJ"> |
||||
<modifierMask key="keyEquivalentModifierMask"/> |
||||
<connections> |
||||
<action selector="arrangeInFront:" target="Ady-hI-5gd" id="DRN-fu-gQh"/> |
||||
</connections> |
||||
</menuItem> |
||||
</items> |
||||
</menu> |
||||
</menuItem> |
||||
<menuItem title="Help" id="wpr-3q-Mcd"> |
||||
<modifierMask key="keyEquivalentModifierMask"/> |
||||
<menu key="submenu" title="Help" systemMenu="help" id="F2S-fz-NVQ"> |
||||
<items> |
||||
<menuItem title="HelloWorld Help" keyEquivalent="?" id="FKE-Sm-Kum"> |
||||
<connections> |
||||
<action selector="showHelp:" target="Ady-hI-5gd" id="y7X-2Q-9no"/> |
||||
</connections> |
||||
</menuItem> |
||||
</items> |
||||
</menu> |
||||
</menuItem> |
||||
</items> |
||||
</menu> |
||||
<connections> |
||||
<outlet property="delegate" destination="Voe-Tx-rLC" id="PrD-fu-P6m"/> |
||||
</connections> |
||||
</application> |
||||
<customObject id="Voe-Tx-rLC" customClass="AppDelegate" customModuleProvider=""/> |
||||
<customObject id="YLy-65-1bz" customClass="NSFontManager"/> |
||||
<customObject id="Ady-hI-5gd" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/> |
||||
</objects> |
||||
<point key="canvasLocation" x="75" y="0.0"/> |
||||
</scene> |
||||
<!--Window Controller--> |
||||
<scene sceneID="R2V-B0-nI4"> |
||||
<objects> |
||||
<windowController id="B8D-0N-5wS" sceneMemberID="viewController"> |
||||
<window key="window" title="Window" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" oneShot="NO" releasedWhenClosed="NO" showsToolbarButton="NO" visibleAtLaunch="NO" animationBehavior="default" id="IQv-IB-iLA"> |
||||
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/> |
||||
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/> |
||||
<rect key="contentRect" x="196" y="240" width="480" height="270"/> |
||||
<rect key="screenRect" x="0.0" y="0.0" width="1680" height="1027"/> |
||||
<connections> |
||||
<outlet property="delegate" destination="B8D-0N-5wS" id="98r-iN-zZc"/> |
||||
</connections> |
||||
</window> |
||||
<connections> |
||||
<segue destination="XfG-lQ-9wD" kind="relationship" relationship="window.shadowedContentViewController" id="cq2-FE-JQM"/> |
||||
</connections> |
||||
</windowController> |
||||
<customObject id="Oky-zY-oP4" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/> |
||||
</objects> |
||||
<point key="canvasLocation" x="75" y="250"/> |
||||
</scene> |
||||
<!--View Controller--> |
||||
<scene sceneID="hIz-AP-VOD"> |
||||
<objects> |
||||
<viewController id="XfG-lQ-9wD" customClass="ViewController" customModuleProvider="" sceneMemberID="viewController"> |
||||
<view key="view" wantsLayer="YES" id="m2S-Jp-Qdl"> |
||||
<rect key="frame" x="0.0" y="0.0" width="480" height="270"/> |
||||
<autoresizingMask key="autoresizingMask"/> |
||||
</view> |
||||
</viewController> |
||||
<customObject id="rPt-NT-nkU" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/> |
||||
</objects> |
||||
<point key="canvasLocation" x="75" y="655"/> |
||||
</scene> |
||||
</scenes> |
||||
</document> |
@ -0,0 +1,5 @@ |
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> |
||||
<plist version="1.0"> |
||||
<dict/> |
||||
</plist> |
@ -0,0 +1,32 @@ |
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> |
||||
<plist version="1.0"> |
||||
<dict> |
||||
<key>CFBundleDevelopmentRegion</key> |
||||
<string>$(DEVELOPMENT_LANGUAGE)</string> |
||||
<key>CFBundleExecutable</key> |
||||
<string>$(EXECUTABLE_NAME)</string> |
||||
<key>CFBundleIconFile</key> |
||||
<string></string> |
||||
<key>CFBundleIdentifier</key> |
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string> |
||||
<key>CFBundleInfoDictionaryVersion</key> |
||||
<string>6.0</string> |
||||
<key>CFBundleName</key> |
||||
<string>$(PRODUCT_NAME)</string> |
||||
<key>CFBundlePackageType</key> |
||||
<string>APPL</string> |
||||
<key>CFBundleShortVersionString</key> |
||||
<string>1.0</string> |
||||
<key>CFBundleVersion</key> |
||||
<string>1</string> |
||||
<key>LSMinimumSystemVersion</key> |
||||
<string>$(MACOSX_DEPLOYMENT_TARGET)</string> |
||||
<key>NSHumanReadableCopyright</key> |
||||
<string>Copyright © 2018 gRPC. All rights reserved.</string> |
||||
<key>NSMainStoryboardFile</key> |
||||
<string>Main</string> |
||||
<key>NSPrincipalClass</key> |
||||
<string>NSApplication</string> |
||||
</dict> |
||||
</plist> |
@ -0,0 +1,25 @@ |
||||
/*
|
||||
* |
||||
* 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. |
||||
* |
||||
*/ |
||||
|
||||
#import <Cocoa/Cocoa.h> |
||||
|
||||
@interface ViewController : NSViewController |
||||
|
||||
|
||||
@end |
||||
|
@ -0,0 +1,37 @@ |
||||
/* |
||||
* |
||||
* 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. |
||||
* |
||||
*/ |
||||
|
||||
#import "ViewController.h" |
||||
|
||||
@implementation ViewController |
||||
|
||||
- (void)viewDidLoad { |
||||
[super viewDidLoad]; |
||||
|
||||
// Do any additional setup after loading the view. |
||||
} |
||||
|
||||
|
||||
- (void)setRepresentedObject:(id)representedObject { |
||||
[super setRepresentedObject:representedObject]; |
||||
|
||||
// Update the view, if already loaded. |
||||
} |
||||
|
||||
|
||||
@end |
@ -0,0 +1,9 @@ |
||||
source 'https://github.com/CocoaPods/Specs.git' |
||||
platform :macos, '10.9' |
||||
|
||||
install! 'cocoapods', :deterministic_uuids => false |
||||
|
||||
target 'HelloWorld' do |
||||
# Depend on the generated HelloWorld library. |
||||
pod 'HelloWorld', :path => '.' |
||||
end |
@ -0,0 +1,6 @@ |
||||
# gRPC Objective-C Mac OS Hello World Example |
||||
|
||||
A hello world example app on Mac OS. Note that Mac OS is not a first class supported platform of gRPC |
||||
Objective-C library. This example is only for reference. |
||||
|
||||
Refer to [Hello World Example](../helloworld) for instructions on installation and running. |
@ -0,0 +1,43 @@ |
||||
/* |
||||
* |
||||
* 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. |
||||
* |
||||
*/ |
||||
|
||||
#import <Cocoa/Cocoa.h> |
||||
|
||||
#import <GRPCClient/GRPCCall+ChannelArg.h> |
||||
#import <GRPCClient/GRPCCall+Tests.h> |
||||
#import <HelloWorld/Helloworld.pbrpc.h> |
||||
|
||||
static NSString * const kHostAddress = @"localhost:50051"; |
||||
|
||||
int main(int argc, const char * argv[]) { |
||||
@autoreleasepool { |
||||
[GRPCCall useInsecureConnectionsForHost:kHostAddress]; |
||||
[GRPCCall setUserAgentPrefix:@"HelloWorld/1.0" forHost:kHostAddress]; |
||||
|
||||
HLWGreeter *client = [[HLWGreeter alloc] initWithHost:kHostAddress]; |
||||
|
||||
HLWHelloRequest *request = [HLWHelloRequest message]; |
||||
request.name = @"Objective-C"; |
||||
|
||||
[client sayHelloWithRequest:request handler:^(HLWHelloReply *response, NSError *error) { |
||||
NSLog(@"%@", response.message); |
||||
}]; |
||||
} |
||||
|
||||
return NSApplicationMain(argc, argv); |
||||
} |
@ -0,0 +1,44 @@ |
||||
# 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. |
||||
"""The Python implementation of the GRPC helloworld.Greeter client with channel options and call timeout parameters.""" |
||||
|
||||
from __future__ import print_function |
||||
|
||||
import grpc |
||||
|
||||
import helloworld_pb2 |
||||
import helloworld_pb2_grpc |
||||
|
||||
|
||||
def run(): |
||||
# NOTE(gRPC Python Team): .close() is possible on a channel and should be |
||||
# used in circumstances in which the with statement does not fit the needs |
||||
# of the code. |
||||
# |
||||
# For more channel options, please see https://grpc.io/grpc/core/group__grpc__arg__keys.html |
||||
with grpc.insecure_channel( |
||||
target='localhost:50051', |
||||
options=[('grpc.lb_policy_name', 'pick_first'), |
||||
('grpc.enable_retries', 0), ('grpc.keepalive_timeout_ms', |
||||
10000)]) as channel: |
||||
stub = helloworld_pb2_grpc.GreeterStub(channel) |
||||
# Timeout in seconds. |
||||
# Please refer gRPC Python documents for more detail. https://grpc.io/grpc/python/grpc.html |
||||
response = stub.SayHello( |
||||
helloworld_pb2.HelloRequest(name='you'), timeout=10) |
||||
print("Greeter client received: " + response.message) |
||||
|
||||
|
||||
if __name__ == '__main__': |
||||
run() |
@ -0,0 +1,919 @@ |
||||
/*
|
||||
* |
||||
* 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. |
||||
* |
||||
*/ |
||||
|
||||
#ifndef GRPCPP_IMPL_CODEGEN_CALL_OP_SET_H |
||||
#define GRPCPP_IMPL_CODEGEN_CALL_OP_SET_H |
||||
|
||||
#include <assert.h> |
||||
#include <array> |
||||
#include <cstring> |
||||
#include <functional> |
||||
#include <map> |
||||
#include <memory> |
||||
#include <vector> |
||||
|
||||
#include <grpcpp/impl/codegen/byte_buffer.h> |
||||
#include <grpcpp/impl/codegen/call.h> |
||||
#include <grpcpp/impl/codegen/call_hook.h> |
||||
#include <grpcpp/impl/codegen/call_op_set_interface.h> |
||||
#include <grpcpp/impl/codegen/client_context.h> |
||||
#include <grpcpp/impl/codegen/completion_queue_tag.h> |
||||
#include <grpcpp/impl/codegen/config.h> |
||||
#include <grpcpp/impl/codegen/core_codegen_interface.h> |
||||
#include <grpcpp/impl/codegen/intercepted_channel.h> |
||||
#include <grpcpp/impl/codegen/interceptor_common.h> |
||||
#include <grpcpp/impl/codegen/serialization_traits.h> |
||||
#include <grpcpp/impl/codegen/slice.h> |
||||
#include <grpcpp/impl/codegen/string_ref.h> |
||||
|
||||
#include <grpc/impl/codegen/atm.h> |
||||
#include <grpc/impl/codegen/compression_types.h> |
||||
#include <grpc/impl/codegen/grpc_types.h> |
||||
|
||||
namespace grpc { |
||||
|
||||
class CompletionQueue; |
||||
extern CoreCodegenInterface* g_core_codegen_interface; |
||||
|
||||
namespace internal { |
||||
class Call; |
||||
class CallHook; |
||||
|
||||
// TODO(yangg) if the map is changed before we send, the pointers will be a
|
||||
// mess. Make sure it does not happen.
|
||||
inline grpc_metadata* FillMetadataArray( |
||||
const std::multimap<grpc::string, grpc::string>& metadata, |
||||
size_t* metadata_count, const grpc::string& optional_error_details) { |
||||
*metadata_count = metadata.size() + (optional_error_details.empty() ? 0 : 1); |
||||
if (*metadata_count == 0) { |
||||
return nullptr; |
||||
} |
||||
grpc_metadata* metadata_array = |
||||
(grpc_metadata*)(g_core_codegen_interface->gpr_malloc( |
||||
(*metadata_count) * sizeof(grpc_metadata))); |
||||
size_t i = 0; |
||||
for (auto iter = metadata.cbegin(); iter != metadata.cend(); ++iter, ++i) { |
||||
metadata_array[i].key = SliceReferencingString(iter->first); |
||||
metadata_array[i].value = SliceReferencingString(iter->second); |
||||
} |
||||
if (!optional_error_details.empty()) { |
||||
metadata_array[i].key = |
||||
g_core_codegen_interface->grpc_slice_from_static_buffer( |
||||
kBinaryErrorDetailsKey, sizeof(kBinaryErrorDetailsKey) - 1); |
||||
metadata_array[i].value = SliceReferencingString(optional_error_details); |
||||
} |
||||
return metadata_array; |
||||
} |
||||
} // namespace internal
|
||||
|
||||
/// Per-message write options.
|
||||
class WriteOptions { |
||||
public: |
||||
WriteOptions() : flags_(0), last_message_(false) {} |
||||
WriteOptions(const WriteOptions& other) |
||||
: flags_(other.flags_), last_message_(other.last_message_) {} |
||||
|
||||
/// Clear all flags.
|
||||
inline void Clear() { flags_ = 0; } |
||||
|
||||
/// Returns raw flags bitset.
|
||||
inline uint32_t flags() const { return flags_; } |
||||
|
||||
/// Sets flag for the disabling of compression for the next message write.
|
||||
///
|
||||
/// \sa GRPC_WRITE_NO_COMPRESS
|
||||
inline WriteOptions& set_no_compression() { |
||||
SetBit(GRPC_WRITE_NO_COMPRESS); |
||||
return *this; |
||||
} |
||||
|
||||
/// Clears flag for the disabling of compression for the next message write.
|
||||
///
|
||||
/// \sa GRPC_WRITE_NO_COMPRESS
|
||||
inline WriteOptions& clear_no_compression() { |
||||
ClearBit(GRPC_WRITE_NO_COMPRESS); |
||||
return *this; |
||||
} |
||||
|
||||
/// Get value for the flag indicating whether compression for the next
|
||||
/// message write is forcefully disabled.
|
||||
///
|
||||
/// \sa GRPC_WRITE_NO_COMPRESS
|
||||
inline bool get_no_compression() const { |
||||
return GetBit(GRPC_WRITE_NO_COMPRESS); |
||||
} |
||||
|
||||
/// Sets flag indicating that the write may be buffered and need not go out on
|
||||
/// the wire immediately.
|
||||
///
|
||||
/// \sa GRPC_WRITE_BUFFER_HINT
|
||||
inline WriteOptions& set_buffer_hint() { |
||||
SetBit(GRPC_WRITE_BUFFER_HINT); |
||||
return *this; |
||||
} |
||||
|
||||
/// Clears flag indicating that the write may be buffered and need not go out
|
||||
/// on the wire immediately.
|
||||
///
|
||||
/// \sa GRPC_WRITE_BUFFER_HINT
|
||||
inline WriteOptions& clear_buffer_hint() { |
||||
ClearBit(GRPC_WRITE_BUFFER_HINT); |
||||
return *this; |
||||
} |
||||
|
||||
/// Get value for the flag indicating that the write may be buffered and need
|
||||
/// not go out on the wire immediately.
|
||||
///
|
||||
/// \sa GRPC_WRITE_BUFFER_HINT
|
||||
inline bool get_buffer_hint() const { return GetBit(GRPC_WRITE_BUFFER_HINT); } |
||||
|
||||
/// corked bit: aliases set_buffer_hint currently, with the intent that
|
||||
/// set_buffer_hint will be removed in the future
|
||||
inline WriteOptions& set_corked() { |
||||
SetBit(GRPC_WRITE_BUFFER_HINT); |
||||
return *this; |
||||
} |
||||
|
||||
inline WriteOptions& clear_corked() { |
||||
ClearBit(GRPC_WRITE_BUFFER_HINT); |
||||
return *this; |
||||
} |
||||
|
||||
inline bool is_corked() const { return GetBit(GRPC_WRITE_BUFFER_HINT); } |
||||
|
||||
/// last-message bit: indicates this is the last message in a stream
|
||||
/// client-side: makes Write the equivalent of performing Write, WritesDone
|
||||
/// in a single step
|
||||
/// server-side: hold the Write until the service handler returns (sync api)
|
||||
/// or until Finish is called (async api)
|
||||
inline WriteOptions& set_last_message() { |
||||
last_message_ = true; |
||||
return *this; |
||||
} |
||||
|
||||
/// Clears flag indicating that this is the last message in a stream,
|
||||
/// disabling coalescing.
|
||||
inline WriteOptions& clear_last_message() { |
||||
last_message_ = false; |
||||
return *this; |
||||
} |
||||
|
||||
/// Guarantee that all bytes have been written to the socket before completing
|
||||
/// this write (usually writes are completed when they pass flow control).
|
||||
inline WriteOptions& set_write_through() { |
||||
SetBit(GRPC_WRITE_THROUGH); |
||||
return *this; |
||||
} |
||||
|
||||
inline bool is_write_through() const { return GetBit(GRPC_WRITE_THROUGH); } |
||||
|
||||
/// Get value for the flag indicating that this is the last message, and
|
||||
/// should be coalesced with trailing metadata.
|
||||
///
|
||||
/// \sa GRPC_WRITE_LAST_MESSAGE
|
||||
bool is_last_message() const { return last_message_; } |
||||
|
||||
WriteOptions& operator=(const WriteOptions& rhs) { |
||||
flags_ = rhs.flags_; |
||||
return *this; |
||||
} |
||||
|
||||
private: |
||||
void SetBit(const uint32_t mask) { flags_ |= mask; } |
||||
|
||||
void ClearBit(const uint32_t mask) { flags_ &= ~mask; } |
||||
|
||||
bool GetBit(const uint32_t mask) const { return (flags_ & mask) != 0; } |
||||
|
||||
uint32_t flags_; |
||||
bool last_message_; |
||||
}; |
||||
|
||||
namespace internal { |
||||
|
||||
/// Default argument for CallOpSet. I is unused by the class, but can be
|
||||
/// used for generating multiple names for the same thing.
|
||||
template <int I> |
||||
class CallNoOp { |
||||
protected: |
||||
void AddOp(grpc_op* ops, size_t* nops) {} |
||||
void FinishOp(bool* status) {} |
||||
void SetInterceptionHookPoint( |
||||
InterceptorBatchMethodsImpl* interceptor_methods) {} |
||||
void SetFinishInterceptionHookPoint( |
||||
InterceptorBatchMethodsImpl* interceptor_methods) {} |
||||
void SetHijackingState(InterceptorBatchMethodsImpl* interceptor_methods) {} |
||||
}; |
||||
|
||||
class CallOpSendInitialMetadata { |
||||
public: |
||||
CallOpSendInitialMetadata() : send_(false) { |
||||
maybe_compression_level_.is_set = false; |
||||
} |
||||
|
||||
void SendInitialMetadata(std::multimap<grpc::string, grpc::string>* metadata, |
||||
uint32_t flags) { |
||||
maybe_compression_level_.is_set = false; |
||||
send_ = true; |
||||
flags_ = flags; |
||||
metadata_map_ = metadata; |
||||
} |
||||
|
||||
void set_compression_level(grpc_compression_level level) { |
||||
maybe_compression_level_.is_set = true; |
||||
maybe_compression_level_.level = level; |
||||
} |
||||
|
||||
protected: |
||||
void AddOp(grpc_op* ops, size_t* nops) { |
||||
if (!send_ || hijacked_) return; |
||||
grpc_op* op = &ops[(*nops)++]; |
||||
op->op = GRPC_OP_SEND_INITIAL_METADATA; |
||||
op->flags = flags_; |
||||
op->reserved = NULL; |
||||
initial_metadata_ = |
||||
FillMetadataArray(*metadata_map_, &initial_metadata_count_, ""); |
||||
op->data.send_initial_metadata.count = initial_metadata_count_; |
||||
op->data.send_initial_metadata.metadata = initial_metadata_; |
||||
op->data.send_initial_metadata.maybe_compression_level.is_set = |
||||
maybe_compression_level_.is_set; |
||||
if (maybe_compression_level_.is_set) { |
||||
op->data.send_initial_metadata.maybe_compression_level.level = |
||||
maybe_compression_level_.level; |
||||
} |
||||
} |
||||
void FinishOp(bool* status) { |
||||
if (!send_ || hijacked_) return; |
||||
g_core_codegen_interface->gpr_free(initial_metadata_); |
||||
send_ = false; |
||||
} |
||||
|
||||
void SetInterceptionHookPoint( |
||||
InterceptorBatchMethodsImpl* interceptor_methods) { |
||||
if (!send_) return; |
||||
interceptor_methods->AddInterceptionHookPoint( |
||||
experimental::InterceptionHookPoints::PRE_SEND_INITIAL_METADATA); |
||||
interceptor_methods->SetSendInitialMetadata(metadata_map_); |
||||
} |
||||
|
||||
void SetFinishInterceptionHookPoint( |
||||
InterceptorBatchMethodsImpl* interceptor_methods) {} |
||||
|
||||
void SetHijackingState(InterceptorBatchMethodsImpl* interceptor_methods) { |
||||
hijacked_ = true; |
||||
} |
||||
|
||||
bool hijacked_ = false; |
||||
bool send_; |
||||
uint32_t flags_; |
||||
size_t initial_metadata_count_; |
||||
std::multimap<grpc::string, grpc::string>* metadata_map_; |
||||
grpc_metadata* initial_metadata_; |
||||
struct { |
||||
bool is_set; |
||||
grpc_compression_level level; |
||||
} maybe_compression_level_; |
||||
}; |
||||
|
||||
class CallOpSendMessage { |
||||
public: |
||||
CallOpSendMessage() : send_buf_() {} |
||||
|
||||
/// Send \a message using \a options for the write. The \a options are cleared
|
||||
/// after use.
|
||||
template <class M> |
||||
Status SendMessage(const M& message, |
||||
WriteOptions options) GRPC_MUST_USE_RESULT; |
||||
|
||||
template <class M> |
||||
Status SendMessage(const M& message) GRPC_MUST_USE_RESULT; |
||||
|
||||
protected: |
||||
void AddOp(grpc_op* ops, size_t* nops) { |
||||
if (!send_buf_.Valid() || hijacked_) return; |
||||
grpc_op* op = &ops[(*nops)++]; |
||||
op->op = GRPC_OP_SEND_MESSAGE; |
||||
op->flags = write_options_.flags(); |
||||
op->reserved = NULL; |
||||
op->data.send_message.send_message = send_buf_.c_buffer(); |
||||
// Flags are per-message: clear them after use.
|
||||
write_options_.Clear(); |
||||
} |
||||
void FinishOp(bool* status) { send_buf_.Clear(); } |
||||
|
||||
void SetInterceptionHookPoint( |
||||
InterceptorBatchMethodsImpl* interceptor_methods) { |
||||
if (!send_buf_.Valid()) return; |
||||
interceptor_methods->AddInterceptionHookPoint( |
||||
experimental::InterceptionHookPoints::PRE_SEND_MESSAGE); |
||||
interceptor_methods->SetSendMessage(&send_buf_); |
||||
} |
||||
|
||||
void SetFinishInterceptionHookPoint( |
||||
InterceptorBatchMethodsImpl* interceptor_methods) {} |
||||
|
||||
void SetHijackingState(InterceptorBatchMethodsImpl* interceptor_methods) { |
||||
hijacked_ = true; |
||||
} |
||||
|
||||
private: |
||||
bool hijacked_ = false; |
||||
ByteBuffer send_buf_; |
||||
WriteOptions write_options_; |
||||
}; |
||||
|
||||
template <class M> |
||||
Status CallOpSendMessage::SendMessage(const M& message, WriteOptions options) { |
||||
write_options_ = options; |
||||
bool own_buf; |
||||
// TODO(vjpai): Remove the void below when possible
|
||||
// The void in the template parameter below should not be needed
|
||||
// (since it should be implicit) but is needed due to an observed
|
||||
// difference in behavior between clang and gcc for certain internal users
|
||||
Status result = SerializationTraits<M, void>::Serialize( |
||||
message, send_buf_.bbuf_ptr(), &own_buf); |
||||
if (!own_buf) { |
||||
send_buf_.Duplicate(); |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
template <class M> |
||||
Status CallOpSendMessage::SendMessage(const M& message) { |
||||
return SendMessage(message, WriteOptions()); |
||||
} |
||||
|
||||
template <class R> |
||||
class CallOpRecvMessage { |
||||
public: |
||||
CallOpRecvMessage() |
||||
: got_message(false), |
||||
message_(nullptr), |
||||
allow_not_getting_message_(false) {} |
||||
|
||||
void RecvMessage(R* message) { message_ = message; } |
||||
|
||||
// Do not change status if no message is received.
|
||||
void AllowNoMessage() { allow_not_getting_message_ = true; } |
||||
|
||||
bool got_message; |
||||
|
||||
protected: |
||||
void AddOp(grpc_op* ops, size_t* nops) { |
||||
if (message_ == nullptr || hijacked_) return; |
||||
grpc_op* op = &ops[(*nops)++]; |
||||
op->op = GRPC_OP_RECV_MESSAGE; |
||||
op->flags = 0; |
||||
op->reserved = NULL; |
||||
op->data.recv_message.recv_message = recv_buf_.c_buffer_ptr(); |
||||
} |
||||
|
||||
void FinishOp(bool* status) { |
||||
if (message_ == nullptr || hijacked_) return; |
||||
if (recv_buf_.Valid()) { |
||||
if (*status) { |
||||
got_message = *status = |
||||
SerializationTraits<R>::Deserialize(recv_buf_.bbuf_ptr(), message_) |
||||
.ok(); |
||||
recv_buf_.Release(); |
||||
} else { |
||||
got_message = false; |
||||
recv_buf_.Clear(); |
||||
} |
||||
} else { |
||||
got_message = false; |
||||
if (!allow_not_getting_message_) { |
||||
*status = false; |
||||
} |
||||
} |
||||
message_ = nullptr; |
||||
} |
||||
|
||||
void SetInterceptionHookPoint( |
||||
InterceptorBatchMethodsImpl* interceptor_methods) { |
||||
interceptor_methods->SetRecvMessage(message_); |
||||
} |
||||
|
||||
void SetFinishInterceptionHookPoint( |
||||
InterceptorBatchMethodsImpl* interceptor_methods) { |
||||
if (!got_message) return; |
||||
interceptor_methods->AddInterceptionHookPoint( |
||||
experimental::InterceptionHookPoints::POST_RECV_MESSAGE); |
||||
} |
||||
void SetHijackingState(InterceptorBatchMethodsImpl* interceptor_methods) { |
||||
hijacked_ = true; |
||||
if (message_ == nullptr) return; |
||||
interceptor_methods->AddInterceptionHookPoint( |
||||
experimental::InterceptionHookPoints::PRE_RECV_MESSAGE); |
||||
got_message = true; |
||||
} |
||||
|
||||
private: |
||||
R* message_; |
||||
ByteBuffer recv_buf_; |
||||
bool allow_not_getting_message_; |
||||
bool hijacked_ = false; |
||||
}; |
||||
|
||||
class DeserializeFunc { |
||||
public: |
||||
virtual Status Deserialize(ByteBuffer* buf) = 0; |
||||
virtual ~DeserializeFunc() {} |
||||
}; |
||||
|
||||
template <class R> |
||||
class DeserializeFuncType final : public DeserializeFunc { |
||||
public: |
||||
DeserializeFuncType(R* message) : message_(message) {} |
||||
Status Deserialize(ByteBuffer* buf) override { |
||||
return SerializationTraits<R>::Deserialize(buf->bbuf_ptr(), message_); |
||||
} |
||||
|
||||
~DeserializeFuncType() override {} |
||||
|
||||
private: |
||||
R* message_; // Not a managed pointer because management is external to this
|
||||
}; |
||||
|
||||
class CallOpGenericRecvMessage { |
||||
public: |
||||
CallOpGenericRecvMessage() |
||||
: got_message(false), allow_not_getting_message_(false) {} |
||||
|
||||
template <class R> |
||||
void RecvMessage(R* message) { |
||||
// Use an explicit base class pointer to avoid resolution error in the
|
||||
// following unique_ptr::reset for some old implementations.
|
||||
DeserializeFunc* func = new DeserializeFuncType<R>(message); |
||||
deserialize_.reset(func); |
||||
message_ = message; |
||||
} |
||||
|
||||
// Do not change status if no message is received.
|
||||
void AllowNoMessage() { allow_not_getting_message_ = true; } |
||||
|
||||
bool got_message; |
||||
|
||||
protected: |
||||
void AddOp(grpc_op* ops, size_t* nops) { |
||||
if (!deserialize_ || hijacked_) return; |
||||
grpc_op* op = &ops[(*nops)++]; |
||||
op->op = GRPC_OP_RECV_MESSAGE; |
||||
op->flags = 0; |
||||
op->reserved = NULL; |
||||
op->data.recv_message.recv_message = recv_buf_.c_buffer_ptr(); |
||||
} |
||||
|
||||
void FinishOp(bool* status) { |
||||
if (!deserialize_ || hijacked_) return; |
||||
if (recv_buf_.Valid()) { |
||||
if (*status) { |
||||
got_message = true; |
||||
*status = deserialize_->Deserialize(&recv_buf_).ok(); |
||||
recv_buf_.Release(); |
||||
} else { |
||||
got_message = false; |
||||
recv_buf_.Clear(); |
||||
} |
||||
} else { |
||||
got_message = false; |
||||
if (!allow_not_getting_message_) { |
||||
*status = false; |
||||
} |
||||
} |
||||
deserialize_.reset(); |
||||
} |
||||
|
||||
void SetInterceptionHookPoint( |
||||
InterceptorBatchMethodsImpl* interceptor_methods) { |
||||
interceptor_methods->SetRecvMessage(message_); |
||||
} |
||||
|
||||
void SetFinishInterceptionHookPoint( |
||||
InterceptorBatchMethodsImpl* interceptor_methods) { |
||||
if (!got_message) return; |
||||
interceptor_methods->AddInterceptionHookPoint( |
||||
experimental::InterceptionHookPoints::POST_RECV_MESSAGE); |
||||
} |
||||
void SetHijackingState(InterceptorBatchMethodsImpl* interceptor_methods) { |
||||
hijacked_ = true; |
||||
if (!deserialize_) return; |
||||
interceptor_methods->AddInterceptionHookPoint( |
||||
experimental::InterceptionHookPoints::PRE_RECV_MESSAGE); |
||||
} |
||||
|
||||
private: |
||||
void* message_; |
||||
bool hijacked_ = false; |
||||
std::unique_ptr<DeserializeFunc> deserialize_; |
||||
ByteBuffer recv_buf_; |
||||
bool allow_not_getting_message_; |
||||
}; |
||||
|
||||
class CallOpClientSendClose { |
||||
public: |
||||
CallOpClientSendClose() : send_(false) {} |
||||
|
||||
void ClientSendClose() { send_ = true; } |
||||
|
||||
protected: |
||||
void AddOp(grpc_op* ops, size_t* nops) { |
||||
if (!send_ || hijacked_) return; |
||||
grpc_op* op = &ops[(*nops)++]; |
||||
op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; |
||||
op->flags = 0; |
||||
op->reserved = NULL; |
||||
} |
||||
void FinishOp(bool* status) { send_ = false; } |
||||
|
||||
void SetInterceptionHookPoint( |
||||
InterceptorBatchMethodsImpl* interceptor_methods) { |
||||
if (!send_) return; |
||||
interceptor_methods->AddInterceptionHookPoint( |
||||
experimental::InterceptionHookPoints::PRE_SEND_CLOSE); |
||||
} |
||||
|
||||
void SetFinishInterceptionHookPoint( |
||||
InterceptorBatchMethodsImpl* interceptor_methods) {} |
||||
|
||||
void SetHijackingState(InterceptorBatchMethodsImpl* interceptor_methods) { |
||||
hijacked_ = true; |
||||
} |
||||
|
||||
private: |
||||
bool hijacked_ = false; |
||||
bool send_; |
||||
}; |
||||
|
||||
class CallOpServerSendStatus { |
||||
public: |
||||
CallOpServerSendStatus() : send_status_available_(false) {} |
||||
|
||||
void ServerSendStatus( |
||||
std::multimap<grpc::string, grpc::string>* trailing_metadata, |
||||
const Status& status) { |
||||
send_error_details_ = status.error_details(); |
||||
metadata_map_ = trailing_metadata; |
||||
send_status_available_ = true; |
||||
send_status_code_ = static_cast<grpc_status_code>(status.error_code()); |
||||
send_error_message_ = status.error_message(); |
||||
} |
||||
|
||||
protected: |
||||
void AddOp(grpc_op* ops, size_t* nops) { |
||||
if (!send_status_available_ || hijacked_) return; |
||||
trailing_metadata_ = FillMetadataArray( |
||||
*metadata_map_, &trailing_metadata_count_, send_error_details_); |
||||
grpc_op* op = &ops[(*nops)++]; |
||||
op->op = GRPC_OP_SEND_STATUS_FROM_SERVER; |
||||
op->data.send_status_from_server.trailing_metadata_count = |
||||
trailing_metadata_count_; |
||||
op->data.send_status_from_server.trailing_metadata = trailing_metadata_; |
||||
op->data.send_status_from_server.status = send_status_code_; |
||||
error_message_slice_ = SliceReferencingString(send_error_message_); |
||||
op->data.send_status_from_server.status_details = |
||||
send_error_message_.empty() ? nullptr : &error_message_slice_; |
||||
op->flags = 0; |
||||
op->reserved = NULL; |
||||
} |
||||
|
||||
void FinishOp(bool* status) { |
||||
if (!send_status_available_ || hijacked_) return; |
||||
g_core_codegen_interface->gpr_free(trailing_metadata_); |
||||
send_status_available_ = false; |
||||
} |
||||
|
||||
void SetInterceptionHookPoint( |
||||
InterceptorBatchMethodsImpl* interceptor_methods) { |
||||
if (!send_status_available_) return; |
||||
interceptor_methods->AddInterceptionHookPoint( |
||||
experimental::InterceptionHookPoints::PRE_SEND_STATUS); |
||||
interceptor_methods->SetSendTrailingMetadata(metadata_map_); |
||||
interceptor_methods->SetSendStatus(&send_status_code_, &send_error_details_, |
||||
&send_error_message_); |
||||
} |
||||
|
||||
void SetFinishInterceptionHookPoint( |
||||
InterceptorBatchMethodsImpl* interceptor_methods) {} |
||||
|
||||
void SetHijackingState(InterceptorBatchMethodsImpl* interceptor_methods) { |
||||
hijacked_ = true; |
||||
} |
||||
|
||||
private: |
||||
bool hijacked_ = false; |
||||
bool send_status_available_; |
||||
grpc_status_code send_status_code_; |
||||
grpc::string send_error_details_; |
||||
grpc::string send_error_message_; |
||||
size_t trailing_metadata_count_; |
||||
std::multimap<grpc::string, grpc::string>* metadata_map_; |
||||
grpc_metadata* trailing_metadata_; |
||||
grpc_slice error_message_slice_; |
||||
}; |
||||
|
||||
class CallOpRecvInitialMetadata { |
||||
public: |
||||
CallOpRecvInitialMetadata() : metadata_map_(nullptr) {} |
||||
|
||||
void RecvInitialMetadata(ClientContext* context) { |
||||
context->initial_metadata_received_ = true; |
||||
metadata_map_ = &context->recv_initial_metadata_; |
||||
} |
||||
|
||||
protected: |
||||
void AddOp(grpc_op* ops, size_t* nops) { |
||||
if (metadata_map_ == nullptr || hijacked_) return; |
||||
grpc_op* op = &ops[(*nops)++]; |
||||
op->op = GRPC_OP_RECV_INITIAL_METADATA; |
||||
op->data.recv_initial_metadata.recv_initial_metadata = metadata_map_->arr(); |
||||
op->flags = 0; |
||||
op->reserved = NULL; |
||||
} |
||||
|
||||
void FinishOp(bool* status) { |
||||
if (metadata_map_ == nullptr || hijacked_) return; |
||||
} |
||||
|
||||
void SetInterceptionHookPoint( |
||||
InterceptorBatchMethodsImpl* interceptor_methods) { |
||||
interceptor_methods->SetRecvInitialMetadata(metadata_map_); |
||||
} |
||||
|
||||
void SetFinishInterceptionHookPoint( |
||||
InterceptorBatchMethodsImpl* interceptor_methods) { |
||||
if (metadata_map_ == nullptr) return; |
||||
interceptor_methods->AddInterceptionHookPoint( |
||||
experimental::InterceptionHookPoints::POST_RECV_INITIAL_METADATA); |
||||
metadata_map_ = nullptr; |
||||
} |
||||
|
||||
void SetHijackingState(InterceptorBatchMethodsImpl* interceptor_methods) { |
||||
hijacked_ = true; |
||||
if (metadata_map_ == nullptr) return; |
||||
interceptor_methods->AddInterceptionHookPoint( |
||||
experimental::InterceptionHookPoints::PRE_RECV_INITIAL_METADATA); |
||||
} |
||||
|
||||
private: |
||||
bool hijacked_ = false; |
||||
MetadataMap* metadata_map_; |
||||
}; |
||||
|
||||
class CallOpClientRecvStatus { |
||||
public: |
||||
CallOpClientRecvStatus() |
||||
: recv_status_(nullptr), debug_error_string_(nullptr) {} |
||||
|
||||
void ClientRecvStatus(ClientContext* context, Status* status) { |
||||
client_context_ = context; |
||||
metadata_map_ = &client_context_->trailing_metadata_; |
||||
recv_status_ = status; |
||||
error_message_ = g_core_codegen_interface->grpc_empty_slice(); |
||||
} |
||||
|
||||
protected: |
||||
void AddOp(grpc_op* ops, size_t* nops) { |
||||
if (recv_status_ == nullptr || hijacked_) return; |
||||
grpc_op* op = &ops[(*nops)++]; |
||||
op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; |
||||
op->data.recv_status_on_client.trailing_metadata = metadata_map_->arr(); |
||||
op->data.recv_status_on_client.status = &status_code_; |
||||
op->data.recv_status_on_client.status_details = &error_message_; |
||||
op->data.recv_status_on_client.error_string = &debug_error_string_; |
||||
op->flags = 0; |
||||
op->reserved = NULL; |
||||
} |
||||
|
||||
void FinishOp(bool* status) { |
||||
if (recv_status_ == nullptr || hijacked_) return; |
||||
grpc::string binary_error_details = metadata_map_->GetBinaryErrorDetails(); |
||||
*recv_status_ = |
||||
Status(static_cast<StatusCode>(status_code_), |
||||
GRPC_SLICE_IS_EMPTY(error_message_) |
||||
? grpc::string() |
||||
: grpc::string(GRPC_SLICE_START_PTR(error_message_), |
||||
GRPC_SLICE_END_PTR(error_message_)), |
||||
binary_error_details); |
||||
client_context_->set_debug_error_string( |
||||
debug_error_string_ != nullptr ? debug_error_string_ : ""); |
||||
g_core_codegen_interface->grpc_slice_unref(error_message_); |
||||
if (debug_error_string_ != nullptr) { |
||||
g_core_codegen_interface->gpr_free((void*)debug_error_string_); |
||||
} |
||||
} |
||||
|
||||
void SetInterceptionHookPoint( |
||||
InterceptorBatchMethodsImpl* interceptor_methods) { |
||||
interceptor_methods->SetRecvStatus(recv_status_); |
||||
interceptor_methods->SetRecvTrailingMetadata(metadata_map_); |
||||
} |
||||
|
||||
void SetFinishInterceptionHookPoint( |
||||
InterceptorBatchMethodsImpl* interceptor_methods) { |
||||
if (recv_status_ == nullptr) return; |
||||
interceptor_methods->AddInterceptionHookPoint( |
||||
experimental::InterceptionHookPoints::POST_RECV_STATUS); |
||||
recv_status_ = nullptr; |
||||
} |
||||
|
||||
void SetHijackingState(InterceptorBatchMethodsImpl* interceptor_methods) { |
||||
hijacked_ = true; |
||||
if (recv_status_ == nullptr) return; |
||||
interceptor_methods->AddInterceptionHookPoint( |
||||
experimental::InterceptionHookPoints::PRE_RECV_STATUS); |
||||
} |
||||
|
||||
private: |
||||
bool hijacked_ = false; |
||||
ClientContext* client_context_; |
||||
MetadataMap* metadata_map_; |
||||
Status* recv_status_; |
||||
const char* debug_error_string_; |
||||
grpc_status_code status_code_; |
||||
grpc_slice error_message_; |
||||
}; |
||||
|
||||
template <class Op1 = CallNoOp<1>, class Op2 = CallNoOp<2>, |
||||
class Op3 = CallNoOp<3>, class Op4 = CallNoOp<4>, |
||||
class Op5 = CallNoOp<5>, class Op6 = CallNoOp<6>> |
||||
class CallOpSet; |
||||
|
||||
/// Primary implementation of CallOpSetInterface.
|
||||
/// Since we cannot use variadic templates, we declare slots up to
|
||||
/// the maximum count of ops we'll need in a set. We leverage the
|
||||
/// empty base class optimization to slim this class (especially
|
||||
/// when there are many unused slots used). To avoid duplicate base classes,
|
||||
/// the template parmeter for CallNoOp is varied by argument position.
|
||||
template <class Op1, class Op2, class Op3, class Op4, class Op5, class Op6> |
||||
class CallOpSet : public CallOpSetInterface, |
||||
public Op1, |
||||
public Op2, |
||||
public Op3, |
||||
public Op4, |
||||
public Op5, |
||||
public Op6 { |
||||
public: |
||||
CallOpSet() : core_cq_tag_(this), return_tag_(this) {} |
||||
// The copy constructor and assignment operator reset the value of
|
||||
// core_cq_tag_, return_tag_, done_intercepting_ and interceptor_methods_
|
||||
// since those are only meaningful on a specific object, not across objects.
|
||||
CallOpSet(const CallOpSet& other) |
||||
: core_cq_tag_(this), |
||||
return_tag_(this), |
||||
call_(other.call_), |
||||
done_intercepting_(false), |
||||
interceptor_methods_(InterceptorBatchMethodsImpl()) {} |
||||
|
||||
CallOpSet& operator=(const CallOpSet& other) { |
||||
core_cq_tag_ = this; |
||||
return_tag_ = this; |
||||
call_ = other.call_; |
||||
done_intercepting_ = false; |
||||
interceptor_methods_ = InterceptorBatchMethodsImpl(); |
||||
return *this; |
||||
} |
||||
|
||||
void FillOps(Call* call) override { |
||||
done_intercepting_ = false; |
||||
g_core_codegen_interface->grpc_call_ref(call->call()); |
||||
call_ = |
||||
*call; // It's fine to create a copy of call since it's just pointers
|
||||
|
||||
if (RunInterceptors()) { |
||||
ContinueFillOpsAfterInterception(); |
||||
} else { |
||||
// After the interceptors are run, ContinueFillOpsAfterInterception will
|
||||
// be run
|
||||
} |
||||
} |
||||
|
||||
bool FinalizeResult(void** tag, bool* status) override { |
||||
if (done_intercepting_) { |
||||
// We have already finished intercepting and filling in the results. This
|
||||
// round trip from the core needed to be made because interceptors were
|
||||
// run
|
||||
*tag = return_tag_; |
||||
*status = saved_status_; |
||||
g_core_codegen_interface->grpc_call_unref(call_.call()); |
||||
return true; |
||||
} |
||||
|
||||
this->Op1::FinishOp(status); |
||||
this->Op2::FinishOp(status); |
||||
this->Op3::FinishOp(status); |
||||
this->Op4::FinishOp(status); |
||||
this->Op5::FinishOp(status); |
||||
this->Op6::FinishOp(status); |
||||
saved_status_ = *status; |
||||
if (RunInterceptorsPostRecv()) { |
||||
*tag = return_tag_; |
||||
g_core_codegen_interface->grpc_call_unref(call_.call()); |
||||
return true; |
||||
} |
||||
// Interceptors are going to be run, so we can't return the tag just yet.
|
||||
// After the interceptors are run, ContinueFinalizeResultAfterInterception
|
||||
return false; |
||||
} |
||||
|
||||
void set_output_tag(void* return_tag) { return_tag_ = return_tag; } |
||||
|
||||
void* core_cq_tag() override { return core_cq_tag_; } |
||||
|
||||
/// set_core_cq_tag is used to provide a different core CQ tag than "this".
|
||||
/// This is used for callback-based tags, where the core tag is the core
|
||||
/// callback function. It does not change the use or behavior of any other
|
||||
/// function (such as FinalizeResult)
|
||||
void set_core_cq_tag(void* core_cq_tag) { core_cq_tag_ = core_cq_tag; } |
||||
|
||||
// This will be called while interceptors are run if the RPC is a hijacked
|
||||
// RPC. This should set hijacking state for each of the ops.
|
||||
void SetHijackingState() override { |
||||
this->Op1::SetHijackingState(&interceptor_methods_); |
||||
this->Op2::SetHijackingState(&interceptor_methods_); |
||||
this->Op3::SetHijackingState(&interceptor_methods_); |
||||
this->Op4::SetHijackingState(&interceptor_methods_); |
||||
this->Op5::SetHijackingState(&interceptor_methods_); |
||||
this->Op6::SetHijackingState(&interceptor_methods_); |
||||
} |
||||
|
||||
// Should be called after interceptors are done running
|
||||
void ContinueFillOpsAfterInterception() override { |
||||
static const size_t MAX_OPS = 6; |
||||
grpc_op ops[MAX_OPS]; |
||||
size_t nops = 0; |
||||
this->Op1::AddOp(ops, &nops); |
||||
this->Op2::AddOp(ops, &nops); |
||||
this->Op3::AddOp(ops, &nops); |
||||
this->Op4::AddOp(ops, &nops); |
||||
this->Op5::AddOp(ops, &nops); |
||||
this->Op6::AddOp(ops, &nops); |
||||
GPR_CODEGEN_ASSERT(GRPC_CALL_OK == |
||||
g_core_codegen_interface->grpc_call_start_batch( |
||||
call_.call(), ops, nops, core_cq_tag(), nullptr)); |
||||
} |
||||
|
||||
// Should be called after interceptors are done running on the finalize result
|
||||
// path
|
||||
void ContinueFinalizeResultAfterInterception() override { |
||||
done_intercepting_ = true; |
||||
GPR_CODEGEN_ASSERT(GRPC_CALL_OK == |
||||
g_core_codegen_interface->grpc_call_start_batch( |
||||
call_.call(), nullptr, 0, core_cq_tag(), nullptr)); |
||||
} |
||||
|
||||
private: |
||||
// Returns true if no interceptors need to be run
|
||||
bool RunInterceptors() { |
||||
interceptor_methods_.ClearState(); |
||||
interceptor_methods_.SetCallOpSetInterface(this); |
||||
interceptor_methods_.SetCall(&call_); |
||||
this->Op1::SetInterceptionHookPoint(&interceptor_methods_); |
||||
this->Op2::SetInterceptionHookPoint(&interceptor_methods_); |
||||
this->Op3::SetInterceptionHookPoint(&interceptor_methods_); |
||||
this->Op4::SetInterceptionHookPoint(&interceptor_methods_); |
||||
this->Op5::SetInterceptionHookPoint(&interceptor_methods_); |
||||
this->Op6::SetInterceptionHookPoint(&interceptor_methods_); |
||||
return interceptor_methods_.RunInterceptors(); |
||||
} |
||||
// Returns true if no interceptors need to be run
|
||||
bool RunInterceptorsPostRecv() { |
||||
// Call and OpSet had already been set on the set state.
|
||||
// SetReverse also clears previously set hook points
|
||||
interceptor_methods_.SetReverse(); |
||||
this->Op1::SetFinishInterceptionHookPoint(&interceptor_methods_); |
||||
this->Op2::SetFinishInterceptionHookPoint(&interceptor_methods_); |
||||
this->Op3::SetFinishInterceptionHookPoint(&interceptor_methods_); |
||||
this->Op4::SetFinishInterceptionHookPoint(&interceptor_methods_); |
||||
this->Op5::SetFinishInterceptionHookPoint(&interceptor_methods_); |
||||
this->Op6::SetFinishInterceptionHookPoint(&interceptor_methods_); |
||||
return interceptor_methods_.RunInterceptors(); |
||||
} |
||||
|
||||
void* core_cq_tag_; |
||||
void* return_tag_; |
||||
Call call_; |
||||
bool done_intercepting_ = false; |
||||
InterceptorBatchMethodsImpl interceptor_methods_; |
||||
bool saved_status_; |
||||
}; |
||||
|
||||
} // namespace internal
|
||||
} // namespace grpc
|
||||
|
||||
#endif // GRPCPP_IMPL_CODEGEN_CALL_OP_SET_H
|