diff --git a/.gitignore b/.gitignore index 99ed843..50fbd7a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,5 @@ dist .stack-work/ +*.pyc +*.pyo +*.o diff --git a/examples/echo/echo-client/Main.hs b/examples/echo/echo-client/Main.hs new file mode 100644 index 0000000..f6377cb --- /dev/null +++ b/examples/echo/echo-client/Main.hs @@ -0,0 +1,37 @@ +{-# LANGUAGE OverloadedStrings #-} + +import Control.Concurrent (threadDelay) +import Control.Monad (forever) +import Data.ByteString () +import qualified Data.Map as M +import Network.GRPC.LowLevel + +echoMethod :: MethodName +echoMethod = MethodName "/echo.Echo/DoEcho" + +ntimes :: Int -> IO () -> IO () +ntimes 1 f = f +ntimes n f = f >> (ntimes (n-1) f) + +unregClient :: IO () +unregClient = do + withGRPC $ \grpc -> + withClient grpc (ClientConfig "localhost" 50051) $ \client -> + ntimes 100000 $ do + reqResult <- clientRequest client echoMethod "localhost:50051" 1 "hi" M.empty + case reqResult of + Left x -> error $ "Got client error: " ++ show x + Right resp -> return () + +regClient :: IO () +regClient = do + withGRPC $ \grpc -> + withClient grpc (ClientConfig "localhost" 50051) $ \client -> ntimes 100000 $ do + regMethod <- clientRegisterMethod client echoMethod "localhost:50051" Normal + reqResult <- clientRegisteredRequest client regMethod 1 "hi" M.empty + case reqResult of + Left x -> error $ "Got client error: " ++ show x + Right resp -> return () + +main :: IO () +main = regClient diff --git a/examples/echo/echo-cpp/Makefile b/examples/echo/echo-cpp/Makefile new file mode 100644 index 0000000..63df926 --- /dev/null +++ b/examples/echo/echo-cpp/Makefile @@ -0,0 +1,10 @@ +CXX = clang++ +CXXFLAGS += -std=c++11 +CPPFLAGS += -I/usr/local/include -pthread +LDFLAGS += -L/usr/local/lib -lgrpc++_unsecure -lgrpc -lprotobuf -lpthread -ldl + +echo-server: echo.pb.o echo.grpc.pb.o echo-server.o + clang++ $^ $(LDFLAGS) -o $@ + +echo-client: echo.pb.o echo.grpc.pb.o echo-client.o + clang++ $^ $(LDFLAGS) -o $@ diff --git a/examples/echo/echo-cpp/echo-client b/examples/echo/echo-cpp/echo-client new file mode 100755 index 0000000..6956847 Binary files /dev/null and b/examples/echo/echo-cpp/echo-client differ diff --git a/examples/echo/echo-cpp/echo-client.cc b/examples/echo/echo-cpp/echo-client.cc new file mode 100644 index 0000000..13ad30d --- /dev/null +++ b/examples/echo/echo-cpp/echo-client.cc @@ -0,0 +1,48 @@ +#include +#include + +#include + +#include "echo.grpc.pb.h" + +using namespace std; + +using grpc::Channel; +using grpc::ClientContext; +using grpc::Status; +using echo::EchoRequest; +using echo::Echo; + +class EchoClient { +public: + EchoClient(shared_ptr chan) : stub_(Echo::NewStub(chan)) {} + + Status DoEcho(const string& msg){ + EchoRequest req; + req.set_message(msg); + + EchoRequest resp; + + ClientContext ctx; + + return stub_->DoEcho(&ctx, req, &resp); + } + +private: + unique_ptr stub_; +}; + +int main(){ + EchoClient client(grpc::CreateChannel("localhost:50051", + grpc::InsecureChannelCredentials())); + string msg("hi"); + for(int i = 0; i < 100000; i++){ + Status status = client.DoEcho(msg); + if(!status.ok()){ + cout<<"Error: "< + +#include +#include "echo.grpc.pb.h" + +using namespace std; + +using grpc::Server; +using grpc::ServerBuilder; +using grpc::ServerContext; +using grpc::Status; + +using echo::EchoRequest; +using echo::Echo; + +class EchoServiceImpl final : public Echo::Service { + Status DoEcho(ServerContext* ctx, const EchoRequest* req, + EchoRequest* resp) override { + resp->set_message(req->message()); + return Status::OK; + } +}; + +int main(){ + string server_address("localhost:50051"); + EchoServiceImpl service; + + ServerBuilder builder; + builder.AddListeningPort(server_address, grpc::InsecureServerCredentials()); + builder.RegisterService(&service); + unique_ptr server(builder.BuildAndStart()); + server->Wait(); + return 0; +} diff --git a/examples/echo/echo-cpp/echo.grpc.pb.cc b/examples/echo/echo-cpp/echo.grpc.pb.cc new file mode 100644 index 0000000..cc65070 --- /dev/null +++ b/examples/echo/echo-cpp/echo.grpc.pb.cc @@ -0,0 +1,59 @@ +// Generated by the gRPC protobuf plugin. +// If you make any local change, they will be lost. +// source: echo.proto + +#include "echo.pb.h" +#include "echo.grpc.pb.h" + +#include +#include +#include +#include +#include +#include +#include +#include +namespace echo { + +static const char* Echo_method_names[] = { + "/echo.Echo/DoEcho", +}; + +std::unique_ptr< Echo::Stub> Echo::NewStub(const std::shared_ptr< ::grpc::ChannelInterface>& channel, const ::grpc::StubOptions& options) { + std::unique_ptr< Echo::Stub> stub(new Echo::Stub(channel)); + return stub; +} + +Echo::Stub::Stub(const std::shared_ptr< ::grpc::ChannelInterface>& channel) + : channel_(channel), rpcmethod_DoEcho_(Echo_method_names[0], ::grpc::RpcMethod::NORMAL_RPC, channel) + {} + +::grpc::Status Echo::Stub::DoEcho(::grpc::ClientContext* context, const ::echo::EchoRequest& request, ::echo::EchoRequest* response) { + return ::grpc::BlockingUnaryCall(channel_.get(), rpcmethod_DoEcho_, context, request, response); +} + +::grpc::ClientAsyncResponseReader< ::echo::EchoRequest>* Echo::Stub::AsyncDoEchoRaw(::grpc::ClientContext* context, const ::echo::EchoRequest& request, ::grpc::CompletionQueue* cq) { + return new ::grpc::ClientAsyncResponseReader< ::echo::EchoRequest>(channel_.get(), cq, rpcmethod_DoEcho_, context, request); +} + +Echo::Service::Service() { + AddMethod(new ::grpc::RpcServiceMethod( + Echo_method_names[0], + ::grpc::RpcMethod::NORMAL_RPC, + new ::grpc::RpcMethodHandler< Echo::Service, ::echo::EchoRequest, ::echo::EchoRequest>( + std::mem_fn(&Echo::Service::DoEcho), this))); +} + +Echo::Service::~Service() { +} + +::grpc::Status Echo::Service::DoEcho(::grpc::ServerContext* context, const ::echo::EchoRequest* request, ::echo::EchoRequest* response) { + (void) context; + (void) request; + (void) response; + return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); +} + + +} // namespace echo + diff --git a/examples/echo/echo-cpp/echo.grpc.pb.h b/examples/echo/echo-cpp/echo.grpc.pb.h new file mode 100644 index 0000000..df1c128 --- /dev/null +++ b/examples/echo/echo-cpp/echo.grpc.pb.h @@ -0,0 +1,103 @@ +// Generated by the gRPC protobuf plugin. +// If you make any local change, they will be lost. +// source: echo.proto +#ifndef GRPC_echo_2eproto__INCLUDED +#define GRPC_echo_2eproto__INCLUDED + +#include "echo.pb.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace grpc { +class CompletionQueue; +class RpcService; +class ServerCompletionQueue; +class ServerContext; +} // namespace grpc + +namespace echo { + +class Echo GRPC_FINAL { + public: + class StubInterface { + public: + virtual ~StubInterface() {} + virtual ::grpc::Status DoEcho(::grpc::ClientContext* context, const ::echo::EchoRequest& request, ::echo::EchoRequest* response) = 0; + std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::echo::EchoRequest>> AsyncDoEcho(::grpc::ClientContext* context, const ::echo::EchoRequest& request, ::grpc::CompletionQueue* cq) { + return std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::echo::EchoRequest>>(AsyncDoEchoRaw(context, request, cq)); + } + private: + virtual ::grpc::ClientAsyncResponseReaderInterface< ::echo::EchoRequest>* AsyncDoEchoRaw(::grpc::ClientContext* context, const ::echo::EchoRequest& request, ::grpc::CompletionQueue* cq) = 0; + }; + class Stub GRPC_FINAL : public StubInterface { + public: + Stub(const std::shared_ptr< ::grpc::ChannelInterface>& channel); + ::grpc::Status DoEcho(::grpc::ClientContext* context, const ::echo::EchoRequest& request, ::echo::EchoRequest* response) GRPC_OVERRIDE; + std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::echo::EchoRequest>> AsyncDoEcho(::grpc::ClientContext* context, const ::echo::EchoRequest& request, ::grpc::CompletionQueue* cq) { + return std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::echo::EchoRequest>>(AsyncDoEchoRaw(context, request, cq)); + } + + private: + std::shared_ptr< ::grpc::ChannelInterface> channel_; + ::grpc::ClientAsyncResponseReader< ::echo::EchoRequest>* AsyncDoEchoRaw(::grpc::ClientContext* context, const ::echo::EchoRequest& request, ::grpc::CompletionQueue* cq) GRPC_OVERRIDE; + const ::grpc::RpcMethod rpcmethod_DoEcho_; + }; + static std::unique_ptr NewStub(const std::shared_ptr< ::grpc::ChannelInterface>& channel, const ::grpc::StubOptions& options = ::grpc::StubOptions()); + + class Service : public ::grpc::Service { + public: + Service(); + virtual ~Service(); + virtual ::grpc::Status DoEcho(::grpc::ServerContext* context, const ::echo::EchoRequest* request, ::echo::EchoRequest* response); + }; + template + class WithAsyncMethod_DoEcho : public BaseClass { + private: + void BaseClassMustBeDerivedFromService(Service *service) {} + public: + WithAsyncMethod_DoEcho() { + ::grpc::Service::MarkMethodAsync(0); + } + ~WithAsyncMethod_DoEcho() GRPC_OVERRIDE { + BaseClassMustBeDerivedFromService(this); + } + // disable synchronous version of this method + ::grpc::Status DoEcho(::grpc::ServerContext* context, const ::echo::EchoRequest* request, ::echo::EchoRequest* response) GRPC_FINAL GRPC_OVERRIDE { + abort(); + return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); + } + void RequestDoEcho(::grpc::ServerContext* context, ::echo::EchoRequest* request, ::grpc::ServerAsyncResponseWriter< ::echo::EchoRequest>* response, ::grpc::CompletionQueue* new_call_cq, ::grpc::ServerCompletionQueue* notification_cq, void *tag) { + ::grpc::Service::RequestAsyncUnary(0, context, request, response, new_call_cq, notification_cq, tag); + } + }; + typedef WithAsyncMethod_DoEcho AsyncService; + template + class WithGenericMethod_DoEcho : public BaseClass { + private: + void BaseClassMustBeDerivedFromService(Service *service) {} + public: + WithGenericMethod_DoEcho() { + ::grpc::Service::MarkMethodGeneric(0); + } + ~WithGenericMethod_DoEcho() GRPC_OVERRIDE { + BaseClassMustBeDerivedFromService(this); + } + // disable synchronous version of this method + ::grpc::Status DoEcho(::grpc::ServerContext* context, const ::echo::EchoRequest* request, ::echo::EchoRequest* response) GRPC_FINAL GRPC_OVERRIDE { + abort(); + return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); + } + }; +}; + +} // namespace echo + + +#endif // GRPC_echo_2eproto__INCLUDED diff --git a/examples/echo/echo-cpp/echo.pb.cc b/examples/echo/echo-cpp/echo.pb.cc new file mode 100644 index 0000000..c68cac1 --- /dev/null +++ b/examples/echo/echo-cpp/echo.pb.cc @@ -0,0 +1,384 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: echo.proto + +#define INTERNAL_SUPPRESS_PROTOBUF_FIELD_DEPRECATION +#include "echo.pb.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +// @@protoc_insertion_point(includes) + +namespace echo { + +namespace { + +const ::google::protobuf::Descriptor* EchoRequest_descriptor_ = NULL; +const ::google::protobuf::internal::GeneratedMessageReflection* + EchoRequest_reflection_ = NULL; + +} // namespace + + +void protobuf_AssignDesc_echo_2eproto() { + protobuf_AddDesc_echo_2eproto(); + const ::google::protobuf::FileDescriptor* file = + ::google::protobuf::DescriptorPool::generated_pool()->FindFileByName( + "echo.proto"); + GOOGLE_CHECK(file != NULL); + EchoRequest_descriptor_ = file->message_type(0); + static const int EchoRequest_offsets_[1] = { + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(EchoRequest, message_), + }; + EchoRequest_reflection_ = + ::google::protobuf::internal::GeneratedMessageReflection::NewGeneratedMessageReflection( + EchoRequest_descriptor_, + EchoRequest::default_instance_, + EchoRequest_offsets_, + -1, + -1, + -1, + sizeof(EchoRequest), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(EchoRequest, _internal_metadata_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(EchoRequest, _is_default_instance_)); +} + +namespace { + +GOOGLE_PROTOBUF_DECLARE_ONCE(protobuf_AssignDescriptors_once_); +inline void protobuf_AssignDescriptorsOnce() { + ::google::protobuf::GoogleOnceInit(&protobuf_AssignDescriptors_once_, + &protobuf_AssignDesc_echo_2eproto); +} + +void protobuf_RegisterTypes(const ::std::string&) { + protobuf_AssignDescriptorsOnce(); + ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage( + EchoRequest_descriptor_, &EchoRequest::default_instance()); +} + +} // namespace + +void protobuf_ShutdownFile_echo_2eproto() { + delete EchoRequest::default_instance_; + delete EchoRequest_reflection_; +} + +void protobuf_AddDesc_echo_2eproto() { + static bool already_here = false; + if (already_here) return; + already_here = true; + GOOGLE_PROTOBUF_VERIFY_VERSION; + + ::google::protobuf::DescriptorPool::InternalAddGeneratedFile( + "\n\necho.proto\022\004echo\"\036\n\013EchoRequest\022\017\n\007mes" + "sage\030\001 \001(\t28\n\004Echo\0220\n\006DoEcho\022\021.echo.Echo" + "Request\032\021.echo.EchoRequest\"\000b\006proto3", 116); + ::google::protobuf::MessageFactory::InternalRegisterGeneratedFile( + "echo.proto", &protobuf_RegisterTypes); + EchoRequest::default_instance_ = new EchoRequest(); + EchoRequest::default_instance_->InitAsDefaultInstance(); + ::google::protobuf::internal::OnShutdown(&protobuf_ShutdownFile_echo_2eproto); +} + +// Force AddDescriptors() to be called at static initialization time. +struct StaticDescriptorInitializer_echo_2eproto { + StaticDescriptorInitializer_echo_2eproto() { + protobuf_AddDesc_echo_2eproto(); + } +} static_descriptor_initializer_echo_2eproto_; + +namespace { + +static void MergeFromFail(int line) GOOGLE_ATTRIBUTE_COLD; +static void MergeFromFail(int line) { + GOOGLE_CHECK(false) << __FILE__ << ":" << line; +} + +} // namespace + + +// =================================================================== + +#if !defined(_MSC_VER) || _MSC_VER >= 1900 +const int EchoRequest::kMessageFieldNumber; +#endif // !defined(_MSC_VER) || _MSC_VER >= 1900 + +EchoRequest::EchoRequest() + : ::google::protobuf::Message(), _internal_metadata_(NULL) { + SharedCtor(); + // @@protoc_insertion_point(constructor:echo.EchoRequest) +} + +void EchoRequest::InitAsDefaultInstance() { + _is_default_instance_ = true; +} + +EchoRequest::EchoRequest(const EchoRequest& from) + : ::google::protobuf::Message(), + _internal_metadata_(NULL) { + SharedCtor(); + MergeFrom(from); + // @@protoc_insertion_point(copy_constructor:echo.EchoRequest) +} + +void EchoRequest::SharedCtor() { + _is_default_instance_ = false; + ::google::protobuf::internal::GetEmptyString(); + _cached_size_ = 0; + message_.UnsafeSetDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} + +EchoRequest::~EchoRequest() { + // @@protoc_insertion_point(destructor:echo.EchoRequest) + SharedDtor(); +} + +void EchoRequest::SharedDtor() { + message_.DestroyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); + if (this != default_instance_) { + } +} + +void EchoRequest::SetCachedSize(int size) const { + GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); + _cached_size_ = size; + GOOGLE_SAFE_CONCURRENT_WRITES_END(); +} +const ::google::protobuf::Descriptor* EchoRequest::descriptor() { + protobuf_AssignDescriptorsOnce(); + return EchoRequest_descriptor_; +} + +const EchoRequest& EchoRequest::default_instance() { + if (default_instance_ == NULL) protobuf_AddDesc_echo_2eproto(); + return *default_instance_; +} + +EchoRequest* EchoRequest::default_instance_ = NULL; + +EchoRequest* EchoRequest::New(::google::protobuf::Arena* arena) const { + EchoRequest* n = new EchoRequest; + if (arena != NULL) { + arena->Own(n); + } + return n; +} + +void EchoRequest::Clear() { + message_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} + +bool EchoRequest::MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input) { +#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure + ::google::protobuf::uint32 tag; + // @@protoc_insertion_point(parse_start:echo.EchoRequest) + for (;;) { + ::std::pair< ::google::protobuf::uint32, bool> p = input->ReadTagWithCutoff(127); + tag = p.first; + if (!p.second) goto handle_unusual; + switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) { + // optional string message = 1; + case 1: { + if (tag == 10) { + DO_(::google::protobuf::internal::WireFormatLite::ReadString( + input, this->mutable_message())); + DO_(::google::protobuf::internal::WireFormatLite::VerifyUtf8String( + this->message().data(), this->message().length(), + ::google::protobuf::internal::WireFormatLite::PARSE, + "echo.EchoRequest.message")); + } else { + goto handle_unusual; + } + if (input->ExpectAtEnd()) goto success; + break; + } + + default: { + handle_unusual: + if (tag == 0 || + ::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) { + goto success; + } + DO_(::google::protobuf::internal::WireFormatLite::SkipField(input, tag)); + break; + } + } + } +success: + // @@protoc_insertion_point(parse_success:echo.EchoRequest) + return true; +failure: + // @@protoc_insertion_point(parse_failure:echo.EchoRequest) + return false; +#undef DO_ +} + +void EchoRequest::SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const { + // @@protoc_insertion_point(serialize_start:echo.EchoRequest) + // optional string message = 1; + if (this->message().size() > 0) { + ::google::protobuf::internal::WireFormatLite::VerifyUtf8String( + this->message().data(), this->message().length(), + ::google::protobuf::internal::WireFormatLite::SERIALIZE, + "echo.EchoRequest.message"); + ::google::protobuf::internal::WireFormatLite::WriteStringMaybeAliased( + 1, this->message(), output); + } + + // @@protoc_insertion_point(serialize_end:echo.EchoRequest) +} + +::google::protobuf::uint8* EchoRequest::SerializeWithCachedSizesToArray( + ::google::protobuf::uint8* target) const { + // @@protoc_insertion_point(serialize_to_array_start:echo.EchoRequest) + // optional string message = 1; + if (this->message().size() > 0) { + ::google::protobuf::internal::WireFormatLite::VerifyUtf8String( + this->message().data(), this->message().length(), + ::google::protobuf::internal::WireFormatLite::SERIALIZE, + "echo.EchoRequest.message"); + target = + ::google::protobuf::internal::WireFormatLite::WriteStringToArray( + 1, this->message(), target); + } + + // @@protoc_insertion_point(serialize_to_array_end:echo.EchoRequest) + return target; +} + +int EchoRequest::ByteSize() const { + int total_size = 0; + + // optional string message = 1; + if (this->message().size() > 0) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::StringSize( + this->message()); + } + + GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); + _cached_size_ = total_size; + GOOGLE_SAFE_CONCURRENT_WRITES_END(); + return total_size; +} + +void EchoRequest::MergeFrom(const ::google::protobuf::Message& from) { + if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); + const EchoRequest* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); + if (source == NULL) { + ::google::protobuf::internal::ReflectionOps::Merge(from, this); + } else { + MergeFrom(*source); + } +} + +void EchoRequest::MergeFrom(const EchoRequest& from) { + if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); + if (from.message().size() > 0) { + + message_.AssignWithDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), from.message_); + } +} + +void EchoRequest::CopyFrom(const ::google::protobuf::Message& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +void EchoRequest::CopyFrom(const EchoRequest& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool EchoRequest::IsInitialized() const { + + return true; +} + +void EchoRequest::Swap(EchoRequest* other) { + if (other == this) return; + InternalSwap(other); +} +void EchoRequest::InternalSwap(EchoRequest* other) { + message_.Swap(&other->message_); + _internal_metadata_.Swap(&other->_internal_metadata_); + std::swap(_cached_size_, other->_cached_size_); +} + +::google::protobuf::Metadata EchoRequest::GetMetadata() const { + protobuf_AssignDescriptorsOnce(); + ::google::protobuf::Metadata metadata; + metadata.descriptor = EchoRequest_descriptor_; + metadata.reflection = EchoRequest_reflection_; + return metadata; +} + +#if PROTOBUF_INLINE_NOT_IN_HEADERS +// EchoRequest + +// optional string message = 1; +void EchoRequest::clear_message() { + message_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} + const ::std::string& EchoRequest::message() const { + // @@protoc_insertion_point(field_get:echo.EchoRequest.message) + return message_.GetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} + void EchoRequest::set_message(const ::std::string& value) { + + message_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value); + // @@protoc_insertion_point(field_set:echo.EchoRequest.message) +} + void EchoRequest::set_message(const char* value) { + + message_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), ::std::string(value)); + // @@protoc_insertion_point(field_set_char:echo.EchoRequest.message) +} + void EchoRequest::set_message(const char* value, size_t size) { + + message_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), + ::std::string(reinterpret_cast(value), size)); + // @@protoc_insertion_point(field_set_pointer:echo.EchoRequest.message) +} + ::std::string* EchoRequest::mutable_message() { + + // @@protoc_insertion_point(field_mutable:echo.EchoRequest.message) + return message_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} + ::std::string* EchoRequest::release_message() { + + return message_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} + void EchoRequest::set_allocated_message(::std::string* message) { + if (message != NULL) { + + } else { + + } + message_.SetAllocatedNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), message); + // @@protoc_insertion_point(field_set_allocated:echo.EchoRequest.message) +} + +#endif // PROTOBUF_INLINE_NOT_IN_HEADERS + +// @@protoc_insertion_point(namespace_scope) + +} // namespace echo + +// @@protoc_insertion_point(global_scope) diff --git a/examples/echo/echo-cpp/echo.pb.h b/examples/echo/echo-cpp/echo.pb.h new file mode 100644 index 0000000..089f94a --- /dev/null +++ b/examples/echo/echo-cpp/echo.pb.h @@ -0,0 +1,183 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: echo.proto + +#ifndef PROTOBUF_echo_2eproto__INCLUDED +#define PROTOBUF_echo_2eproto__INCLUDED + +#include + +#include + +#if GOOGLE_PROTOBUF_VERSION < 3000000 +#error This file was generated by a newer version of protoc which is +#error incompatible with your Protocol Buffer headers. Please update +#error your headers. +#endif +#if 3000000 < GOOGLE_PROTOBUF_MIN_PROTOC_VERSION +#error This file was generated by an older version of protoc which is +#error incompatible with your Protocol Buffer headers. Please +#error regenerate this file with a newer version of protoc. +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +// @@protoc_insertion_point(includes) + +namespace echo { + +// Internal implementation detail -- do not call these. +void protobuf_AddDesc_echo_2eproto(); +void protobuf_AssignDesc_echo_2eproto(); +void protobuf_ShutdownFile_echo_2eproto(); + +class EchoRequest; + +// =================================================================== + +class EchoRequest : public ::google::protobuf::Message { + public: + EchoRequest(); + virtual ~EchoRequest(); + + EchoRequest(const EchoRequest& from); + + inline EchoRequest& operator=(const EchoRequest& from) { + CopyFrom(from); + return *this; + } + + static const ::google::protobuf::Descriptor* descriptor(); + static const EchoRequest& default_instance(); + + void Swap(EchoRequest* other); + + // implements Message ---------------------------------------------- + + inline EchoRequest* New() const { return New(NULL); } + + EchoRequest* New(::google::protobuf::Arena* arena) const; + void CopyFrom(const ::google::protobuf::Message& from); + void MergeFrom(const ::google::protobuf::Message& from); + void CopyFrom(const EchoRequest& from); + void MergeFrom(const EchoRequest& from); + void Clear(); + bool IsInitialized() const; + + int ByteSize() const; + bool MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input); + void SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const; + ::google::protobuf::uint8* SerializeWithCachedSizesToArray(::google::protobuf::uint8* output) const; + int GetCachedSize() const { return _cached_size_; } + private: + void SharedCtor(); + void SharedDtor(); + void SetCachedSize(int size) const; + void InternalSwap(EchoRequest* other); + private: + inline ::google::protobuf::Arena* GetArenaNoVirtual() const { + return _internal_metadata_.arena(); + } + inline void* MaybeArenaPtr() const { + return _internal_metadata_.raw_arena_ptr(); + } + public: + + ::google::protobuf::Metadata GetMetadata() const; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + // optional string message = 1; + void clear_message(); + static const int kMessageFieldNumber = 1; + const ::std::string& message() const; + void set_message(const ::std::string& value); + void set_message(const char* value); + void set_message(const char* value, size_t size); + ::std::string* mutable_message(); + ::std::string* release_message(); + void set_allocated_message(::std::string* message); + + // @@protoc_insertion_point(class_scope:echo.EchoRequest) + private: + + ::google::protobuf::internal::InternalMetadataWithArena _internal_metadata_; + bool _is_default_instance_; + ::google::protobuf::internal::ArenaStringPtr message_; + mutable int _cached_size_; + friend void protobuf_AddDesc_echo_2eproto(); + friend void protobuf_AssignDesc_echo_2eproto(); + friend void protobuf_ShutdownFile_echo_2eproto(); + + void InitAsDefaultInstance(); + static EchoRequest* default_instance_; +}; +// =================================================================== + + +// =================================================================== + +#if !PROTOBUF_INLINE_NOT_IN_HEADERS +// EchoRequest + +// optional string message = 1; +inline void EchoRequest::clear_message() { + message_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} +inline const ::std::string& EchoRequest::message() const { + // @@protoc_insertion_point(field_get:echo.EchoRequest.message) + return message_.GetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} +inline void EchoRequest::set_message(const ::std::string& value) { + + message_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value); + // @@protoc_insertion_point(field_set:echo.EchoRequest.message) +} +inline void EchoRequest::set_message(const char* value) { + + message_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), ::std::string(value)); + // @@protoc_insertion_point(field_set_char:echo.EchoRequest.message) +} +inline void EchoRequest::set_message(const char* value, size_t size) { + + message_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), + ::std::string(reinterpret_cast(value), size)); + // @@protoc_insertion_point(field_set_pointer:echo.EchoRequest.message) +} +inline ::std::string* EchoRequest::mutable_message() { + + // @@protoc_insertion_point(field_mutable:echo.EchoRequest.message) + return message_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} +inline ::std::string* EchoRequest::release_message() { + + return message_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} +inline void EchoRequest::set_allocated_message(::std::string* message) { + if (message != NULL) { + + } else { + + } + message_.SetAllocatedNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), message); + // @@protoc_insertion_point(field_set_allocated:echo.EchoRequest.message) +} + +#endif // !PROTOBUF_INLINE_NOT_IN_HEADERS + +// @@protoc_insertion_point(namespace_scope) + +} // namespace echo + +// @@protoc_insertion_point(global_scope) + +#endif // PROTOBUF_echo_2eproto__INCLUDED diff --git a/examples/echo/echo-python/echo_client.py b/examples/echo/echo-python/echo_client.py new file mode 100644 index 0000000..46fc45e --- /dev/null +++ b/examples/echo/echo-python/echo_client.py @@ -0,0 +1,13 @@ +from grpc.beta import implementations + +import echo_pb2 + +def main(): + for _ in xrange(1000): + channel = implementations.insecure_channel('localhost', 50051) + stub = echo_pb2.beta_create_Echo_stub(channel) + message = echo_pb2.EchoRequest(message='foo') + response = stub.DoEcho(message, 15) + +if __name__ == '__main__': + main() diff --git a/examples/echo/echo-python/echo_pb2.py b/examples/echo/echo-python/echo_pb2.py new file mode 100644 index 0000000..3fc37b0 --- /dev/null +++ b/examples/echo/echo-python/echo_pb2.py @@ -0,0 +1,118 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: echo.proto + +import sys +_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database +from google.protobuf import descriptor_pb2 +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor.FileDescriptor( + name='echo.proto', + package='echo', + syntax='proto3', + serialized_pb=_b('\n\necho.proto\x12\x04\x65\x63ho\"\x1e\n\x0b\x45\x63hoRequest\x12\x0f\n\x07message\x18\x01 \x01(\t28\n\x04\x45\x63ho\x12\x30\n\x06\x44oEcho\x12\x11.echo.EchoRequest\x1a\x11.echo.EchoRequest\"\x00\x62\x06proto3') +) +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + + + + +_ECHOREQUEST = _descriptor.Descriptor( + name='EchoRequest', + full_name='echo.EchoRequest', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='message', full_name='echo.EchoRequest.message', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=20, + serialized_end=50, +) + +DESCRIPTOR.message_types_by_name['EchoRequest'] = _ECHOREQUEST + +EchoRequest = _reflection.GeneratedProtocolMessageType('EchoRequest', (_message.Message,), dict( + DESCRIPTOR = _ECHOREQUEST, + __module__ = 'echo_pb2' + # @@protoc_insertion_point(class_scope:echo.EchoRequest) + )) +_sym_db.RegisterMessage(EchoRequest) + + +import abc +from grpc.beta import implementations as beta_implementations +from grpc.framework.common import cardinality +from grpc.framework.interfaces.face import utilities as face_utilities + +class BetaEchoServicer(object): + """""" + __metaclass__ = abc.ABCMeta + @abc.abstractmethod + def DoEcho(self, request, context): + raise NotImplementedError() + +class BetaEchoStub(object): + """The interface to which stubs will conform.""" + __metaclass__ = abc.ABCMeta + @abc.abstractmethod + def DoEcho(self, request, timeout): + raise NotImplementedError() + DoEcho.future = None + +def beta_create_Echo_server(servicer, pool=None, pool_size=None, default_timeout=None, maximum_timeout=None): + import echo_pb2 + import echo_pb2 + request_deserializers = { + ('echo.Echo', 'DoEcho'): echo_pb2.EchoRequest.FromString, + } + response_serializers = { + ('echo.Echo', 'DoEcho'): echo_pb2.EchoRequest.SerializeToString, + } + method_implementations = { + ('echo.Echo', 'DoEcho'): face_utilities.unary_unary_inline(servicer.DoEcho), + } + server_options = beta_implementations.server_options(request_deserializers=request_deserializers, response_serializers=response_serializers, thread_pool=pool, thread_pool_size=pool_size, default_timeout=default_timeout, maximum_timeout=maximum_timeout) + return beta_implementations.server(method_implementations, options=server_options) + +def beta_create_Echo_stub(channel, host=None, metadata_transformer=None, pool=None, pool_size=None): + import echo_pb2 + import echo_pb2 + request_serializers = { + ('echo.Echo', 'DoEcho'): echo_pb2.EchoRequest.SerializeToString, + } + response_deserializers = { + ('echo.Echo', 'DoEcho'): echo_pb2.EchoRequest.FromString, + } + cardinalities = { + 'DoEcho': cardinality.Cardinality.UNARY_UNARY, + } + stub_options = beta_implementations.stub_options(host=host, metadata_transformer=metadata_transformer, request_serializers=request_serializers, response_deserializers=response_deserializers, thread_pool=pool, thread_pool_size=pool_size) + return beta_implementations.dynamic_stub(channel, 'echo.Echo', cardinalities, options=stub_options) +# @@protoc_insertion_point(module_scope) diff --git a/examples/echo/echo-python/echo_server.py b/examples/echo/echo-python/echo_server.py new file mode 100644 index 0000000..c792486 --- /dev/null +++ b/examples/echo/echo-python/echo_server.py @@ -0,0 +1,20 @@ +from grpc.beta import implementations + +import echo_pb2 +import time + +class Echo(echo_pb2.BetaEchoServicer): + def DoEcho(self, request, context): + return echo_pb2.EchoRequest(message=request.message) + +def main(): + server = echo_pb2.beta_create_Echo_server(Echo()) + server.add_insecure_port('[::]:50051') + server.start() + try: + time.sleep(600) + except KeyboardInterrupt: + server.stop(0) + +if __name__ == '__main__': + main() diff --git a/examples/echo/echo-server/Main.hs b/examples/echo/echo-server/Main.hs new file mode 100644 index 0000000..30d8b2b --- /dev/null +++ b/examples/echo/echo-server/Main.hs @@ -0,0 +1,64 @@ +{-# LANGUAGE OverloadedStrings #-} + +import Control.Concurrent.Async (async, wait) +import Control.Monad (forever) +import Data.ByteString (ByteString) +import qualified Data.Map as M +import Network.GRPC.LowLevel + + +serverMeta :: MetadataMap +serverMeta = M.fromList [("test_meta", "test_meta_value")] + +handler :: ByteString -> MetadataMap -> MethodName + -> IO (ByteString, MetadataMap, StatusDetails) +handler reqBody reqMeta method = do + --putStrLn $ "Got request for method: " ++ show method + --putStrLn $ "Got metadata: " ++ show reqMeta + return (reqBody, serverMeta, StatusDetails "") + +unregMain :: IO () +unregMain = withGRPC $ \grpc -> do + withServer grpc (ServerConfig "localhost" 50051 []) $ \server -> forever $ do + result <- serverHandleNormalCall server 15 serverMeta handler + case result of + Left x -> putStrLn $ "handle call result error: " ++ show x + Right _ -> return () + +regMain :: IO () +regMain = withGRPC $ \grpc -> do + let methods = [(MethodName "/echo.Echo/DoEcho", Normal)] + withServer grpc (ServerConfig "localhost" 50051 methods) $ \server -> + forever $ do + let method = head (registeredMethods server) + result <- serverHandleNormalRegisteredCall server method 15 serverMeta $ + \reqBody _reqMeta -> return (reqBody, serverMeta, serverMeta, + StatusDetails "") + case result of + Left x -> putStrLn $ "registered call result error: " ++ show x + Right _ -> return () + +-- | loop to fork n times +regLoop :: Server -> RegisteredMethod -> IO () +regLoop server method = forever $ do + result <- serverHandleNormalRegisteredCall server method 15 serverMeta $ + \reqBody _reqMeta -> return (reqBody, serverMeta, serverMeta, + StatusDetails "") + case result of + Left x -> putStrLn $ "registered call result error: " ++ show x + Right _ -> return () + +regMainThreaded :: IO () +regMainThreaded = do + withGRPC $ \grpc -> do + let methods = [(MethodName "/echo.Echo/DoEcho", Normal)] + withServer grpc (ServerConfig "localhost" 50051 methods) $ \server -> do + let method = head (registeredMethods server) + tid1 <- async $ regLoop server method + tid2 <- async $ regLoop server method + wait tid1 + wait tid2 + return () + +main :: IO () +main = regMainThreaded diff --git a/examples/echo/echo.proto b/examples/echo/echo.proto new file mode 100644 index 0000000..ed192c7 --- /dev/null +++ b/examples/echo/echo.proto @@ -0,0 +1,11 @@ +syntax = "proto3"; + +package echo; + +service Echo { + rpc DoEcho (EchoRequest) returns (EchoRequest) {} +} + +message EchoRequest { + string message = 1; +} diff --git a/grpc-haskell.cabal b/grpc-haskell.cabal index 9e726a9..51f2d9b 100644 --- a/grpc-haskell.cabal +++ b/grpc-haskell.cabal @@ -18,6 +18,11 @@ Flag Debug Manual: True Default: False +flag with-examples + description: Also build example executables. + manual: True + default: False + library build-depends: base ==4.8.* @@ -65,6 +70,35 @@ library CPP-Options: -DDEBUG CC-Options: -DGRPC_HASKELL_DEBUG +executable echo-server + if flag(with-examples) + build-depends: + base ==4.8.* + , bytestring == 0.10.* + , grpc-haskell + , containers ==0.5.* + , async + else + buildable: False + default-language: Haskell2010 + ghc-options: -Wall -g -threaded + hs-source-dirs: examples/echo/echo-server + main-is: Main.hs + +executable echo-client + if flag(with-examples) + build-depends: + base ==4.8.* + , bytestring == 0.10.* + , grpc-haskell + , containers ==0.5.* + else + buildable: False + default-language: Haskell2010 + ghc-options: -Wall -g -threaded + hs-source-dirs: examples/echo/echo-client + main-is: Main.hs + test-suite test build-depends: base ==4.8.* diff --git a/src/Network/GRPC/LowLevel.hs b/src/Network/GRPC/LowLevel.hs index 9023012..aa96213 100644 --- a/src/Network/GRPC/LowLevel.hs +++ b/src/Network/GRPC/LowLevel.hs @@ -22,6 +22,9 @@ GRPC , GRPCMethodType(..) , RegisteredMethod , NormalRequestResult(..) +, MetadataMap +, MethodName(..) +, StatusDetails(..) -- * Server , ServerConfig(..) @@ -53,7 +56,6 @@ GRPC , runServerUnregOps , Op(..) , OpRecvResult(..) -, StatusDetails(..) ) where diff --git a/src/Network/GRPC/LowLevel/Call.hs b/src/Network/GRPC/LowLevel/Call.hs index 5c9d885..5190e83 100644 --- a/src/Network/GRPC/LowLevel/Call.hs +++ b/src/Network/GRPC/LowLevel/Call.hs @@ -90,6 +90,10 @@ serverUnregCallGetMethodName :: ServerUnregCall -> IO MethodName serverUnregCallGetMethodName ServerUnregCall{..} = MethodName <$> C.callDetailsGetMethod callDetails +serverUnregCallGetHost :: ServerUnregCall -> IO Host +serverUnregCallGetHost ServerUnregCall{..} = + Host <$> C.callDetailsGetHost callDetails + debugClientCall :: ClientCall -> IO () {-# INLINE debugClientCall #-} #ifdef DEBUG diff --git a/src/Network/GRPC/LowLevel/Server.hs b/src/Network/GRPC/LowLevel/Server.hs index aaa124b..60a3ea0 100644 --- a/src/Network/GRPC/LowLevel/Server.hs +++ b/src/Network/GRPC/LowLevel/Server.hs @@ -35,7 +35,7 @@ data ServerConfig = -- how this is used. Setting to "localhost" works fine in tests. port :: Int, -- ^ Port to listen for requests on. - methodsToRegister :: [(MethodName, Host, GRPCMethodType)] + methodsToRegister :: [(MethodName, GRPCMethodType)] -- ^ List of (method name, method host, method type) tuples -- specifying all methods to register. You can also handle -- other unregistered methods with `serverHandleNormalCall`. @@ -51,8 +51,8 @@ startServer grpc ServerConfig{..} = do cq <- createCompletionQueue grpc serverRegisterCompletionQueue server cq methods <- forM methodsToRegister $ - \(name, host, mtype) -> - serverRegisterMethod server name host mtype + \(name, mtype) -> + serverRegisterMethod server name (Host hostPort) mtype C.grpcServerStart server return $ Server server cq methods @@ -249,6 +249,8 @@ serverHandleNormalCall s@Server{..} timeLimit srvMetadata f = do requestMeta <- serverUnregCallGetMetadata call grpcDebug $ "got client metadata: " ++ show requestMeta methodName <- serverUnregCallGetMethodName call + hostName <- serverUnregCallGetHost call + grpcDebug $ "call_details host is: " ++ show hostName (respBody, respMetadata, details) <- f body requestMeta methodName let status = C.GrpcStatusOk let respOps = serverOpsSendNormalResponse diff --git a/tests/LowLevelTests.hs b/tests/LowLevelTests.hs index f5a9d4d..8022b16 100644 --- a/tests/LowLevelTests.hs +++ b/tests/LowLevelTests.hs @@ -44,7 +44,7 @@ testClientCreateDestroy = payloadLowLevelServer :: TestServer payloadLowLevelServer = TestServer $ \grpc -> do - let conf = (ServerConfig "localhost" 50051 [("/foo", "localhost", Normal)]) + let conf = (ServerConfig "localhost" 50051 [("/foo", Normal)]) withServer grpc conf $ \server -> do let method = head (registeredMethods server) result <- serverHandleNormalRegisteredCall server method 11 M.empty $ @@ -109,7 +109,7 @@ testClientRequestNoServer = testServerAwaitNoClient :: TestTree testServerAwaitNoClient = testCase "server wait times out when no client " $ do withGRPC $ \grpc -> do - let conf = (ServerConfig "localhost" 50051 [("/foo", "localhost", Normal)]) + let conf = (ServerConfig "localhost" 50051 [("/foo", Normal)]) withServer grpc conf $ \server -> do let method = head (registeredMethods server) result <- serverHandleNormalRegisteredCall server method 1 M.empty $