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