mirror of https://github.com/grpc/grpc.git
commit
811b07876c
218 changed files with 26198 additions and 3660 deletions
@ -0,0 +1,60 @@ |
|||||||
|
/*
|
||||||
|
* |
||||||
|
* Copyright 2014, Google Inc. |
||||||
|
* All rights reserved. |
||||||
|
* |
||||||
|
* Redistribution and use in source and binary forms, with or without |
||||||
|
* modification, are permitted provided that the following conditions are |
||||||
|
* met: |
||||||
|
* |
||||||
|
* * Redistributions of source code must retain the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer. |
||||||
|
* * Redistributions in binary form must reproduce the above |
||||||
|
* copyright notice, this list of conditions and the following disclaimer |
||||||
|
* in the documentation and/or other materials provided with the |
||||||
|
* distribution. |
||||||
|
* * Neither the name of Google Inc. nor the names of its |
||||||
|
* contributors may be used to endorse or promote products derived from |
||||||
|
* this software without specific prior written permission. |
||||||
|
* |
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
* |
||||||
|
*/ |
||||||
|
|
||||||
|
#ifndef __GRPC_SUPPORT_ENV_H__ |
||||||
|
#define __GRPC_SUPPORT_ENV_H__ |
||||||
|
|
||||||
|
#include <stdio.h> |
||||||
|
|
||||||
|
#include <grpc/support/slice.h> |
||||||
|
|
||||||
|
#ifdef __cplusplus |
||||||
|
extern "C" { |
||||||
|
#endif |
||||||
|
|
||||||
|
/* Env utility functions */ |
||||||
|
|
||||||
|
/* Gets the environment variable value with the specified name.
|
||||||
|
Returns a newly allocated string. It is the responsability of the caller to |
||||||
|
gpr_free the return value if not NULL (which means that the environment |
||||||
|
variable exists). */ |
||||||
|
char *gpr_getenv(const char *name); |
||||||
|
|
||||||
|
/* Sets the the environment with the specified name to the specified value. */ |
||||||
|
void gpr_setenv(const char *name, const char *value); |
||||||
|
|
||||||
|
#ifdef __cplusplus |
||||||
|
} |
||||||
|
#endif |
||||||
|
|
||||||
|
#endif /* __GRPC_SUPPORT_ENV_H__ */ |
@ -0,0 +1,61 @@ |
|||||||
|
/*
|
||||||
|
* |
||||||
|
* Copyright 2014, Google Inc. |
||||||
|
* All rights reserved. |
||||||
|
* |
||||||
|
* Redistribution and use in source and binary forms, with or without |
||||||
|
* modification, are permitted provided that the following conditions are |
||||||
|
* met: |
||||||
|
* |
||||||
|
* * Redistributions of source code must retain the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer. |
||||||
|
* * Redistributions in binary form must reproduce the above |
||||||
|
* copyright notice, this list of conditions and the following disclaimer |
||||||
|
* in the documentation and/or other materials provided with the |
||||||
|
* distribution. |
||||||
|
* * Neither the name of Google Inc. nor the names of its |
||||||
|
* contributors may be used to endorse or promote products derived from |
||||||
|
* this software without specific prior written permission. |
||||||
|
* |
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
* |
||||||
|
*/ |
||||||
|
|
||||||
|
/* for secure_getenv. */ |
||||||
|
#ifndef _GNU_SOURCE |
||||||
|
#define _GNU_SOURCE |
||||||
|
#endif |
||||||
|
|
||||||
|
#include <grpc/support/port_platform.h> |
||||||
|
|
||||||
|
#ifdef GPR_LINUX_ENV |
||||||
|
|
||||||
|
#include "src/core/support/env.h" |
||||||
|
|
||||||
|
#include <stdlib.h> |
||||||
|
|
||||||
|
#include <grpc/support/log.h> |
||||||
|
|
||||||
|
#include "src/core/support/string.h" |
||||||
|
|
||||||
|
char *gpr_getenv(const char *name) { |
||||||
|
char *result = secure_getenv(name); |
||||||
|
return result == NULL ? result : gpr_strdup(result); |
||||||
|
} |
||||||
|
|
||||||
|
void gpr_setenv(const char *name, const char *value) { |
||||||
|
int res = setenv(name, value, 1); |
||||||
|
GPR_ASSERT(res == 0); |
||||||
|
} |
||||||
|
|
||||||
|
#endif /* GPR_LINUX_ENV */ |
@ -0,0 +1,56 @@ |
|||||||
|
/*
|
||||||
|
* |
||||||
|
* Copyright 2014, Google Inc. |
||||||
|
* All rights reserved. |
||||||
|
* |
||||||
|
* Redistribution and use in source and binary forms, with or without |
||||||
|
* modification, are permitted provided that the following conditions are |
||||||
|
* met: |
||||||
|
* |
||||||
|
* * Redistributions of source code must retain the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer. |
||||||
|
* * Redistributions in binary form must reproduce the above |
||||||
|
* copyright notice, this list of conditions and the following disclaimer |
||||||
|
* in the documentation and/or other materials provided with the |
||||||
|
* distribution. |
||||||
|
* * Neither the name of Google Inc. nor the names of its |
||||||
|
* contributors may be used to endorse or promote products derived from |
||||||
|
* this software without specific prior written permission. |
||||||
|
* |
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
* |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <grpc/support/port_platform.h> |
||||||
|
|
||||||
|
#ifdef GPR_POSIX_ENV |
||||||
|
|
||||||
|
#include "src/core/support/env.h" |
||||||
|
|
||||||
|
#include <stdlib.h> |
||||||
|
|
||||||
|
#include <grpc/support/log.h> |
||||||
|
|
||||||
|
#include "src/core/support/string.h" |
||||||
|
|
||||||
|
char *gpr_getenv(const char *name) { |
||||||
|
char *result = getenv(name); |
||||||
|
return result == NULL ? result : gpr_strdup(result); |
||||||
|
} |
||||||
|
|
||||||
|
void gpr_setenv(const char *name, const char *value) { |
||||||
|
int res = setenv(name, value, 1); |
||||||
|
GPR_ASSERT(res == 0); |
||||||
|
} |
||||||
|
|
||||||
|
#endif /* GPR_POSIX_ENV */ |
@ -0,0 +1,61 @@ |
|||||||
|
/*
|
||||||
|
* |
||||||
|
* Copyright 2014, Google Inc. |
||||||
|
* All rights reserved. |
||||||
|
* |
||||||
|
* Redistribution and use in source and binary forms, with or without |
||||||
|
* modification, are permitted provided that the following conditions are |
||||||
|
* met: |
||||||
|
* |
||||||
|
* * Redistributions of source code must retain the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer. |
||||||
|
* * Redistributions in binary form must reproduce the above |
||||||
|
* copyright notice, this list of conditions and the following disclaimer |
||||||
|
* in the documentation and/or other materials provided with the |
||||||
|
* distribution. |
||||||
|
* * Neither the name of Google Inc. nor the names of its |
||||||
|
* contributors may be used to endorse or promote products derived from |
||||||
|
* this software without specific prior written permission. |
||||||
|
* |
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
* |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <grpc/support/port_platform.h> |
||||||
|
|
||||||
|
#ifdef GPR_WIN32 |
||||||
|
|
||||||
|
#include "src/core/support/env.h" |
||||||
|
|
||||||
|
#include <stdlib.h> |
||||||
|
|
||||||
|
#include <grpc/support/alloc.h> |
||||||
|
#include <grpc/support/log.h> |
||||||
|
|
||||||
|
char *gpr_getenv(const char *name) { |
||||||
|
size_t required_size; |
||||||
|
char *result = NULL; |
||||||
|
|
||||||
|
getenv_s(&required_size, NULL, 0, name); |
||||||
|
if (required_size == 0) return NULL; |
||||||
|
result = gpr_malloc(required_size); |
||||||
|
getenv_s(&required_size, result, required_size, name); |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
void gpr_setenv(const char *name, const char *value) { |
||||||
|
errno_t res = _putenv_s(name, value); |
||||||
|
GPR_ASSERT(res == 0); |
||||||
|
} |
||||||
|
|
||||||
|
#endif /* GPR_WIN32 */ |
@ -0,0 +1,89 @@ |
|||||||
|
/*
|
||||||
|
* |
||||||
|
* Copyright 2014, Google Inc. |
||||||
|
* All rights reserved. |
||||||
|
* |
||||||
|
* Redistribution and use in source and binary forms, with or without |
||||||
|
* modification, are permitted provided that the following conditions are |
||||||
|
* met: |
||||||
|
* |
||||||
|
* * Redistributions of source code must retain the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer. |
||||||
|
* * Redistributions in binary form must reproduce the above |
||||||
|
* copyright notice, this list of conditions and the following disclaimer |
||||||
|
* in the documentation and/or other materials provided with the |
||||||
|
* distribution. |
||||||
|
* * Neither the name of Google Inc. nor the names of its |
||||||
|
* contributors may be used to endorse or promote products derived from |
||||||
|
* this software without specific prior written permission. |
||||||
|
* |
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
* |
||||||
|
*/ |
||||||
|
|
||||||
|
#include "src/core/support/file.h" |
||||||
|
|
||||||
|
#include <errno.h> |
||||||
|
#include <string.h> |
||||||
|
|
||||||
|
#include <grpc/support/alloc.h> |
||||||
|
#include <grpc/support/log.h> |
||||||
|
|
||||||
|
#include "src/core/support/string.h" |
||||||
|
|
||||||
|
gpr_slice gpr_load_file(const char *filename, int *success) { |
||||||
|
unsigned char *contents = NULL; |
||||||
|
size_t contents_size = 0; |
||||||
|
unsigned char buf[4096]; |
||||||
|
char *error_msg = NULL; |
||||||
|
gpr_slice result = gpr_empty_slice(); |
||||||
|
FILE *file = fopen(filename, "rb"); |
||||||
|
|
||||||
|
if (file == NULL) { |
||||||
|
gpr_asprintf(&error_msg, "Could not open file %s (error = %s).", filename, |
||||||
|
strerror(errno)); |
||||||
|
GPR_ASSERT(error_msg != NULL); |
||||||
|
goto end; |
||||||
|
} |
||||||
|
|
||||||
|
while (1) { |
||||||
|
size_t bytes_read = fread(buf, 1, sizeof(buf), file); |
||||||
|
if (bytes_read > 0) { |
||||||
|
contents = gpr_realloc(contents, contents_size + bytes_read); |
||||||
|
memcpy(contents + contents_size, buf, bytes_read); |
||||||
|
contents_size += bytes_read; |
||||||
|
} |
||||||
|
if (bytes_read < sizeof(buf)) { |
||||||
|
if (ferror(file)) { |
||||||
|
gpr_asprintf(&error_msg, "Error %s occured while reading file %s.", |
||||||
|
strerror(errno), filename); |
||||||
|
GPR_ASSERT(error_msg != NULL); |
||||||
|
goto end; |
||||||
|
} else { |
||||||
|
GPR_ASSERT(feof(file)); |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
if (success != NULL) *success = 1; |
||||||
|
result = gpr_slice_new(contents, contents_size, gpr_free); |
||||||
|
|
||||||
|
end: |
||||||
|
if (error_msg != NULL) { |
||||||
|
gpr_log(GPR_ERROR, "%s", error_msg); |
||||||
|
gpr_free(error_msg); |
||||||
|
if (success != NULL) *success = 0; |
||||||
|
} |
||||||
|
if (file != NULL) fclose(file); |
||||||
|
return result; |
||||||
|
} |
@ -0,0 +1,61 @@ |
|||||||
|
/*
|
||||||
|
* |
||||||
|
* Copyright 2014, Google Inc. |
||||||
|
* All rights reserved. |
||||||
|
* |
||||||
|
* Redistribution and use in source and binary forms, with or without |
||||||
|
* modification, are permitted provided that the following conditions are |
||||||
|
* met: |
||||||
|
* |
||||||
|
* * Redistributions of source code must retain the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer. |
||||||
|
* * Redistributions in binary form must reproduce the above |
||||||
|
* copyright notice, this list of conditions and the following disclaimer |
||||||
|
* in the documentation and/or other materials provided with the |
||||||
|
* distribution. |
||||||
|
* * Neither the name of Google Inc. nor the names of its |
||||||
|
* contributors may be used to endorse or promote products derived from |
||||||
|
* this software without specific prior written permission. |
||||||
|
* |
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
* |
||||||
|
*/ |
||||||
|
|
||||||
|
#ifndef __GRPC_SUPPORT_FILE_H__ |
||||||
|
#define __GRPC_SUPPORT_FILE_H__ |
||||||
|
|
||||||
|
#include <stdio.h> |
||||||
|
|
||||||
|
#include <grpc/support/slice.h> |
||||||
|
|
||||||
|
#ifdef __cplusplus |
||||||
|
extern "C" { |
||||||
|
#endif |
||||||
|
|
||||||
|
/* File utility functions */ |
||||||
|
|
||||||
|
/* Loads the content of a file into a slice. The success parameter, if not NULL,
|
||||||
|
will be set to 1 in case of success and 0 in case of failure. */ |
||||||
|
gpr_slice gpr_load_file(const char *filename, int *success); |
||||||
|
|
||||||
|
/* Creates a temporary file from a prefix.
|
||||||
|
If tmp_filename is not NULL, *tmp_filename is assigned the name of the |
||||||
|
created file and it is the responsibility of the caller to gpr_free it |
||||||
|
unless an error occurs in which case it will be set to NULL. */ |
||||||
|
FILE *gpr_tmpfile(const char *prefix, char **tmp_filename); |
||||||
|
|
||||||
|
#ifdef __cplusplus |
||||||
|
} |
||||||
|
#endif |
||||||
|
|
||||||
|
#endif /* __GRPC_SUPPORT_FILE_H__ */ |
@ -0,0 +1,97 @@ |
|||||||
|
/*
|
||||||
|
* |
||||||
|
* Copyright 2014, Google Inc. |
||||||
|
* All rights reserved. |
||||||
|
* |
||||||
|
* Redistribution and use in source and binary forms, with or without |
||||||
|
* modification, are permitted provided that the following conditions are |
||||||
|
* met: |
||||||
|
* |
||||||
|
* * Redistributions of source code must retain the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer. |
||||||
|
* * Redistributions in binary form must reproduce the above |
||||||
|
* copyright notice, this list of conditions and the following disclaimer |
||||||
|
* in the documentation and/or other materials provided with the |
||||||
|
* distribution. |
||||||
|
* * Neither the name of Google Inc. nor the names of its |
||||||
|
* contributors may be used to endorse or promote products derived from |
||||||
|
* this software without specific prior written permission. |
||||||
|
* |
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
* |
||||||
|
*/ |
||||||
|
|
||||||
|
/* Posix code for gpr fdopen and mkstemp support. */ |
||||||
|
|
||||||
|
#if !defined _POSIX_C_SOURCE || _POSIX_C_SOURCE < 200112L |
||||||
|
#undef _POSIX_C_SOURCE |
||||||
|
#define _POSIX_C_SOURCE 200112L |
||||||
|
#endif |
||||||
|
|
||||||
|
/* Don't know why I have to do this for mkstemp, looks like _POSIX_C_SOURCE
|
||||||
|
should be enough... */ |
||||||
|
#ifndef _BSD_SOURCE |
||||||
|
#define _BSD_SOURCE |
||||||
|
#endif |
||||||
|
|
||||||
|
#include <grpc/support/port_platform.h> |
||||||
|
|
||||||
|
#ifdef GPR_POSIX_FILE |
||||||
|
|
||||||
|
#include "src/core/support/file.h" |
||||||
|
|
||||||
|
#include <errno.h> |
||||||
|
#include <stdlib.h> |
||||||
|
#include <string.h> |
||||||
|
#include <unistd.h> |
||||||
|
|
||||||
|
#include <grpc/support/alloc.h> |
||||||
|
#include <grpc/support/log.h> |
||||||
|
|
||||||
|
#include "src/core/support/string.h" |
||||||
|
|
||||||
|
FILE *gpr_tmpfile(const char *prefix, char **tmp_filename) { |
||||||
|
FILE *result = NULL; |
||||||
|
char *template; |
||||||
|
int fd; |
||||||
|
|
||||||
|
if (tmp_filename != NULL) *tmp_filename = NULL; |
||||||
|
|
||||||
|
gpr_asprintf(&template, "/tmp/%s_XXXXXX", prefix); |
||||||
|
GPR_ASSERT(template != NULL); |
||||||
|
|
||||||
|
fd = mkstemp(template); |
||||||
|
if (fd == -1) { |
||||||
|
gpr_log(GPR_ERROR, "mkstemp failed for template %s with error %s.", |
||||||
|
template, strerror(errno)); |
||||||
|
goto end; |
||||||
|
} |
||||||
|
result = fdopen(fd, "w+"); |
||||||
|
if (result == NULL) { |
||||||
|
gpr_log(GPR_ERROR, "Could not open file %s from fd %d (error = %s).", |
||||||
|
template, fd, strerror(errno)); |
||||||
|
unlink(template); |
||||||
|
close(fd); |
||||||
|
goto end; |
||||||
|
} |
||||||
|
|
||||||
|
end: |
||||||
|
if (result != NULL && tmp_filename != NULL) { |
||||||
|
*tmp_filename = template; |
||||||
|
} else { |
||||||
|
gpr_free(template); |
||||||
|
} |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
#endif /* GPR_POSIX_FILE */ |
@ -0,0 +1,83 @@ |
|||||||
|
/*
|
||||||
|
* |
||||||
|
* Copyright 2014, Google Inc. |
||||||
|
* All rights reserved. |
||||||
|
* |
||||||
|
* Redistribution and use in source and binary forms, with or without |
||||||
|
* modification, are permitted provided that the following conditions are |
||||||
|
* met: |
||||||
|
* |
||||||
|
* * Redistributions of source code must retain the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer. |
||||||
|
* * Redistributions in binary form must reproduce the above |
||||||
|
* copyright notice, this list of conditions and the following disclaimer |
||||||
|
* in the documentation and/or other materials provided with the |
||||||
|
* distribution. |
||||||
|
* * Neither the name of Google Inc. nor the names of its |
||||||
|
* contributors may be used to endorse or promote products derived from |
||||||
|
* this software without specific prior written permission. |
||||||
|
* |
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
* |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <grpc/support/port_platform.h> |
||||||
|
|
||||||
|
#ifdef GPR_WIN32 |
||||||
|
|
||||||
|
#include <io.h> |
||||||
|
#include <stdio.h> |
||||||
|
#include <string.h> |
||||||
|
#include <tchar.h> |
||||||
|
|
||||||
|
#include <grpc/support/alloc.h> |
||||||
|
#include <grpc/support/log.h> |
||||||
|
|
||||||
|
#include "src/core/support/file.h" |
||||||
|
#include "src/core/support/string_win32.h" |
||||||
|
|
||||||
|
FILE *gpr_tmpfile(const char *prefix, char **tmp_filename_out) { |
||||||
|
FILE *result = NULL; |
||||||
|
LPTSTR template_string = NULL; |
||||||
|
TCHAR tmp_path[MAX_PATH]; |
||||||
|
TCHAR tmp_filename[MAX_PATH]; |
||||||
|
DWORD status; |
||||||
|
UINT success; |
||||||
|
|
||||||
|
if (tmp_filename_out != NULL) *tmp_filename_out = NULL; |
||||||
|
|
||||||
|
/* Convert our prefix to TCHAR. */ |
||||||
|
template_string = gpr_char_to_tchar(prefix); |
||||||
|
GPR_ASSERT(template_string); |
||||||
|
|
||||||
|
/* Get the path to the best temporary folder available. */ |
||||||
|
status = GetTempPath(MAX_PATH, tmp_path); |
||||||
|
if (status == 0 || status > MAX_PATH) goto end; |
||||||
|
|
||||||
|
/* Generate a unique filename with our template + temporary path. */ |
||||||
|
success = GetTempFileName(tmp_path, template_string, 0, tmp_filename); |
||||||
|
if (!success) goto end; |
||||||
|
|
||||||
|
/* Open a file there. */ |
||||||
|
if (_tfopen_s(&result, tmp_filename, TEXT("wb+")) != 0) goto end; |
||||||
|
|
||||||
|
end: |
||||||
|
if (result && tmp_filename) { |
||||||
|
*tmp_filename_out = gpr_tchar_to_char(tmp_filename); |
||||||
|
} |
||||||
|
|
||||||
|
gpr_free(tmp_filename); |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
#endif /* GPR_WIN32 */ |
@ -0,0 +1,49 @@ |
|||||||
|
/*
|
||||||
|
* |
||||||
|
* Copyright 2014, Google Inc. |
||||||
|
* All rights reserved. |
||||||
|
* |
||||||
|
* Redistribution and use in source and binary forms, with or without |
||||||
|
* modification, are permitted provided that the following conditions are |
||||||
|
* met: |
||||||
|
* |
||||||
|
* * Redistributions of source code must retain the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer. |
||||||
|
* * Redistributions in binary form must reproduce the above |
||||||
|
* copyright notice, this list of conditions and the following disclaimer |
||||||
|
* in the documentation and/or other materials provided with the |
||||||
|
* distribution. |
||||||
|
* * Neither the name of Google Inc. nor the names of its |
||||||
|
* contributors may be used to endorse or promote products derived from |
||||||
|
* this software without specific prior written permission. |
||||||
|
* |
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
* |
||||||
|
*/ |
||||||
|
|
||||||
|
#ifndef __GRPC_SUPPORT_STRING_WIN32_H__ |
||||||
|
#define __GRPC_SUPPORT_STRING_WIN32_H__ |
||||||
|
|
||||||
|
#include <grpc/support/port_platform.h> |
||||||
|
|
||||||
|
#ifdef GPR_WIN32 |
||||||
|
|
||||||
|
#include <windows.h> |
||||||
|
|
||||||
|
/* These allocate new strings using gpr_malloc to convert from and to utf-8. */ |
||||||
|
LPTSTR gpr_char_to_tchar(LPCSTR input); |
||||||
|
LPSTR gpr_tchar_to_char(LPCTSTR input); |
||||||
|
|
||||||
|
#endif /* GPR_WIN32 */ |
||||||
|
|
||||||
|
#endif /* __GRPC_SUPPORT_STRING_WIN32_H__ */ |
@ -0,0 +1,91 @@ |
|||||||
|
/*
|
||||||
|
* |
||||||
|
* Copyright 2014, Google Inc. |
||||||
|
* All rights reserved. |
||||||
|
* |
||||||
|
* Redistribution and use in source and binary forms, with or without |
||||||
|
* modification, are permitted provided that the following conditions are |
||||||
|
* met: |
||||||
|
* |
||||||
|
* * Redistributions of source code must retain the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer. |
||||||
|
* * Redistributions in binary form must reproduce the above |
||||||
|
* copyright notice, this list of conditions and the following disclaimer |
||||||
|
* in the documentation and/or other materials provided with the |
||||||
|
* distribution. |
||||||
|
* * Neither the name of Google Inc. nor the names of its |
||||||
|
* contributors may be used to endorse or promote products derived from |
||||||
|
* this software without specific prior written permission. |
||||||
|
* |
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
* |
||||||
|
*/ |
||||||
|
|
||||||
|
#include "src/core/surface/byte_buffer_queue.h" |
||||||
|
#include <grpc/support/alloc.h> |
||||||
|
#include <grpc/support/useful.h> |
||||||
|
|
||||||
|
static void bba_destroy(grpc_bbq_array *array, size_t start_pos) { |
||||||
|
size_t i; |
||||||
|
for (i = start_pos; i < array->count; i++) { |
||||||
|
grpc_byte_buffer_destroy(array->data[i]); |
||||||
|
} |
||||||
|
gpr_free(array->data); |
||||||
|
} |
||||||
|
|
||||||
|
/* Append an operation to an array, expanding as needed */ |
||||||
|
static void bba_push(grpc_bbq_array *a, grpc_byte_buffer *buffer) { |
||||||
|
if (a->count == a->capacity) { |
||||||
|
a->capacity = GPR_MAX(a->capacity * 2, 8); |
||||||
|
a->data = gpr_realloc(a->data, sizeof(grpc_byte_buffer *) * a->capacity); |
||||||
|
} |
||||||
|
a->data[a->count++] = buffer; |
||||||
|
} |
||||||
|
|
||||||
|
void grpc_bbq_destroy(grpc_byte_buffer_queue *q) { |
||||||
|
bba_destroy(&q->filling, 0); |
||||||
|
bba_destroy(&q->draining, q->drain_pos); |
||||||
|
} |
||||||
|
|
||||||
|
int grpc_bbq_empty(grpc_byte_buffer_queue *q) { |
||||||
|
return (q->drain_pos == q->draining.count && q->filling.count == 0); |
||||||
|
} |
||||||
|
|
||||||
|
void grpc_bbq_push(grpc_byte_buffer_queue *q, grpc_byte_buffer *buffer) { |
||||||
|
bba_push(&q->filling, buffer); |
||||||
|
} |
||||||
|
|
||||||
|
void grpc_bbq_flush(grpc_byte_buffer_queue *q) { |
||||||
|
grpc_byte_buffer *bb; |
||||||
|
while ((bb = grpc_bbq_pop(q))) { |
||||||
|
grpc_byte_buffer_destroy(bb); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
grpc_byte_buffer *grpc_bbq_pop(grpc_byte_buffer_queue *q) { |
||||||
|
grpc_bbq_array temp_array; |
||||||
|
|
||||||
|
if (q->drain_pos == q->draining.count) { |
||||||
|
if (q->filling.count == 0) { |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
q->draining.count = 0; |
||||||
|
q->drain_pos = 0; |
||||||
|
/* swap arrays */ |
||||||
|
temp_array = q->filling; |
||||||
|
q->filling = q->draining; |
||||||
|
q->draining = temp_array; |
||||||
|
} |
||||||
|
|
||||||
|
return q->draining.data[q->drain_pos++]; |
||||||
|
} |
@ -0,0 +1,60 @@ |
|||||||
|
/*
|
||||||
|
* |
||||||
|
* Copyright 2014, Google Inc. |
||||||
|
* All rights reserved. |
||||||
|
* |
||||||
|
* Redistribution and use in source and binary forms, with or without |
||||||
|
* modification, are permitted provided that the following conditions are |
||||||
|
* met: |
||||||
|
* |
||||||
|
* * Redistributions of source code must retain the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer. |
||||||
|
* * Redistributions in binary form must reproduce the above |
||||||
|
* copyright notice, this list of conditions and the following disclaimer |
||||||
|
* in the documentation and/or other materials provided with the |
||||||
|
* distribution. |
||||||
|
* * Neither the name of Google Inc. nor the names of its |
||||||
|
* contributors may be used to endorse or promote products derived from |
||||||
|
* this software without specific prior written permission. |
||||||
|
* |
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
* |
||||||
|
*/ |
||||||
|
|
||||||
|
#ifndef __GRPC_INTERNAL_SURFACE_BYTE_BUFFER_QUEUE_H__ |
||||||
|
#define __GRPC_INTERNAL_SURFACE_BYTE_BUFFER_QUEUE_H__ |
||||||
|
|
||||||
|
#include <grpc/byte_buffer.h> |
||||||
|
|
||||||
|
/* TODO(ctiller): inline an element or two into this struct to avoid per-call
|
||||||
|
allocations */ |
||||||
|
typedef struct { |
||||||
|
grpc_byte_buffer **data; |
||||||
|
size_t count; |
||||||
|
size_t capacity; |
||||||
|
} grpc_bbq_array; |
||||||
|
|
||||||
|
/* should be initialized by zeroing memory */ |
||||||
|
typedef struct { |
||||||
|
size_t drain_pos; |
||||||
|
grpc_bbq_array filling; |
||||||
|
grpc_bbq_array draining; |
||||||
|
} grpc_byte_buffer_queue; |
||||||
|
|
||||||
|
void grpc_bbq_destroy(grpc_byte_buffer_queue *q); |
||||||
|
grpc_byte_buffer *grpc_bbq_pop(grpc_byte_buffer_queue *q); |
||||||
|
void grpc_bbq_flush(grpc_byte_buffer_queue *q); |
||||||
|
int grpc_bbq_empty(grpc_byte_buffer_queue *q); |
||||||
|
void grpc_bbq_push(grpc_byte_buffer_queue *q, grpc_byte_buffer *bb); |
||||||
|
|
||||||
|
#endif /* __GRPC_INTERNAL_SURFACE_BYTE_BUFFER_QUEUE_H__ */ |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,13 @@ |
|||||||
|
#include <grpc/grpc.h> |
||||||
|
#include <grpc/support/alloc.h> |
||||||
|
|
||||||
|
#include <string.h> |
||||||
|
|
||||||
|
void grpc_call_details_init(grpc_call_details *cd) { |
||||||
|
memset(cd, 0, sizeof(*cd)); |
||||||
|
} |
||||||
|
|
||||||
|
void grpc_call_details_destroy(grpc_call_details *cd) { |
||||||
|
gpr_free(cd->method); |
||||||
|
gpr_free(cd->host); |
||||||
|
} |
@ -0,0 +1,12 @@ |
|||||||
|
#include <grpc/grpc.h> |
||||||
|
#include <grpc/support/alloc.h> |
||||||
|
|
||||||
|
#include <string.h> |
||||||
|
|
||||||
|
void grpc_metadata_array_init(grpc_metadata_array *array) { |
||||||
|
memset(array, 0, sizeof(*array)); |
||||||
|
} |
||||||
|
|
||||||
|
void grpc_metadata_array_destroy(grpc_metadata_array *array) { |
||||||
|
gpr_free(array->metadata); |
||||||
|
} |
@ -0,0 +1,2 @@ |
|||||||
|
test-results |
||||||
|
bin |
@ -0,0 +1,282 @@ |
|||||||
|
// Generated by ProtoGen, Version=2.4.1.521, Culture=neutral, PublicKeyToken=17b3b1f090c3ea48. DO NOT EDIT! |
||||||
|
#pragma warning disable 1591, 0612, 3021 |
||||||
|
#region Designer generated code |
||||||
|
|
||||||
|
using pb = global::Google.ProtocolBuffers; |
||||||
|
using pbc = global::Google.ProtocolBuffers.Collections; |
||||||
|
using pbd = global::Google.ProtocolBuffers.Descriptors; |
||||||
|
using scg = global::System.Collections.Generic; |
||||||
|
namespace grpc.testing { |
||||||
|
|
||||||
|
namespace Proto { |
||||||
|
|
||||||
|
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()] |
||||||
|
public static partial class Empty { |
||||||
|
|
||||||
|
#region Extension registration |
||||||
|
public static void RegisterAllExtensions(pb::ExtensionRegistry registry) { |
||||||
|
} |
||||||
|
#endregion |
||||||
|
#region Static variables |
||||||
|
internal static pbd::MessageDescriptor internal__static_grpc_testing_Empty__Descriptor; |
||||||
|
internal static pb::FieldAccess.FieldAccessorTable<global::grpc.testing.Empty, global::grpc.testing.Empty.Builder> internal__static_grpc_testing_Empty__FieldAccessorTable; |
||||||
|
#endregion |
||||||
|
#region Descriptor |
||||||
|
public static pbd::FileDescriptor Descriptor { |
||||||
|
get { return descriptor; } |
||||||
|
} |
||||||
|
private static pbd::FileDescriptor descriptor; |
||||||
|
|
||||||
|
static Empty() { |
||||||
|
byte[] descriptorData = global::System.Convert.FromBase64String( |
||||||
|
string.Concat( |
||||||
|
"CgtlbXB0eS5wcm90bxIMZ3JwYy50ZXN0aW5nIgcKBUVtcHR5")); |
||||||
|
pbd::FileDescriptor.InternalDescriptorAssigner assigner = delegate(pbd::FileDescriptor root) { |
||||||
|
descriptor = root; |
||||||
|
internal__static_grpc_testing_Empty__Descriptor = Descriptor.MessageTypes[0]; |
||||||
|
internal__static_grpc_testing_Empty__FieldAccessorTable = |
||||||
|
new pb::FieldAccess.FieldAccessorTable<global::grpc.testing.Empty, global::grpc.testing.Empty.Builder>(internal__static_grpc_testing_Empty__Descriptor, |
||||||
|
new string[] { }); |
||||||
|
return null; |
||||||
|
}; |
||||||
|
pbd::FileDescriptor.InternalBuildGeneratedFileFrom(descriptorData, |
||||||
|
new pbd::FileDescriptor[] { |
||||||
|
}, assigner); |
||||||
|
} |
||||||
|
#endregion |
||||||
|
|
||||||
|
} |
||||||
|
} |
||||||
|
#region Messages |
||||||
|
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()] |
||||||
|
public sealed partial class Empty : pb::GeneratedMessage<Empty, Empty.Builder> { |
||||||
|
private Empty() { } |
||||||
|
private static readonly Empty defaultInstance = new Empty().MakeReadOnly(); |
||||||
|
private static readonly string[] _emptyFieldNames = new string[] { }; |
||||||
|
private static readonly uint[] _emptyFieldTags = new uint[] { }; |
||||||
|
public static Empty DefaultInstance { |
||||||
|
get { return defaultInstance; } |
||||||
|
} |
||||||
|
|
||||||
|
public override Empty DefaultInstanceForType { |
||||||
|
get { return DefaultInstance; } |
||||||
|
} |
||||||
|
|
||||||
|
protected override Empty ThisMessage { |
||||||
|
get { return this; } |
||||||
|
} |
||||||
|
|
||||||
|
public static pbd::MessageDescriptor Descriptor { |
||||||
|
get { return global::grpc.testing.Proto.Empty.internal__static_grpc_testing_Empty__Descriptor; } |
||||||
|
} |
||||||
|
|
||||||
|
protected override pb::FieldAccess.FieldAccessorTable<Empty, Empty.Builder> InternalFieldAccessors { |
||||||
|
get { return global::grpc.testing.Proto.Empty.internal__static_grpc_testing_Empty__FieldAccessorTable; } |
||||||
|
} |
||||||
|
|
||||||
|
public override bool IsInitialized { |
||||||
|
get { |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public override void WriteTo(pb::ICodedOutputStream output) { |
||||||
|
int size = SerializedSize; |
||||||
|
string[] field_names = _emptyFieldNames; |
||||||
|
UnknownFields.WriteTo(output); |
||||||
|
} |
||||||
|
|
||||||
|
private int memoizedSerializedSize = -1; |
||||||
|
public override int SerializedSize { |
||||||
|
get { |
||||||
|
int size = memoizedSerializedSize; |
||||||
|
if (size != -1) return size; |
||||||
|
|
||||||
|
size = 0; |
||||||
|
size += UnknownFields.SerializedSize; |
||||||
|
memoizedSerializedSize = size; |
||||||
|
return size; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public static Empty ParseFrom(pb::ByteString data) { |
||||||
|
return ((Builder) CreateBuilder().MergeFrom(data)).BuildParsed(); |
||||||
|
} |
||||||
|
public static Empty ParseFrom(pb::ByteString data, pb::ExtensionRegistry extensionRegistry) { |
||||||
|
return ((Builder) CreateBuilder().MergeFrom(data, extensionRegistry)).BuildParsed(); |
||||||
|
} |
||||||
|
public static Empty ParseFrom(byte[] data) { |
||||||
|
return ((Builder) CreateBuilder().MergeFrom(data)).BuildParsed(); |
||||||
|
} |
||||||
|
public static Empty ParseFrom(byte[] data, pb::ExtensionRegistry extensionRegistry) { |
||||||
|
return ((Builder) CreateBuilder().MergeFrom(data, extensionRegistry)).BuildParsed(); |
||||||
|
} |
||||||
|
public static Empty ParseFrom(global::System.IO.Stream input) { |
||||||
|
return ((Builder) CreateBuilder().MergeFrom(input)).BuildParsed(); |
||||||
|
} |
||||||
|
public static Empty ParseFrom(global::System.IO.Stream input, pb::ExtensionRegistry extensionRegistry) { |
||||||
|
return ((Builder) CreateBuilder().MergeFrom(input, extensionRegistry)).BuildParsed(); |
||||||
|
} |
||||||
|
public static Empty ParseDelimitedFrom(global::System.IO.Stream input) { |
||||||
|
return CreateBuilder().MergeDelimitedFrom(input).BuildParsed(); |
||||||
|
} |
||||||
|
public static Empty ParseDelimitedFrom(global::System.IO.Stream input, pb::ExtensionRegistry extensionRegistry) { |
||||||
|
return CreateBuilder().MergeDelimitedFrom(input, extensionRegistry).BuildParsed(); |
||||||
|
} |
||||||
|
public static Empty ParseFrom(pb::ICodedInputStream input) { |
||||||
|
return ((Builder) CreateBuilder().MergeFrom(input)).BuildParsed(); |
||||||
|
} |
||||||
|
public static Empty ParseFrom(pb::ICodedInputStream input, pb::ExtensionRegistry extensionRegistry) { |
||||||
|
return ((Builder) CreateBuilder().MergeFrom(input, extensionRegistry)).BuildParsed(); |
||||||
|
} |
||||||
|
private Empty MakeReadOnly() { |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public static Builder CreateBuilder() { return new Builder(); } |
||||||
|
public override Builder ToBuilder() { return CreateBuilder(this); } |
||||||
|
public override Builder CreateBuilderForType() { return new Builder(); } |
||||||
|
public static Builder CreateBuilder(Empty prototype) { |
||||||
|
return new Builder(prototype); |
||||||
|
} |
||||||
|
|
||||||
|
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()] |
||||||
|
public sealed partial class Builder : pb::GeneratedBuilder<Empty, Builder> { |
||||||
|
protected override Builder ThisBuilder { |
||||||
|
get { return this; } |
||||||
|
} |
||||||
|
public Builder() { |
||||||
|
result = DefaultInstance; |
||||||
|
resultIsReadOnly = true; |
||||||
|
} |
||||||
|
internal Builder(Empty cloneFrom) { |
||||||
|
result = cloneFrom; |
||||||
|
resultIsReadOnly = true; |
||||||
|
} |
||||||
|
|
||||||
|
private bool resultIsReadOnly; |
||||||
|
private Empty result; |
||||||
|
|
||||||
|
private Empty PrepareBuilder() { |
||||||
|
if (resultIsReadOnly) { |
||||||
|
Empty original = result; |
||||||
|
result = new Empty(); |
||||||
|
resultIsReadOnly = false; |
||||||
|
MergeFrom(original); |
||||||
|
} |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
public override bool IsInitialized { |
||||||
|
get { return result.IsInitialized; } |
||||||
|
} |
||||||
|
|
||||||
|
protected override Empty MessageBeingBuilt { |
||||||
|
get { return PrepareBuilder(); } |
||||||
|
} |
||||||
|
|
||||||
|
public override Builder Clear() { |
||||||
|
result = DefaultInstance; |
||||||
|
resultIsReadOnly = true; |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public override Builder Clone() { |
||||||
|
if (resultIsReadOnly) { |
||||||
|
return new Builder(result); |
||||||
|
} else { |
||||||
|
return new Builder().MergeFrom(result); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public override pbd::MessageDescriptor DescriptorForType { |
||||||
|
get { return global::grpc.testing.Empty.Descriptor; } |
||||||
|
} |
||||||
|
|
||||||
|
public override Empty DefaultInstanceForType { |
||||||
|
get { return global::grpc.testing.Empty.DefaultInstance; } |
||||||
|
} |
||||||
|
|
||||||
|
public override Empty BuildPartial() { |
||||||
|
if (resultIsReadOnly) { |
||||||
|
return result; |
||||||
|
} |
||||||
|
resultIsReadOnly = true; |
||||||
|
return result.MakeReadOnly(); |
||||||
|
} |
||||||
|
|
||||||
|
public override Builder MergeFrom(pb::IMessage other) { |
||||||
|
if (other is Empty) { |
||||||
|
return MergeFrom((Empty) other); |
||||||
|
} else { |
||||||
|
base.MergeFrom(other); |
||||||
|
return this; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public override Builder MergeFrom(Empty other) { |
||||||
|
if (other == global::grpc.testing.Empty.DefaultInstance) return this; |
||||||
|
PrepareBuilder(); |
||||||
|
this.MergeUnknownFields(other.UnknownFields); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public override Builder MergeFrom(pb::ICodedInputStream input) { |
||||||
|
return MergeFrom(input, pb::ExtensionRegistry.Empty); |
||||||
|
} |
||||||
|
|
||||||
|
public override Builder MergeFrom(pb::ICodedInputStream input, pb::ExtensionRegistry extensionRegistry) { |
||||||
|
PrepareBuilder(); |
||||||
|
pb::UnknownFieldSet.Builder unknownFields = null; |
||||||
|
uint tag; |
||||||
|
string field_name; |
||||||
|
while (input.ReadTag(out tag, out field_name)) { |
||||||
|
if(tag == 0 && field_name != null) { |
||||||
|
int field_ordinal = global::System.Array.BinarySearch(_emptyFieldNames, field_name, global::System.StringComparer.Ordinal); |
||||||
|
if(field_ordinal >= 0) |
||||||
|
tag = _emptyFieldTags[field_ordinal]; |
||||||
|
else { |
||||||
|
if (unknownFields == null) { |
||||||
|
unknownFields = pb::UnknownFieldSet.CreateBuilder(this.UnknownFields); |
||||||
|
} |
||||||
|
ParseUnknownField(input, unknownFields, extensionRegistry, tag, field_name); |
||||||
|
continue; |
||||||
|
} |
||||||
|
} |
||||||
|
switch (tag) { |
||||||
|
case 0: { |
||||||
|
throw pb::InvalidProtocolBufferException.InvalidTag(); |
||||||
|
} |
||||||
|
default: { |
||||||
|
if (pb::WireFormat.IsEndGroupTag(tag)) { |
||||||
|
if (unknownFields != null) { |
||||||
|
this.UnknownFields = unknownFields.Build(); |
||||||
|
} |
||||||
|
return this; |
||||||
|
} |
||||||
|
if (unknownFields == null) { |
||||||
|
unknownFields = pb::UnknownFieldSet.CreateBuilder(this.UnknownFields); |
||||||
|
} |
||||||
|
ParseUnknownField(input, unknownFields, extensionRegistry, tag, field_name); |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (unknownFields != null) { |
||||||
|
this.UnknownFields = unknownFields.Build(); |
||||||
|
} |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
static Empty() { |
||||||
|
object.ReferenceEquals(global::grpc.testing.Proto.Empty.Descriptor, null); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#endregion |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
#endregion Designer generated code |
@ -0,0 +1,74 @@ |
|||||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||||
|
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
||||||
|
<PropertyGroup> |
||||||
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> |
||||||
|
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> |
||||||
|
<ProductVersion>10.0.0</ProductVersion> |
||||||
|
<SchemaVersion>2.0</SchemaVersion> |
||||||
|
<ProjectGuid>{7DC1433E-3225-42C7-B7EA-546D56E27A4B}</ProjectGuid> |
||||||
|
<OutputType>Library</OutputType> |
||||||
|
<RootNamespace>GrpcApi</RootNamespace> |
||||||
|
<AssemblyName>GrpcApi</AssemblyName> |
||||||
|
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion> |
||||||
|
</PropertyGroup> |
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> |
||||||
|
<DebugSymbols>true</DebugSymbols> |
||||||
|
<DebugType>full</DebugType> |
||||||
|
<Optimize>false</Optimize> |
||||||
|
<OutputPath>bin\Debug</OutputPath> |
||||||
|
<DefineConstants>DEBUG;</DefineConstants> |
||||||
|
<ErrorReport>prompt</ErrorReport> |
||||||
|
<WarningLevel>4</WarningLevel> |
||||||
|
<ConsolePause>false</ConsolePause> |
||||||
|
</PropertyGroup> |
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> |
||||||
|
<DebugType>full</DebugType> |
||||||
|
<Optimize>true</Optimize> |
||||||
|
<OutputPath>bin\Release</OutputPath> |
||||||
|
<ErrorReport>prompt</ErrorReport> |
||||||
|
<WarningLevel>4</WarningLevel> |
||||||
|
<ConsolePause>false</ConsolePause> |
||||||
|
</PropertyGroup> |
||||||
|
<ItemGroup> |
||||||
|
<Reference Include="System" /> |
||||||
|
<Reference Include="System.Reactive.Linq, Version=2.2.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"> |
||||||
|
<Private>False</Private> |
||||||
|
</Reference> |
||||||
|
<Reference Include="System.Data.Linq" /> |
||||||
|
<Reference Include="System.Reactive.Interfaces, Version=2.2.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"> |
||||||
|
<Private>False</Private> |
||||||
|
</Reference> |
||||||
|
<Reference Include="System.Reactive.Core, Version=2.2.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"> |
||||||
|
<Private>False</Private> |
||||||
|
</Reference> |
||||||
|
<Reference Include="Google.ProtocolBuffers"> |
||||||
|
<HintPath>..\lib\Google.ProtocolBuffers.dll</HintPath> |
||||||
|
</Reference> |
||||||
|
</ItemGroup> |
||||||
|
<ItemGroup> |
||||||
|
<Compile Include="Properties\AssemblyInfo.cs" /> |
||||||
|
<Compile Include="Math.cs" /> |
||||||
|
<Compile Include="MathGrpc.cs" /> |
||||||
|
<Compile Include="MathServiceImpl.cs" /> |
||||||
|
<Compile Include="Empty.cs" /> |
||||||
|
<Compile Include="Messages.cs" /> |
||||||
|
<Compile Include="TestServiceGrpc.cs" /> |
||||||
|
<Compile Include="MathExamples.cs" /> |
||||||
|
</ItemGroup> |
||||||
|
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> |
||||||
|
<ItemGroup> |
||||||
|
<ProjectReference Include="..\GrpcCore\GrpcCore.csproj"> |
||||||
|
<Project>{CCC4440E-49F7-4790-B0AF-FEABB0837AE7}</Project> |
||||||
|
<Name>GrpcCore</Name> |
||||||
|
</ProjectReference> |
||||||
|
</ItemGroup> |
||||||
|
<ItemGroup> |
||||||
|
<None Include="proto\math.proto" /> |
||||||
|
<None Include="proto\empty.proto" /> |
||||||
|
<None Include="proto\messages.proto" /> |
||||||
|
<None Include="proto\test.proto" /> |
||||||
|
</ItemGroup> |
||||||
|
<ItemGroup> |
||||||
|
<Folder Include="proto\" /> |
||||||
|
</ItemGroup> |
||||||
|
</Project> |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,98 @@ |
|||||||
|
using System; |
||||||
|
using System.Threading.Tasks; |
||||||
|
using System.Collections.Generic; |
||||||
|
using System.Reactive.Linq; |
||||||
|
using Google.GRPC.Core.Utils; |
||||||
|
|
||||||
|
namespace math |
||||||
|
{ |
||||||
|
public static class MathExamples |
||||||
|
{ |
||||||
|
public static void DivExample(MathGrpc.IMathServiceClient stub) |
||||||
|
{ |
||||||
|
DivReply result = stub.Div(new DivArgs.Builder { Dividend = 10, Divisor = 3 }.Build()); |
||||||
|
Console.WriteLine("Div Result: " + result); |
||||||
|
} |
||||||
|
|
||||||
|
public static void DivAsyncExample(MathGrpc.IMathServiceClient stub) |
||||||
|
{ |
||||||
|
Task<DivReply> call = stub.DivAsync(new DivArgs.Builder { Dividend = 4, Divisor = 5 }.Build()); |
||||||
|
DivReply result = call.Result; |
||||||
|
Console.WriteLine(result); |
||||||
|
} |
||||||
|
|
||||||
|
public static void DivAsyncWithCancellationExample(MathGrpc.IMathServiceClient stub) |
||||||
|
{ |
||||||
|
Task<DivReply> call = stub.DivAsync(new DivArgs.Builder { Dividend = 4, Divisor = 5 }.Build()); |
||||||
|
DivReply result = call.Result; |
||||||
|
Console.WriteLine(result); |
||||||
|
} |
||||||
|
|
||||||
|
public static void FibExample(MathGrpc.IMathServiceClient stub) |
||||||
|
{ |
||||||
|
var recorder = new RecordingObserver<Num>(); |
||||||
|
stub.Fib(new FibArgs.Builder { Limit = 5 }.Build(), recorder); |
||||||
|
|
||||||
|
List<Num> numbers = recorder.ToList().Result; |
||||||
|
Console.WriteLine("Fib Result: " + string.Join("|", recorder.ToList().Result)); |
||||||
|
} |
||||||
|
|
||||||
|
public static void SumExample(MathGrpc.IMathServiceClient stub) |
||||||
|
{ |
||||||
|
List<Num> numbers = new List<Num>{new Num.Builder { Num_ = 1 }.Build(), |
||||||
|
new Num.Builder { Num_ = 2 }.Build(), |
||||||
|
new Num.Builder { Num_ = 3 }.Build()}; |
||||||
|
|
||||||
|
var res = stub.Sum(); |
||||||
|
foreach (var num in numbers) { |
||||||
|
res.Inputs.OnNext(num); |
||||||
|
} |
||||||
|
res.Inputs.OnCompleted(); |
||||||
|
|
||||||
|
Console.WriteLine("Sum Result: " + res.Task.Result); |
||||||
|
} |
||||||
|
|
||||||
|
public static void DivManyExample(MathGrpc.IMathServiceClient stub) |
||||||
|
{ |
||||||
|
List<DivArgs> divArgsList = new List<DivArgs>{ |
||||||
|
new DivArgs.Builder { Dividend = 10, Divisor = 3 }.Build(), |
||||||
|
new DivArgs.Builder { Dividend = 100, Divisor = 21 }.Build(), |
||||||
|
new DivArgs.Builder { Dividend = 7, Divisor = 2 }.Build() |
||||||
|
}; |
||||||
|
|
||||||
|
var recorder = new RecordingObserver<DivReply>(); |
||||||
|
|
||||||
|
var inputs = stub.DivMany(recorder); |
||||||
|
foreach (var input in divArgsList) |
||||||
|
{ |
||||||
|
inputs.OnNext(input); |
||||||
|
} |
||||||
|
inputs.OnCompleted(); |
||||||
|
|
||||||
|
Console.WriteLine("DivMany Result: " + string.Join("|", recorder.ToList().Result)); |
||||||
|
} |
||||||
|
|
||||||
|
public static void DependendRequestsExample(MathGrpc.IMathServiceClient stub) |
||||||
|
{ |
||||||
|
var numberList = new List<Num> |
||||||
|
{ new Num.Builder{ Num_ = 1 }.Build(), |
||||||
|
new Num.Builder{ Num_ = 2 }.Build(), new Num.Builder{ Num_ = 3 }.Build() |
||||||
|
}; |
||||||
|
|
||||||
|
numberList.ToObservable(); |
||||||
|
|
||||||
|
//IObserver<Num> numbers; |
||||||
|
//Task<Num> call = stub.Sum(out numbers); |
||||||
|
//foreach (var num in numberList) |
||||||
|
//{ |
||||||
|
// numbers.OnNext(num); |
||||||
|
//} |
||||||
|
//numbers.OnCompleted(); |
||||||
|
|
||||||
|
//Num sum = call.Result; |
||||||
|
|
||||||
|
//DivReply result = stub.Div(new DivArgs.Builder { Dividend = sum.Num_, Divisor = numberList.Count }.Build()); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
@ -0,0 +1,124 @@ |
|||||||
|
using System; |
||||||
|
using System.Threading; |
||||||
|
using System.Threading.Tasks; |
||||||
|
using System.Collections.Generic; |
||||||
|
using System.Reactive.Linq; |
||||||
|
using Google.GRPC.Core; |
||||||
|
|
||||||
|
namespace math |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// Math service definitions (this is handwritten version of code that will normally be generated). |
||||||
|
/// </summary> |
||||||
|
public class MathGrpc |
||||||
|
{ |
||||||
|
readonly static Marshaller<DivArgs> divArgsMarshaller = Marshallers.Create((arg) => arg.ToByteArray(), DivArgs.ParseFrom); |
||||||
|
readonly static Marshaller<DivReply> divReplyMarshaller = Marshallers.Create((arg) => arg.ToByteArray(), DivReply.ParseFrom); |
||||||
|
readonly static Marshaller<Num> numMarshaller = Marshallers.Create((arg) => arg.ToByteArray(), Num.ParseFrom); |
||||||
|
readonly static Marshaller<FibArgs> fibArgsMarshaller = Marshallers.Create((arg) => arg.ToByteArray(), FibArgs.ParseFrom); |
||||||
|
|
||||||
|
readonly static Method<DivArgs, DivReply> divMethod = new Method<DivArgs, DivReply>( |
||||||
|
MethodType.Unary, |
||||||
|
"/math.Math/Div", |
||||||
|
divArgsMarshaller, |
||||||
|
divReplyMarshaller |
||||||
|
); |
||||||
|
readonly static Method<FibArgs, Num> fibMethod = new Method<FibArgs, Num>( |
||||||
|
MethodType.ServerStreaming, |
||||||
|
"/math.Math/Fib", |
||||||
|
fibArgsMarshaller, |
||||||
|
numMarshaller |
||||||
|
); |
||||||
|
readonly static Method<Num, Num> sumMethod = new Method<Num, Num>( |
||||||
|
MethodType.ClientStreaming, |
||||||
|
"/math.Math/Sum", |
||||||
|
numMarshaller, |
||||||
|
numMarshaller |
||||||
|
); |
||||||
|
readonly static Method<DivArgs, DivReply> divManyMethod = new Method<DivArgs, DivReply>( |
||||||
|
MethodType.DuplexStreaming, |
||||||
|
"/math.Math/DivMany", |
||||||
|
divArgsMarshaller, |
||||||
|
divReplyMarshaller |
||||||
|
); |
||||||
|
|
||||||
|
public interface IMathServiceClient |
||||||
|
{ |
||||||
|
DivReply Div(DivArgs request, CancellationToken token = default(CancellationToken)); |
||||||
|
|
||||||
|
Task<DivReply> DivAsync(DivArgs request, CancellationToken token = default(CancellationToken)); |
||||||
|
|
||||||
|
Task Fib(FibArgs request, IObserver<Num> responseObserver, CancellationToken token = default(CancellationToken)); |
||||||
|
|
||||||
|
ClientStreamingAsyncResult<Num, Num> Sum(CancellationToken token = default(CancellationToken)); |
||||||
|
|
||||||
|
IObserver<DivArgs> DivMany(IObserver<DivReply> responseObserver, CancellationToken token = default(CancellationToken)); |
||||||
|
} |
||||||
|
|
||||||
|
public class MathServiceClientStub : IMathServiceClient |
||||||
|
{ |
||||||
|
readonly Channel channel; |
||||||
|
|
||||||
|
public MathServiceClientStub(Channel channel) |
||||||
|
{ |
||||||
|
this.channel = channel; |
||||||
|
} |
||||||
|
|
||||||
|
public DivReply Div(DivArgs request, CancellationToken token = default(CancellationToken)) |
||||||
|
{ |
||||||
|
var call = new Google.GRPC.Core.Call<DivArgs, DivReply>(divMethod, channel); |
||||||
|
return Calls.BlockingUnaryCall(call, request, token); |
||||||
|
} |
||||||
|
|
||||||
|
public Task<DivReply> DivAsync(DivArgs request, CancellationToken token = default(CancellationToken)) |
||||||
|
{ |
||||||
|
var call = new Google.GRPC.Core.Call<DivArgs, DivReply>(divMethod, channel); |
||||||
|
return Calls.AsyncUnaryCall(call, request, token); |
||||||
|
} |
||||||
|
|
||||||
|
public Task Fib(FibArgs request, IObserver<Num> responseObserver, CancellationToken token = default(CancellationToken)) |
||||||
|
{ |
||||||
|
var call = new Google.GRPC.Core.Call<FibArgs, Num>(fibMethod, channel); |
||||||
|
return Calls.AsyncServerStreamingCall(call, request, responseObserver, token); |
||||||
|
} |
||||||
|
|
||||||
|
public ClientStreamingAsyncResult<Num, Num> Sum(CancellationToken token = default(CancellationToken)) |
||||||
|
{ |
||||||
|
var call = new Google.GRPC.Core.Call<Num, Num>(sumMethod, channel); |
||||||
|
return Calls.AsyncClientStreamingCall(call, token); |
||||||
|
} |
||||||
|
|
||||||
|
public IObserver<DivArgs> DivMany(IObserver<DivReply> responseObserver, CancellationToken token = default(CancellationToken)) |
||||||
|
{ |
||||||
|
var call = new Google.GRPC.Core.Call<DivArgs, DivReply>(divManyMethod, channel); |
||||||
|
return Calls.DuplexStreamingCall(call, responseObserver, token); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// server-side interface |
||||||
|
public interface IMathService |
||||||
|
{ |
||||||
|
void Div(DivArgs request, IObserver<DivReply> responseObserver); |
||||||
|
|
||||||
|
void Fib(FibArgs request, IObserver<Num> responseObserver); |
||||||
|
|
||||||
|
IObserver<Num> Sum(IObserver<Num> responseObserver); |
||||||
|
|
||||||
|
IObserver<DivArgs> DivMany(IObserver<DivReply> responseObserver); |
||||||
|
} |
||||||
|
|
||||||
|
public static ServerServiceDefinition BindService(IMathService serviceImpl) |
||||||
|
{ |
||||||
|
return ServerServiceDefinition.CreateBuilder("/math.Math/") |
||||||
|
.AddMethod(divMethod, serviceImpl.Div) |
||||||
|
.AddMethod(fibMethod, serviceImpl.Fib) |
||||||
|
.AddMethod(sumMethod, serviceImpl.Sum) |
||||||
|
.AddMethod(divManyMethod, serviceImpl.DivMany).Build(); |
||||||
|
} |
||||||
|
|
||||||
|
public static IMathServiceClient NewStub(Channel channel) |
||||||
|
{ |
||||||
|
return new MathServiceClientStub(channel); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,119 @@ |
|||||||
|
using System; |
||||||
|
using System.Threading; |
||||||
|
using System.Threading.Tasks; |
||||||
|
using System.Collections.Generic; |
||||||
|
using System.Reactive.Linq; |
||||||
|
using Google.GRPC.Core.Utils; |
||||||
|
|
||||||
|
namespace math |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// Implementation of MathService server |
||||||
|
/// </summary> |
||||||
|
public class MathServiceImpl : MathGrpc.IMathService |
||||||
|
{ |
||||||
|
public void Div(DivArgs request, IObserver<DivReply> responseObserver) |
||||||
|
{ |
||||||
|
var response = DivInternal(request); |
||||||
|
responseObserver.OnNext(response); |
||||||
|
responseObserver.OnCompleted(); |
||||||
|
} |
||||||
|
|
||||||
|
public void Fib(FibArgs request, IObserver<Num> responseObserver) |
||||||
|
{ |
||||||
|
if (request.Limit <= 0) |
||||||
|
{ |
||||||
|
// TODO: support cancellation.... |
||||||
|
throw new NotImplementedException("Not implemented yet"); |
||||||
|
} |
||||||
|
|
||||||
|
if (request.Limit > 0) |
||||||
|
{ |
||||||
|
foreach (var num in FibInternal(request.Limit)) |
||||||
|
{ |
||||||
|
responseObserver.OnNext(num); |
||||||
|
} |
||||||
|
responseObserver.OnCompleted(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public IObserver<Num> Sum(IObserver<Num> responseObserver) |
||||||
|
{ |
||||||
|
var recorder = new RecordingObserver<Num>(); |
||||||
|
Task.Factory.StartNew(() => { |
||||||
|
|
||||||
|
List<Num> inputs = recorder.ToList().Result; |
||||||
|
|
||||||
|
long sum = 0; |
||||||
|
foreach (Num num in inputs) |
||||||
|
{ |
||||||
|
sum += num.Num_; |
||||||
|
} |
||||||
|
|
||||||
|
responseObserver.OnNext(Num.CreateBuilder().SetNum_(sum).Build()); |
||||||
|
responseObserver.OnCompleted(); |
||||||
|
}); |
||||||
|
return recorder; |
||||||
|
} |
||||||
|
|
||||||
|
public IObserver<DivArgs> DivMany(IObserver<DivReply> responseObserver) |
||||||
|
{ |
||||||
|
return new DivObserver(responseObserver); |
||||||
|
} |
||||||
|
|
||||||
|
static DivReply DivInternal(DivArgs args) |
||||||
|
{ |
||||||
|
long quotient = args.Dividend / args.Divisor; |
||||||
|
long remainder = args.Dividend % args.Divisor; |
||||||
|
return new DivReply.Builder { Quotient = quotient, Remainder = remainder }.Build(); |
||||||
|
} |
||||||
|
|
||||||
|
static IEnumerable<Num> FibInternal(long n) |
||||||
|
{ |
||||||
|
long a = 1; |
||||||
|
yield return new Num.Builder { Num_=a }.Build(); |
||||||
|
|
||||||
|
long b = 1; |
||||||
|
for (long i = 0; i < n - 1; i++) |
||||||
|
{ |
||||||
|
long temp = a; |
||||||
|
a = b; |
||||||
|
b = temp + b; |
||||||
|
yield return new Num.Builder { Num_=a }.Build(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private class DivObserver : IObserver<DivArgs> { |
||||||
|
|
||||||
|
readonly IObserver<DivReply> responseObserver; |
||||||
|
|
||||||
|
public DivObserver(IObserver<DivReply> responseObserver) |
||||||
|
{ |
||||||
|
this.responseObserver = responseObserver; |
||||||
|
} |
||||||
|
|
||||||
|
public void OnCompleted() |
||||||
|
{ |
||||||
|
Task.Factory.StartNew(() => |
||||||
|
responseObserver.OnCompleted()); |
||||||
|
} |
||||||
|
|
||||||
|
public void OnError(Exception error) |
||||||
|
{ |
||||||
|
throw new NotImplementedException(); |
||||||
|
} |
||||||
|
|
||||||
|
public void OnNext(DivArgs value) |
||||||
|
{ |
||||||
|
// TODO: currently we need this indirection because |
||||||
|
// responseObserver waits for write to finish, this |
||||||
|
// callback is called from grpc threadpool which |
||||||
|
// currently only has one thread. |
||||||
|
// Same story for OnCompleted(). |
||||||
|
Task.Factory.StartNew(() => |
||||||
|
responseObserver.OnNext(DivInternal(value))); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,22 @@ |
|||||||
|
using System.Reflection; |
||||||
|
using System.Runtime.CompilerServices; |
||||||
|
|
||||||
|
// Information about this assembly is defined by the following attributes. |
||||||
|
// Change them to the values specific to your project. |
||||||
|
[assembly: AssemblyTitle ("GrpcApi")] |
||||||
|
[assembly: AssemblyDescription ("")] |
||||||
|
[assembly: AssemblyConfiguration ("")] |
||||||
|
[assembly: AssemblyCompany ("")] |
||||||
|
[assembly: AssemblyProduct ("")] |
||||||
|
[assembly: AssemblyCopyright ("jtattermusch")] |
||||||
|
[assembly: AssemblyTrademark ("")] |
||||||
|
[assembly: AssemblyCulture ("")] |
||||||
|
// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". |
||||||
|
// The form "{Major}.{Minor}.*" will automatically update the build and revision, |
||||||
|
// and "{Major}.{Minor}.{Build}.*" will update just the revision. |
||||||
|
[assembly: AssemblyVersion ("1.0.*")] |
||||||
|
// The following attributes are used to specify the signing key for the assembly, |
||||||
|
// if desired. See the Mono documentation for more information about signing. |
||||||
|
//[assembly: AssemblyDelaySign(false)] |
||||||
|
//[assembly: AssemblyKeyFile("")] |
||||||
|
|
@ -0,0 +1,170 @@ |
|||||||
|
using System; |
||||||
|
using System.Threading; |
||||||
|
using System.Threading.Tasks; |
||||||
|
using System.Collections.Generic; |
||||||
|
using System.Reactive.Linq; |
||||||
|
using Google.GRPC.Core; |
||||||
|
|
||||||
|
namespace grpc.testing |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// TestService (this is handwritten version of code that will normally be generated). |
||||||
|
/// </summary> |
||||||
|
public class TestServiceGrpc |
||||||
|
{ |
||||||
|
readonly static Marshaller<Empty> emptyMarshaller = Marshallers.Create((arg) => arg.ToByteArray(), Empty.ParseFrom); |
||||||
|
readonly static Marshaller<SimpleRequest> simpleRequestMarshaller = Marshallers.Create((arg) => arg.ToByteArray(), SimpleRequest.ParseFrom); |
||||||
|
readonly static Marshaller<SimpleResponse> simpleResponseMarshaller = Marshallers.Create((arg) => arg.ToByteArray(), SimpleResponse.ParseFrom); |
||||||
|
readonly static Marshaller<StreamingOutputCallRequest> streamingOutputCallRequestMarshaller = Marshallers.Create((arg) => arg.ToByteArray(), StreamingOutputCallRequest.ParseFrom); |
||||||
|
readonly static Marshaller<StreamingOutputCallResponse> streamingOutputCallResponseMarshaller = Marshallers.Create((arg) => arg.ToByteArray(), StreamingOutputCallResponse.ParseFrom); |
||||||
|
readonly static Marshaller<StreamingInputCallRequest> streamingInputCallRequestMarshaller = Marshallers.Create((arg) => arg.ToByteArray(), StreamingInputCallRequest.ParseFrom); |
||||||
|
readonly static Marshaller<StreamingInputCallResponse> streamingInputCallResponseMarshaller = Marshallers.Create((arg) => arg.ToByteArray(), StreamingInputCallResponse.ParseFrom); |
||||||
|
|
||||||
|
readonly static Method<Empty, Empty> emptyCallMethod = new Method<Empty, Empty>( |
||||||
|
MethodType.Unary, |
||||||
|
"/grpc.testing.TestService/EmptyCall", |
||||||
|
emptyMarshaller, |
||||||
|
emptyMarshaller |
||||||
|
); |
||||||
|
readonly static Method<SimpleRequest, SimpleResponse> unaryCallMethod = new Method<SimpleRequest, SimpleResponse>( |
||||||
|
MethodType.Unary, |
||||||
|
"/grpc.testing.TestService/UnaryCall", |
||||||
|
simpleRequestMarshaller, |
||||||
|
simpleResponseMarshaller |
||||||
|
); |
||||||
|
readonly static Method<StreamingOutputCallRequest, StreamingOutputCallResponse> streamingOutputCallMethod = new Method<StreamingOutputCallRequest, StreamingOutputCallResponse>( |
||||||
|
MethodType.ServerStreaming, |
||||||
|
"/grpc.testing.TestService/StreamingOutputCall", |
||||||
|
streamingOutputCallRequestMarshaller, |
||||||
|
streamingOutputCallResponseMarshaller |
||||||
|
); |
||||||
|
readonly static Method<StreamingInputCallRequest, StreamingInputCallResponse> streamingInputCallMethod = new Method<StreamingInputCallRequest, StreamingInputCallResponse>( |
||||||
|
MethodType.ClientStreaming, |
||||||
|
"/grpc.testing.TestService/StreamingInputCall", |
||||||
|
streamingInputCallRequestMarshaller, |
||||||
|
streamingInputCallResponseMarshaller |
||||||
|
); |
||||||
|
readonly static Method<StreamingOutputCallRequest, StreamingOutputCallResponse> fullDuplexCallMethod = new Method<StreamingOutputCallRequest, StreamingOutputCallResponse>( |
||||||
|
MethodType.DuplexStreaming, |
||||||
|
"/grpc.testing.TestService/FullDuplexCall", |
||||||
|
streamingOutputCallRequestMarshaller, |
||||||
|
streamingOutputCallResponseMarshaller |
||||||
|
); |
||||||
|
readonly static Method<StreamingOutputCallRequest, StreamingOutputCallResponse> halfDuplexCallMethod = new Method<StreamingOutputCallRequest, StreamingOutputCallResponse>( |
||||||
|
MethodType.DuplexStreaming, |
||||||
|
"/grpc.testing.TestService/HalfDuplexCall", |
||||||
|
streamingOutputCallRequestMarshaller, |
||||||
|
streamingOutputCallResponseMarshaller |
||||||
|
); |
||||||
|
|
||||||
|
public interface ITestServiceClient |
||||||
|
{ |
||||||
|
Empty EmptyCall(Empty request, CancellationToken token = default(CancellationToken)); |
||||||
|
|
||||||
|
Task<Empty> EmptyCallAsync(Empty request, CancellationToken token = default(CancellationToken)); |
||||||
|
|
||||||
|
SimpleResponse UnaryCall(SimpleRequest request, CancellationToken token = default(CancellationToken)); |
||||||
|
|
||||||
|
Task<SimpleResponse> UnaryCallAsync(SimpleRequest request, CancellationToken token = default(CancellationToken)); |
||||||
|
|
||||||
|
Task StreamingOutputCall(StreamingOutputCallRequest request, IObserver<StreamingOutputCallResponse> responseObserver, CancellationToken token = default(CancellationToken)); |
||||||
|
|
||||||
|
ClientStreamingAsyncResult<StreamingInputCallRequest, StreamingInputCallResponse> StreamingInputCall(CancellationToken token = default(CancellationToken)); |
||||||
|
|
||||||
|
IObserver<StreamingOutputCallRequest> FullDuplexCall(IObserver<StreamingOutputCallResponse> responseObserver, CancellationToken token = default(CancellationToken)); |
||||||
|
|
||||||
|
IObserver<StreamingOutputCallRequest> HalfDuplexCall(IObserver<StreamingOutputCallResponse> responseObserver, CancellationToken token = default(CancellationToken)); |
||||||
|
} |
||||||
|
|
||||||
|
public class TestServiceClientStub : ITestServiceClient |
||||||
|
{ |
||||||
|
readonly Channel channel; |
||||||
|
|
||||||
|
public TestServiceClientStub(Channel channel) |
||||||
|
{ |
||||||
|
this.channel = channel; |
||||||
|
} |
||||||
|
|
||||||
|
public Empty EmptyCall(Empty request, CancellationToken token = default(CancellationToken)) |
||||||
|
{ |
||||||
|
var call = new Google.GRPC.Core.Call<Empty, Empty>(emptyCallMethod, channel); |
||||||
|
return Calls.BlockingUnaryCall(call, request, token); |
||||||
|
} |
||||||
|
|
||||||
|
public Task<Empty> EmptyCallAsync(Empty request, CancellationToken token = default(CancellationToken)) |
||||||
|
{ |
||||||
|
var call = new Google.GRPC.Core.Call<Empty, Empty>(emptyCallMethod, channel); |
||||||
|
return Calls.AsyncUnaryCall(call, request, token); |
||||||
|
} |
||||||
|
|
||||||
|
public SimpleResponse UnaryCall(SimpleRequest request, CancellationToken token = default(CancellationToken)) |
||||||
|
{ |
||||||
|
var call = new Google.GRPC.Core.Call<SimpleRequest, SimpleResponse>(unaryCallMethod, channel); |
||||||
|
return Calls.BlockingUnaryCall(call, request, token); |
||||||
|
} |
||||||
|
|
||||||
|
public Task<SimpleResponse> UnaryCallAsync(SimpleRequest request, CancellationToken token = default(CancellationToken)) |
||||||
|
{ |
||||||
|
var call = new Google.GRPC.Core.Call<SimpleRequest, SimpleResponse>(unaryCallMethod, channel); |
||||||
|
return Calls.AsyncUnaryCall(call, request, token); |
||||||
|
} |
||||||
|
|
||||||
|
public Task StreamingOutputCall(StreamingOutputCallRequest request, IObserver<StreamingOutputCallResponse> responseObserver, CancellationToken token = default(CancellationToken)) { |
||||||
|
var call = new Google.GRPC.Core.Call<StreamingOutputCallRequest, StreamingOutputCallResponse>(streamingOutputCallMethod, channel); |
||||||
|
return Calls.AsyncServerStreamingCall(call, request, responseObserver, token); |
||||||
|
} |
||||||
|
|
||||||
|
public ClientStreamingAsyncResult<StreamingInputCallRequest, StreamingInputCallResponse> StreamingInputCall(CancellationToken token = default(CancellationToken)) |
||||||
|
{ |
||||||
|
var call = new Google.GRPC.Core.Call<StreamingInputCallRequest, StreamingInputCallResponse>(streamingInputCallMethod, channel); |
||||||
|
return Calls.AsyncClientStreamingCall(call, token); |
||||||
|
} |
||||||
|
|
||||||
|
public IObserver<StreamingOutputCallRequest> FullDuplexCall(IObserver<StreamingOutputCallResponse> responseObserver, CancellationToken token = default(CancellationToken)) |
||||||
|
{ |
||||||
|
var call = new Google.GRPC.Core.Call<StreamingOutputCallRequest, StreamingOutputCallResponse>(fullDuplexCallMethod, channel); |
||||||
|
return Calls.DuplexStreamingCall(call, responseObserver, token); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
public IObserver<StreamingOutputCallRequest> HalfDuplexCall(IObserver<StreamingOutputCallResponse> responseObserver, CancellationToken token = default(CancellationToken)) |
||||||
|
{ |
||||||
|
var call = new Google.GRPC.Core.Call<StreamingOutputCallRequest, StreamingOutputCallResponse>(halfDuplexCallMethod, channel); |
||||||
|
return Calls.DuplexStreamingCall(call, responseObserver, token); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// server-side interface |
||||||
|
public interface ITestService |
||||||
|
{ |
||||||
|
void EmptyCall(Empty request, IObserver<Empty> responseObserver); |
||||||
|
|
||||||
|
void UnaryCall(SimpleRequest request, IObserver<SimpleResponse> responseObserver); |
||||||
|
|
||||||
|
void StreamingOutputCall(StreamingOutputCallRequest request, IObserver<StreamingOutputCallResponse> responseObserver); |
||||||
|
|
||||||
|
IObserver<StreamingInputCallRequest> StreamingInputCall(IObserver<StreamingInputCallResponse> responseObserver); |
||||||
|
|
||||||
|
IObserver<StreamingOutputCallRequest> FullDuplexCall(IObserver<StreamingOutputCallResponse> responseObserver); |
||||||
|
|
||||||
|
IObserver<StreamingOutputCallRequest> HalfDuplexCall(IObserver<StreamingOutputCallResponse> responseObserver); |
||||||
|
} |
||||||
|
|
||||||
|
public static ServerServiceDefinition BindService(ITestService serviceImpl) |
||||||
|
{ |
||||||
|
return ServerServiceDefinition.CreateBuilder("/grpc.testing.TestService/") |
||||||
|
.AddMethod(emptyCallMethod, serviceImpl.EmptyCall) |
||||||
|
.AddMethod(unaryCallMethod, serviceImpl.UnaryCall) |
||||||
|
.AddMethod(streamingOutputCallMethod, serviceImpl.StreamingOutputCall) |
||||||
|
.AddMethod(streamingInputCallMethod, serviceImpl.StreamingInputCall) |
||||||
|
.AddMethod(fullDuplexCallMethod, serviceImpl.FullDuplexCall) |
||||||
|
.AddMethod(halfDuplexCallMethod, serviceImpl.HalfDuplexCall) |
||||||
|
.Build(); |
||||||
|
} |
||||||
|
|
||||||
|
public static ITestServiceClient NewStub(Channel channel) |
||||||
|
{ |
||||||
|
return new TestServiceClientStub(channel); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,13 @@ |
|||||||
|
syntax = "proto2"; |
||||||
|
|
||||||
|
package grpc.testing; |
||||||
|
|
||||||
|
// An empty message that you can re-use to avoid defining duplicated empty |
||||||
|
// messages in your project. A typical example is to use it as argument or the |
||||||
|
// return value of a service API. For instance: |
||||||
|
// |
||||||
|
// service Foo { |
||||||
|
// rpc Bar (grpc.testing.Empty) returns (grpc.testing.Empty) { }; |
||||||
|
// }; |
||||||
|
// |
||||||
|
message Empty {} |
@ -0,0 +1,50 @@ |
|||||||
|
syntax = "proto2"; |
||||||
|
|
||||||
|
package math; |
||||||
|
|
||||||
|
message DivArgs { |
||||||
|
optional int64 dividend = 1; |
||||||
|
optional int64 divisor = 2; |
||||||
|
} |
||||||
|
|
||||||
|
message DivReply { |
||||||
|
optional int64 quotient = 1; |
||||||
|
optional int64 remainder = 2; |
||||||
|
} |
||||||
|
|
||||||
|
message FibArgs { |
||||||
|
optional int64 limit = 1; |
||||||
|
} |
||||||
|
|
||||||
|
message Num { |
||||||
|
optional int64 num = 1; |
||||||
|
} |
||||||
|
|
||||||
|
message FibReply { |
||||||
|
optional int64 count = 1; |
||||||
|
} |
||||||
|
|
||||||
|
service Math { |
||||||
|
// Div divides args.dividend by args.divisor and returns the quotient and |
||||||
|
// remainder. |
||||||
|
rpc Div (DivArgs) returns (DivReply) { |
||||||
|
} |
||||||
|
|
||||||
|
// DivMany accepts an arbitrary number of division args from the client stream |
||||||
|
// and sends back the results in the reply stream. The stream continues until |
||||||
|
// the client closes its end; the server does the same after sending all the |
||||||
|
// replies. The stream ends immediately if either end aborts. |
||||||
|
rpc DivMany (stream DivArgs) returns (stream DivReply) { |
||||||
|
} |
||||||
|
|
||||||
|
// Fib generates numbers in the Fibonacci sequence. If args.limit > 0, Fib |
||||||
|
// generates up to limit numbers; otherwise it continues until the call is |
||||||
|
// canceled. Unlike Fib above, Fib has no final FibReply. |
||||||
|
rpc Fib (FibArgs) returns (stream Num) { |
||||||
|
} |
||||||
|
|
||||||
|
// Sum sums a stream of numbers, returning the final result once the stream |
||||||
|
// is closed. |
||||||
|
rpc Sum (stream Num) returns (Num) { |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,102 @@ |
|||||||
|
// Message definitions to be used by integration test service definitions. |
||||||
|
|
||||||
|
syntax = "proto2"; |
||||||
|
|
||||||
|
package grpc.testing; |
||||||
|
|
||||||
|
// The type of payload that should be returned. |
||||||
|
enum PayloadType { |
||||||
|
// Compressable text format. |
||||||
|
COMPRESSABLE = 0; |
||||||
|
|
||||||
|
// Uncompressable binary format. |
||||||
|
UNCOMPRESSABLE = 1; |
||||||
|
|
||||||
|
// Randomly chosen from all other formats defined in this enum. |
||||||
|
RANDOM = 2; |
||||||
|
} |
||||||
|
|
||||||
|
// A block of data, to simply increase gRPC message size. |
||||||
|
message Payload { |
||||||
|
// The type of data in body. |
||||||
|
optional PayloadType type = 1; |
||||||
|
// Primary contents of payload. |
||||||
|
optional bytes body = 2; |
||||||
|
} |
||||||
|
|
||||||
|
// Unary request. |
||||||
|
message SimpleRequest { |
||||||
|
// Desired payload type in the response from the server. |
||||||
|
// If response_type is RANDOM, server randomly chooses one from other formats. |
||||||
|
optional PayloadType response_type = 1; |
||||||
|
|
||||||
|
// Desired payload size in the response from the server. |
||||||
|
// If response_type is COMPRESSABLE, this denotes the size before compression. |
||||||
|
optional int32 response_size = 2; |
||||||
|
|
||||||
|
// Optional input payload sent along with the request. |
||||||
|
optional Payload payload = 3; |
||||||
|
|
||||||
|
// Whether SimpleResponse should include username. |
||||||
|
optional bool fill_username = 4; |
||||||
|
|
||||||
|
// Whether SimpleResponse should include OAuth scope. |
||||||
|
optional bool fill_oauth_scope = 5; |
||||||
|
} |
||||||
|
|
||||||
|
// Unary response, as configured by the request. |
||||||
|
message SimpleResponse { |
||||||
|
// Payload to increase message size. |
||||||
|
optional Payload payload = 1; |
||||||
|
// The user the request came from, for verifying authentication was |
||||||
|
// successful when the client expected it. |
||||||
|
optional string username = 2; |
||||||
|
// OAuth scope. |
||||||
|
optional string oauth_scope = 3; |
||||||
|
} |
||||||
|
|
||||||
|
// Client-streaming request. |
||||||
|
message StreamingInputCallRequest { |
||||||
|
// Optional input payload sent along with the request. |
||||||
|
optional Payload payload = 1; |
||||||
|
|
||||||
|
// Not expecting any payload from the response. |
||||||
|
} |
||||||
|
|
||||||
|
// Client-streaming response. |
||||||
|
message StreamingInputCallResponse { |
||||||
|
// Aggregated size of payloads received from the client. |
||||||
|
optional int32 aggregated_payload_size = 1; |
||||||
|
} |
||||||
|
|
||||||
|
// Configuration for a particular response. |
||||||
|
message ResponseParameters { |
||||||
|
// Desired payload sizes in responses from the server. |
||||||
|
// If response_type is COMPRESSABLE, this denotes the size before compression. |
||||||
|
optional int32 size = 1; |
||||||
|
|
||||||
|
// Desired interval between consecutive responses in the response stream in |
||||||
|
// microseconds. |
||||||
|
optional int32 interval_us = 2; |
||||||
|
} |
||||||
|
|
||||||
|
// Server-streaming request. |
||||||
|
message StreamingOutputCallRequest { |
||||||
|
// Desired payload type in the response from the server. |
||||||
|
// If response_type is RANDOM, the payload from each response in the stream |
||||||
|
// might be of different types. This is to simulate a mixed type of payload |
||||||
|
// stream. |
||||||
|
optional PayloadType response_type = 1; |
||||||
|
|
||||||
|
// Configuration for each expected response message. |
||||||
|
repeated ResponseParameters response_parameters = 2; |
||||||
|
|
||||||
|
// Optional input payload sent along with the request. |
||||||
|
optional Payload payload = 3; |
||||||
|
} |
||||||
|
|
||||||
|
// Server-streaming response, as configured by the request and parameters. |
||||||
|
message StreamingOutputCallResponse { |
||||||
|
// Payload to increase response size. |
||||||
|
optional Payload payload = 1; |
||||||
|
} |
@ -0,0 +1,42 @@ |
|||||||
|
// An integration test service that covers all the method signature permutations |
||||||
|
// of unary/streaming requests/responses. |
||||||
|
syntax = "proto2"; |
||||||
|
|
||||||
|
import "empty.proto"; |
||||||
|
import "messages.proto"; |
||||||
|
|
||||||
|
package grpc.testing; |
||||||
|
|
||||||
|
// A simple service to test the various types of RPCs and experiment with |
||||||
|
// performance with various types of payload. |
||||||
|
service TestService { |
||||||
|
// One empty request followed by one empty response. |
||||||
|
rpc EmptyCall(grpc.testing.Empty) returns (grpc.testing.Empty); |
||||||
|
|
||||||
|
// One request followed by one response. |
||||||
|
// The server returns the client payload as-is. |
||||||
|
rpc UnaryCall(SimpleRequest) returns (SimpleResponse); |
||||||
|
|
||||||
|
// One request followed by a sequence of responses (streamed download). |
||||||
|
// The server returns the payload with client desired type and sizes. |
||||||
|
rpc StreamingOutputCall(StreamingOutputCallRequest) |
||||||
|
returns (stream StreamingOutputCallResponse); |
||||||
|
|
||||||
|
// A sequence of requests followed by one response (streamed upload). |
||||||
|
// The server returns the aggregated size of client payload as the result. |
||||||
|
rpc StreamingInputCall(stream StreamingInputCallRequest) |
||||||
|
returns (StreamingInputCallResponse); |
||||||
|
|
||||||
|
// A sequence of requests with each request served by the server immediately. |
||||||
|
// As one request could lead to multiple responses, this interface |
||||||
|
// demonstrates the idea of full duplexing. |
||||||
|
rpc FullDuplexCall(stream StreamingOutputCallRequest) |
||||||
|
returns (stream StreamingOutputCallResponse); |
||||||
|
|
||||||
|
// A sequence of requests followed by a sequence of responses. |
||||||
|
// The server buffers all the client requests and then serves them in order. A |
||||||
|
// stream of responses are returned to the client when the server starts with |
||||||
|
// first request. |
||||||
|
rpc HalfDuplexCall(stream StreamingOutputCallRequest) |
||||||
|
returns (stream StreamingOutputCallResponse); |
||||||
|
} |
@ -0,0 +1,2 @@ |
|||||||
|
test-results |
||||||
|
bin |
@ -0,0 +1,56 @@ |
|||||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||||
|
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
||||||
|
<PropertyGroup> |
||||||
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> |
||||||
|
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> |
||||||
|
<ProductVersion>10.0.0</ProductVersion> |
||||||
|
<SchemaVersion>2.0</SchemaVersion> |
||||||
|
<ProjectGuid>{143B1C29-C442-4BE0-BF3F-A8F92288AC9F}</ProjectGuid> |
||||||
|
<OutputType>Library</OutputType> |
||||||
|
<RootNamespace>GrpcApiTests</RootNamespace> |
||||||
|
<AssemblyName>GrpcApiTests</AssemblyName> |
||||||
|
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion> |
||||||
|
</PropertyGroup> |
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> |
||||||
|
<DebugSymbols>true</DebugSymbols> |
||||||
|
<DebugType>full</DebugType> |
||||||
|
<Optimize>false</Optimize> |
||||||
|
<OutputPath>bin\Debug</OutputPath> |
||||||
|
<DefineConstants>DEBUG;</DefineConstants> |
||||||
|
<ErrorReport>prompt</ErrorReport> |
||||||
|
<WarningLevel>4</WarningLevel> |
||||||
|
<ConsolePause>false</ConsolePause> |
||||||
|
</PropertyGroup> |
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> |
||||||
|
<DebugType>full</DebugType> |
||||||
|
<Optimize>true</Optimize> |
||||||
|
<OutputPath>bin\Release</OutputPath> |
||||||
|
<ErrorReport>prompt</ErrorReport> |
||||||
|
<WarningLevel>4</WarningLevel> |
||||||
|
<ConsolePause>false</ConsolePause> |
||||||
|
</PropertyGroup> |
||||||
|
<ItemGroup> |
||||||
|
<Reference Include="System" /> |
||||||
|
<Reference Include="nunit.framework, Version=2.6.0.0, Culture=neutral, PublicKeyToken=96d09a1eb7f44a77"> |
||||||
|
<Private>False</Private> |
||||||
|
</Reference> |
||||||
|
<Reference Include="Google.ProtocolBuffers"> |
||||||
|
<HintPath>..\lib\Google.ProtocolBuffers.dll</HintPath> |
||||||
|
</Reference> |
||||||
|
</ItemGroup> |
||||||
|
<ItemGroup> |
||||||
|
<Compile Include="Properties\AssemblyInfo.cs" /> |
||||||
|
<Compile Include="MathClientServerTests.cs" /> |
||||||
|
</ItemGroup> |
||||||
|
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> |
||||||
|
<ItemGroup> |
||||||
|
<ProjectReference Include="..\GrpcApi\GrpcApi.csproj"> |
||||||
|
<Project>{7DC1433E-3225-42C7-B7EA-546D56E27A4B}</Project> |
||||||
|
<Name>GrpcApi</Name> |
||||||
|
</ProjectReference> |
||||||
|
<ProjectReference Include="..\GrpcCore\GrpcCore.csproj"> |
||||||
|
<Project>{CCC4440E-49F7-4790-B0AF-FEABB0837AE7}</Project> |
||||||
|
<Name>GrpcCore</Name> |
||||||
|
</ProjectReference> |
||||||
|
</ItemGroup> |
||||||
|
</Project> |
@ -0,0 +1,115 @@ |
|||||||
|
using System; |
||||||
|
using NUnit.Framework; |
||||||
|
using Google.GRPC.Core; |
||||||
|
using System.Threading; |
||||||
|
using System.Threading.Tasks; |
||||||
|
using Google.GRPC.Core.Utils; |
||||||
|
using System.Collections.Generic; |
||||||
|
|
||||||
|
namespace math.Tests |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// Math client talks to local math server. |
||||||
|
/// </summary> |
||||||
|
public class MathClientServerTest |
||||||
|
{ |
||||||
|
string serverAddr = "localhost:" + PortPicker.PickUnusedPort(); |
||||||
|
Server server; |
||||||
|
Channel channel; |
||||||
|
MathGrpc.IMathServiceClient client; |
||||||
|
|
||||||
|
[TestFixtureSetUp] |
||||||
|
public void Init() |
||||||
|
{ |
||||||
|
server = new Server(); |
||||||
|
server.AddServiceDefinition(MathGrpc.BindService(new MathServiceImpl())); |
||||||
|
server.AddPort(serverAddr); |
||||||
|
server.Start(); |
||||||
|
channel = new Channel(serverAddr); |
||||||
|
client = MathGrpc.NewStub(channel); |
||||||
|
} |
||||||
|
|
||||||
|
[Test] |
||||||
|
public void Div1() |
||||||
|
{ |
||||||
|
DivReply response = client.Div(new DivArgs.Builder { Dividend = 10, Divisor = 3 }.Build()); |
||||||
|
Assert.AreEqual(3, response.Quotient); |
||||||
|
Assert.AreEqual(1, response.Remainder); |
||||||
|
} |
||||||
|
|
||||||
|
[Test] |
||||||
|
public void Div2() |
||||||
|
{ |
||||||
|
DivReply response = client.Div(new DivArgs.Builder { Dividend = 0, Divisor = 1 }.Build()); |
||||||
|
Assert.AreEqual(0, response.Quotient); |
||||||
|
Assert.AreEqual(0, response.Remainder); |
||||||
|
} |
||||||
|
|
||||||
|
// TODO: test division by zero |
||||||
|
|
||||||
|
[Test] |
||||||
|
public void DivAsync() |
||||||
|
{ |
||||||
|
DivReply response = client.DivAsync(new DivArgs.Builder { Dividend = 10, Divisor = 3 }.Build()).Result; |
||||||
|
Assert.AreEqual(3, response.Quotient); |
||||||
|
Assert.AreEqual(1, response.Remainder); |
||||||
|
} |
||||||
|
|
||||||
|
[Test] |
||||||
|
public void Fib() |
||||||
|
{ |
||||||
|
var recorder = new RecordingObserver<Num>(); |
||||||
|
client.Fib(new FibArgs.Builder { Limit = 6 }.Build(), recorder); |
||||||
|
|
||||||
|
CollectionAssert.AreEqual(new List<long>{1, 1, 2, 3, 5, 8}, |
||||||
|
recorder.ToList().Result.ConvertAll((n) => n.Num_)); |
||||||
|
} |
||||||
|
|
||||||
|
// TODO: test Fib with limit=0 and cancellation |
||||||
|
[Test] |
||||||
|
public void Sum() |
||||||
|
{ |
||||||
|
var res = client.Sum(); |
||||||
|
foreach (var num in new long[] { 10, 20, 30 }) { |
||||||
|
res.Inputs.OnNext(Num.CreateBuilder().SetNum_(num).Build()); |
||||||
|
} |
||||||
|
res.Inputs.OnCompleted(); |
||||||
|
|
||||||
|
Assert.AreEqual(60, res.Task.Result.Num_); |
||||||
|
} |
||||||
|
|
||||||
|
[Test] |
||||||
|
public void DivMany() |
||||||
|
{ |
||||||
|
List<DivArgs> divArgsList = new List<DivArgs>{ |
||||||
|
new DivArgs.Builder { Dividend = 10, Divisor = 3 }.Build(), |
||||||
|
new DivArgs.Builder { Dividend = 100, Divisor = 21 }.Build(), |
||||||
|
new DivArgs.Builder { Dividend = 7, Divisor = 2 }.Build() |
||||||
|
}; |
||||||
|
|
||||||
|
var recorder = new RecordingObserver<DivReply>(); |
||||||
|
var requestObserver = client.DivMany(recorder); |
||||||
|
|
||||||
|
foreach (var arg in divArgsList) |
||||||
|
{ |
||||||
|
requestObserver.OnNext(arg); |
||||||
|
} |
||||||
|
requestObserver.OnCompleted(); |
||||||
|
|
||||||
|
var result = recorder.ToList().Result; |
||||||
|
|
||||||
|
CollectionAssert.AreEqual(new long[] {3, 4, 3}, result.ConvertAll((divReply) => divReply.Quotient)); |
||||||
|
CollectionAssert.AreEqual(new long[] {1, 16, 1}, result.ConvertAll((divReply) => divReply.Remainder)); |
||||||
|
} |
||||||
|
|
||||||
|
[TestFixtureTearDown] |
||||||
|
public void Cleanup() |
||||||
|
{ |
||||||
|
channel.Dispose(); |
||||||
|
|
||||||
|
server.ShutdownAsync().Wait(); |
||||||
|
GrpcEnvironment.Shutdown(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
@ -0,0 +1,22 @@ |
|||||||
|
using System.Reflection; |
||||||
|
using System.Runtime.CompilerServices; |
||||||
|
|
||||||
|
// Information about this assembly is defined by the following attributes. |
||||||
|
// Change them to the values specific to your project. |
||||||
|
[assembly: AssemblyTitle("GrpcApiTests")] |
||||||
|
[assembly: AssemblyDescription("")] |
||||||
|
[assembly: AssemblyConfiguration("")] |
||||||
|
[assembly: AssemblyCompany("")] |
||||||
|
[assembly: AssemblyProduct("")] |
||||||
|
[assembly: AssemblyCopyright("jtattermusch")] |
||||||
|
[assembly: AssemblyTrademark("")] |
||||||
|
[assembly: AssemblyCulture("")] |
||||||
|
// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". |
||||||
|
// The form "{Major}.{Minor}.*" will automatically update the build and revision, |
||||||
|
// and "{Major}.{Minor}.{Build}.*" will update just the revision. |
||||||
|
[assembly: AssemblyVersion("1.0.*")] |
||||||
|
// The following attributes are used to specify the signing key for the assembly, |
||||||
|
// if desired. See the Mono documentation for more information about signing. |
||||||
|
//[assembly: AssemblyDelaySign(false)] |
||||||
|
//[assembly: AssemblyKeyFile("")] |
||||||
|
|
@ -0,0 +1 @@ |
|||||||
|
bin |
@ -0,0 +1,65 @@ |
|||||||
|
using System; |
||||||
|
using Google.GRPC.Core.Internal; |
||||||
|
|
||||||
|
namespace Google.GRPC.Core |
||||||
|
{ |
||||||
|
public class Call<TRequest, TResponse> |
||||||
|
{ |
||||||
|
readonly string methodName; |
||||||
|
readonly Func<TRequest, byte[]> requestSerializer; |
||||||
|
readonly Func<byte[], TResponse> responseDeserializer; |
||||||
|
readonly Channel channel; |
||||||
|
|
||||||
|
public Call(string methodName, |
||||||
|
Func<TRequest, byte[]> requestSerializer, |
||||||
|
Func<byte[], TResponse> responseDeserializer, |
||||||
|
TimeSpan timeout, |
||||||
|
Channel channel) { |
||||||
|
this.methodName = methodName; |
||||||
|
this.requestSerializer = requestSerializer; |
||||||
|
this.responseDeserializer = responseDeserializer; |
||||||
|
this.channel = channel; |
||||||
|
} |
||||||
|
|
||||||
|
public Call(Method<TRequest, TResponse> method, Channel channel) |
||||||
|
{ |
||||||
|
this.methodName = method.Name; |
||||||
|
this.requestSerializer = method.RequestMarshaller.Serializer; |
||||||
|
this.responseDeserializer = method.ResponseMarshaller.Deserializer; |
||||||
|
this.channel = channel; |
||||||
|
} |
||||||
|
|
||||||
|
public Channel Channel |
||||||
|
{ |
||||||
|
get |
||||||
|
{ |
||||||
|
return this.channel; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public string MethodName |
||||||
|
{ |
||||||
|
get |
||||||
|
{ |
||||||
|
return this.methodName; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public Func<TRequest, byte[]> RequestSerializer |
||||||
|
{ |
||||||
|
get |
||||||
|
{ |
||||||
|
return this.requestSerializer; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public Func<byte[], TResponse> ResponseDeserializer |
||||||
|
{ |
||||||
|
get |
||||||
|
{ |
||||||
|
return this.responseDeserializer; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
@ -0,0 +1,85 @@ |
|||||||
|
using System; |
||||||
|
using System.Threading; |
||||||
|
using System.Threading.Tasks; |
||||||
|
using Google.GRPC.Core.Internal; |
||||||
|
|
||||||
|
namespace Google.GRPC.Core |
||||||
|
{ |
||||||
|
// NOTE: this class is work-in-progress |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// Helper methods for generated stubs to make RPC calls. |
||||||
|
/// </summary> |
||||||
|
public static class Calls |
||||||
|
{ |
||||||
|
public static TResponse BlockingUnaryCall<TRequest, TResponse>(Call<TRequest, TResponse> call, TRequest req, CancellationToken token) |
||||||
|
{ |
||||||
|
//TODO: implement this in real synchronous style once new GRPC C core API is available. |
||||||
|
return AsyncUnaryCall(call, req, token).Result; |
||||||
|
} |
||||||
|
|
||||||
|
public static async Task<TResponse> AsyncUnaryCall<TRequest, TResponse>(Call<TRequest, TResponse> call, TRequest req, CancellationToken token) |
||||||
|
{ |
||||||
|
var asyncCall = new AsyncCall<TRequest, TResponse>(call.RequestSerializer, call.ResponseDeserializer); |
||||||
|
asyncCall.Initialize(call.Channel, call.MethodName); |
||||||
|
asyncCall.Start(false, GetCompletionQueue()); |
||||||
|
|
||||||
|
await asyncCall.WriteAsync(req); |
||||||
|
await asyncCall.WritesCompletedAsync(); |
||||||
|
|
||||||
|
TResponse response = await asyncCall.ReadAsync(); |
||||||
|
|
||||||
|
Status status = await asyncCall.Finished; |
||||||
|
|
||||||
|
if (status.StatusCode != StatusCode.GRPC_STATUS_OK) |
||||||
|
{ |
||||||
|
throw new RpcException(status); |
||||||
|
} |
||||||
|
return response; |
||||||
|
} |
||||||
|
|
||||||
|
public static async Task AsyncServerStreamingCall<TRequest, TResponse>(Call<TRequest, TResponse> call, TRequest req, IObserver<TResponse> outputs, CancellationToken token) |
||||||
|
{ |
||||||
|
var asyncCall = new AsyncCall<TRequest, TResponse>(call.RequestSerializer, call.ResponseDeserializer); |
||||||
|
asyncCall.Initialize(call.Channel, call.MethodName); |
||||||
|
asyncCall.Start(false, GetCompletionQueue()); |
||||||
|
|
||||||
|
asyncCall.StartReadingToStream(outputs); |
||||||
|
|
||||||
|
await asyncCall.WriteAsync(req); |
||||||
|
await asyncCall.WritesCompletedAsync(); |
||||||
|
} |
||||||
|
|
||||||
|
public static ClientStreamingAsyncResult<TRequest, TResponse> AsyncClientStreamingCall<TRequest, TResponse>(Call<TRequest, TResponse> call, CancellationToken token) |
||||||
|
{ |
||||||
|
var asyncCall = new AsyncCall<TRequest, TResponse>(call.RequestSerializer, call.ResponseDeserializer); |
||||||
|
asyncCall.Initialize(call.Channel, call.MethodName); |
||||||
|
asyncCall.Start(false, GetCompletionQueue()); |
||||||
|
|
||||||
|
var task = asyncCall.ReadAsync(); |
||||||
|
var inputs = new StreamingInputObserver<TRequest, TResponse>(asyncCall); |
||||||
|
return new ClientStreamingAsyncResult<TRequest, TResponse>(task, inputs); |
||||||
|
} |
||||||
|
|
||||||
|
public static TResponse BlockingClientStreamingCall<TRequest, TResponse>(Call<TRequest, TResponse> call, IObservable<TRequest> inputs, CancellationToken token) |
||||||
|
{ |
||||||
|
throw new NotImplementedException(); |
||||||
|
} |
||||||
|
|
||||||
|
public static IObserver<TRequest> DuplexStreamingCall<TRequest, TResponse>(Call<TRequest, TResponse> call, IObserver<TResponse> outputs, CancellationToken token) |
||||||
|
{ |
||||||
|
var asyncCall = new AsyncCall<TRequest, TResponse>(call.RequestSerializer, call.ResponseDeserializer); |
||||||
|
asyncCall.Initialize(call.Channel, call.MethodName); |
||||||
|
asyncCall.Start(false, GetCompletionQueue()); |
||||||
|
|
||||||
|
asyncCall.StartReadingToStream(outputs); |
||||||
|
var inputs = new StreamingInputObserver<TRequest, TResponse>(asyncCall); |
||||||
|
return inputs; |
||||||
|
} |
||||||
|
|
||||||
|
private static CompletionQueueSafeHandle GetCompletionQueue() { |
||||||
|
return GrpcEnvironment.ThreadPool.CompletionQueue; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
@ -0,0 +1,59 @@ |
|||||||
|
using System; |
||||||
|
using System.Runtime.InteropServices; |
||||||
|
using System.Threading; |
||||||
|
using System.Threading.Tasks; |
||||||
|
using Google.GRPC.Core.Internal; |
||||||
|
|
||||||
|
namespace Google.GRPC.Core |
||||||
|
{ |
||||||
|
public class Channel : IDisposable |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// Make sure GPRC environment is initialized before any channels get used. |
||||||
|
/// </summary> |
||||||
|
static Channel() { |
||||||
|
GrpcEnvironment.EnsureInitialized(); |
||||||
|
} |
||||||
|
|
||||||
|
readonly ChannelSafeHandle handle; |
||||||
|
readonly String target; |
||||||
|
|
||||||
|
// TODO: add way how to create grpc_secure_channel.... |
||||||
|
// TODO: add support for channel args... |
||||||
|
public Channel(string target) |
||||||
|
{ |
||||||
|
this.handle = ChannelSafeHandle.Create(target, IntPtr.Zero); |
||||||
|
this.target = target; |
||||||
|
} |
||||||
|
|
||||||
|
internal ChannelSafeHandle Handle |
||||||
|
{ |
||||||
|
get |
||||||
|
{ |
||||||
|
return this.handle; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public string Target |
||||||
|
{ |
||||||
|
get |
||||||
|
{ |
||||||
|
return this.target; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void Dispose() |
||||||
|
{ |
||||||
|
Dispose(true); |
||||||
|
GC.SuppressFinalize(this); |
||||||
|
} |
||||||
|
|
||||||
|
protected virtual void Dispose(bool disposing) |
||||||
|
{ |
||||||
|
if (handle != null && !handle.IsInvalid) |
||||||
|
{ |
||||||
|
handle.Dispose(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,37 @@ |
|||||||
|
using System; |
||||||
|
using System.Threading.Tasks; |
||||||
|
|
||||||
|
namespace Google.GRPC.Core |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// Return type for client streaming async method. |
||||||
|
/// </summary> |
||||||
|
public struct ClientStreamingAsyncResult<TRequest, TResponse> |
||||||
|
{ |
||||||
|
readonly Task<TResponse> task; |
||||||
|
readonly IObserver<TRequest> inputs; |
||||||
|
|
||||||
|
public ClientStreamingAsyncResult(Task<TResponse> task, IObserver<TRequest> inputs) |
||||||
|
{ |
||||||
|
this.task = task; |
||||||
|
this.inputs = inputs; |
||||||
|
} |
||||||
|
|
||||||
|
public Task<TResponse> Task |
||||||
|
{ |
||||||
|
get |
||||||
|
{ |
||||||
|
return this.task; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public IObserver<TRequest> Inputs |
||||||
|
{ |
||||||
|
get |
||||||
|
{ |
||||||
|
return this.inputs; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
@ -0,0 +1,72 @@ |
|||||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||||
|
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
||||||
|
<PropertyGroup> |
||||||
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> |
||||||
|
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> |
||||||
|
<ProductVersion>10.0.0</ProductVersion> |
||||||
|
<SchemaVersion>2.0</SchemaVersion> |
||||||
|
<ProjectGuid>{CCC4440E-49F7-4790-B0AF-FEABB0837AE7}</ProjectGuid> |
||||||
|
<OutputType>Library</OutputType> |
||||||
|
<RootNamespace>GrpcCore</RootNamespace> |
||||||
|
<AssemblyName>GrpcCore</AssemblyName> |
||||||
|
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion> |
||||||
|
</PropertyGroup> |
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> |
||||||
|
<DebugSymbols>true</DebugSymbols> |
||||||
|
<DebugType>full</DebugType> |
||||||
|
<Optimize>false</Optimize> |
||||||
|
<OutputPath>bin\Debug</OutputPath> |
||||||
|
<DefineConstants>DEBUG;</DefineConstants> |
||||||
|
<ErrorReport>prompt</ErrorReport> |
||||||
|
<WarningLevel>4</WarningLevel> |
||||||
|
<ConsolePause>false</ConsolePause> |
||||||
|
</PropertyGroup> |
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> |
||||||
|
<DebugType>full</DebugType> |
||||||
|
<Optimize>true</Optimize> |
||||||
|
<OutputPath>bin\Release</OutputPath> |
||||||
|
<ErrorReport>prompt</ErrorReport> |
||||||
|
<WarningLevel>4</WarningLevel> |
||||||
|
<ConsolePause>false</ConsolePause> |
||||||
|
</PropertyGroup> |
||||||
|
<ItemGroup> |
||||||
|
<Reference Include="System" /> |
||||||
|
</ItemGroup> |
||||||
|
<ItemGroup> |
||||||
|
<Compile Include="Properties\AssemblyInfo.cs" /> |
||||||
|
<Compile Include="RpcException.cs" /> |
||||||
|
<Compile Include="Calls.cs" /> |
||||||
|
<Compile Include="Call.cs" /> |
||||||
|
<Compile Include="ClientStreamingAsyncResult.cs" /> |
||||||
|
<Compile Include="GrpcEnvironment.cs" /> |
||||||
|
<Compile Include="Status.cs" /> |
||||||
|
<Compile Include="StatusCode.cs" /> |
||||||
|
<Compile Include="Server.cs" /> |
||||||
|
<Compile Include="Channel.cs" /> |
||||||
|
<Compile Include="Internal\CallSafeHandle.cs" /> |
||||||
|
<Compile Include="Internal\ChannelSafeHandle.cs" /> |
||||||
|
<Compile Include="Internal\CompletionQueueSafeHandle.cs" /> |
||||||
|
<Compile Include="Internal\Enums.cs" /> |
||||||
|
<Compile Include="Internal\Event.cs" /> |
||||||
|
<Compile Include="Internal\SafeHandleZeroIsInvalid.cs" /> |
||||||
|
<Compile Include="Internal\Timespec.cs" /> |
||||||
|
<Compile Include="Internal\GrpcThreadPool.cs" /> |
||||||
|
<Compile Include="Internal\AsyncCall.cs" /> |
||||||
|
<Compile Include="Internal\ServerSafeHandle.cs" /> |
||||||
|
<Compile Include="Internal\StreamingInputObserver.cs" /> |
||||||
|
<Compile Include="Method.cs" /> |
||||||
|
<Compile Include="ServerCalls.cs" /> |
||||||
|
<Compile Include="ServerCallHandler.cs" /> |
||||||
|
<Compile Include="Internal\ServerWritingObserver.cs" /> |
||||||
|
<Compile Include="Marshaller.cs" /> |
||||||
|
<Compile Include="ServerServiceDefinition.cs" /> |
||||||
|
<Compile Include="Utils\RecordingObserver.cs" /> |
||||||
|
<Compile Include="Utils\PortPicker.cs" /> |
||||||
|
<Compile Include="Utils\RecordingQueue.cs" /> |
||||||
|
</ItemGroup> |
||||||
|
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> |
||||||
|
<ItemGroup> |
||||||
|
<Folder Include="Internal\" /> |
||||||
|
<Folder Include="Utils\" /> |
||||||
|
</ItemGroup> |
||||||
|
</Project> |
@ -0,0 +1,91 @@ |
|||||||
|
using System; |
||||||
|
using Google.GRPC.Core.Internal; |
||||||
|
using System.Runtime.InteropServices; |
||||||
|
|
||||||
|
namespace Google.GRPC.Core |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// Encapsulates initialization and shutdown of GRPC C core library. |
||||||
|
/// You should not need to initialize it manually, as static constructors |
||||||
|
/// should load the library when needed. |
||||||
|
/// </summary> |
||||||
|
public static class GrpcEnvironment |
||||||
|
{ |
||||||
|
const int THREAD_POOL_SIZE = 1; |
||||||
|
|
||||||
|
[DllImport("libgrpc.so")] |
||||||
|
static extern void grpc_init(); |
||||||
|
|
||||||
|
[DllImport("libgrpc.so")] |
||||||
|
static extern void grpc_shutdown(); |
||||||
|
|
||||||
|
static object staticLock = new object(); |
||||||
|
static bool initCalled = false; |
||||||
|
static bool shutdownCalled = false; |
||||||
|
|
||||||
|
static GrpcThreadPool threadPool = new GrpcThreadPool(THREAD_POOL_SIZE); |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// Makes sure GRPC environment is initialized. |
||||||
|
/// </summary> |
||||||
|
public static void EnsureInitialized() { |
||||||
|
lock(staticLock) |
||||||
|
{ |
||||||
|
if (!initCalled) |
||||||
|
{ |
||||||
|
initCalled = true; |
||||||
|
GrpcInit(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// Shuts down the GRPC environment if it was initialized before. |
||||||
|
/// Repeated invocations have no effect. |
||||||
|
/// </summary> |
||||||
|
public static void Shutdown() |
||||||
|
{ |
||||||
|
lock(staticLock) |
||||||
|
{ |
||||||
|
if (initCalled && !shutdownCalled) |
||||||
|
{ |
||||||
|
shutdownCalled = true; |
||||||
|
GrpcShutdown(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// Initializes GRPC C Core library. |
||||||
|
/// </summary> |
||||||
|
private static void GrpcInit() |
||||||
|
{ |
||||||
|
grpc_init(); |
||||||
|
threadPool.Start(); |
||||||
|
// TODO: use proper logging here |
||||||
|
Console.WriteLine("GRPC initialized."); |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// Shutdown GRPC C Core library. |
||||||
|
/// </summary> |
||||||
|
private static void GrpcShutdown() |
||||||
|
{ |
||||||
|
threadPool.Stop(); |
||||||
|
grpc_shutdown(); |
||||||
|
|
||||||
|
// TODO: use proper logging here |
||||||
|
Console.WriteLine("GRPC shutdown."); |
||||||
|
} |
||||||
|
|
||||||
|
internal static GrpcThreadPool ThreadPool |
||||||
|
{ |
||||||
|
get |
||||||
|
{ |
||||||
|
return threadPool; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
@ -0,0 +1,493 @@ |
|||||||
|
using System; |
||||||
|
using System.Runtime.InteropServices; |
||||||
|
using System.Diagnostics; |
||||||
|
using System.Threading; |
||||||
|
using System.Threading.Tasks; |
||||||
|
using System.Runtime.CompilerServices; |
||||||
|
using Google.GRPC.Core.Internal; |
||||||
|
|
||||||
|
namespace Google.GRPC.Core.Internal |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// Listener for call events that can be delivered from a completion queue. |
||||||
|
/// </summary> |
||||||
|
internal interface ICallEventListener { |
||||||
|
|
||||||
|
void OnClientMetadata(); |
||||||
|
|
||||||
|
void OnRead(byte[] payload); |
||||||
|
|
||||||
|
void OnWriteAccepted(GRPCOpError error); |
||||||
|
|
||||||
|
void OnFinishAccepted(GRPCOpError error); |
||||||
|
|
||||||
|
// ignore the status on server |
||||||
|
void OnFinished(Status status); |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// Handle native call lifecycle and provides convenience methods. |
||||||
|
/// </summary> |
||||||
|
internal class AsyncCall<TWrite, TRead>: ICallEventListener, IDisposable |
||||||
|
{ |
||||||
|
readonly Func<TWrite, byte[]> serializer; |
||||||
|
readonly Func<byte[], TRead> deserializer; |
||||||
|
|
||||||
|
// TODO: make sure the delegate doesn't get garbage collected while |
||||||
|
// native callbacks are in the completion queue. |
||||||
|
readonly EventCallbackDelegate callbackHandler; |
||||||
|
|
||||||
|
object myLock = new object(); |
||||||
|
bool disposed; |
||||||
|
CallSafeHandle call; |
||||||
|
|
||||||
|
bool started; |
||||||
|
bool errorOccured; |
||||||
|
|
||||||
|
bool cancelRequested; |
||||||
|
bool halfcloseRequested; |
||||||
|
bool halfclosed; |
||||||
|
bool doneWithReading; |
||||||
|
Nullable<Status> finishedStatus; |
||||||
|
|
||||||
|
TaskCompletionSource<object> writeTcs; |
||||||
|
TaskCompletionSource<TRead> readTcs; |
||||||
|
TaskCompletionSource<object> halfcloseTcs = new TaskCompletionSource<object>(); |
||||||
|
TaskCompletionSource<Status> finishedTcs = new TaskCompletionSource<Status>(); |
||||||
|
|
||||||
|
IObserver<TRead> readObserver; |
||||||
|
|
||||||
|
public AsyncCall(Func<TWrite, byte[]> serializer, Func<byte[], TRead> deserializer) |
||||||
|
{ |
||||||
|
this.serializer = serializer; |
||||||
|
this.deserializer = deserializer; |
||||||
|
this.callbackHandler = HandleEvent; |
||||||
|
} |
||||||
|
|
||||||
|
public Task WriteAsync(TWrite msg) |
||||||
|
{ |
||||||
|
return StartWrite(msg, false).Task; |
||||||
|
} |
||||||
|
|
||||||
|
public Task WritesCompletedAsync() |
||||||
|
{ |
||||||
|
WritesDone(); |
||||||
|
return halfcloseTcs.Task; |
||||||
|
} |
||||||
|
|
||||||
|
public Task WriteStatusAsync(Status status) |
||||||
|
{ |
||||||
|
WriteStatus(status); |
||||||
|
return halfcloseTcs.Task; |
||||||
|
} |
||||||
|
|
||||||
|
public Task<TRead> ReadAsync() |
||||||
|
{ |
||||||
|
return StartRead().Task; |
||||||
|
} |
||||||
|
|
||||||
|
public Task Halfclosed |
||||||
|
{ |
||||||
|
get |
||||||
|
{ |
||||||
|
return halfcloseTcs.Task; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public Task<Status> Finished |
||||||
|
{ |
||||||
|
get |
||||||
|
{ |
||||||
|
return finishedTcs.Task; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// Initiates reading to given observer. |
||||||
|
/// </summary> |
||||||
|
public void StartReadingToStream(IObserver<TRead> readObserver) { |
||||||
|
lock (myLock) |
||||||
|
{ |
||||||
|
CheckStarted(); |
||||||
|
if (this.readObserver != null) |
||||||
|
{ |
||||||
|
throw new InvalidOperationException("Already registered an observer."); |
||||||
|
} |
||||||
|
this.readObserver = readObserver; |
||||||
|
StartRead(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void Initialize(Channel channel, String methodName) { |
||||||
|
lock (myLock) |
||||||
|
{ |
||||||
|
this.call = CallSafeHandle.Create(channel.Handle, methodName, channel.Target, Timespec.InfFuture); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void InitializeServer(CallSafeHandle call) |
||||||
|
{ |
||||||
|
lock(myLock) |
||||||
|
{ |
||||||
|
this.call = call; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Client only |
||||||
|
public void Start(bool buffered, CompletionQueueSafeHandle cq) |
||||||
|
{ |
||||||
|
lock (myLock) |
||||||
|
{ |
||||||
|
if (started) |
||||||
|
{ |
||||||
|
throw new InvalidOperationException("Already started."); |
||||||
|
} |
||||||
|
|
||||||
|
call.Invoke(cq, buffered, callbackHandler, callbackHandler); |
||||||
|
started = true; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Server only |
||||||
|
public void Accept(CompletionQueueSafeHandle cq) |
||||||
|
{ |
||||||
|
lock (myLock) |
||||||
|
{ |
||||||
|
if (started) |
||||||
|
{ |
||||||
|
throw new InvalidOperationException("Already started."); |
||||||
|
} |
||||||
|
|
||||||
|
call.ServerAccept(cq, callbackHandler); |
||||||
|
call.ServerEndInitialMetadata(0); |
||||||
|
started = true; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public TaskCompletionSource<object> StartWrite(TWrite msg, bool buffered) |
||||||
|
{ |
||||||
|
lock (myLock) |
||||||
|
{ |
||||||
|
CheckStarted(); |
||||||
|
CheckNotFinished(); |
||||||
|
CheckNoError(); |
||||||
|
CheckCancelNotRequested(); |
||||||
|
|
||||||
|
if (halfcloseRequested || halfclosed) |
||||||
|
{ |
||||||
|
throw new InvalidOperationException("Already halfclosed."); |
||||||
|
} |
||||||
|
|
||||||
|
if (writeTcs != null) |
||||||
|
{ |
||||||
|
throw new InvalidOperationException("Only one write can be pending at a time"); |
||||||
|
} |
||||||
|
|
||||||
|
// TODO: wrap serialization... |
||||||
|
byte[] payload = serializer(msg); |
||||||
|
|
||||||
|
call.StartWrite(payload, buffered, callbackHandler); |
||||||
|
writeTcs = new TaskCompletionSource<object>(); |
||||||
|
return writeTcs; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// client only |
||||||
|
public void WritesDone() |
||||||
|
{ |
||||||
|
lock (myLock) |
||||||
|
{ |
||||||
|
CheckStarted(); |
||||||
|
CheckNotFinished(); |
||||||
|
CheckNoError(); |
||||||
|
CheckCancelNotRequested(); |
||||||
|
|
||||||
|
if (halfcloseRequested || halfclosed) |
||||||
|
{ |
||||||
|
throw new InvalidOperationException("Already halfclosed."); |
||||||
|
} |
||||||
|
|
||||||
|
call.WritesDone(callbackHandler); |
||||||
|
halfcloseRequested = true; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// server only |
||||||
|
public void WriteStatus(Status status) |
||||||
|
{ |
||||||
|
lock (myLock) |
||||||
|
{ |
||||||
|
CheckStarted(); |
||||||
|
CheckNotFinished(); |
||||||
|
CheckNoError(); |
||||||
|
CheckCancelNotRequested(); |
||||||
|
|
||||||
|
if (halfcloseRequested || halfclosed) |
||||||
|
{ |
||||||
|
throw new InvalidOperationException("Already halfclosed."); |
||||||
|
} |
||||||
|
|
||||||
|
call.StartWriteStatus(status, callbackHandler); |
||||||
|
halfcloseRequested = true; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public TaskCompletionSource<TRead> StartRead() |
||||||
|
{ |
||||||
|
lock (myLock) |
||||||
|
{ |
||||||
|
CheckStarted(); |
||||||
|
CheckNotFinished(); |
||||||
|
CheckNoError(); |
||||||
|
|
||||||
|
// TODO: add check for not cancelled? |
||||||
|
|
||||||
|
if (doneWithReading) |
||||||
|
{ |
||||||
|
throw new InvalidOperationException("Already read the last message."); |
||||||
|
} |
||||||
|
|
||||||
|
if (readTcs != null) |
||||||
|
{ |
||||||
|
throw new InvalidOperationException("Only one read can be pending at a time"); |
||||||
|
} |
||||||
|
|
||||||
|
call.StartRead(callbackHandler); |
||||||
|
|
||||||
|
readTcs = new TaskCompletionSource<TRead>(); |
||||||
|
return readTcs; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void Cancel() |
||||||
|
{ |
||||||
|
lock (myLock) |
||||||
|
{ |
||||||
|
CheckStarted(); |
||||||
|
CheckNotFinished(); |
||||||
|
|
||||||
|
cancelRequested = true; |
||||||
|
} |
||||||
|
// grpc_call_cancel is threadsafe |
||||||
|
call.Cancel(); |
||||||
|
} |
||||||
|
|
||||||
|
public void CancelWithStatus(Status status) |
||||||
|
{ |
||||||
|
lock (myLock) |
||||||
|
{ |
||||||
|
CheckStarted(); |
||||||
|
CheckNotFinished(); |
||||||
|
|
||||||
|
cancelRequested = true; |
||||||
|
} |
||||||
|
// grpc_call_cancel_with_status is threadsafe |
||||||
|
call.CancelWithStatus(status); |
||||||
|
} |
||||||
|
|
||||||
|
public void OnClientMetadata() |
||||||
|
{ |
||||||
|
// TODO: implement.... |
||||||
|
} |
||||||
|
|
||||||
|
public void OnRead(byte[] payload) |
||||||
|
{ |
||||||
|
TaskCompletionSource<TRead> oldTcs = null; |
||||||
|
IObserver<TRead> observer = null; |
||||||
|
lock (myLock) |
||||||
|
{ |
||||||
|
oldTcs = readTcs; |
||||||
|
readTcs = null; |
||||||
|
if (payload == null) |
||||||
|
{ |
||||||
|
doneWithReading = true; |
||||||
|
} |
||||||
|
observer = readObserver; |
||||||
|
} |
||||||
|
|
||||||
|
// TODO: wrap deserialization... |
||||||
|
TRead msg = payload != null ? deserializer(payload) : default(TRead); |
||||||
|
|
||||||
|
oldTcs.SetResult(msg); |
||||||
|
|
||||||
|
// TODO: make sure we deliver reads in the right order. |
||||||
|
|
||||||
|
if (observer != null) |
||||||
|
{ |
||||||
|
if (payload != null) |
||||||
|
{ |
||||||
|
// TODO: wrap to handle exceptions |
||||||
|
observer.OnNext(msg); |
||||||
|
|
||||||
|
// start a new read |
||||||
|
StartRead(); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
// TODO: wrap to handle exceptions; |
||||||
|
observer.OnCompleted(); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void OnWriteAccepted(GRPCOpError error) |
||||||
|
{ |
||||||
|
TaskCompletionSource<object> oldTcs = null; |
||||||
|
lock (myLock) |
||||||
|
{ |
||||||
|
UpdateErrorOccured(error); |
||||||
|
oldTcs = writeTcs; |
||||||
|
writeTcs = null; |
||||||
|
} |
||||||
|
|
||||||
|
if (errorOccured) |
||||||
|
{ |
||||||
|
// TODO: use the right type of exception... |
||||||
|
oldTcs.SetException(new Exception("Write failed")); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
// TODO: where does the continuation run? |
||||||
|
oldTcs.SetResult(null); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void OnFinishAccepted(GRPCOpError error) |
||||||
|
{ |
||||||
|
lock (myLock) |
||||||
|
{ |
||||||
|
UpdateErrorOccured(error); |
||||||
|
halfclosed = true; |
||||||
|
} |
||||||
|
|
||||||
|
if (errorOccured) |
||||||
|
{ |
||||||
|
halfcloseTcs.SetException(new Exception("Halfclose failed")); |
||||||
|
|
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
halfcloseTcs.SetResult(null); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
public void OnFinished(Status status) |
||||||
|
{ |
||||||
|
lock (myLock) |
||||||
|
{ |
||||||
|
finishedStatus = status; |
||||||
|
|
||||||
|
DisposeResourcesIfNeeded(); |
||||||
|
} |
||||||
|
finishedTcs.SetResult(status); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
public void Dispose() |
||||||
|
{ |
||||||
|
Dispose(true); |
||||||
|
GC.SuppressFinalize(this); |
||||||
|
} |
||||||
|
|
||||||
|
protected virtual void Dispose(bool disposing) |
||||||
|
{ |
||||||
|
if (!disposed) |
||||||
|
{ |
||||||
|
if (disposing) |
||||||
|
{ |
||||||
|
if (call != null) |
||||||
|
{ |
||||||
|
call.Dispose(); |
||||||
|
} |
||||||
|
} |
||||||
|
disposed = true; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void UpdateErrorOccured(GRPCOpError error) |
||||||
|
{ |
||||||
|
if (error == GRPCOpError.GRPC_OP_ERROR) |
||||||
|
{ |
||||||
|
errorOccured = true; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void CheckStarted() |
||||||
|
{ |
||||||
|
if (!started) |
||||||
|
{ |
||||||
|
throw new InvalidOperationException("Call not started"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void CheckNoError() |
||||||
|
{ |
||||||
|
if (errorOccured) |
||||||
|
{ |
||||||
|
throw new InvalidOperationException("Error occured when processing call."); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void CheckNotFinished() |
||||||
|
{ |
||||||
|
if (finishedStatus.HasValue) |
||||||
|
{ |
||||||
|
throw new InvalidOperationException("Already finished."); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void CheckCancelNotRequested() |
||||||
|
{ |
||||||
|
if (cancelRequested) |
||||||
|
{ |
||||||
|
throw new InvalidOperationException("Cancel has been requested."); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void DisposeResourcesIfNeeded() |
||||||
|
{ |
||||||
|
if (call != null && started && finishedStatus.HasValue) |
||||||
|
{ |
||||||
|
// TODO: should we also wait for all the pending events to finish? |
||||||
|
|
||||||
|
call.Dispose(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void HandleEvent(IntPtr eventPtr) { |
||||||
|
try { |
||||||
|
var ev = new EventSafeHandleNotOwned(eventPtr); |
||||||
|
switch (ev.GetCompletionType()) |
||||||
|
{ |
||||||
|
case GRPCCompletionType.GRPC_CLIENT_METADATA_READ: |
||||||
|
OnClientMetadata(); |
||||||
|
break; |
||||||
|
|
||||||
|
case GRPCCompletionType.GRPC_READ: |
||||||
|
byte[] payload = ev.GetReadData(); |
||||||
|
OnRead(payload); |
||||||
|
break; |
||||||
|
|
||||||
|
case GRPCCompletionType.GRPC_WRITE_ACCEPTED: |
||||||
|
OnWriteAccepted(ev.GetWriteAccepted()); |
||||||
|
break; |
||||||
|
|
||||||
|
case GRPCCompletionType.GRPC_FINISH_ACCEPTED: |
||||||
|
OnFinishAccepted(ev.GetFinishAccepted()); |
||||||
|
break; |
||||||
|
|
||||||
|
case GRPCCompletionType.GRPC_FINISHED: |
||||||
|
OnFinished(ev.GetFinished()); |
||||||
|
break; |
||||||
|
|
||||||
|
default: |
||||||
|
throw new ArgumentException("Unexpected completion type"); |
||||||
|
} |
||||||
|
} catch(Exception e) { |
||||||
|
Console.WriteLine("Caught exception in a native handler: " + e); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,182 @@ |
|||||||
|
using System; |
||||||
|
using System.Runtime.InteropServices; |
||||||
|
using System.Diagnostics; |
||||||
|
using Google.GRPC.Core; |
||||||
|
|
||||||
|
namespace Google.GRPC.Core.Internal |
||||||
|
{ |
||||||
|
// TODO: we need to make sure that the delegates are not collected before invoked. |
||||||
|
internal delegate void EventCallbackDelegate(IntPtr eventPtr); |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// grpc_call from <grpc/grpc.h> |
||||||
|
/// </summary> |
||||||
|
internal class CallSafeHandle : SafeHandleZeroIsInvalid |
||||||
|
{ |
||||||
|
const UInt32 GRPC_WRITE_BUFFER_HINT = 1; |
||||||
|
|
||||||
|
[DllImport("libgrpc.so")] |
||||||
|
static extern CallSafeHandle grpc_channel_create_call_old(ChannelSafeHandle channel, string method, string host, Timespec deadline); |
||||||
|
|
||||||
|
[DllImport("libgrpc.so")] |
||||||
|
static extern GRPCCallError grpc_call_add_metadata(CallSafeHandle call, IntPtr metadata, UInt32 flags); |
||||||
|
|
||||||
|
[DllImport("libgrpc.so")] |
||||||
|
static extern GRPCCallError grpc_call_invoke_old(CallSafeHandle call, CompletionQueueSafeHandle cq, IntPtr metadataReadTag, IntPtr finishedTag, UInt32 flags); |
||||||
|
|
||||||
|
[DllImport("libgrpc.so", EntryPoint = "grpc_call_invoke_old")] |
||||||
|
static extern GRPCCallError grpc_call_invoke_old_CALLBACK(CallSafeHandle call, CompletionQueueSafeHandle cq, |
||||||
|
[MarshalAs(UnmanagedType.FunctionPtr)] EventCallbackDelegate metadataReadCallback, |
||||||
|
[MarshalAs(UnmanagedType.FunctionPtr)] EventCallbackDelegate finishedCallback, |
||||||
|
UInt32 flags); |
||||||
|
|
||||||
|
[DllImport("libgrpc.so")] |
||||||
|
static extern GRPCCallError grpc_call_server_accept_old(CallSafeHandle call, CompletionQueueSafeHandle completionQueue, IntPtr finishedTag); |
||||||
|
|
||||||
|
[DllImport("libgrpc.so", EntryPoint = "grpc_call_server_accept_old")] |
||||||
|
static extern GRPCCallError grpc_call_server_accept_old_CALLBACK(CallSafeHandle call, CompletionQueueSafeHandle completionQueue, [MarshalAs(UnmanagedType.FunctionPtr)] EventCallbackDelegate finishedCallback); |
||||||
|
|
||||||
|
[DllImport("libgrpc.so")] |
||||||
|
static extern GRPCCallError grpc_call_server_end_initial_metadata_old(CallSafeHandle call, UInt32 flags); |
||||||
|
|
||||||
|
[DllImport("libgrpc.so")] |
||||||
|
static extern GRPCCallError grpc_call_cancel(CallSafeHandle call); |
||||||
|
|
||||||
|
[DllImport("libgrpc.so")] |
||||||
|
static extern GRPCCallError grpc_call_cancel_with_status(CallSafeHandle call, StatusCode status, string description); |
||||||
|
|
||||||
|
[DllImport("libgrpc.so")] |
||||||
|
static extern GRPCCallError grpc_call_start_write_status_old(CallSafeHandle call, StatusCode statusCode, string statusMessage, IntPtr tag); |
||||||
|
|
||||||
|
[DllImport("libgrpc.so", EntryPoint = "grpc_call_start_write_status_old")] |
||||||
|
static extern GRPCCallError grpc_call_start_write_status_old_CALLBACK(CallSafeHandle call, StatusCode statusCode, string statusMessage, [MarshalAs(UnmanagedType.FunctionPtr)] EventCallbackDelegate callback); |
||||||
|
|
||||||
|
[DllImport("libgrpc.so")] |
||||||
|
static extern GRPCCallError grpc_call_writes_done_old(CallSafeHandle call, IntPtr tag); |
||||||
|
|
||||||
|
[DllImport("libgrpc.so", EntryPoint = "grpc_call_writes_done_old")] |
||||||
|
static extern GRPCCallError grpc_call_writes_done_old_CALLBACK(CallSafeHandle call, [MarshalAs(UnmanagedType.FunctionPtr)] EventCallbackDelegate callback); |
||||||
|
|
||||||
|
[DllImport("libgrpc.so")] |
||||||
|
static extern GRPCCallError grpc_call_start_read_old(CallSafeHandle call, IntPtr tag); |
||||||
|
|
||||||
|
[DllImport("libgrpc.so", EntryPoint = "grpc_call_start_read_old")] |
||||||
|
static extern GRPCCallError grpc_call_start_read_old_CALLBACK(CallSafeHandle call, [MarshalAs(UnmanagedType.FunctionPtr)] EventCallbackDelegate callback); |
||||||
|
|
||||||
|
[DllImport("libgrpc_csharp_ext.so")] |
||||||
|
static extern void grpc_call_start_write_from_copied_buffer(CallSafeHandle call, |
||||||
|
byte[] buffer, UIntPtr length, |
||||||
|
IntPtr tag, UInt32 flags); |
||||||
|
|
||||||
|
[DllImport("libgrpc_csharp_ext.so", EntryPoint = "grpc_call_start_write_from_copied_buffer")] |
||||||
|
static extern void grpc_call_start_write_from_copied_buffer_CALLBACK(CallSafeHandle call, |
||||||
|
byte[] buffer, UIntPtr length, |
||||||
|
[MarshalAs(UnmanagedType.FunctionPtr)] EventCallbackDelegate callback, |
||||||
|
UInt32 flags); |
||||||
|
|
||||||
|
[DllImport("libgrpc.so")] |
||||||
|
static extern void grpc_call_destroy(IntPtr call); |
||||||
|
|
||||||
|
private CallSafeHandle() |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// Creates a client call. |
||||||
|
/// </summary> |
||||||
|
public static CallSafeHandle Create(ChannelSafeHandle channel, string method, string host, Timespec deadline) |
||||||
|
{ |
||||||
|
return grpc_channel_create_call_old(channel, method, host, deadline); |
||||||
|
} |
||||||
|
|
||||||
|
public void Invoke(CompletionQueueSafeHandle cq, IntPtr metadataReadTag, IntPtr finishedTag, bool buffered) |
||||||
|
{ |
||||||
|
AssertCallOk(grpc_call_invoke_old(this, cq, metadataReadTag, finishedTag, GetFlags(buffered))); |
||||||
|
} |
||||||
|
|
||||||
|
public void Invoke(CompletionQueueSafeHandle cq, bool buffered, EventCallbackDelegate metadataReadCallback, EventCallbackDelegate finishedCallback) |
||||||
|
{ |
||||||
|
AssertCallOk(grpc_call_invoke_old_CALLBACK(this, cq, metadataReadCallback, finishedCallback, GetFlags(buffered))); |
||||||
|
} |
||||||
|
|
||||||
|
public void ServerAccept(CompletionQueueSafeHandle cq, IntPtr finishedTag) |
||||||
|
{ |
||||||
|
AssertCallOk(grpc_call_server_accept_old(this, cq, finishedTag)); |
||||||
|
} |
||||||
|
|
||||||
|
public void ServerAccept(CompletionQueueSafeHandle cq, EventCallbackDelegate callback) |
||||||
|
{ |
||||||
|
AssertCallOk(grpc_call_server_accept_old_CALLBACK(this, cq, callback)); |
||||||
|
} |
||||||
|
|
||||||
|
public void ServerEndInitialMetadata(UInt32 flags) |
||||||
|
{ |
||||||
|
AssertCallOk(grpc_call_server_end_initial_metadata_old(this, flags)); |
||||||
|
} |
||||||
|
|
||||||
|
public void StartWrite(byte[] payload, IntPtr tag, bool buffered) |
||||||
|
{ |
||||||
|
grpc_call_start_write_from_copied_buffer(this, payload, new UIntPtr((ulong) payload.Length), tag, GetFlags(buffered)); |
||||||
|
} |
||||||
|
|
||||||
|
public void StartWrite(byte[] payload, bool buffered, EventCallbackDelegate callback) |
||||||
|
{ |
||||||
|
grpc_call_start_write_from_copied_buffer_CALLBACK(this, payload, new UIntPtr((ulong) payload.Length), callback, GetFlags(buffered)); |
||||||
|
} |
||||||
|
|
||||||
|
public void StartWriteStatus(Status status, IntPtr tag) |
||||||
|
{ |
||||||
|
AssertCallOk(grpc_call_start_write_status_old(this, status.StatusCode, status.Detail, tag)); |
||||||
|
} |
||||||
|
|
||||||
|
public void StartWriteStatus(Status status, EventCallbackDelegate callback) |
||||||
|
{ |
||||||
|
AssertCallOk(grpc_call_start_write_status_old_CALLBACK(this, status.StatusCode, status.Detail, callback)); |
||||||
|
} |
||||||
|
|
||||||
|
public void WritesDone(IntPtr tag) |
||||||
|
{ |
||||||
|
AssertCallOk(grpc_call_writes_done_old(this, tag)); |
||||||
|
} |
||||||
|
|
||||||
|
public void WritesDone(EventCallbackDelegate callback) |
||||||
|
{ |
||||||
|
AssertCallOk(grpc_call_writes_done_old_CALLBACK(this, callback)); |
||||||
|
} |
||||||
|
|
||||||
|
public void StartRead(IntPtr tag) |
||||||
|
{ |
||||||
|
AssertCallOk(grpc_call_start_read_old(this, tag)); |
||||||
|
} |
||||||
|
|
||||||
|
public void StartRead(EventCallbackDelegate callback) |
||||||
|
{ |
||||||
|
AssertCallOk(grpc_call_start_read_old_CALLBACK(this, callback)); |
||||||
|
} |
||||||
|
|
||||||
|
public void Cancel() |
||||||
|
{ |
||||||
|
AssertCallOk(grpc_call_cancel(this)); |
||||||
|
} |
||||||
|
|
||||||
|
public void CancelWithStatus(Status status) |
||||||
|
{ |
||||||
|
AssertCallOk(grpc_call_cancel_with_status(this, status.StatusCode, status.Detail)); |
||||||
|
} |
||||||
|
|
||||||
|
protected override bool ReleaseHandle() |
||||||
|
{ |
||||||
|
grpc_call_destroy(handle); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
private static void AssertCallOk(GRPCCallError callError) |
||||||
|
{ |
||||||
|
Trace.Assert(callError == GRPCCallError.GRPC_CALL_OK, "Status not GRPC_CALL_OK"); |
||||||
|
} |
||||||
|
|
||||||
|
private static UInt32 GetFlags(bool buffered) { |
||||||
|
return buffered ? 0 : GRPC_WRITE_BUFFER_HINT; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,34 @@ |
|||||||
|
using System; |
||||||
|
using System.Runtime.InteropServices; |
||||||
|
using System.Threading; |
||||||
|
using System.Threading.Tasks; |
||||||
|
|
||||||
|
namespace Google.GRPC.Core.Internal |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// grpc_channel from <grpc/grpc.h> |
||||||
|
/// </summary> |
||||||
|
internal class ChannelSafeHandle : SafeHandleZeroIsInvalid |
||||||
|
{ |
||||||
|
[DllImport("libgrpc.so")] |
||||||
|
static extern ChannelSafeHandle grpc_channel_create(string target, IntPtr channelArgs); |
||||||
|
|
||||||
|
[DllImport("libgrpc.so")] |
||||||
|
static extern void grpc_channel_destroy(IntPtr channel); |
||||||
|
|
||||||
|
private ChannelSafeHandle() |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
public static ChannelSafeHandle Create(string target, IntPtr channelArgs) |
||||||
|
{ |
||||||
|
return grpc_channel_create(target, channelArgs); |
||||||
|
} |
||||||
|
|
||||||
|
protected override bool ReleaseHandle() |
||||||
|
{ |
||||||
|
grpc_channel_destroy(handle); |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,66 @@ |
|||||||
|
using System; |
||||||
|
using System.Runtime.InteropServices; |
||||||
|
using System.Threading.Tasks; |
||||||
|
|
||||||
|
namespace Google.GRPC.Core.Internal |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// grpc_completion_queue from <grpc/grpc.h> |
||||||
|
/// </summary> |
||||||
|
internal class CompletionQueueSafeHandle : SafeHandleZeroIsInvalid |
||||||
|
{ |
||||||
|
[DllImport("libgrpc.so")] |
||||||
|
static extern CompletionQueueSafeHandle grpc_completion_queue_create(); |
||||||
|
|
||||||
|
[DllImport("libgrpc.so")] |
||||||
|
static extern EventSafeHandle grpc_completion_queue_pluck(CompletionQueueSafeHandle cq, IntPtr tag, Timespec deadline); |
||||||
|
|
||||||
|
[DllImport("libgrpc.so")] |
||||||
|
static extern EventSafeHandle grpc_completion_queue_next(CompletionQueueSafeHandle cq, Timespec deadline); |
||||||
|
|
||||||
|
[DllImport("libgrpc.so")] |
||||||
|
static extern void grpc_completion_queue_shutdown(CompletionQueueSafeHandle cq); |
||||||
|
|
||||||
|
[DllImport("libgrpc_csharp_ext.so")] |
||||||
|
static extern GRPCCompletionType grpc_completion_queue_next_with_callback(CompletionQueueSafeHandle cq); |
||||||
|
|
||||||
|
[DllImport("libgrpc.so")] |
||||||
|
static extern void grpc_completion_queue_destroy(IntPtr cq); |
||||||
|
|
||||||
|
private CompletionQueueSafeHandle() |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
public static CompletionQueueSafeHandle Create() |
||||||
|
{ |
||||||
|
return grpc_completion_queue_create(); |
||||||
|
} |
||||||
|
|
||||||
|
public EventSafeHandle Next(Timespec deadline) |
||||||
|
{ |
||||||
|
return grpc_completion_queue_next(this, deadline); |
||||||
|
} |
||||||
|
|
||||||
|
public GRPCCompletionType NextWithCallback() |
||||||
|
{ |
||||||
|
return grpc_completion_queue_next_with_callback(this); |
||||||
|
} |
||||||
|
|
||||||
|
public EventSafeHandle Pluck(IntPtr tag, Timespec deadline) |
||||||
|
{ |
||||||
|
return grpc_completion_queue_pluck(this, tag, deadline); |
||||||
|
} |
||||||
|
|
||||||
|
public void Shutdown() |
||||||
|
{ |
||||||
|
grpc_completion_queue_shutdown(this); |
||||||
|
} |
||||||
|
|
||||||
|
protected override bool ReleaseHandle() |
||||||
|
{ |
||||||
|
grpc_completion_queue_destroy(handle); |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
@ -0,0 +1,75 @@ |
|||||||
|
using System; |
||||||
|
using System.Runtime.InteropServices; |
||||||
|
|
||||||
|
namespace Google.GRPC.Core.Internal |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// from grpc/grpc.h |
||||||
|
/// </summary> |
||||||
|
internal enum GRPCCallError |
||||||
|
{ |
||||||
|
/* everything went ok */ |
||||||
|
GRPC_CALL_OK = 0, |
||||||
|
/* something failed, we don't know what */ |
||||||
|
GRPC_CALL_ERROR, |
||||||
|
/* this method is not available on the server */ |
||||||
|
GRPC_CALL_ERROR_NOT_ON_SERVER, |
||||||
|
/* this method is not available on the client */ |
||||||
|
GRPC_CALL_ERROR_NOT_ON_CLIENT, |
||||||
|
/* this method must be called before server_accept */ |
||||||
|
GRPC_CALL_ERROR_ALREADY_ACCEPTED, |
||||||
|
/* this method must be called before invoke */ |
||||||
|
GRPC_CALL_ERROR_ALREADY_INVOKED, |
||||||
|
/* this method must be called after invoke */ |
||||||
|
GRPC_CALL_ERROR_NOT_INVOKED, |
||||||
|
/* this call is already finished |
||||||
|
(writes_done or write_status has already been called) */ |
||||||
|
GRPC_CALL_ERROR_ALREADY_FINISHED, |
||||||
|
/* there is already an outstanding read/write operation on the call */ |
||||||
|
GRPC_CALL_ERROR_TOO_MANY_OPERATIONS, |
||||||
|
/* the flags value was illegal for this call */ |
||||||
|
GRPC_CALL_ERROR_INVALID_FLAGS |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// grpc_completion_type from grpc/grpc.h |
||||||
|
/// </summary> |
||||||
|
internal enum GRPCCompletionType |
||||||
|
{ |
||||||
|
GRPC_QUEUE_SHUTDOWN, |
||||||
|
/* Shutting down */ |
||||||
|
GRPC_READ, |
||||||
|
/* A read has completed */ |
||||||
|
GRPC_INVOKE_ACCEPTED, |
||||||
|
/* An invoke call has been accepted by flow |
||||||
|
control */ |
||||||
|
GRPC_WRITE_ACCEPTED, |
||||||
|
/* A write has been accepted by |
||||||
|
flow control */ |
||||||
|
GRPC_FINISH_ACCEPTED, |
||||||
|
/* writes_done or write_status has been accepted */ |
||||||
|
GRPC_CLIENT_METADATA_READ, |
||||||
|
/* The metadata array sent by server received at |
||||||
|
client */ |
||||||
|
GRPC_FINISHED, |
||||||
|
/* An RPC has finished. The event contains status. |
||||||
|
On the server this will be OK or Cancelled. */ |
||||||
|
GRPC_SERVER_RPC_NEW, |
||||||
|
/* A new RPC has arrived at the server */ |
||||||
|
GRPC_COMPLETION_DO_NOT_USE |
||||||
|
/* must be last, forces users to include |
||||||
|
a default: case */ |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// grpc_op_error from grpc/grpc.h |
||||||
|
/// </summary> |
||||||
|
internal enum GRPCOpError |
||||||
|
{ |
||||||
|
/* everything went ok */ |
||||||
|
GRPC_OP_OK = 0, |
||||||
|
/* something failed, we don't know what */ |
||||||
|
GRPC_OP_ERROR |
||||||
|
} |
||||||
|
} |
||||||
|
|
@ -0,0 +1,191 @@ |
|||||||
|
using System; |
||||||
|
using System.Runtime.InteropServices; |
||||||
|
using Google.GRPC.Core; |
||||||
|
|
||||||
|
namespace Google.GRPC.Core.Internal |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// grpc_event from grpc/grpc.h |
||||||
|
/// </summary> |
||||||
|
internal class EventSafeHandle : SafeHandleZeroIsInvalid |
||||||
|
{ |
||||||
|
[DllImport("libgrpc.so")] |
||||||
|
static extern void grpc_event_finish(IntPtr ev); |
||||||
|
|
||||||
|
[DllImport("libgrpc_csharp_ext.so")] |
||||||
|
static extern GRPCCompletionType grpc_event_type(EventSafeHandle ev); |
||||||
|
|
||||||
|
[DllImport("libgrpc_csharp_ext.so")] |
||||||
|
static extern CallSafeHandle grpc_event_call(EventSafeHandle ev); |
||||||
|
|
||||||
|
[DllImport("libgrpc_csharp_ext.so")] |
||||||
|
static extern GRPCOpError grpc_event_write_accepted(EventSafeHandle ev); |
||||||
|
|
||||||
|
[DllImport("libgrpc_csharp_ext.so")] |
||||||
|
static extern GRPCOpError grpc_event_finish_accepted(EventSafeHandle ev); |
||||||
|
|
||||||
|
[DllImport("libgrpc_csharp_ext.so")] |
||||||
|
static extern StatusCode grpc_event_finished_status(EventSafeHandle ev); |
||||||
|
|
||||||
|
[DllImport("libgrpc_csharp_ext.so")] |
||||||
|
static extern IntPtr grpc_event_finished_details(EventSafeHandle ev); // returns const char* |
||||||
|
|
||||||
|
[DllImport("libgrpc_csharp_ext.so")] |
||||||
|
static extern IntPtr grpc_event_read_length(EventSafeHandle ev); |
||||||
|
|
||||||
|
[DllImport("libgrpc_csharp_ext.so")] |
||||||
|
static extern void grpc_event_read_copy_to_buffer(EventSafeHandle ev, byte[] buffer, UIntPtr bufferLen); |
||||||
|
|
||||||
|
[DllImport("libgrpc_csharp_ext.so")] |
||||||
|
static extern IntPtr grpc_event_server_rpc_new_method(EventSafeHandle ev); // returns const char* |
||||||
|
|
||||||
|
public GRPCCompletionType GetCompletionType() |
||||||
|
{ |
||||||
|
return grpc_event_type(this); |
||||||
|
} |
||||||
|
|
||||||
|
public GRPCOpError GetWriteAccepted() |
||||||
|
{ |
||||||
|
return grpc_event_write_accepted(this); |
||||||
|
} |
||||||
|
|
||||||
|
public GRPCOpError GetFinishAccepted() |
||||||
|
{ |
||||||
|
return grpc_event_finish_accepted(this); |
||||||
|
} |
||||||
|
|
||||||
|
public Status GetFinished() |
||||||
|
{ |
||||||
|
// TODO: can the native method return string directly? |
||||||
|
string details = Marshal.PtrToStringAnsi(grpc_event_finished_details(this)); |
||||||
|
return new Status(grpc_event_finished_status(this), details); |
||||||
|
} |
||||||
|
|
||||||
|
public byte[] GetReadData() |
||||||
|
{ |
||||||
|
IntPtr len = grpc_event_read_length(this); |
||||||
|
if (len == new IntPtr(-1)) |
||||||
|
{ |
||||||
|
return null; |
||||||
|
} |
||||||
|
byte[] data = new byte[(int) len]; |
||||||
|
grpc_event_read_copy_to_buffer(this, data, new UIntPtr((ulong)data.Length)); |
||||||
|
return data; |
||||||
|
} |
||||||
|
|
||||||
|
public CallSafeHandle GetCall() { |
||||||
|
return grpc_event_call(this); |
||||||
|
} |
||||||
|
|
||||||
|
public string GetServerRpcNewMethod() { |
||||||
|
// TODO: can the native method return string directly? |
||||||
|
return Marshal.PtrToStringAnsi(grpc_event_server_rpc_new_method(this)); |
||||||
|
} |
||||||
|
|
||||||
|
//TODO: client_metadata_read event type |
||||||
|
|
||||||
|
protected override bool ReleaseHandle() |
||||||
|
{ |
||||||
|
grpc_event_finish(handle); |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// TODO: this is basically c&p of EventSafeHandle. Unify! |
||||||
|
/// <summary> |
||||||
|
/// Not owned version of |
||||||
|
/// grpc_event from grpc/grpc.h |
||||||
|
/// </summary> |
||||||
|
internal class EventSafeHandleNotOwned : SafeHandleZeroIsInvalid |
||||||
|
{ |
||||||
|
[DllImport("libgrpc.so")] |
||||||
|
static extern void grpc_event_finish(IntPtr ev); |
||||||
|
|
||||||
|
[DllImport("libgrpc_csharp_ext.so")] |
||||||
|
static extern GRPCCompletionType grpc_event_type(EventSafeHandleNotOwned ev); |
||||||
|
|
||||||
|
[DllImport("libgrpc_csharp_ext.so")] |
||||||
|
static extern CallSafeHandle grpc_event_call(EventSafeHandleNotOwned ev); |
||||||
|
|
||||||
|
[DllImport("libgrpc_csharp_ext.so")] |
||||||
|
static extern GRPCOpError grpc_event_write_accepted(EventSafeHandleNotOwned ev); |
||||||
|
|
||||||
|
[DllImport("libgrpc_csharp_ext.so")] |
||||||
|
static extern GRPCOpError grpc_event_finish_accepted(EventSafeHandleNotOwned ev); |
||||||
|
|
||||||
|
[DllImport("libgrpc_csharp_ext.so")] |
||||||
|
static extern StatusCode grpc_event_finished_status(EventSafeHandleNotOwned ev); |
||||||
|
|
||||||
|
[DllImport("libgrpc_csharp_ext.so")] |
||||||
|
static extern IntPtr grpc_event_finished_details(EventSafeHandleNotOwned ev); // returns const char* |
||||||
|
|
||||||
|
[DllImport("libgrpc_csharp_ext.so")] |
||||||
|
static extern IntPtr grpc_event_read_length(EventSafeHandleNotOwned ev); |
||||||
|
|
||||||
|
[DllImport("libgrpc_csharp_ext.so")] |
||||||
|
static extern void grpc_event_read_copy_to_buffer(EventSafeHandleNotOwned ev, byte[] buffer, UIntPtr bufferLen); |
||||||
|
|
||||||
|
[DllImport("libgrpc_csharp_ext.so")] |
||||||
|
static extern IntPtr grpc_event_server_rpc_new_method(EventSafeHandleNotOwned ev); // returns const char* |
||||||
|
|
||||||
|
public EventSafeHandleNotOwned() : base(false) |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
public EventSafeHandleNotOwned(IntPtr handle) : base(false) |
||||||
|
{ |
||||||
|
SetHandle(handle); |
||||||
|
} |
||||||
|
|
||||||
|
public GRPCCompletionType GetCompletionType() |
||||||
|
{ |
||||||
|
return grpc_event_type(this); |
||||||
|
} |
||||||
|
|
||||||
|
public GRPCOpError GetWriteAccepted() |
||||||
|
{ |
||||||
|
return grpc_event_write_accepted(this); |
||||||
|
} |
||||||
|
|
||||||
|
public GRPCOpError GetFinishAccepted() |
||||||
|
{ |
||||||
|
return grpc_event_finish_accepted(this); |
||||||
|
} |
||||||
|
|
||||||
|
public Status GetFinished() |
||||||
|
{ |
||||||
|
// TODO: can the native method return string directly? |
||||||
|
string details = Marshal.PtrToStringAnsi(grpc_event_finished_details(this)); |
||||||
|
return new Status(grpc_event_finished_status(this), details); |
||||||
|
} |
||||||
|
|
||||||
|
public byte[] GetReadData() |
||||||
|
{ |
||||||
|
IntPtr len = grpc_event_read_length(this); |
||||||
|
if (len == new IntPtr(-1)) |
||||||
|
{ |
||||||
|
return null; |
||||||
|
} |
||||||
|
byte[] data = new byte[(int) len]; |
||||||
|
grpc_event_read_copy_to_buffer(this, data, new UIntPtr((ulong)data.Length)); |
||||||
|
return data; |
||||||
|
} |
||||||
|
|
||||||
|
public CallSafeHandle GetCall() { |
||||||
|
return grpc_event_call(this); |
||||||
|
} |
||||||
|
|
||||||
|
public string GetServerRpcNewMethod() { |
||||||
|
// TODO: can the native method return string directly? |
||||||
|
return Marshal.PtrToStringAnsi(grpc_event_server_rpc_new_method(this)); |
||||||
|
} |
||||||
|
|
||||||
|
//TODO: client_metadata_read event type |
||||||
|
|
||||||
|
protected override bool ReleaseHandle() |
||||||
|
{ |
||||||
|
grpc_event_finish(handle); |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,129 @@ |
|||||||
|
using System; |
||||||
|
using Google.GRPC.Core.Internal; |
||||||
|
using System.Runtime.InteropServices; |
||||||
|
using System.Threading; |
||||||
|
using System.Threading.Tasks; |
||||||
|
using System.Collections.Generic; |
||||||
|
|
||||||
|
namespace Google.GRPC.Core.Internal |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// Pool of threads polling on the same completion queue. |
||||||
|
/// </summary> |
||||||
|
internal class GrpcThreadPool |
||||||
|
{ |
||||||
|
readonly object myLock = new object(); |
||||||
|
readonly List<Thread> threads = new List<Thread>(); |
||||||
|
readonly int poolSize; |
||||||
|
readonly Action<EventSafeHandle> eventHandler; |
||||||
|
|
||||||
|
CompletionQueueSafeHandle cq; |
||||||
|
|
||||||
|
public GrpcThreadPool(int poolSize) { |
||||||
|
this.poolSize = poolSize; |
||||||
|
} |
||||||
|
|
||||||
|
internal GrpcThreadPool(int poolSize, Action<EventSafeHandle> eventHandler) { |
||||||
|
this.poolSize = poolSize; |
||||||
|
this.eventHandler = eventHandler; |
||||||
|
} |
||||||
|
|
||||||
|
public void Start() { |
||||||
|
|
||||||
|
lock (myLock) |
||||||
|
{ |
||||||
|
if (cq != null) |
||||||
|
{ |
||||||
|
throw new InvalidOperationException("Already started."); |
||||||
|
} |
||||||
|
|
||||||
|
cq = CompletionQueueSafeHandle.Create(); |
||||||
|
|
||||||
|
for (int i = 0; i < poolSize; i++) |
||||||
|
{ |
||||||
|
threads.Add(CreateAndStartThread(i)); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void Stop() { |
||||||
|
|
||||||
|
lock (myLock) |
||||||
|
{ |
||||||
|
cq.Shutdown(); |
||||||
|
|
||||||
|
Console.WriteLine("Waiting for GPRC threads to finish."); |
||||||
|
foreach (var thread in threads) |
||||||
|
{ |
||||||
|
thread.Join(); |
||||||
|
} |
||||||
|
|
||||||
|
cq.Dispose(); |
||||||
|
|
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
internal CompletionQueueSafeHandle CompletionQueue |
||||||
|
{ |
||||||
|
get |
||||||
|
{ |
||||||
|
return cq; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private Thread CreateAndStartThread(int i) { |
||||||
|
Action body; |
||||||
|
if (eventHandler != null) |
||||||
|
{ |
||||||
|
body = ThreadBodyWithHandler; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
body = ThreadBodyNoHandler; |
||||||
|
} |
||||||
|
var thread = new Thread(new ThreadStart(body)); |
||||||
|
thread.IsBackground = false; |
||||||
|
thread.Start(); |
||||||
|
if (eventHandler != null) |
||||||
|
{ |
||||||
|
thread.Name = "grpc_server_newrpc " + i; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
thread.Name = "grpc " + i; |
||||||
|
} |
||||||
|
return thread; |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// Body of the polling thread. |
||||||
|
/// </summary> |
||||||
|
private void ThreadBodyNoHandler() |
||||||
|
{ |
||||||
|
GRPCCompletionType completionType; |
||||||
|
do |
||||||
|
{ |
||||||
|
completionType = cq.NextWithCallback(); |
||||||
|
} while(completionType != GRPCCompletionType.GRPC_QUEUE_SHUTDOWN); |
||||||
|
Console.WriteLine("Completion queue has shutdown successfully, thread " + Thread.CurrentThread.Name + " exiting."); |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// Body of the polling thread. |
||||||
|
/// </summary> |
||||||
|
private void ThreadBodyWithHandler() |
||||||
|
{ |
||||||
|
GRPCCompletionType completionType; |
||||||
|
do |
||||||
|
{ |
||||||
|
using (EventSafeHandle ev = cq.Next(Timespec.InfFuture)) { |
||||||
|
completionType = ev.GetCompletionType(); |
||||||
|
eventHandler(ev); |
||||||
|
} |
||||||
|
} while(completionType != GRPCCompletionType.GRPC_QUEUE_SHUTDOWN); |
||||||
|
Console.WriteLine("Completion queue has shutdown successfully, thread " + Thread.CurrentThread.Name + " exiting."); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
@ -0,0 +1,28 @@ |
|||||||
|
using System; |
||||||
|
using System.Runtime.InteropServices; |
||||||
|
|
||||||
|
namespace Google.GRPC.Core.Internal |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// Safe handle to wrap native objects. |
||||||
|
/// </summary> |
||||||
|
internal abstract class SafeHandleZeroIsInvalid : SafeHandle |
||||||
|
{ |
||||||
|
public SafeHandleZeroIsInvalid() : base(IntPtr.Zero, true) |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
public SafeHandleZeroIsInvalid(bool ownsHandle) : base(IntPtr.Zero, ownsHandle) |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
public override bool IsInvalid |
||||||
|
{ |
||||||
|
get |
||||||
|
{ |
||||||
|
return handle == IntPtr.Zero; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
@ -0,0 +1,81 @@ |
|||||||
|
using System; |
||||||
|
using System.Runtime.InteropServices; |
||||||
|
using System.Diagnostics; |
||||||
|
using System.Collections.Concurrent; |
||||||
|
|
||||||
|
namespace Google.GRPC.Core.Internal |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// grpc_server from grpc/grpc.h |
||||||
|
/// </summary> |
||||||
|
internal sealed class ServerSafeHandle : SafeHandleZeroIsInvalid |
||||||
|
{ |
||||||
|
[DllImport("libgrpc.so", EntryPoint = "grpc_server_request_call_old")] |
||||||
|
static extern GRPCCallError grpc_server_request_call_old_CALLBACK(ServerSafeHandle server, [MarshalAs(UnmanagedType.FunctionPtr)] EventCallbackDelegate callback); |
||||||
|
|
||||||
|
[DllImport("libgrpc.so")] |
||||||
|
static extern ServerSafeHandle grpc_server_create(CompletionQueueSafeHandle cq, IntPtr args); |
||||||
|
|
||||||
|
// TODO: check int representation size |
||||||
|
[DllImport("libgrpc.so")] |
||||||
|
static extern int grpc_server_add_http2_port(ServerSafeHandle server, string addr); |
||||||
|
|
||||||
|
// TODO: check int representation size |
||||||
|
[DllImport("libgrpc.so")] |
||||||
|
static extern int grpc_server_add_secure_http2_port(ServerSafeHandle server, string addr); |
||||||
|
|
||||||
|
[DllImport("libgrpc.so")] |
||||||
|
static extern void grpc_server_start(ServerSafeHandle server); |
||||||
|
|
||||||
|
[DllImport("libgrpc.so")] |
||||||
|
static extern void grpc_server_shutdown(ServerSafeHandle server); |
||||||
|
|
||||||
|
[DllImport("libgrpc.so", EntryPoint = "grpc_server_shutdown_and_notify")] |
||||||
|
static extern void grpc_server_shutdown_and_notify_CALLBACK(ServerSafeHandle server, [MarshalAs(UnmanagedType.FunctionPtr)] EventCallbackDelegate callback); |
||||||
|
|
||||||
|
[DllImport("libgrpc.so")] |
||||||
|
static extern void grpc_server_destroy(IntPtr server); |
||||||
|
|
||||||
|
private ServerSafeHandle() |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
public static ServerSafeHandle NewServer(CompletionQueueSafeHandle cq, IntPtr args) |
||||||
|
{ |
||||||
|
// TODO: also grpc_secure_server_create... |
||||||
|
return grpc_server_create(cq, args); |
||||||
|
} |
||||||
|
|
||||||
|
public int AddPort(string addr) |
||||||
|
{ |
||||||
|
// TODO: also grpc_server_add_secure_http2_port... |
||||||
|
return grpc_server_add_http2_port(this, addr); |
||||||
|
} |
||||||
|
|
||||||
|
public void Start() |
||||||
|
{ |
||||||
|
grpc_server_start(this); |
||||||
|
} |
||||||
|
|
||||||
|
public void Shutdown() |
||||||
|
{ |
||||||
|
grpc_server_shutdown(this); |
||||||
|
} |
||||||
|
|
||||||
|
public void ShutdownAndNotify(EventCallbackDelegate callback) |
||||||
|
{ |
||||||
|
grpc_server_shutdown_and_notify_CALLBACK(this, callback); |
||||||
|
} |
||||||
|
|
||||||
|
public GRPCCallError RequestCall(EventCallbackDelegate callback) |
||||||
|
{ |
||||||
|
return grpc_server_request_call_old_CALLBACK(this, callback); |
||||||
|
} |
||||||
|
|
||||||
|
protected override bool ReleaseHandle() |
||||||
|
{ |
||||||
|
grpc_server_destroy(handle); |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,38 @@ |
|||||||
|
using System; |
||||||
|
using Google.GRPC.Core.Internal; |
||||||
|
|
||||||
|
namespace Google.GRPC.Core.Internal |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// Observer that writes all arriving messages to a call abstraction (in blocking fashion) |
||||||
|
/// and then halfcloses the call. Used for server-side call handling. |
||||||
|
/// </summary> |
||||||
|
internal class ServerWritingObserver<TWrite, TRead> : IObserver<TWrite> |
||||||
|
{ |
||||||
|
readonly AsyncCall<TWrite, TRead> call; |
||||||
|
|
||||||
|
public ServerWritingObserver(AsyncCall<TWrite, TRead> call) |
||||||
|
{ |
||||||
|
this.call = call; |
||||||
|
} |
||||||
|
|
||||||
|
public void OnCompleted() |
||||||
|
{ |
||||||
|
// TODO: how bad is the Wait here? |
||||||
|
call.WriteStatusAsync(new Status(StatusCode.GRPC_STATUS_OK, "")).Wait(); |
||||||
|
} |
||||||
|
|
||||||
|
public void OnError(Exception error) |
||||||
|
{ |
||||||
|
// TODO: handle this... |
||||||
|
throw new InvalidOperationException("This should never be called."); |
||||||
|
} |
||||||
|
|
||||||
|
public void OnNext(TWrite value) |
||||||
|
{ |
||||||
|
// TODO: how bad is the Wait here? |
||||||
|
call.WriteAsync(value).Wait(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
@ -0,0 +1,33 @@ |
|||||||
|
using System; |
||||||
|
using Google.GRPC.Core.Internal; |
||||||
|
|
||||||
|
namespace Google.GRPC.Core.Internal |
||||||
|
{ |
||||||
|
internal class StreamingInputObserver<TWrite, TRead> : IObserver<TWrite> |
||||||
|
{ |
||||||
|
readonly AsyncCall<TWrite, TRead> call; |
||||||
|
|
||||||
|
public StreamingInputObserver(AsyncCall<TWrite, TRead> call) |
||||||
|
{ |
||||||
|
this.call = call; |
||||||
|
} |
||||||
|
|
||||||
|
public void OnCompleted() |
||||||
|
{ |
||||||
|
// TODO: how bad is the Wait here? |
||||||
|
call.WritesCompletedAsync().Wait(); |
||||||
|
} |
||||||
|
|
||||||
|
public void OnError(Exception error) |
||||||
|
{ |
||||||
|
throw new InvalidOperationException("This should never be called."); |
||||||
|
} |
||||||
|
|
||||||
|
public void OnNext(TWrite value) |
||||||
|
{ |
||||||
|
// TODO: how bad is the Wait here? |
||||||
|
call.WriteAsync(value).Wait(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
@ -0,0 +1,67 @@ |
|||||||
|
using System; |
||||||
|
using System.Runtime.InteropServices; |
||||||
|
using System.Threading; |
||||||
|
|
||||||
|
namespace Google.GRPC.Core.Internal |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// gpr_timespec from grpc/support/time.h |
||||||
|
/// </summary> |
||||||
|
[StructLayout(LayoutKind.Sequential)] |
||||||
|
internal struct Timespec |
||||||
|
{ |
||||||
|
const int nanosPerSecond = 1000 * 1000 * 1000; |
||||||
|
const int nanosPerTick = 100; |
||||||
|
|
||||||
|
[DllImport("libgpr.so")] |
||||||
|
static extern Timespec gpr_now(); |
||||||
|
|
||||||
|
// TODO: this only works on 64bit linux, can we autoselect the right size of ints? |
||||||
|
// perhaps using IntPtr would work. |
||||||
|
public System.Int64 tv_sec; |
||||||
|
public System.Int64 tv_nsec; |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// Timespec a long time in the future. |
||||||
|
/// </summary> |
||||||
|
public static Timespec InfFuture |
||||||
|
{ |
||||||
|
get |
||||||
|
{ |
||||||
|
// TODO: set correct value based on the length of the struct |
||||||
|
return new Timespec { tv_sec = Int32.MaxValue, tv_nsec = 0 }; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public static Timespec Now |
||||||
|
{ |
||||||
|
get |
||||||
|
{ |
||||||
|
return gpr_now(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// Creates a GPR deadline from current instant and given timeout. |
||||||
|
/// </summary> |
||||||
|
/// <returns>The from timeout.</returns> |
||||||
|
public static Timespec DeadlineFromTimeout(TimeSpan timeout) { |
||||||
|
if (timeout == Timeout.InfiniteTimeSpan) |
||||||
|
{ |
||||||
|
return Timespec.InfFuture; |
||||||
|
} |
||||||
|
return Timespec.Now.Add(timeout); |
||||||
|
} |
||||||
|
|
||||||
|
public Timespec Add(TimeSpan timeSpan) { |
||||||
|
long nanos = tv_nsec + (timeSpan.Ticks % TimeSpan.TicksPerSecond) * nanosPerTick; |
||||||
|
long overflow_sec = (nanos > nanosPerSecond) ? 1 : 0; |
||||||
|
|
||||||
|
Timespec result; |
||||||
|
result.tv_nsec = nanos % nanosPerSecond; |
||||||
|
result.tv_sec = tv_sec + (timeSpan.Ticks / TimeSpan.TicksPerSecond) + overflow_sec; |
||||||
|
return result; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
@ -0,0 +1,54 @@ |
|||||||
|
using System; |
||||||
|
|
||||||
|
namespace Google.GRPC.Core |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// For serializing and deserializing messages. |
||||||
|
/// </summary> |
||||||
|
public struct Marshaller<T> |
||||||
|
{ |
||||||
|
readonly Func<T,byte[]> serializer; |
||||||
|
readonly Func<byte[],T> deserializer; |
||||||
|
|
||||||
|
public Marshaller(Func<T, byte[]> serializer, Func<byte[], T> deserializer) |
||||||
|
{ |
||||||
|
this.serializer = serializer; |
||||||
|
this.deserializer = deserializer; |
||||||
|
} |
||||||
|
|
||||||
|
public Func<T, byte[]> Serializer |
||||||
|
{ |
||||||
|
get |
||||||
|
{ |
||||||
|
return this.serializer; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public Func<byte[], T> Deserializer |
||||||
|
{ |
||||||
|
get |
||||||
|
{ |
||||||
|
return this.deserializer; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public static class Marshallers { |
||||||
|
|
||||||
|
public static Marshaller<T> Create<T>(Func<T,byte[]> serializer, Func<byte[],T> deserializer) |
||||||
|
{ |
||||||
|
return new Marshaller<T>(serializer, deserializer); |
||||||
|
} |
||||||
|
|
||||||
|
public static Marshaller<string> StringMarshaller |
||||||
|
{ |
||||||
|
get |
||||||
|
{ |
||||||
|
return new Marshaller<string>(System.Text.Encoding.UTF8.GetBytes, |
||||||
|
System.Text.Encoding.UTF8.GetString); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
} |
||||||
|
|
@ -0,0 +1,64 @@ |
|||||||
|
using System; |
||||||
|
|
||||||
|
namespace Google.GRPC.Core |
||||||
|
{ |
||||||
|
public enum MethodType |
||||||
|
{ |
||||||
|
Unary, |
||||||
|
ClientStreaming, |
||||||
|
ServerStreaming, |
||||||
|
DuplexStreaming |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// A description of a service method. |
||||||
|
/// </summary> |
||||||
|
public class Method<TRequest, TResponse> |
||||||
|
{ |
||||||
|
readonly MethodType type; |
||||||
|
readonly string name; |
||||||
|
readonly Marshaller<TRequest> requestMarshaller; |
||||||
|
readonly Marshaller<TResponse> responseMarshaller; |
||||||
|
|
||||||
|
public Method(MethodType type, string name, Marshaller<TRequest> requestMarshaller, Marshaller<TResponse> responseMarshaller) |
||||||
|
{ |
||||||
|
this.type = type; |
||||||
|
this.name = name; |
||||||
|
this.requestMarshaller = requestMarshaller; |
||||||
|
this.responseMarshaller = responseMarshaller; |
||||||
|
} |
||||||
|
|
||||||
|
public MethodType Type |
||||||
|
{ |
||||||
|
get |
||||||
|
{ |
||||||
|
return this.type; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public string Name |
||||||
|
{ |
||||||
|
get |
||||||
|
{ |
||||||
|
return this.name; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public Marshaller<TRequest> RequestMarshaller |
||||||
|
{ |
||||||
|
get |
||||||
|
{ |
||||||
|
return this.requestMarshaller; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public Marshaller<TResponse> ResponseMarshaller |
||||||
|
{ |
||||||
|
get |
||||||
|
{ |
||||||
|
return this.responseMarshaller; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
@ -0,0 +1,24 @@ |
|||||||
|
using System.Reflection; |
||||||
|
using System.Runtime.CompilerServices; |
||||||
|
|
||||||
|
// Information about this assembly is defined by the following attributes. |
||||||
|
// Change them to the values specific to your project. |
||||||
|
[assembly: AssemblyTitle ("GrpcCore")] |
||||||
|
[assembly: AssemblyDescription ("")] |
||||||
|
[assembly: AssemblyConfiguration ("")] |
||||||
|
[assembly: AssemblyCompany ("")] |
||||||
|
[assembly: AssemblyProduct ("")] |
||||||
|
[assembly: AssemblyCopyright ("jtattermusch")] |
||||||
|
[assembly: AssemblyTrademark ("")] |
||||||
|
[assembly: AssemblyCulture ("")] |
||||||
|
// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". |
||||||
|
// The form "{Major}.{Minor}.*" will automatically update the build and revision, |
||||||
|
// and "{Major}.{Minor}.{Build}.*" will update just the revision. |
||||||
|
[assembly: AssemblyVersion ("1.0.*")] |
||||||
|
// The following attributes are used to specify the signing key for the assembly, |
||||||
|
// if desired. See the Mono documentation for more information about signing. |
||||||
|
//[assembly: AssemblyDelaySign(false)] |
||||||
|
//[assembly: AssemblyKeyFile("")] |
||||||
|
|
||||||
|
[assembly: InternalsVisibleTo("GrpcCoreTests")] |
||||||
|
|
@ -0,0 +1,27 @@ |
|||||||
|
using System; |
||||||
|
|
||||||
|
namespace Google.GRPC.Core |
||||||
|
{ |
||||||
|
public class RpcException : Exception |
||||||
|
{ |
||||||
|
private readonly Status status; |
||||||
|
|
||||||
|
public RpcException(Status status) |
||||||
|
{ |
||||||
|
this.status = status; |
||||||
|
} |
||||||
|
|
||||||
|
public RpcException(Status status, string message) : base(message) |
||||||
|
{ |
||||||
|
this.status = status; |
||||||
|
} |
||||||
|
|
||||||
|
public Status Status { |
||||||
|
get |
||||||
|
{ |
||||||
|
return status; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
@ -0,0 +1,183 @@ |
|||||||
|
using System; |
||||||
|
using System.Runtime.InteropServices; |
||||||
|
using System.Diagnostics; |
||||||
|
using System.Threading.Tasks; |
||||||
|
using System.Collections.Concurrent; |
||||||
|
using System.Collections.Generic; |
||||||
|
using Google.GRPC.Core.Internal; |
||||||
|
|
||||||
|
namespace Google.GRPC.Core |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// Server is implemented only to be able to do |
||||||
|
/// in-process testing. |
||||||
|
/// </summary> |
||||||
|
public class Server |
||||||
|
{ |
||||||
|
// TODO: make sure the delegate doesn't get garbage collected while |
||||||
|
// native callbacks are in the completion queue. |
||||||
|
readonly EventCallbackDelegate newRpcHandler; |
||||||
|
readonly EventCallbackDelegate serverShutdownHandler; |
||||||
|
|
||||||
|
readonly BlockingCollection<NewRpcInfo> newRpcQueue = new BlockingCollection<NewRpcInfo>(); |
||||||
|
readonly ServerSafeHandle handle; |
||||||
|
|
||||||
|
readonly Dictionary<string, IServerCallHandler> callHandlers = new Dictionary<string, IServerCallHandler>(); |
||||||
|
|
||||||
|
readonly TaskCompletionSource<object> shutdownTcs = new TaskCompletionSource<object>(); |
||||||
|
|
||||||
|
static Server() { |
||||||
|
GrpcEnvironment.EnsureInitialized(); |
||||||
|
} |
||||||
|
|
||||||
|
public Server() |
||||||
|
{ |
||||||
|
// TODO: what is the tag for server shutdown? |
||||||
|
this.handle = ServerSafeHandle.NewServer(GetCompletionQueue(), IntPtr.Zero); |
||||||
|
this.newRpcHandler = HandleNewRpc; |
||||||
|
this.serverShutdownHandler = HandleServerShutdown; |
||||||
|
} |
||||||
|
|
||||||
|
// only call this before Start() |
||||||
|
public void AddServiceDefinition(ServerServiceDefinition serviceDefinition) { |
||||||
|
foreach(var entry in serviceDefinition.CallHandlers) |
||||||
|
{ |
||||||
|
callHandlers.Add(entry.Key, entry.Value); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// only call before Start() |
||||||
|
public int AddPort(string addr) { |
||||||
|
return handle.AddPort(addr); |
||||||
|
} |
||||||
|
|
||||||
|
public void Start() |
||||||
|
{ |
||||||
|
handle.Start(); |
||||||
|
|
||||||
|
// TODO: this basically means the server is single threaded.... |
||||||
|
StartHandlingRpcs(); |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// Requests and handles single RPC call. |
||||||
|
/// </summary> |
||||||
|
internal void RunRpc() |
||||||
|
{ |
||||||
|
AllowOneRpc(); |
||||||
|
|
||||||
|
try |
||||||
|
{ |
||||||
|
var rpcInfo = newRpcQueue.Take(); |
||||||
|
|
||||||
|
Console.WriteLine("Server received RPC " + rpcInfo.Method); |
||||||
|
|
||||||
|
IServerCallHandler callHandler; |
||||||
|
if (!callHandlers.TryGetValue(rpcInfo.Method, out callHandler)) |
||||||
|
{ |
||||||
|
callHandler = new NoSuchMethodCallHandler(); |
||||||
|
} |
||||||
|
callHandler.StartCall(rpcInfo.Method, rpcInfo.Call, GetCompletionQueue()); |
||||||
|
} |
||||||
|
catch(Exception e) |
||||||
|
{ |
||||||
|
Console.WriteLine("Exception while handling RPC: " + e); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// Requests server shutdown and when there are no more calls being serviced, |
||||||
|
/// cleans up used resources. |
||||||
|
/// </summary> |
||||||
|
/// <returns>The async.</returns> |
||||||
|
public async Task ShutdownAsync() { |
||||||
|
handle.ShutdownAndNotify(serverShutdownHandler); |
||||||
|
await shutdownTcs.Task; |
||||||
|
handle.Dispose(); |
||||||
|
} |
||||||
|
|
||||||
|
public void Kill() { |
||||||
|
handle.Dispose(); |
||||||
|
} |
||||||
|
|
||||||
|
private async Task StartHandlingRpcs() { |
||||||
|
while (true) |
||||||
|
{ |
||||||
|
await Task.Factory.StartNew(RunRpc); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void AllowOneRpc() |
||||||
|
{ |
||||||
|
AssertCallOk(handle.RequestCall(newRpcHandler)); |
||||||
|
} |
||||||
|
|
||||||
|
private void HandleNewRpc(IntPtr eventPtr) |
||||||
|
{ |
||||||
|
try |
||||||
|
{ |
||||||
|
var ev = new EventSafeHandleNotOwned(eventPtr); |
||||||
|
var rpcInfo = new NewRpcInfo(ev.GetCall(), ev.GetServerRpcNewMethod()); |
||||||
|
|
||||||
|
// after server shutdown, the callback returns with null call |
||||||
|
if (!rpcInfo.Call.IsInvalid) { |
||||||
|
newRpcQueue.Add(rpcInfo); |
||||||
|
} |
||||||
|
} |
||||||
|
catch (Exception e) |
||||||
|
{ |
||||||
|
Console.WriteLine("Caught exception in a native handler: " + e); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void HandleServerShutdown(IntPtr eventPtr) |
||||||
|
{ |
||||||
|
try |
||||||
|
{ |
||||||
|
shutdownTcs.SetResult(null); |
||||||
|
} |
||||||
|
catch (Exception e) |
||||||
|
{ |
||||||
|
Console.WriteLine("Caught exception in a native handler: " + e); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private static void AssertCallOk(GRPCCallError callError) |
||||||
|
{ |
||||||
|
Trace.Assert(callError == GRPCCallError.GRPC_CALL_OK, "Status not GRPC_CALL_OK"); |
||||||
|
} |
||||||
|
|
||||||
|
private static CompletionQueueSafeHandle GetCompletionQueue() |
||||||
|
{ |
||||||
|
return GrpcEnvironment.ThreadPool.CompletionQueue; |
||||||
|
} |
||||||
|
|
||||||
|
private struct NewRpcInfo |
||||||
|
{ |
||||||
|
private CallSafeHandle call; |
||||||
|
private string method; |
||||||
|
|
||||||
|
public NewRpcInfo(CallSafeHandle call, string method) |
||||||
|
{ |
||||||
|
this.call = call; |
||||||
|
this.method = method; |
||||||
|
} |
||||||
|
|
||||||
|
public CallSafeHandle Call |
||||||
|
{ |
||||||
|
get |
||||||
|
{ |
||||||
|
return this.call; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public string Method |
||||||
|
{ |
||||||
|
get |
||||||
|
{ |
||||||
|
return this.method; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue