diff options
author | Craig Tiller <ctiller@google.com> | 2015-01-13 16:11:30 -0800 |
---|---|---|
committer | Craig Tiller <ctiller@google.com> | 2015-01-13 16:11:30 -0800 |
commit | 375605b65f9d58b7544b4af8bc93d4cf8489f010 (patch) | |
tree | bc0e1a8861a3dfdb6e8b0bb43a219d31efb88064 /src | |
parent | 80fa15c15121a7d0ec020dec8bfa3697a96058b6 (diff) | |
parent | 49a06a6cb843b8ce592312c28b43c9dc527b99ee (diff) |
Merge github.com:google/grpc into api
Diffstat (limited to 'src')
205 files changed, 7761 insertions, 3085 deletions
diff --git a/src/compiler/cpp_generator.cc b/src/compiler/cpp_generator.cc index 14d5005dc0..1116049806 100644 --- a/src/compiler/cpp_generator.cc +++ b/src/compiler/cpp_generator.cc @@ -43,23 +43,19 @@ namespace grpc_cpp_generator { namespace { bool NoStreaming(const google::protobuf::MethodDescriptor* method) { - return !method->client_streaming() && - !method->server_streaming(); + return !method->client_streaming() && !method->server_streaming(); } bool ClientOnlyStreaming(const google::protobuf::MethodDescriptor* method) { - return method->client_streaming() && - !method->server_streaming(); + return method->client_streaming() && !method->server_streaming(); } bool ServerOnlyStreaming(const google::protobuf::MethodDescriptor* method) { - return !method->client_streaming() && - method->server_streaming(); + return !method->client_streaming() && method->server_streaming(); } bool BidiStreaming(const google::protobuf::MethodDescriptor* method) { - return method->client_streaming() && - method->server_streaming(); + return method->client_streaming() && method->server_streaming(); } bool HasClientOnlyStreaming(const google::protobuf::FileDescriptor* file) { @@ -98,7 +94,7 @@ bool HasBidiStreaming(const google::protobuf::FileDescriptor* file) { string GetHeaderIncludes(const google::protobuf::FileDescriptor* file) { string temp = - "#include \"src/cpp/client/internal_stub.h\"\n" + "#include \"grpc++/impl/internal_stub.h\"\n" "#include \"grpc++/status.h\"\n" "\n" "namespace grpc {\n" @@ -126,15 +122,15 @@ string GetHeaderIncludes(const google::protobuf::FileDescriptor* file) { } string GetSourceIncludes() { - return "#include \"src/cpp/rpc_method.h\"\n" - "#include \"src/cpp/server/rpc_service_method.h\"\n" - "#include \"grpc++/channel_interface.h\"\n" + return "#include \"grpc++/channel_interface.h\"\n" + "#include \"grpc++/impl/rpc_method.h\"\n" + "#include \"grpc++/impl/rpc_service_method.h\"\n" "#include \"grpc++/stream.h\"\n"; } void PrintHeaderClientMethod(google::protobuf::io::Printer* printer, - const google::protobuf::MethodDescriptor* method, - map<string, string>* vars) { + const google::protobuf::MethodDescriptor* method, + map<string, string>* vars) { (*vars)["Method"] = method->name(); (*vars)["Request"] = grpc_cpp_generator::ClassName(method->input_type(), true); @@ -205,8 +201,9 @@ void PrintHeaderService(google::protobuf::io::Printer* printer, printer->Indent(); // Client side - printer->Print("class Stub : public ::grpc::InternalStub {\n" - " public:\n"); + printer->Print( + "class Stub : public ::grpc::InternalStub {\n" + " public:\n"); printer->Indent(); for (int i = 0; i < service->method_count(); ++i) { PrintHeaderClientMethod(printer, service->method(i), vars); @@ -220,8 +217,9 @@ void PrintHeaderService(google::protobuf::io::Printer* printer, printer->Print("\n"); // Server side - printer->Print("class Service {\n" - " public:\n"); + printer->Print( + "class Service {\n" + " public:\n"); printer->Indent(); printer->Print("Service() : service_(nullptr) {}\n"); printer->Print("virtual ~Service();\n"); @@ -230,8 +228,9 @@ void PrintHeaderService(google::protobuf::io::Printer* printer, } printer->Print("::grpc::RpcService* service();\n"); printer->Outdent(); - printer->Print(" private:\n" - " ::grpc::RpcService* service_;\n"); + printer->Print( + " private:\n" + " ::grpc::RpcService* service_;\n"); printer->Print("};\n"); printer->Outdent(); @@ -252,8 +251,8 @@ string GetHeaderServices(const google::protobuf::FileDescriptor* file) { } void PrintSourceClientMethod(google::protobuf::io::Printer* printer, - const google::protobuf::MethodDescriptor* method, - map<string, string>* vars) { + const google::protobuf::MethodDescriptor* method, + map<string, string>* vars) { (*vars)["Method"] = method->name(); (*vars)["Request"] = grpc_cpp_generator::ClassName(method->input_type(), true); @@ -309,8 +308,8 @@ void PrintSourceClientMethod(google::protobuf::io::Printer* printer, } void PrintSourceServerMethod(google::protobuf::io::Printer* printer, - const google::protobuf::MethodDescriptor* method, - map<string, string>* vars) { + const google::protobuf::MethodDescriptor* method, + map<string, string>* vars) { (*vars)["Method"] = method->name(); (*vars)["Request"] = grpc_cpp_generator::ClassName(method->input_type(), true); @@ -363,12 +362,12 @@ void PrintSourceService(google::protobuf::io::Printer* printer, map<string, string>* vars) { (*vars)["Service"] = service->name(); printer->Print(*vars, - "$Service$::Stub* $Service$::NewStub(" - "const std::shared_ptr<::grpc::ChannelInterface>& channel) {\n" - " $Service$::Stub* stub = new $Service$::Stub();\n" - " stub->set_channel(channel);\n" - " return stub;\n" - "};\n\n"); + "$Service$::Stub* $Service$::NewStub(" + "const std::shared_ptr<::grpc::ChannelInterface>& channel) {\n" + " $Service$::Stub* stub = new $Service$::Stub();\n" + " stub->set_channel(channel);\n" + " return stub;\n" + "};\n\n"); for (int i = 0; i < service->method_count(); ++i) { PrintSourceClientMethod(printer, service->method(i), vars); } @@ -381,11 +380,12 @@ void PrintSourceService(google::protobuf::io::Printer* printer, PrintSourceServerMethod(printer, service->method(i), vars); } printer->Print(*vars, - "::grpc::RpcService* $Service$::Service::service() {\n"); + "::grpc::RpcService* $Service$::Service::service() {\n"); printer->Indent(); - printer->Print("if (service_ != nullptr) {\n" - " return service_;\n" - "}\n"); + printer->Print( + "if (service_ != nullptr) {\n" + " return service_;\n" + "}\n"); printer->Print("service_ = new ::grpc::RpcService();\n"); for (int i = 0; i < service->method_count(); ++i) { const google::protobuf::MethodDescriptor* method = service->method(i); diff --git a/src/compiler/cpp_generator_helpers.h b/src/compiler/cpp_generator_helpers.h index 8d8b03ea43..ba251faf29 100644 --- a/src/compiler/cpp_generator_helpers.h +++ b/src/compiler/cpp_generator_helpers.h @@ -85,7 +85,8 @@ inline string DotsToUnderscores(const string& name) { return StringReplace(name, ".", "_"); } -inline string ClassName(const google::protobuf::Descriptor* descriptor, bool qualified) { +inline string ClassName(const google::protobuf::Descriptor* descriptor, + bool qualified) { // Find "outer", the descriptor of the top-level message in which // "descriptor" is embedded. const google::protobuf::Descriptor* outer = descriptor; diff --git a/src/compiler/cpp_plugin.cc b/src/compiler/cpp_plugin.cc index 7aa745a4f1..17836b1612 100644 --- a/src/compiler/cpp_plugin.cc +++ b/src/compiler/cpp_plugin.cc @@ -54,9 +54,10 @@ class CppGrpcGenerator : public google::protobuf::compiler::CodeGenerator { google::protobuf::compiler::GeneratorContext* context, string* error) const { if (file->options().cc_generic_services()) { - *error = "cpp grpc proto compiler plugin does not work with generic " - "services. To generate cpp grpc APIs, please set \"" - "cc_generic_service = false\"."; + *error = + "cpp grpc proto compiler plugin does not work with generic " + "services. To generate cpp grpc APIs, please set \"" + "cc_generic_service = false\"."; return false; } diff --git a/src/compiler/go_generator.cc b/src/compiler/go_generator.cc new file mode 100644 index 0000000000..84aa27668e --- /dev/null +++ b/src/compiler/go_generator.cc @@ -0,0 +1,530 @@ +/* + * + * 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/compiler/go_generator.h" + +#include <cctype> + +#include <google/protobuf/io/printer.h> +#include <google/protobuf/io/zero_copy_stream_impl_lite.h> +#include <google/protobuf/descriptor.pb.h> +#include <google/protobuf/descriptor.h> + +using namespace std; + +namespace grpc_go_generator { + +bool NoStreaming(const google::protobuf::MethodDescriptor* method) { + return !method->client_streaming() && !method->server_streaming(); +} + +bool ClientOnlyStreaming(const google::protobuf::MethodDescriptor* method) { + return method->client_streaming() && !method->server_streaming(); +} + +bool ServerOnlyStreaming(const google::protobuf::MethodDescriptor* method) { + return !method->client_streaming() && method->server_streaming(); +} + +bool BidiStreaming(const google::protobuf::MethodDescriptor* method) { + return method->client_streaming() && method->server_streaming(); +} + +bool HasClientOnlyStreaming(const google::protobuf::FileDescriptor* file) { + for (int i = 0; i < file->service_count(); i++) { + for (int j = 0; j < file->service(i)->method_count(); j++) { + if (ClientOnlyStreaming(file->service(i)->method(j))) { + return true; + } + } + } + return false; +} + +string LowerCaseService(const string& service) { + string ret = service; + if (!ret.empty() && ret[0] >= 'A' && ret[0] <= 'Z') { + ret[0] = ret[0] - 'A' + 'a'; + } + return ret; +} + +void PrintClientMethodDef(google::protobuf::io::Printer* printer, + const google::protobuf::MethodDescriptor* method, + map<string, string>* vars) { + (*vars)["Method"] = method->name(); + (*vars)["Request"] = method->input_type()->name(); + (*vars)["Response"] = method->output_type()->name(); + if (NoStreaming(method)) { + printer->Print(*vars, + "\t$Method$(ctx context.Context, in *$Request$, opts " + "...rpc.CallOption) " + "(*$Response$, error)\n"); + } else if (BidiStreaming(method)) { + printer->Print(*vars, + "\t$Method$(ctx context.Context, opts ...rpc.CallOption) " + "($Service$_$Method$Client, error)\n"); + } else if (ServerOnlyStreaming(method)) { + printer->Print( + *vars, + "\t$Method$(ctx context.Context, m *$Request$, opts ...rpc.CallOption) " + "($Service$_$Method$Client, error)\n"); + } else if (ClientOnlyStreaming(method)) { + printer->Print(*vars, + "\t$Method$(ctx context.Context, opts ...rpc.CallOption) " + "($Service$_$Method$Client, error)\n"); + } +} + +void PrintClientMethodImpl(google::protobuf::io::Printer* printer, + const google::protobuf::MethodDescriptor* method, + map<string, string>* vars) { + (*vars)["Method"] = method->name(); + (*vars)["Request"] = method->input_type()->name(); + (*vars)["Response"] = method->output_type()->name(); + + if (NoStreaming(method)) { + printer->Print( + *vars, + "func (c *$ServiceStruct$Client) $Method$(ctx context.Context, " + "in *$Request$, opts ...rpc.CallOption) (*$Response$, error) {\n"); + printer->Print(*vars, "\tout := new($Response$)\n"); + printer->Print(*vars, + "\terr := rpc.Invoke(ctx, \"/$Package$$Service$/$Method$\", " + "in, out, c.cc, opts...)\n"); + printer->Print("\tif err != nil {\n"); + printer->Print("\t\treturn nil, err\n"); + printer->Print("\t}\n"); + printer->Print("\treturn out, nil\n"); + printer->Print("}\n\n"); + } else if (BidiStreaming(method)) { + printer->Print( + *vars, + "func (c *$ServiceStruct$Client) $Method$(ctx context.Context, opts " + "...rpc.CallOption) ($Service$_$Method$Client, error) {\n" + "\tstream, err := rpc.NewClientStream(ctx, c.cc, " + "\"/$Package$$Service$/$Method$\", opts...)\n" + "\tif err != nil {\n" + "\t\treturn nil, err\n" + "\t}\n" + "\treturn &$ServiceStruct$$Method$Client{stream}, nil\n" + "}\n\n"); + printer->Print(*vars, + "type $Service$_$Method$Client interface {\n" + "\tSend(*$Request$) error\n" + "\tRecv() (*$Response$, error)\n" + "\trpc.ClientStream\n" + "}\n\n"); + printer->Print(*vars, + "type $ServiceStruct$$Method$Client struct {\n" + "\trpc.ClientStream\n" + "}\n\n"); + printer->Print( + *vars, + "func (x *$ServiceStruct$$Method$Client) Send(m *$Request$) error {\n" + "\treturn x.ClientStream.SendProto(m)\n" + "}\n\n"); + printer->Print( + *vars, + "func (x *$ServiceStruct$$Method$Client) Recv() (*$Response$, error) " + "{\n" + "\tm := new($Response$)\n" + "\tif err := x.ClientStream.RecvProto(m); err != nil {\n" + "\t\treturn nil, err\n" + "\t}\n" + "\treturn m, nil\n" + "}\n\n"); + } else if (ServerOnlyStreaming(method)) { + printer->Print( + *vars, + "func (c *$ServiceStruct$Client) $Method$(ctx context.Context, m " + "*$Request$, " + "opts ...rpc.CallOption) ($Service$_$Method$Client, error) {\n" + "\tstream, err := rpc.NewClientStream(ctx, c.cc, " + "\"/$Package$$Service$/$Method$\", opts...)\n" + "\tif err != nil {\n" + "\t\treturn nil, err\n" + "\t}\n" + "\tx := &$ServiceStruct$$Method$Client{stream}\n" + "\tif err := x.ClientStream.SendProto(m); err != nil {\n" + "\t\treturn nil, err\n" + "\t}\n" + "\tif err := x.ClientStream.CloseSend(); err != nil {\n" + "\t\treturn nil, err\n" + "\t}\n" + "\treturn x, nil\n" + "}\n\n"); + printer->Print(*vars, + "type $Service$_$Method$Client interface {\n" + "\tRecv() (*$Response$, error)\n" + "\trpc.ClientStream\n" + "}\n\n"); + printer->Print(*vars, + "type $ServiceStruct$$Method$Client struct {\n" + "\trpc.ClientStream\n" + "}\n\n"); + printer->Print( + *vars, + "func (x *$ServiceStruct$$Method$Client) Recv() (*$Response$, error) " + "{\n" + "\tm := new($Response$)\n" + "\tif err := x.ClientStream.RecvProto(m); err != nil {\n" + "\t\treturn nil, err\n" + "\t}\n" + "\treturn m, nil\n" + "}\n\n"); + } else if (ClientOnlyStreaming(method)) { + printer->Print( + *vars, + "func (c *$ServiceStruct$Client) $Method$(ctx context.Context, opts " + "...rpc.CallOption) ($Service$_$Method$Client, error) {\n" + "\tstream, err := rpc.NewClientStream(ctx, c.cc, " + "\"/$Package$$Service$/$Method$\", opts...)\n" + "\tif err != nil {\n" + "\t\treturn nil, err\n" + "\t}\n" + "\treturn &$ServiceStruct$$Method$Client{stream}, nil\n" + "}\n\n"); + printer->Print(*vars, + "type $Service$_$Method$Client interface {\n" + "\tSend(*$Request$) error\n" + "\tCloseAndRecv() (*$Response$, error)\n" + "\trpc.ClientStream\n" + "}\n\n"); + printer->Print(*vars, + "type $ServiceStruct$$Method$Client struct {\n" + "\trpc.ClientStream\n" + "}\n\n"); + printer->Print( + *vars, + "func (x *$ServiceStruct$$Method$Client) Send(m *$Request$) error {\n" + "\treturn x.ClientStream.SendProto(m)\n" + "}\n\n"); + printer->Print( + *vars, + "func (x *$ServiceStruct$$Method$Client) CloseAndRecv() (*$Response$, " + "error) {\n" + "\tif err := x.ClientStream.CloseSend(); err != nil {\n" + "\t\treturn nil, err\n" + "\t}\n" + "\tm := new($Response$)\n" + "\tif err := x.ClientStream.RecvProto(m); err != nil {\n" + "\t\treturn nil, err\n" + "\t}\n" + "\t// Read EOF.\n" + "\tif err := x.ClientStream.RecvProto(m); err == io.EOF {\n" + "\t\treturn m, io.EOF\n" + "\t}\n" + "\t// gRPC protocol violation.\n" + "\treturn m, fmt.Errorf(\"Violate gRPC client streaming protocol: no " + "EOF after the response.\")\n" + "}\n\n"); + } +} + +void PrintClient(google::protobuf::io::Printer* printer, + const google::protobuf::ServiceDescriptor* service, + map<string, string>* vars) { + (*vars)["Service"] = service->name(); + (*vars)["ServiceStruct"] = LowerCaseService(service->name()); + printer->Print(*vars, "type $Service$Client interface {\n"); + for (int i = 0; i < service->method_count(); ++i) { + PrintClientMethodDef(printer, service->method(i), vars); + } + printer->Print("}\n\n"); + + printer->Print(*vars, + "type $ServiceStruct$Client struct {\n" + "\tcc *rpc.ClientConn\n" + "}\n\n"); + printer->Print( + *vars, + "func New$Service$Client(cc *rpc.ClientConn) $Service$Client {\n" + "\treturn &$ServiceStruct$Client{cc}\n" + "}\n\n"); + for (int i = 0; i < service->method_count(); ++i) { + PrintClientMethodImpl(printer, service->method(i), vars); + } +} + +void PrintServerMethodDef(google::protobuf::io::Printer* printer, + const google::protobuf::MethodDescriptor* method, + map<string, string>* vars) { + (*vars)["Method"] = method->name(); + (*vars)["Request"] = method->input_type()->name(); + (*vars)["Response"] = method->output_type()->name(); + if (NoStreaming(method)) { + printer->Print( + *vars, + "\t$Method$(context.Context, *$Request$) (*$Response$, error)\n"); + } else if (BidiStreaming(method)) { + printer->Print(*vars, "\t$Method$($Service$_$Method$Server) error\n"); + } else if (ServerOnlyStreaming(method)) { + printer->Print(*vars, + "\t$Method$(*$Request$, $Service$_$Method$Server) error\n"); + } else if (ClientOnlyStreaming(method)) { + printer->Print(*vars, "\t$Method$($Service$_$Method$Server) error\n"); + } +} + +void PrintServerHandler(google::protobuf::io::Printer* printer, + const google::protobuf::MethodDescriptor* method, + map<string, string>* vars) { + (*vars)["Method"] = method->name(); + (*vars)["Request"] = method->input_type()->name(); + (*vars)["Response"] = method->output_type()->name(); + if (NoStreaming(method)) { + printer->Print( + *vars, + "func _$Service$_$Method$_Handler(srv interface{}, ctx context.Context," + " buf []byte) (proto.Message, error) {\n"); + printer->Print(*vars, "\tin := new($Request$)\n"); + printer->Print("\tif err := proto.Unmarshal(buf, in); err != nil {\n"); + printer->Print("\t\treturn nil, err\n"); + printer->Print("\t}\n"); + printer->Print(*vars, + "\tout, err := srv.($Service$Server).$Method$(ctx, in)\n"); + printer->Print("\tif err != nil {\n"); + printer->Print("\t\treturn nil, err\n"); + printer->Print("\t}\n"); + printer->Print("\treturn out, nil\n"); + printer->Print("}\n\n"); + } else if (BidiStreaming(method)) { + printer->Print( + *vars, + "func _$Service$_$Method$_Handler(srv interface{}, stream rpc.Stream) " + "error {\n" + "\treturn srv.($Service$Server).$Method$(&$ServiceStruct$$Method$Server" + "{stream})\n" + "}\n\n"); + printer->Print(*vars, + "type $Service$_$Method$Server interface {\n" + "\tSend(*$Response$) error\n" + "\tRecv() (*$Request$, error)\n" + "\trpc.Stream\n" + "}\n\n"); + printer->Print(*vars, + "type $ServiceStruct$$Method$Server struct {\n" + "\trpc.Stream\n" + "}\n\n"); + printer->Print( + *vars, + "func (x *$ServiceStruct$$Method$Server) Send(m *$Response$) error {\n" + "\treturn x.Stream.SendProto(m)\n" + "}\n\n"); + printer->Print( + *vars, + "func (x *$ServiceStruct$$Method$Server) Recv() (*$Request$, error) " + "{\n" + "\tm := new($Request$)\n" + "\tif err := x.Stream.RecvProto(m); err != nil {\n" + "\t\treturn nil, err\n" + "\t}\n" + "\treturn m, nil\n" + "}\n\n"); + } else if (ServerOnlyStreaming(method)) { + printer->Print( + *vars, + "func _$Service$_$Method$_Handler(srv interface{}, stream rpc.Stream) " + "error {\n" + "\tm := new($Request$)\n" + "\tif err := stream.RecvProto(m); err != nil {\n" + "\t\treturn err\n" + "\t}\n" + "\treturn srv.($Service$Server).$Method$(m, " + "&$ServiceStruct$$Method$Server{stream})\n" + "}\n\n"); + printer->Print(*vars, + "type $Service$_$Method$Server interface {\n" + "\tSend(*$Response$) error\n" + "\trpc.Stream\n" + "}\n\n"); + printer->Print(*vars, + "type $ServiceStruct$$Method$Server struct {\n" + "\trpc.Stream\n" + "}\n\n"); + printer->Print( + *vars, + "func (x *$ServiceStruct$$Method$Server) Send(m *$Response$) error {\n" + "\treturn x.Stream.SendProto(m)\n" + "}\n\n"); + } else if (ClientOnlyStreaming(method)) { + printer->Print( + *vars, + "func _$Service$_$Method$_Handler(srv interface{}, stream rpc.Stream) " + "error {\n" + "\treturn srv.($Service$Server).$Method$(&$ServiceStruct$$Method$Server" + "{stream})\n" + "}\n\n"); + printer->Print(*vars, + "type $Service$_$Method$Server interface {\n" + "\tSendAndClose(*$Response$) error\n" + "\tRecv() (*$Request$, error)\n" + "\trpc.Stream\n" + "}\n\n"); + printer->Print(*vars, + "type $ServiceStruct$$Method$Server struct {\n" + "\trpc.Stream\n" + "}\n\n"); + printer->Print( + *vars, + "func (x *$ServiceStruct$$Method$Server) SendAndClose(m *$Response$) " + "error {\n" + "\tif err := x.Stream.SendProto(m); err != nil {\n" + "\t\treturn err\n" + "\t}\n" + "\treturn nil\n" + "}\n\n"); + printer->Print( + *vars, + "func (x *$ServiceStruct$$Method$Server) Recv() (*$Request$, error) {\n" + "\tm := new($Request$)\n" + "\tif err := x.Stream.RecvProto(m); err != nil {\n" + "\t\treturn nil, err\n" + "\t}\n" + "\treturn m, nil\n" + "}\n\n"); + } +} + +void PrintServerMethodDesc(google::protobuf::io::Printer* printer, + const google::protobuf::MethodDescriptor* method, + map<string, string>* vars) { + (*vars)["Method"] = method->name(); + printer->Print("\t\t{\n"); + printer->Print(*vars, "\t\t\tMethodName:\t\"$Method$\",\n"); + printer->Print(*vars, "\t\t\tHandler:\t_$Service$_$Method$_Handler,\n"); + printer->Print("\t\t},\n"); +} + +void PrintServerStreamingMethodDesc( + google::protobuf::io::Printer* printer, + const google::protobuf::MethodDescriptor* method, + map<string, string>* vars) { + (*vars)["Method"] = method->name(); + printer->Print("\t\t{\n"); + printer->Print(*vars, "\t\t\tStreamName:\t\"$Method$\",\n"); + printer->Print(*vars, "\t\t\tHandler:\t_$Service$_$Method$_Handler,\n"); + printer->Print("\t\t},\n"); +} + +void PrintServer(google::protobuf::io::Printer* printer, + const google::protobuf::ServiceDescriptor* service, + map<string, string>* vars) { + (*vars)["Service"] = service->name(); + printer->Print(*vars, "type $Service$Server interface {\n"); + for (int i = 0; i < service->method_count(); ++i) { + PrintServerMethodDef(printer, service->method(i), vars); + } + printer->Print("}\n\n"); + + printer->Print(*vars, + "func RegisterService(s *rpc.Server, srv $Service$Server) {\n" + "\ts.RegisterService(&_$Service$_serviceDesc, srv)\n" + "}\n\n"); + + for (int i = 0; i < service->method_count(); ++i) { + PrintServerHandler(printer, service->method(i), vars); + } + + printer->Print(*vars, + "var _$Service$_serviceDesc = rpc.ServiceDesc{\n" + "\tServiceName: \"$Package$$Service$\",\n" + "\tHandlerType: (*$Service$Server)(nil),\n" + "\tMethods: []rpc.MethodDesc{\n"); + for (int i = 0; i < service->method_count(); ++i) { + if (NoStreaming(service->method(i))) { + PrintServerMethodDesc(printer, service->method(i), vars); + } + } + printer->Print("\t},\n"); + + printer->Print("\tStreams: []rpc.StreamDesc{\n"); + for (int i = 0; i < service->method_count(); ++i) { + if (!NoStreaming(service->method(i))) { + PrintServerStreamingMethodDesc(printer, service->method(i), vars); + } + } + printer->Print( + "\t},\n" + "}\n\n"); +} + +std::string BadToUnderscore(std::string str) { + for (unsigned i = 0; i < str.size(); ++i) { + if (!std::isalnum(str[i])) { + str[i] = '_'; + } + } + return str; +} + +string GetServices(const google::protobuf::FileDescriptor* file) { + string output; + google::protobuf::io::StringOutputStream output_stream(&output); + google::protobuf::io::Printer printer(&output_stream, '$'); + map<string, string> vars; + + string package_name = !file->options().go_package().empty() + ? file->options().go_package() + : file->package(); + vars["PackageName"] = BadToUnderscore(package_name); + printer.Print(vars, "package $PackageName$\n\n"); + printer.Print("import (\n"); + if (HasClientOnlyStreaming(file)) { + printer.Print( + "\t\"fmt\"\n" + "\t\"io\"\n"); + } + printer.Print( + "\t\"google/net/grpc/go/rpc\"\n" + "\tcontext \"google/third_party/golang/go_net/context/context\"\n" + "\tproto \"google/net/proto2/go/proto\"\n" + ")\n\n"); + + // $Package$ is used to fully qualify method names. + vars["Package"] = file->package(); + if (!file->package().empty()) { + vars["Package"].append("."); + } + + for (int i = 0; i < file->service_count(); ++i) { + PrintClient(&printer, file->service(0), &vars); + printer.Print("\n"); + PrintServer(&printer, file->service(0), &vars); + printer.Print("\n"); + } + return output; +} + +} // namespace grpc_go_generator diff --git a/src/cpp/rpc_method.h b/src/compiler/go_generator.h index 24a34bed89..5744345b56 100644 --- a/src/cpp/rpc_method.h +++ b/src/compiler/go_generator.h @@ -31,39 +31,21 @@ * */ -#ifndef __GRPCPP_INTERNAL_RPC_METHOD_H__ -#define __GRPCPP_INTERNAL_RPC_METHOD_H__ +#ifndef NET_GRPC_COMPILER_GO_GENERATOR_H_ +#define NET_GRPC_COMPILER_GO_GENERATOR_H_ + +#include <string> namespace google { namespace protobuf { -class Message; -} -} - -namespace grpc { - -class RpcMethod { - public: - enum RpcType { - NORMAL_RPC = 0, - CLIENT_STREAMING, // request streaming - SERVER_STREAMING, // response streaming - BIDI_STREAMING - }; - - explicit RpcMethod(const char* name) - : name_(name), method_type_(NORMAL_RPC) {} - RpcMethod(const char* name, RpcType type) : name_(name), method_type_(type) {} - - const char *name() const { return name_; } +class FileDescriptor; +} // namespace protobuf +} // namespace google - RpcType method_type() const { return method_type_; } +namespace grpc_go_generator { - private: - const char *name_; - const RpcType method_type_; -}; +std::string GetServices(const google::protobuf::FileDescriptor* file); -} // namespace grpc +} // namespace grpc_go_generator -#endif // __GRPCPP_INTERNAL_RPC_METHOD_H__ +#endif // NET_GRPC_COMPILER_GO_GENERATOR_H_ diff --git a/src/compiler/go_plugin.cc b/src/compiler/go_plugin.cc new file mode 100644 index 0000000000..c81612c0ab --- /dev/null +++ b/src/compiler/go_plugin.cc @@ -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. + * + */ + +// Generates go gRPC service interface out of Protobuf IDL. +// +// This is a Proto2 compiler plugin. See net/proto2/compiler/proto/plugin.proto +// and net/proto2/compiler/public/plugin.h for more information on plugins. + +#include <fstream> +#include <memory> + +using namespace std; + +#include "src/compiler/go_generator.h" +#include <google/protobuf/compiler/code_generator.h> +#include <google/protobuf/compiler/plugin.h> +#include <google/protobuf/io/coded_stream.h> +#include <google/protobuf/io/zero_copy_stream.h> +#include <google/protobuf/descriptor.h> + +class GoGrpcGenerator : public google::protobuf::compiler::CodeGenerator { + public: + GoGrpcGenerator() {} + virtual ~GoGrpcGenerator() {} + + virtual bool Generate(const google::protobuf::FileDescriptor* file, + const string& parameter, + google::protobuf::compiler::GeneratorContext* context, + string* error) const { + // Get output file name. + string file_name; + if (file->name().size() > 6 && + file->name().find_last_of(".proto") == file->name().size() - 1) { + file_name = + file->name().substr(0, file->name().size() - 6) + "_grpc.pb.go"; + } else { + *error = "Invalid proto file name. Proto file must end with .proto"; + return false; + } + + std::unique_ptr<google::protobuf::io::ZeroCopyOutputStream> output( + context->Open(file_name)); + google::protobuf::io::CodedOutputStream coded_out(output.get()); + string code = grpc_go_generator::GetServices(file); + coded_out.WriteRaw(code.data(), code.size()); + return true; + } +}; + +int main(int argc, char* argv[]) { + GoGrpcGenerator generator; + return google::protobuf::compiler::PluginMain(argc, argv, &generator); +} diff --git a/src/compiler/ruby_generator.cc b/src/compiler/ruby_generator.cc index 20485b47a5..fae8858ff7 100644 --- a/src/compiler/ruby_generator.cc +++ b/src/compiler/ruby_generator.cc @@ -67,9 +67,8 @@ void PrintMethod(const MethodDescriptor* method, const string& package, output_type = "stream(" + output_type + ")"; } map<string, string> method_vars = ListToDict({ - "mth.name", method->name(), - "input.type", input_type, - "output.type", output_type, + "mth.name", method->name(), "input.type", input_type, "output.type", + output_type, }); out->Print(method_vars, "rpc :$mth.name$, $input.type$, $output.type$\n"); } @@ -105,8 +104,7 @@ void PrintService(const ServiceDescriptor* service, const string& package, out->Print("self.marshal_class_method = :encode\n"); out->Print("self.unmarshal_class_method = :decode\n"); map<string, string> pkg_vars = ListToDict({ - "service.name", service->name(), - "pkg.name", package, + "service.name", service->name(), "pkg.name", package, }); out->Print(pkg_vars, "self.service_name = '$pkg.name$.$service.name$'\n"); out->Print("\n"); @@ -139,9 +137,8 @@ string GetServices(const FileDescriptor* file) { // Write out a file header. map<string, string> header_comment_vars = ListToDict({ - "file.name", file->name(), - "file.package", file->package(), - }); + "file.name", file->name(), "file.package", file->package(), + }); out.Print("# Generated by the protocol buffer compiler. DO NOT EDIT!\n"); out.Print(header_comment_vars, "# Source: $file.name$ for package '$file.package$'\n"); diff --git a/src/compiler/ruby_generator_helpers-inl.h b/src/compiler/ruby_generator_helpers-inl.h index 67a5eedd09..8263e353b2 100644 --- a/src/compiler/ruby_generator_helpers-inl.h +++ b/src/compiler/ruby_generator_helpers-inl.h @@ -47,8 +47,9 @@ inline bool ServicesFilename(const google::protobuf::FileDescriptor* file, static const unsigned proto_suffix_length = 6; // length of ".proto" if (file->name().size() > proto_suffix_length && file->name().find_last_of(".proto") == file->name().size() - 1) { - *file_name_or_error = file->name().substr( - 0, file->name().size() - proto_suffix_length) + "_services.rb"; + *file_name_or_error = + file->name().substr(0, file->name().size() - proto_suffix_length) + + "_services.rb"; return true; } else { *file_name_or_error = "Invalid proto file name: must end with .proto"; @@ -56,7 +57,8 @@ inline bool ServicesFilename(const google::protobuf::FileDescriptor* file, } } -inline string MessagesRequireName(const google::protobuf::FileDescriptor* file) { +inline string MessagesRequireName( + const google::protobuf::FileDescriptor* file) { return Replace(file->name(), ".proto", ""); } diff --git a/src/compiler/ruby_generator_map-inl.h b/src/compiler/ruby_generator_map-inl.h index ea5050652b..fa86fbb956 100644 --- a/src/compiler/ruby_generator_map-inl.h +++ b/src/compiler/ruby_generator_map-inl.h @@ -40,7 +40,6 @@ #include <string> #include <vector> - using std::initializer_list; using std::map; using std::vector; @@ -51,11 +50,12 @@ namespace grpc_ruby_generator { // into a map of key* to value*. Is merely a readability helper for later code. inline map<string, string> ListToDict(const initializer_list<string>& values) { if (values.size() % 2 != 0) { - // MOE: insert std::cerr << "Not every 'key' has a value in `values`." << std::endl; + // MOE: insert std::cerr << "Not every 'key' has a value in `values`." + // << std::endl; } map<string, string> value_map; auto value_iter = values.begin(); - for (unsigned i = 0; i < values.size()/2; ++i) { + for (unsigned i = 0; i < values.size() / 2; ++i) { string key = *value_iter; ++value_iter; string value = *value_iter; diff --git a/src/compiler/ruby_generator_string-inl.h b/src/compiler/ruby_generator_string-inl.h index 85a76fa421..44e17a202a 100644 --- a/src/compiler/ruby_generator_string-inl.h +++ b/src/compiler/ruby_generator_string-inl.h @@ -45,7 +45,7 @@ using std::transform; namespace grpc_ruby_generator { // Split splits a string using char into elems. -inline vector<string> &Split(const string &s, char delim, +inline vector<string>& Split(const string& s, char delim, vector<string>* elems) { stringstream ss(s); string item; @@ -56,7 +56,7 @@ inline vector<string> &Split(const string &s, char delim, } // Split splits a string using char, returning the result in a vector. -inline vector<string> Split(const string &s, char delim) { +inline vector<string> Split(const string& s, char delim) { vector<string> elems; Split(s, delim, &elems); return elems; @@ -106,7 +106,7 @@ inline string CapitalizeFirst(string s) { inline string RubyTypeOf(const string& a_type, const string& package) { string res(a_type); ReplacePrefix(&res, package, ""); // remove the leading package if present - ReplacePrefix(&res, ".", ""); // remove the leading . (no package) + ReplacePrefix(&res, ".", ""); // remove the leading . (no package) if (res.find('.') == string::npos) { return res; } else { diff --git a/src/core/channel/channel_args.h b/src/core/channel/channel_args.h index cf38d5d01f..92280450a1 100644 --- a/src/core/channel/channel_args.h +++ b/src/core/channel/channel_args.h @@ -51,4 +51,4 @@ void grpc_channel_args_destroy(grpc_channel_args *a); is specified in channel args, otherwise returns 0. */ int grpc_channel_args_is_census_enabled(const grpc_channel_args *a); -#endif /* __GRPC_INTERNAL_CHANNEL_CHANNEL_ARGS_H__ */ +#endif /* __GRPC_INTERNAL_CHANNEL_CHANNEL_ARGS_H__ */ diff --git a/src/core/channel/channel_stack.c b/src/core/channel/channel_stack.c index 5ee412bf7d..14fc800778 100644 --- a/src/core/channel/channel_stack.c +++ b/src/core/channel/channel_stack.c @@ -54,7 +54,7 @@ /* Given a size, round up to the next multiple of sizeof(void*) */ #define ROUND_UP_TO_ALIGNMENT_SIZE(x) \ - (((x)+GPR_MAX_ALIGNMENT - 1) & ~(GPR_MAX_ALIGNMENT - 1)) + (((x) + GPR_MAX_ALIGNMENT - 1) & ~(GPR_MAX_ALIGNMENT - 1)) size_t grpc_channel_stack_size(const grpc_channel_filter **filters, size_t filter_count) { @@ -190,14 +190,13 @@ void grpc_channel_next_op(grpc_channel_element *elem, grpc_channel_op *op) { grpc_channel_stack *grpc_channel_stack_from_top_element( grpc_channel_element *elem) { - return (grpc_channel_stack *)((char *)(elem) - - ROUND_UP_TO_ALIGNMENT_SIZE( - sizeof(grpc_channel_stack))); + return (grpc_channel_stack *)((char *)(elem)-ROUND_UP_TO_ALIGNMENT_SIZE( + sizeof(grpc_channel_stack))); } grpc_call_stack *grpc_call_stack_from_top_element(grpc_call_element *elem) { - return (grpc_call_stack *)((char *)(elem) - ROUND_UP_TO_ALIGNMENT_SIZE( - sizeof(grpc_call_stack))); + return (grpc_call_stack *)((char *)(elem)-ROUND_UP_TO_ALIGNMENT_SIZE( + sizeof(grpc_call_stack))); } static void do_nothing(void *user_data, grpc_op_error error) {} diff --git a/src/core/channel/channel_stack.h b/src/core/channel/channel_stack.h index 14e972f539..eb68102b43 100644 --- a/src/core/channel/channel_stack.h +++ b/src/core/channel/channel_stack.h @@ -302,4 +302,4 @@ void grpc_call_element_send_cancel(grpc_call_element *cur_elem); } while (0) #endif -#endif /* __GRPC_INTERNAL_CHANNEL_CHANNEL_STACK_H__ */ +#endif /* __GRPC_INTERNAL_CHANNEL_CHANNEL_STACK_H__ */ diff --git a/src/core/channel/client_channel.h b/src/core/channel/client_channel.h index 576af64ec7..6b8a7d95a8 100644 --- a/src/core/channel/client_channel.h +++ b/src/core/channel/client_channel.h @@ -59,4 +59,4 @@ grpc_transport_setup_result grpc_client_channel_transport_setup_complete( grpc_channel_filter const **channel_filters, size_t num_channel_filters, grpc_mdctx *mdctx); -#endif /* __GRPC_INTERNAL_CHANNEL_CLIENT_CHANNEL_H__ */ +#endif /* __GRPC_INTERNAL_CHANNEL_CLIENT_CHANNEL_H__ */ diff --git a/src/core/channel/client_setup.h b/src/core/channel/client_setup.h index a508785e60..155a9a5b1a 100644 --- a/src/core/channel/client_setup.h +++ b/src/core/channel/client_setup.h @@ -64,4 +64,4 @@ gpr_timespec grpc_client_setup_request_deadline(grpc_client_setup_request *r); grpc_mdctx *grpc_client_setup_get_mdctx(grpc_client_setup_request *r); -#endif /* __GRPC_INTERNAL_CHANNEL_CLIENT_SETUP_H__ */ +#endif /* __GRPC_INTERNAL_CHANNEL_CLIENT_SETUP_H__ */ diff --git a/src/core/channel/connected_channel.c b/src/core/channel/connected_channel.c index 5faa03c2f4..30de10905c 100644 --- a/src/core/channel/connected_channel.c +++ b/src/core/channel/connected_channel.c @@ -69,7 +69,7 @@ typedef struct { /* We perform a small hack to locate transport data alongside the connected channel data in call allocations, to allow everything to be pulled in minimal cache line requests */ -#define TRANSPORT_STREAM_FROM_CALL_DATA(calld) ((grpc_stream *)((calld)+1)) +#define TRANSPORT_STREAM_FROM_CALL_DATA(calld) ((grpc_stream *)((calld) + 1)) #define CALL_DATA_FROM_TRANSPORT_STREAM(transport_stream) \ (((call_data *)(transport_stream)) - 1) @@ -257,9 +257,9 @@ static void destroy_channel_elem(grpc_channel_element *elem) { } const grpc_channel_filter grpc_connected_channel_filter = { - call_op, channel_op, + call_op, channel_op, - sizeof(call_data), init_call_elem, destroy_call_elem, + sizeof(call_data), init_call_elem, destroy_call_elem, sizeof(channel_data), init_channel_elem, destroy_channel_elem, @@ -289,12 +289,8 @@ static void accept_stream(void *user_data, grpc_transport *transport, } static void recv_error(channel_data *chand, call_data *calld, int line, - const char *fmt, ...) { - va_list a; - - va_start(a, fmt); - gpr_vlog(__FILE__, line, GPR_LOG_SEVERITY_ERROR, fmt, a); - va_end(a); + const char *message) { + gpr_log_message(__FILE__, line, GPR_LOG_SEVERITY_ERROR, message); if (chand->transport) { grpc_transport_abort_stream(chand->transport, @@ -388,19 +384,23 @@ static void recv_batch(void *user_data, grpc_transport *transport, case GRPC_OP_BEGIN_MESSAGE: /* can't begin a message when we're still reading a message */ if (calld->reading_message) { - recv_error(chand, calld, __LINE__, - "Message terminated early; read %d bytes, expected %d", - calld->incoming_message.length, - calld->incoming_message_length); + char message[128]; + sprintf(message, + "Message terminated early; read %d bytes, expected %d", + (int)calld->incoming_message.length, + (int)calld->incoming_message_length); + recv_error(chand, calld, __LINE__, message); return; } /* stash away parameters, and prepare for incoming slices */ length = stream_op->data.begin_message.length; if (length > calld->max_message_length) { - recv_error( - chand, calld, __LINE__, + char message[128]; + sprintf( + message, "Maximum message length of %d exceeded by a message of length %d", calld->max_message_length, length); + recv_error(chand, calld, __LINE__, message); } else if (length > 0) { calld->reading_message = 1; calld->incoming_message_length = length; @@ -423,10 +423,12 @@ static void recv_batch(void *user_data, grpc_transport *transport, gpr_slice_buffer_add(&calld->incoming_message, stream_op->data.slice); if (calld->incoming_message.length > calld->incoming_message_length) { /* if we got too many bytes, complain */ - recv_error(chand, calld, __LINE__, - "Receiving message overflow; read %d bytes, expected %d", - calld->incoming_message.length, - calld->incoming_message_length); + char message[128]; + sprintf(message, + "Receiving message overflow; read %d bytes, expected %d", + (int)calld->incoming_message.length, + (int)calld->incoming_message_length); + recv_error(chand, calld, __LINE__, message); return; } else if (calld->incoming_message.length == calld->incoming_message_length) { @@ -439,10 +441,11 @@ static void recv_batch(void *user_data, grpc_transport *transport, final_state == GRPC_STREAM_CLOSED)) { calld->got_read_close = 1; if (calld->reading_message) { - recv_error(chand, calld, __LINE__, - "Last message truncated; read %d bytes, expected %d", - calld->incoming_message.length, - calld->incoming_message_length); + char message[128]; + sprintf(message, "Last message truncated; read %d bytes, expected %d", + (int)calld->incoming_message.length, + (int)calld->incoming_message_length); + recv_error(chand, calld, __LINE__, message); } call_op.type = GRPC_RECV_HALF_CLOSE; call_op.dir = GRPC_CALL_UP; diff --git a/src/core/channel/connected_channel.h b/src/core/channel/connected_channel.h index 660ea7ad89..9d143fc135 100644 --- a/src/core/channel/connected_channel.h +++ b/src/core/channel/connected_channel.h @@ -46,4 +46,4 @@ extern const grpc_channel_filter grpc_connected_channel_filter; grpc_transport_setup_result grpc_connected_channel_bind_transport( grpc_channel_stack *channel_stack, grpc_transport *transport); -#endif /* __GRPC_INTERNAL_CHANNEL_CONNECTED_CHANNEL_H__ */ +#endif /* __GRPC_INTERNAL_CHANNEL_CONNECTED_CHANNEL_H__ */ diff --git a/src/core/channel/http_server_filter.c b/src/core/channel/http_server_filter.c index 9a20d79c61..44eab43f09 100644 --- a/src/core/channel/http_server_filter.c +++ b/src/core/channel/http_server_filter.c @@ -32,13 +32,26 @@ */ #include "src/core/channel/http_server_filter.h" + +#include <string.h> #include <grpc/support/log.h> -typedef struct call_data { int sent_status; } call_data; +typedef struct call_data { + int sent_status; + int seen_scheme; + int seen_method; + int seen_te_trailers; +} call_data; typedef struct channel_data { grpc_mdelem *te_trailers; - grpc_mdelem *status_md; + grpc_mdelem *method; + grpc_mdelem *http_scheme; + grpc_mdelem *https_scheme; + /* TODO(klempner): Remove this once we stop using it */ + grpc_mdelem *grpc_scheme; + grpc_mdelem *content_type; + grpc_mdelem *status; } channel_data; /* used to silence 'variable not used' warnings */ @@ -56,20 +69,54 @@ static void call_op(grpc_call_element *elem, grpc_call_element *from_elem, channel_data *channeld = elem->channel_data; GRPC_CALL_LOG_OP(GPR_INFO, elem, op); - ignore_unused(calld); - ignore_unused(channeld); - switch (op->type) { case GRPC_RECV_METADATA: - /* check if it's a te: trailers header */ - if (op->data.metadata == channeld->te_trailers) { + /* Check if it is one of the headers we care about. */ + if (op->data.metadata == channeld->te_trailers || + op->data.metadata == channeld->method || + op->data.metadata == channeld->http_scheme || + op->data.metadata == channeld->https_scheme || + op->data.metadata == channeld->grpc_scheme || + op->data.metadata == channeld->content_type) { /* swallow it */ + if (op->data.metadata == channeld->method) { + calld->seen_method = 1; + } else if (op->data.metadata->key == channeld->http_scheme->key) { + calld->seen_scheme = 1; + } else if (op->data.metadata == channeld->te_trailers) { + calld->seen_te_trailers = 1; + } + /* TODO(klempner): Track that we've seen all the headers we should + require */ grpc_mdelem_unref(op->data.metadata); op->done_cb(op->user_data, GRPC_OP_OK); - } else if (op->data.metadata->key == channeld->te_trailers->key) { - gpr_log(GPR_ERROR, "Invalid te: header: '%s'", + } else if (op->data.metadata->key == channeld->content_type->key) { + if (strncmp(grpc_mdstr_as_c_string(op->data.metadata->value), + "application/grpc+", 17) == 0) { + /* Although the C implementation doesn't (currently) generate them, + any + custom +-suffix is explicitly valid. */ + /* TODO(klempner): We should consider preallocating common values such + as +proto or +json, or at least stashing them if we see them. */ + /* TODO(klempner): Should we be surfacing this to application code? */ + } else { + /* TODO(klempner): We're currently allowing this, but we shouldn't + see it without a proxy so log for now. */ + gpr_log(GPR_INFO, "Unexpected content-type %s", + channeld->content_type->key); + } + grpc_mdelem_unref(op->data.metadata); + op->done_cb(op->user_data, GRPC_OP_OK); + } else if (op->data.metadata->key == channeld->te_trailers->key || + op->data.metadata->key == channeld->method->key || + op->data.metadata->key == channeld->http_scheme->key || + op->data.metadata->key == channeld->content_type->key) { + gpr_log(GPR_ERROR, "Invalid %s: header: '%s'", + grpc_mdstr_as_c_string(op->data.metadata->key), grpc_mdstr_as_c_string(op->data.metadata->value)); - /* swallow it */ + /* swallow it and error everything out. */ + /* TODO(klempner): We ought to generate more descriptive error messages + on the wire here. */ grpc_mdelem_unref(op->data.metadata); op->done_cb(op->user_data, GRPC_OP_OK); grpc_call_element_send_cancel(elem); @@ -78,14 +125,33 @@ static void call_op(grpc_call_element *elem, grpc_call_element *from_elem, grpc_call_next_op(elem, op); } break; + case GRPC_RECV_END_OF_INITIAL_METADATA: + /* Have we seen the required http2 transport headers? + (:method, :scheme, content-type, with :path and :authority covered + at the channel level right now) */ + if (calld->seen_method && calld->seen_scheme && calld->seen_te_trailers) { + grpc_call_next_op(elem, op); + } else { + if (!calld->seen_method) { + gpr_log(GPR_ERROR, "Missing :method header"); + } else if (!calld->seen_scheme) { + gpr_log(GPR_ERROR, "Missing :scheme header"); + } else if (!calld->seen_te_trailers) { + gpr_log(GPR_ERROR, "Missing te trailers header"); + } + /* Error this call out */ + op->done_cb(op->user_data, GRPC_OP_OK); + grpc_call_element_send_cancel(elem); + } + break; case GRPC_SEND_START: case GRPC_SEND_METADATA: /* If we haven't sent status 200 yet, we need to so so because it needs to come before any non : prefixed metadata. */ if (!calld->sent_status) { calld->sent_status = 1; - /* status_md is reffed by grpc_call_element_send_metadata */ - grpc_call_element_send_metadata(elem, channeld->status_md); + /* status is reffed by grpc_call_element_send_metadata */ + grpc_call_element_send_metadata(elem, channeld->status); } grpc_call_next_op(elem, op); break; @@ -124,6 +190,9 @@ static void init_call_elem(grpc_call_element *elem, /* initialize members */ calld->sent_status = 0; + calld->seen_scheme = 0; + calld->seen_method = 0; + calld->seen_te_trailers = 0; } /* Destructor for call_data */ @@ -151,7 +220,13 @@ static void init_channel_elem(grpc_channel_element *elem, /* initialize members */ channeld->te_trailers = grpc_mdelem_from_strings(mdctx, "te", "trailers"); - channeld->status_md = grpc_mdelem_from_strings(mdctx, ":status", "200"); + channeld->status = grpc_mdelem_from_strings(mdctx, ":status", "200"); + channeld->method = grpc_mdelem_from_strings(mdctx, ":method", "POST"); + channeld->http_scheme = grpc_mdelem_from_strings(mdctx, ":scheme", "http"); + channeld->https_scheme = grpc_mdelem_from_strings(mdctx, ":scheme", "https"); + channeld->grpc_scheme = grpc_mdelem_from_strings(mdctx, ":scheme", "grpc"); + channeld->content_type = + grpc_mdelem_from_strings(mdctx, "content-type", "application/grpc"); } /* Destructor for channel data */ @@ -160,7 +235,12 @@ static void destroy_channel_elem(grpc_channel_element *elem) { channel_data *channeld = elem->channel_data; grpc_mdelem_unref(channeld->te_trailers); - grpc_mdelem_unref(channeld->status_md); + grpc_mdelem_unref(channeld->status); + grpc_mdelem_unref(channeld->method); + grpc_mdelem_unref(channeld->http_scheme); + grpc_mdelem_unref(channeld->https_scheme); + grpc_mdelem_unref(channeld->grpc_scheme); + grpc_mdelem_unref(channeld->content_type); } const grpc_channel_filter grpc_http_server_filter = { diff --git a/src/core/channel/metadata_buffer.c b/src/core/channel/metadata_buffer.c index 75fd90b707..d4de4ba576 100644 --- a/src/core/channel/metadata_buffer.c +++ b/src/core/channel/metadata_buffer.c @@ -61,7 +61,7 @@ struct grpc_metadata_buffer_impl { size_t elem_cap; }; -#define ELEMS(buffer) ((qelem *)((buffer)+1)) +#define ELEMS(buffer) ((qelem *)((buffer) + 1)) void grpc_metadata_buffer_init(grpc_metadata_buffer *buffer) { /* start buffer as NULL, indicating no elements */ diff --git a/src/core/channel/metadata_buffer.h b/src/core/channel/metadata_buffer.h index 818b290ce2..011dabed1b 100644 --- a/src/core/channel/metadata_buffer.h +++ b/src/core/channel/metadata_buffer.h @@ -67,4 +67,4 @@ grpc_metadata *grpc_metadata_buffer_extract_elements( grpc_metadata_buffer *buffer); void grpc_metadata_buffer_cleanup_elements(void *elements, grpc_op_error error); -#endif /* __GRPC_INTERNAL_CHANNEL_METADATA_BUFFER_H__ */ +#endif /* __GRPC_INTERNAL_CHANNEL_METADATA_BUFFER_H__ */ diff --git a/src/core/channel/noop_filter.c b/src/core/channel/noop_filter.c index b6b3f661f7..6f854a2b87 100644 --- a/src/core/channel/noop_filter.c +++ b/src/core/channel/noop_filter.c @@ -131,9 +131,9 @@ static void destroy_channel_elem(grpc_channel_element *elem) { } const grpc_channel_filter grpc_no_op_filter = { - call_op, channel_op, + call_op, channel_op, - sizeof(call_data), init_call_elem, destroy_call_elem, + sizeof(call_data), init_call_elem, destroy_call_elem, sizeof(channel_data), init_channel_elem, destroy_channel_elem, diff --git a/src/core/channel/noop_filter.h b/src/core/channel/noop_filter.h index 4057ff7ac9..269214f893 100644 --- a/src/core/channel/noop_filter.h +++ b/src/core/channel/noop_filter.h @@ -41,4 +41,4 @@ customize for their own filters */ extern const grpc_channel_filter grpc_no_op_filter; -#endif /* __GRPC_INTERNAL_CHANNEL_NOOP_FILTER_H__ */ +#endif /* __GRPC_INTERNAL_CHANNEL_NOOP_FILTER_H__ */ diff --git a/src/core/compression/algorithm.h b/src/core/compression/algorithm.h index 05895a889e..c5ec6d21b6 100644 --- a/src/core/compression/algorithm.h +++ b/src/core/compression/algorithm.h @@ -46,4 +46,4 @@ typedef enum { const char *grpc_compression_algorithm_name( grpc_compression_algorithm algorithm); -#endif /* __GRPC_INTERNAL_COMPRESSION_ALGORITHM_H__ */ +#endif /* __GRPC_INTERNAL_COMPRESSION_ALGORITHM_H__ */ diff --git a/src/core/compression/message_compress.h b/src/core/compression/message_compress.h index af8a0a5d75..564ca69a87 100644 --- a/src/core/compression/message_compress.h +++ b/src/core/compression/message_compress.h @@ -49,4 +49,4 @@ int grpc_msg_compress(grpc_compression_algorithm algorithm, int grpc_msg_decompress(grpc_compression_algorithm algorithm, gpr_slice_buffer *input, gpr_slice_buffer *output); -#endif /* __GRPC_INTERNAL_COMPRESSION_MESSAGE_COMPRESS_H__ */ +#endif /* __GRPC_INTERNAL_COMPRESSION_MESSAGE_COMPRESS_H__ */ diff --git a/src/core/httpcli/format_request.h b/src/core/httpcli/format_request.h index 988f872563..a82130cb93 100644 --- a/src/core/httpcli/format_request.h +++ b/src/core/httpcli/format_request.h @@ -42,4 +42,4 @@ gpr_slice grpc_httpcli_format_post_request(const grpc_httpcli_request *request, const char *body_bytes, size_t body_size); -#endif /* __GRPC_INTERNAL_HTTPCLI_FORMAT_REQUEST_H__ */ +#endif /* __GRPC_INTERNAL_HTTPCLI_FORMAT_REQUEST_H__ */ diff --git a/src/core/httpcli/httpcli.h b/src/core/httpcli/httpcli.h index ef8031354c..90f89a9366 100644 --- a/src/core/httpcli/httpcli.h +++ b/src/core/httpcli/httpcli.h @@ -115,4 +115,4 @@ typedef int (*grpc_httpcli_post_override)(const grpc_httpcli_request *request, void grpc_httpcli_set_override(grpc_httpcli_get_override get, grpc_httpcli_post_override post); -#endif /* __GRPC_INTERNAL_HTTPCLI_HTTPCLI_H__ */ +#endif /* __GRPC_INTERNAL_HTTPCLI_HTTPCLI_H__ */ diff --git a/src/core/httpcli/httpcli_security_context.h b/src/core/httpcli/httpcli_security_context.h index c267622e60..a73ecca0b3 100644 --- a/src/core/httpcli/httpcli_security_context.h +++ b/src/core/httpcli/httpcli_security_context.h @@ -40,4 +40,4 @@ grpc_security_status grpc_httpcli_ssl_channel_security_context_create( const unsigned char *pem_root_certs, size_t pem_root_certs_size, const char *secure_peer_name, grpc_channel_security_context **ctx); -#endif /* __GRPC_INTERNAL_HTTPCLI_HTTPCLI_SECURITY_CONTEXT_H__ */ +#endif /* __GRPC_INTERNAL_HTTPCLI_HTTPCLI_SECURITY_CONTEXT_H__ */ diff --git a/src/core/httpcli/parser.h b/src/core/httpcli/parser.h index e2c24a9993..520b16fd02 100644 --- a/src/core/httpcli/parser.h +++ b/src/core/httpcli/parser.h @@ -61,4 +61,4 @@ void grpc_httpcli_parser_destroy(grpc_httpcli_parser *parser); int grpc_httpcli_parser_parse(grpc_httpcli_parser *parser, gpr_slice slice); int grpc_httpcli_parser_eof(grpc_httpcli_parser *parser); -#endif /* __GRPC_INTERNAL_HTTPCLI_PARSER_H__ */ +#endif /* __GRPC_INTERNAL_HTTPCLI_PARSER_H__ */ diff --git a/src/core/iomgr/pollset.h b/src/core/iomgr/pollset.h index 7374a4ec13..36d80d5c29 100644 --- a/src/core/iomgr/pollset.h +++ b/src/core/iomgr/pollset.h @@ -35,6 +35,7 @@ #define __GRPC_INTERNAL_IOMGR_POLLSET_H_ #include <grpc/support/port_platform.h> +#include <grpc/support/time.h> /* A grpc_pollset is a set of file descriptors that a higher level item is interested in. For example: diff --git a/src/core/iomgr/sockaddr_utils.c b/src/core/iomgr/sockaddr_utils.c index f709d35162..eca14a4f39 100644 --- a/src/core/iomgr/sockaddr_utils.c +++ b/src/core/iomgr/sockaddr_utils.c @@ -153,3 +153,31 @@ int grpc_sockaddr_to_string(char **out, const struct sockaddr *addr, errno = save_errno; return ret; } + +int grpc_sockaddr_get_port(const struct sockaddr *addr) { + switch (addr->sa_family) { + case AF_INET: + return ntohs(((struct sockaddr_in *)addr)->sin_port); + case AF_INET6: + return ntohs(((struct sockaddr_in6 *)addr)->sin6_port); + default: + gpr_log(GPR_ERROR, "Unknown socket family %d in %s", addr->sa_family, + __FUNCTION__); + return 0; + } +} + +int grpc_sockaddr_set_port(const struct sockaddr *addr, int port) { + switch (addr->sa_family) { + case AF_INET: + ((struct sockaddr_in *)addr)->sin_port = htons(port); + return 1; + case AF_INET6: + ((struct sockaddr_in6 *)addr)->sin6_port = htons(port); + return 1; + default: + gpr_log(GPR_ERROR, "Unknown socket family %d in %s", addr->sa_family, + __FUNCTION__); + return 0; + } +} diff --git a/src/core/iomgr/sockaddr_utils.h b/src/core/iomgr/sockaddr_utils.h index 753d0c824a..3f5b770e86 100644 --- a/src/core/iomgr/sockaddr_utils.h +++ b/src/core/iomgr/sockaddr_utils.h @@ -57,6 +57,12 @@ int grpc_sockaddr_is_wildcard(const struct sockaddr *addr, int *port_out); void grpc_sockaddr_make_wildcards(int port, struct sockaddr_in *wild4_out, struct sockaddr_in6 *wild6_out); +/* Return the IP port number of a sockaddr */ +int grpc_sockaddr_get_port(const struct sockaddr *addr); + +/* Set IP port number of a sockaddr */ +int grpc_sockaddr_set_port(const struct sockaddr *addr, int port); + /* Converts a sockaddr into a newly-allocated human-readable string. Currently, only the AF_INET and AF_INET6 families are recognized. diff --git a/src/core/iomgr/tcp_server.h b/src/core/iomgr/tcp_server.h index 1968246b75..d881e146b9 100644 --- a/src/core/iomgr/tcp_server.h +++ b/src/core/iomgr/tcp_server.h @@ -52,7 +52,8 @@ grpc_tcp_server *grpc_tcp_server_create(); void grpc_tcp_server_start(grpc_tcp_server *server, grpc_pollset *pollset, grpc_tcp_server_cb cb, void *cb_arg); -/* Add a port to the server, returning true on success, or false otherwise. +/* Add a port to the server, returning port number on success, or negative + on failure. The :: and 0.0.0.0 wildcard addresses are treated identically, accepting both IPv4 and IPv6 connections, but :: is the preferred style. This usually @@ -60,6 +61,8 @@ void grpc_tcp_server_start(grpc_tcp_server *server, grpc_pollset *pollset, but not dualstack sockets. For raw access to the underlying sockets, see grpc_tcp_server_get_fd(). */ +/* TODO(ctiller): deprecate this, and make grpc_tcp_server_add_ports to handle + all of the multiple socket port matching logic in one place */ int grpc_tcp_server_add_port(grpc_tcp_server *s, const struct sockaddr *addr, int addr_len); diff --git a/src/core/iomgr/tcp_server_posix.c b/src/core/iomgr/tcp_server_posix.c index 5ed517748a..753e24c38e 100644 --- a/src/core/iomgr/tcp_server_posix.c +++ b/src/core/iomgr/tcp_server_posix.c @@ -154,6 +154,9 @@ static int get_max_accept_queue_size() { /* Prepare a recently-created socket for listening. */ static int prepare_socket(int fd, const struct sockaddr *addr, int addr_len) { + struct sockaddr_storage sockname_temp; + socklen_t sockname_len; + if (fd < 0) { goto error; } @@ -179,13 +182,18 @@ static int prepare_socket(int fd, const struct sockaddr *addr, int addr_len) { goto error; } - return 1; + sockname_len = sizeof(sockname_temp); + if (getsockname(fd, (struct sockaddr *)&sockname_temp, &sockname_len) < 0) { + goto error; + } + + return grpc_sockaddr_get_port((struct sockaddr *)&sockname_temp); error: if (fd >= 0) { close(fd); } - return 0; + return -1; } /* event manager callback when reads are ready */ @@ -234,39 +242,64 @@ error: static int add_socket_to_server(grpc_tcp_server *s, int fd, const struct sockaddr *addr, int addr_len) { server_port *sp; + int port; - if (!prepare_socket(fd, addr, addr_len)) { - return 0; - } - - gpr_mu_lock(&s->mu); - GPR_ASSERT(!s->cb && "must add ports before starting server"); - /* append it to the list under a lock */ - if (s->nports == s->port_capacity) { - s->port_capacity *= 2; - s->ports = gpr_realloc(s->ports, sizeof(server_port *) * s->port_capacity); + port = prepare_socket(fd, addr, addr_len); + if (port >= 0) { + gpr_mu_lock(&s->mu); + GPR_ASSERT(!s->cb && "must add ports before starting server"); + /* append it to the list under a lock */ + if (s->nports == s->port_capacity) { + s->port_capacity *= 2; + s->ports = + gpr_realloc(s->ports, sizeof(server_port *) * s->port_capacity); + } + sp = &s->ports[s->nports++]; + sp->server = s; + sp->fd = fd; + sp->emfd = grpc_fd_create(fd); + GPR_ASSERT(sp->emfd); + gpr_mu_unlock(&s->mu); } - sp = &s->ports[s->nports++]; - sp->server = s; - sp->fd = fd; - sp->emfd = grpc_fd_create(fd); - GPR_ASSERT(sp->emfd); - gpr_mu_unlock(&s->mu); - return 1; + return port; } int grpc_tcp_server_add_port(grpc_tcp_server *s, const struct sockaddr *addr, int addr_len) { - int ok = 0; + int allocated_port1 = -1; + int allocated_port2 = -1; + int i; int fd; grpc_dualstack_mode dsmode; struct sockaddr_in6 addr6_v4mapped; struct sockaddr_in wild4; struct sockaddr_in6 wild6; struct sockaddr_in addr4_copy; + struct sockaddr *allocated_addr = NULL; + struct sockaddr_storage sockname_temp; + socklen_t sockname_len; int port; + /* Check if this is a wildcard port, and if so, try to keep the port the same + as some previously created listener. */ + if (grpc_sockaddr_get_port(addr) == 0) { + for (i = 0; i < s->nports; i++) { + sockname_len = sizeof(sockname_temp); + if (0 == getsockname(s->ports[i].fd, (struct sockaddr *)&sockname_temp, + &sockname_len)) { + port = grpc_sockaddr_get_port((struct sockaddr *)&sockname_temp); + if (port > 0) { + allocated_addr = malloc(addr_len); + memcpy(allocated_addr, addr, addr_len); + grpc_sockaddr_set_port(allocated_addr, port); + addr = allocated_addr; + break; + } + } + } + } + if (grpc_sockaddr_to_v4mapped(addr, &addr6_v4mapped)) { addr = (const struct sockaddr *)&addr6_v4mapped; addr_len = sizeof(addr6_v4mapped); @@ -280,12 +313,15 @@ int grpc_tcp_server_add_port(grpc_tcp_server *s, const struct sockaddr *addr, addr = (struct sockaddr *)&wild6; addr_len = sizeof(wild6); fd = grpc_create_dualstack_socket(addr, SOCK_STREAM, 0, &dsmode); - ok |= add_socket_to_server(s, fd, addr, addr_len); + allocated_port1 = add_socket_to_server(s, fd, addr, addr_len); if (fd >= 0 && dsmode == GRPC_DSMODE_DUALSTACK) { - return ok; + goto done; } /* If we didn't get a dualstack socket, also listen on 0.0.0.0. */ + if (port == 0 && allocated_port1 > 0) { + grpc_sockaddr_set_port((struct sockaddr *)&wild4, allocated_port1); + } addr = (struct sockaddr *)&wild4; addr_len = sizeof(wild4); } @@ -299,8 +335,11 @@ int grpc_tcp_server_add_port(grpc_tcp_server *s, const struct sockaddr *addr, addr = (struct sockaddr *)&addr4_copy; addr_len = sizeof(addr4_copy); } - ok |= add_socket_to_server(s, fd, addr, addr_len); - return ok; + allocated_port2 = add_socket_to_server(s, fd, addr, addr_len); + +done: + gpr_free(allocated_addr); + return allocated_port1 >= 0 ? allocated_port1 : allocated_port2; } int grpc_tcp_server_get_fd(grpc_tcp_server *s, int index) { diff --git a/src/core/security/auth.c b/src/core/security/auth.c index f743b25838..e36bf2382f 100644 --- a/src/core/security/auth.c +++ b/src/core/security/auth.c @@ -157,6 +157,5 @@ static void destroy_channel_elem(grpc_channel_element *elem) { } const grpc_channel_filter grpc_client_auth_filter = { - call_op, channel_op, sizeof(call_data), - init_call_elem, destroy_call_elem, sizeof(channel_data), - init_channel_elem, destroy_channel_elem, "auth"}; + call_op, channel_op, sizeof(call_data), init_call_elem, destroy_call_elem, + sizeof(channel_data), init_channel_elem, destroy_channel_elem, "auth"}; diff --git a/src/core/security/auth.h b/src/core/security/auth.h index 0b279f8740..94fa2aba7d 100644 --- a/src/core/security/auth.h +++ b/src/core/security/auth.h @@ -38,4 +38,4 @@ extern const grpc_channel_filter grpc_client_auth_filter; -#endif /* __GRPC_INTERNAL_SECURITY_AUTH_H__ */ +#endif /* __GRPC_INTERNAL_SECURITY_AUTH_H__ */ diff --git a/src/core/security/credentials.c b/src/core/security/credentials.c index c99ac8021d..d3bba0fb1f 100644 --- a/src/core/security/credentials.c +++ b/src/core/security/credentials.c @@ -819,6 +819,26 @@ const grpc_credentials_array *grpc_composite_credentials_get_credentials( return &c->inner; } +grpc_credentials *grpc_credentials_contains_type( + grpc_credentials *creds, const char *type, + grpc_credentials **composite_creds) { + size_t i; + if (!strcmp(creds->type, type)) { + if (composite_creds != NULL) *composite_creds = NULL; + return creds; + } else if (!strcmp(creds->type, GRPC_CREDENTIALS_TYPE_COMPOSITE)) { + const grpc_credentials_array *inner_creds_array = + grpc_composite_credentials_get_credentials(creds); + for (i = 0; i < inner_creds_array->num_creds; i++) { + if (!strcmp(type, inner_creds_array->creds_array[i]->type)) { + if (composite_creds != NULL) *composite_creds = creds; + return inner_creds_array->creds_array[i]; + } + } + } + return NULL; +} + /* -- IAM credentials. -- */ typedef struct { @@ -877,4 +897,3 @@ grpc_credentials *grpc_iam_credentials_create(const char *token, /* -- Default credentials TODO(jboeuf). -- */ grpc_credentials *grpc_default_credentials_create(void) { return NULL; } - diff --git a/src/core/security/credentials.h b/src/core/security/credentials.h index 036a44493e..4a2532d7c4 100644 --- a/src/core/security/credentials.h +++ b/src/core/security/credentials.h @@ -108,6 +108,14 @@ typedef struct { const grpc_credentials_array *grpc_composite_credentials_get_credentials( grpc_credentials *composite_creds); +/* Returns creds if creds is of the specified type or the inner creds of the + specified type (if found), if the creds is of type COMPOSITE. + If composite_creds is not NULL, *composite_creds will point to creds if of + type COMPOSITE in case of success. */ +grpc_credentials *grpc_credentials_contains_type( + grpc_credentials *creds, const char *type, + grpc_credentials **composite_creds); + /* Exposed for testing only. */ grpc_credentials_status grpc_oauth2_token_fetcher_credentials_parse_server_response( @@ -118,7 +126,6 @@ grpc_oauth2_token_fetcher_credentials_parse_server_response( grpc_credentials *grpc_fake_oauth2_credentials_create( const char *token_md_value, int is_async); - /* --- grpc_server_credentials. --- */ typedef struct { @@ -136,5 +143,4 @@ struct grpc_server_credentials { const grpc_ssl_config *grpc_ssl_server_credentials_get_config( const grpc_server_credentials *ssl_creds); - -#endif /* __GRPC_INTERNAL_SECURITY_CREDENTIALS_H__ */ +#endif /* __GRPC_INTERNAL_SECURITY_CREDENTIALS_H__ */ diff --git a/src/core/security/factories.c b/src/core/security/factories.c new file mode 100644 index 0000000000..d89c692989 --- /dev/null +++ b/src/core/security/factories.c @@ -0,0 +1,80 @@ +/* + * + * 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 <string.h> + +#include "src/core/security/credentials.h" +#include "src/core/security/security_context.h" +#include "src/core/surface/lame_client.h" +#include <grpc/support/alloc.h> +#include <grpc/support/log.h> +#include <grpc/support/useful.h> + +grpc_channel *grpc_secure_channel_create(grpc_credentials *creds, + const char *target, + const grpc_channel_args *args) { + grpc_secure_channel_factory factories[] = { + {GRPC_CREDENTIALS_TYPE_SSL, grpc_ssl_channel_create}, + {GRPC_CREDENTIALS_TYPE_FAKE_TRANSPORT_SECURITY, + grpc_fake_transport_security_channel_create}}; + return grpc_secure_channel_create_with_factories( + factories, GPR_ARRAY_SIZE(factories), creds, target, args); +} + +grpc_server *grpc_secure_server_create(grpc_server_credentials *creds, + grpc_completion_queue *cq, + const grpc_channel_args *args) { + grpc_security_status status = GRPC_SECURITY_ERROR; + grpc_security_context *ctx = NULL; + grpc_server *server = NULL; + if (creds == NULL) return NULL; /* TODO(ctiller): Return lame server. */ + + if (!strcmp(creds->type, GRPC_CREDENTIALS_TYPE_SSL)) { + status = grpc_ssl_server_security_context_create( + grpc_ssl_server_credentials_get_config(creds), &ctx); + } else if (!strcmp(creds->type, + GRPC_CREDENTIALS_TYPE_FAKE_TRANSPORT_SECURITY)) { + ctx = grpc_fake_server_security_context_create(); + status = GRPC_SECURITY_OK; + } + + if (status != GRPC_SECURITY_OK) { + gpr_log(GPR_ERROR, + "Unable to create secure server with credentials of type %s.", + creds->type); + return NULL; /* TODO(ctiller): Return lame server. */ + } + server = grpc_secure_server_create_internal(cq, args, ctx); + grpc_security_context_unref(ctx); + return server; +} diff --git a/src/core/security/google_root_certs.h b/src/core/security/google_root_certs.h index 4bcfaddcdb..30ed16c03b 100644 --- a/src/core/security/google_root_certs.h +++ b/src/core/security/google_root_certs.h @@ -37,4 +37,4 @@ extern unsigned char grpc_google_root_certs[]; extern unsigned int grpc_google_root_certs_size; -#endif /* __GRPC_INTERNAL_SECURITY_GOOGLE_ROOT_CERTS_H__ */ +#endif /* __GRPC_INTERNAL_SECURITY_GOOGLE_ROOT_CERTS_H__ */ diff --git a/src/core/security/secure_endpoint.h b/src/core/security/secure_endpoint.h index d0f0fa7d5b..20143150e0 100644 --- a/src/core/security/secure_endpoint.h +++ b/src/core/security/secure_endpoint.h @@ -44,4 +44,4 @@ grpc_endpoint *grpc_secure_endpoint_create( struct tsi_frame_protector *protector, grpc_endpoint *to_wrap, gpr_slice *leftover_slices, size_t leftover_nslices); -#endif /* __GRPC_INTERNAL_ENDPOINT_SECURE_ENDPOINT_H__ */ +#endif /* __GRPC_INTERNAL_ENDPOINT_SECURE_ENDPOINT_H__ */ diff --git a/src/core/security/secure_transport_setup.h b/src/core/security/secure_transport_setup.h index 50f2b08529..b13d065fbf 100644 --- a/src/core/security/secure_transport_setup.h +++ b/src/core/security/secure_transport_setup.h @@ -50,4 +50,4 @@ void grpc_setup_secure_transport(grpc_security_context *ctx, grpc_secure_transport_setup_done_cb cb, void *user_data); -#endif /* __GRPC_INTERNAL_SECURITY_SECURE_TRANSPORT_SETUP_H__ */ +#endif /* __GRPC_INTERNAL_SECURITY_SECURE_TRANSPORT_SETUP_H__ */ diff --git a/src/core/security/security_context.c b/src/core/security/security_context.c index 13b9a847ee..d519ecab87 100644 --- a/src/core/security/security_context.c +++ b/src/core/security/security_context.c @@ -100,8 +100,7 @@ grpc_arg grpc_security_context_to_arg(grpc_security_context *ctx) { return result; } -grpc_security_context *grpc_security_context_from_arg( - const grpc_arg *arg) { +grpc_security_context *grpc_security_context_from_arg(const grpc_arg *arg) { if (strcmp(arg->key, GRPC_SECURITY_CONTEXT_ARG)) return NULL; if (arg->type != GRPC_ARG_POINTER) { gpr_log(GPR_ERROR, "Invalid type %d for arg %s", arg->type, @@ -140,9 +139,7 @@ static void fake_channel_destroy(grpc_security_context *ctx) { gpr_free(ctx); } -static void fake_server_destroy(grpc_security_context *ctx) { - gpr_free(ctx); -} +static void fake_server_destroy(grpc_security_context *ctx) { gpr_free(ctx); } static grpc_security_status fake_channel_create_handshaker( grpc_security_context *ctx, tsi_handshaker **handshaker) { @@ -234,8 +231,7 @@ static void ssl_channel_destroy(grpc_security_context *ctx) { } static void ssl_server_destroy(grpc_security_context *ctx) { - grpc_ssl_server_security_context *c = - (grpc_ssl_server_security_context *)ctx; + grpc_ssl_server_security_context *c = (grpc_ssl_server_security_context *)ctx; if (c->handshaker_factory != NULL) { tsi_ssl_handshaker_factory_destroy(c->handshaker_factory); } @@ -267,8 +263,7 @@ static grpc_security_status ssl_channel_create_handshaker( static grpc_security_status ssl_server_create_handshaker( grpc_security_context *ctx, tsi_handshaker **handshaker) { - grpc_ssl_server_security_context *c = - (grpc_ssl_server_security_context *)ctx; + grpc_ssl_server_security_context *c = (grpc_ssl_server_security_context *)ctx; return ssl_create_handshaker(c->handshaker_factory, 0, NULL, handshaker); } @@ -438,20 +433,19 @@ error: return GRPC_SECURITY_ERROR; } - - /* -- High level objects. -- */ -static grpc_channel *grpc_ssl_channel_create(grpc_credentials *creds, - const grpc_ssl_config *config, - const char *target, - const grpc_channel_args *args) { +grpc_channel *grpc_ssl_channel_create(grpc_credentials *ssl_creds, + grpc_credentials *request_metadata_creds, + const char *target, + const grpc_channel_args *args) { grpc_channel_security_context *ctx = NULL; grpc_channel *channel = NULL; grpc_security_status status = GRPC_SECURITY_OK; size_t i = 0; const char *secure_peer_name = target; - for (i = 0; i < args->num_args; i++) { + + for (i = 0; args && i < args->num_args; i++) { grpc_arg *arg = &args->args[i]; if (!strcmp(arg->key, GRPC_SSL_TARGET_NAME_OVERRIDE_ARG) && arg->type == GRPC_ARG_STRING) { @@ -459,8 +453,9 @@ static grpc_channel *grpc_ssl_channel_create(grpc_credentials *creds, break; } } - status = grpc_ssl_channel_security_context_create(creds, config, - secure_peer_name, &ctx); + status = grpc_ssl_channel_security_context_create( + request_metadata_creds, grpc_ssl_credentials_get_config(ssl_creds), + secure_peer_name, &ctx); if (status != GRPC_SECURITY_OK) { return grpc_lame_client_channel_create(); } @@ -469,58 +464,47 @@ static grpc_channel *grpc_ssl_channel_create(grpc_credentials *creds, return channel; } - -static grpc_credentials *get_creds_from_composite( - grpc_credentials *composite_creds, const char *type) { - size_t i; - const grpc_credentials_array *inner_creds_array = - grpc_composite_credentials_get_credentials(composite_creds); - for (i = 0; i < inner_creds_array->num_creds; i++) { - if (!strcmp(type, inner_creds_array->creds_array[i]->type)) { - return inner_creds_array->creds_array[i]; - } - } - return NULL; +grpc_channel *grpc_fake_transport_security_channel_create( + grpc_credentials *fake_creds, grpc_credentials *request_metadata_creds, + const char *target, const grpc_channel_args *args) { + grpc_channel_security_context *ctx = + grpc_fake_channel_security_context_create(request_metadata_creds); + grpc_channel *channel = + grpc_secure_channel_create_internal(target, args, ctx); + grpc_security_context_unref(&ctx->base); + return channel; } -static grpc_channel *grpc_channel_create_from_composite_creds( - grpc_credentials *composite_creds, const char *target, +grpc_channel *grpc_secure_channel_create_with_factories( + const grpc_secure_channel_factory *factories, size_t num_factories, + grpc_credentials *creds, const char *target, const grpc_channel_args *args) { - grpc_credentials *creds = - get_creds_from_composite(composite_creds, GRPC_CREDENTIALS_TYPE_SSL); - if (creds != NULL) { - return grpc_ssl_channel_create( - composite_creds, grpc_ssl_credentials_get_config(creds), target, args); + size_t i; + if (creds == NULL) { + gpr_log(GPR_ERROR, "No credentials to create a secure channel."); + return grpc_lame_client_channel_create(); } - return NULL; /* TODO(ctiller): return lame channel. */ -} - -grpc_channel *grpc_secure_channel_create(grpc_credentials *creds, - const char *target, - const grpc_channel_args *args) { if (grpc_credentials_has_request_metadata_only(creds)) { gpr_log(GPR_ERROR, "Credentials is insufficient to create a secure channel."); return grpc_lame_client_channel_create(); } - if (!strcmp(creds->type, GRPC_CREDENTIALS_TYPE_SSL)) { - return grpc_ssl_channel_create(NULL, grpc_ssl_credentials_get_config(creds), - target, args); - } else if (!strcmp(creds->type, - GRPC_CREDENTIALS_TYPE_FAKE_TRANSPORT_SECURITY)) { - grpc_channel_security_context *ctx = - grpc_fake_channel_security_context_create(NULL); - grpc_channel *channel = - grpc_secure_channel_create_internal(target, args, ctx); - grpc_security_context_unref(&ctx->base); - return channel; - } else if (!strcmp(creds->type, GRPC_CREDENTIALS_TYPE_COMPOSITE)) { - return grpc_channel_create_from_composite_creds(creds, target, args); - } else { - gpr_log(GPR_ERROR, - "Unknown credentials type %s for creating a secure channel."); - return grpc_lame_client_channel_create(); + + for (i = 0; i < num_factories; i++) { + grpc_credentials *composite_creds = NULL; + grpc_credentials *transport_security_creds = NULL; + transport_security_creds = grpc_credentials_contains_type( + creds, factories[i].creds_type, &composite_creds); + if (transport_security_creds != NULL) { + return factories[i].factory(transport_security_creds, composite_creds, + target, args); + } } + + gpr_log(GPR_ERROR, + "Unknown credentials type %s for creating a secure channel.", + creds->type); + return grpc_lame_client_channel_create(); } grpc_channel *grpc_default_secure_channel_create( @@ -528,30 +512,3 @@ grpc_channel *grpc_default_secure_channel_create( return grpc_secure_channel_create(grpc_default_credentials_create(), target, args); } - -grpc_server *grpc_secure_server_create(grpc_server_credentials *creds, - grpc_completion_queue *cq, - const grpc_channel_args *args) { - grpc_security_status status = GRPC_SECURITY_ERROR; - grpc_security_context *ctx = NULL; - grpc_server *server = NULL; - if (creds == NULL) return NULL; /* TODO(ctiller): Return lame server. */ - if (!strcmp(creds->type, GRPC_CREDENTIALS_TYPE_SSL)) { - status = grpc_ssl_server_security_context_create( - grpc_ssl_server_credentials_get_config(creds), &ctx); - } else if (!strcmp(creds->type, - GRPC_CREDENTIALS_TYPE_FAKE_TRANSPORT_SECURITY)) { - ctx = grpc_fake_server_security_context_create(); - status = GRPC_SECURITY_OK; - } else { - gpr_log(GPR_ERROR, - "Unable to create secure server with credentials of type %s.", - creds->type); - } - if (status != GRPC_SECURITY_OK) { - return NULL; /* TODO(ctiller): Return lame server. */ - } - server = grpc_secure_server_create_internal(cq, args, ctx); - grpc_security_context_unref(ctx); - return server; -} diff --git a/src/core/security/security_context.h b/src/core/security/security_context.h index bbd7ff3b1a..9ace7f1ccb 100644 --- a/src/core/security/security_context.h +++ b/src/core/security/security_context.h @@ -118,7 +118,7 @@ grpc_security_context *grpc_find_security_context_in_args( typedef struct grpc_channel_security_context grpc_channel_security_context; struct grpc_channel_security_context { - grpc_security_context base; /* requires is_client_side to be non 0. */ + grpc_security_context base; /* requires is_client_side to be non 0. */ grpc_credentials *request_metadata_creds; }; @@ -159,17 +159,41 @@ grpc_security_status grpc_ssl_channel_security_context_create( grpc_security_status grpc_ssl_server_security_context_create( const grpc_ssl_config *config, grpc_security_context **ctx); - /* --- Creation of high level objects. --- */ /* Secure client channel creation. */ + +grpc_channel *grpc_ssl_channel_create(grpc_credentials *ssl_creds, + grpc_credentials *request_metadata_creds, + const char *target, + const grpc_channel_args *args); + +grpc_channel *grpc_fake_transport_security_channel_create( + grpc_credentials *fake_creds, grpc_credentials *request_metadata_creds, + const char *target, const grpc_channel_args *args); + grpc_channel *grpc_secure_channel_create_internal( const char *target, const grpc_channel_args *args, grpc_channel_security_context *ctx); +typedef grpc_channel *(*grpc_secure_channel_factory_func)( + grpc_credentials *transport_security_creds, + grpc_credentials *request_metadata_creds, const char *target, + const grpc_channel_args *args); + +typedef struct { + const char *creds_type; + grpc_secure_channel_factory_func factory; +} grpc_secure_channel_factory; + +grpc_channel *grpc_secure_channel_create_with_factories( + const grpc_secure_channel_factory *factories, size_t num_factories, + grpc_credentials *creds, const char *target, const grpc_channel_args *args); + /* Secure server creation. */ -grpc_server *grpc_secure_server_create_internal( - grpc_completion_queue *cq, const grpc_channel_args *args, - grpc_security_context *ctx); -#endif /* __GRPC_INTERNAL_SECURITY_SECURITY_CONTEXT_H__ */ +grpc_server *grpc_secure_server_create_internal(grpc_completion_queue *cq, + const grpc_channel_args *args, + grpc_security_context *ctx); + +#endif /* __GRPC_INTERNAL_SECURITY_SECURITY_CONTEXT_H__ */ diff --git a/src/core/security/server_secure_chttp2.c b/src/core/security/server_secure_chttp2.c index 9d7c0e5e5a..931fa95651 100644 --- a/src/core/security/server_secure_chttp2.c +++ b/src/core/security/server_secure_chttp2.c @@ -70,8 +70,7 @@ static void on_accept(void *server, grpc_endpoint *tcp) { const grpc_channel_args *args = grpc_server_get_channel_args(server); grpc_security_context *ctx = grpc_find_security_context_in_args(args); GPR_ASSERT(ctx); - grpc_setup_secure_transport(ctx, tcp, on_secure_transport_setup_done, - server); + grpc_setup_secure_transport(ctx, tcp, on_secure_transport_setup_done, server); } /* Note: the following code is the same with server_chttp2.c */ diff --git a/src/core/statistics/census_interface.h b/src/core/statistics/census_interface.h index 7618387ee2..af9c70c4fb 100644 --- a/src/core/statistics/census_interface.h +++ b/src/core/statistics/census_interface.h @@ -73,4 +73,4 @@ census_op_id census_tracing_start_op(); /* Ends tracing. Calling this function will invalidate the input op_id. */ void census_tracing_end_op(census_op_id op_id); -#endif /* __GRPC_INTERNAL_STATISTICS_CENSUS_INTERFACE_H__ */ +#endif /* __GRPC_INTERNAL_STATISTICS_CENSUS_INTERFACE_H__ */ diff --git a/src/core/statistics/census_rpc_stats.h b/src/core/statistics/census_rpc_stats.h index e1ff3ac31b..a9c999aa5c 100644 --- a/src/core/statistics/census_rpc_stats.h +++ b/src/core/statistics/census_rpc_stats.h @@ -98,4 +98,4 @@ void census_stats_store_shutdown(); } #endif -#endif /* __GRPC_INTERNAL_STATISTICS_CENSUS_RPC_STATS_H__ */ +#endif /* __GRPC_INTERNAL_STATISTICS_CENSUS_RPC_STATS_H__ */ diff --git a/src/core/statistics/census_tracing.c b/src/core/statistics/census_tracing.c index d37c427c5b..45302cd6ab 100644 --- a/src/core/statistics/census_tracing.c +++ b/src/core/statistics/census_tracing.c @@ -47,8 +47,8 @@ /* Struct for a trace annotation. */ typedef struct annotation { - gpr_uint64 ts; /* timestamp of the annotation */ - char txt[CENSUS_MAX_ANNOTATION_LENGTH]; /* actual txt annotation */ + gpr_timespec ts; /* timestamp of the annotation */ + char txt[CENSUS_MAX_ANNOTATION_LENGTH + 1]; /* actual txt annotation */ struct annotation* next; } annotation; @@ -107,8 +107,8 @@ census_op_id census_tracing_start_op() { ret->rpc_stats.cnt = 1; ret->ts = gpr_now(); census_ht_insert(g_trace_store, op_id_as_key(&ret->id), (void*)ret); + gpr_log(GPR_DEBUG, "Start tracing for id %lu", g_id); gpr_mu_unlock(&g_mu); - gpr_log(GPR_DEBUG, "Start tracing for id %lu\n", g_id); return ret->id; } } @@ -127,7 +127,27 @@ int census_add_method_tag(census_op_id op_id, const char* method) { return ret; } -void census_tracing_print(census_op_id op_id, const char* annotation) {} +void census_tracing_print(census_op_id op_id, const char* anno_txt) { + trace_obj* trace = NULL; + gpr_mu_lock(&g_mu); + trace = census_ht_find(g_trace_store, op_id_as_key(&op_id)); + if (trace != NULL) { + annotation* anno = gpr_malloc(sizeof(annotation)); + anno->ts = gpr_now(); + { + char* d = anno->txt; + const char* s = anno_txt; + int n = 0; + for (; n < CENSUS_MAX_ANNOTATION_LENGTH && *s != '\0'; ++n) { + *d++ = *s++; + } + *d = '\0'; + } + anno->next = trace->annotations; + trace->annotations = anno; + } + gpr_mu_unlock(&g_mu); +} void census_tracing_end_op(census_op_id op_id) { trace_obj* trace = NULL; @@ -136,7 +156,7 @@ void census_tracing_end_op(census_op_id op_id) { if (trace != NULL) { trace->rpc_stats.elapsed_time_ms = gpr_timespec_to_micros(gpr_time_sub(gpr_now(), trace->ts)); - gpr_log(GPR_DEBUG, "End tracing for id %lu, method %s, latency %f us\n", + gpr_log(GPR_DEBUG, "End tracing for id %lu, method %s, latency %f us", op_id_2_uint64(&op_id), trace->method, trace->rpc_stats.elapsed_time_ms); census_ht_erase(g_trace_store, op_id_as_key(&op_id)); diff --git a/src/core/statistics/hash_table.c b/src/core/statistics/hash_table.c index f0105ee683..1aee86d3a4 100644 --- a/src/core/statistics/hash_table.c +++ b/src/core/statistics/hash_table.c @@ -141,10 +141,10 @@ static gpr_int32 find_bucket_idx(const census_ht* ht, census_ht_key key) { static int keys_match(const census_ht_option* opt, const ht_entry* p, const census_ht_key key) { + GPR_ASSERT(opt->key_type == CENSUS_HT_UINT64 || + opt->key_type == CENSUS_HT_POINTER); if (opt->key_type == CENSUS_HT_UINT64) return p->key.val == key.val; - if (opt->key_type == CENSUS_HT_POINTER) - return !opt->compare_keys((p->key).ptr, key.ptr); - return 0; + return !opt->compare_keys((p->key).ptr, key.ptr); } static entry_locator ht_find(const census_ht* ht, census_ht_key key) { diff --git a/src/core/support/alloc.c b/src/core/support/alloc.c index 658408f334..ddf6789773 100644 --- a/src/core/support/alloc.c +++ b/src/core/support/alloc.c @@ -62,6 +62,4 @@ void *gpr_malloc_aligned(size_t size, size_t alignment) { return (void *)ret; } -void gpr_free_aligned(void *ptr) { - free(((void **)ptr)[-1]); -} +void gpr_free_aligned(void *ptr) { free(((void **)ptr)[-1]); } diff --git a/src/core/support/cpu.h b/src/core/support/cpu.h index 6ac0db35e5..2435ec0353 100644 --- a/src/core/support/cpu.h +++ b/src/core/support/cpu.h @@ -46,4 +46,4 @@ int gpr_cpu_num_cores(); [0, gpr_cpu_num_cores() - 1] */ int gpr_cpu_current_cpu(); -#endif /* __GRPC_INTERNAL_SUPPORT_CPU_H__ */ +#endif /* __GRPC_INTERNAL_SUPPORT_CPU_H__ */ diff --git a/src/core/support/cpu_linux.c b/src/core/support/cpu_linux.c index 4d538a5b1b..922b61c3c5 100644 --- a/src/core/support/cpu_linux.c +++ b/src/core/support/cpu_linux.c @@ -37,13 +37,37 @@ #include "src/core/support/cpu.h" +#ifndef _GNU_SOURCE #define _GNU_SOURCE +#define GRPC_GNU_SOURCE +#endif + +#ifndef __USE_GNU #define __USE_GNU +#define GRPC_USE_GNU +#endif + +#ifndef __USE_MISC #define __USE_MISC +#define GRPC_USE_MISC +#endif + #include <sched.h> + +#ifdef GRPC_GNU_SOURCE #undef _GNU_SOURCE +#undef GRPC_GNU_SOURCE +#endif + +#ifdef GRPC_USE_GNU #undef __USE_GNU +#undef GRPC_USE_GNU +#endif + +#ifdef GRPC_USE_MISC #undef __USE_MISC +#undef GRPC_USE_MISC +#endif #include <errno.h> #include <unistd.h> diff --git a/src/core/support/log.c b/src/core/support/log.c index b9e2897efc..7f102efea8 100644 --- a/src/core/support/log.c +++ b/src/core/support/log.c @@ -34,6 +34,10 @@ #include <grpc/support/log.h> #include <stdio.h> +#include <string.h> + +extern void gpr_default_log(gpr_log_func_args *args); +static gpr_log_func g_log_func = gpr_default_log; const char *gpr_log_severity_string(gpr_log_severity severity) { switch (severity) { @@ -47,12 +51,15 @@ const char *gpr_log_severity_string(gpr_log_severity severity) { return "UNKNOWN"; } -void gpr_log(const char *file, int line, gpr_log_severity severity, - const char *format, ...) { - va_list args; - va_start(args, format); - - gpr_vlog(file, line, severity, format, args); - - va_end(args); +void gpr_log_message(const char *file, int line, gpr_log_severity severity, + const char *message) { + gpr_log_func_args lfargs; + memset(&lfargs, 0, sizeof(lfargs)); + lfargs.file = file; + lfargs.line = line; + lfargs.severity = severity; + lfargs.message = message; + g_log_func(&lfargs); } + +void gpr_set_log_function(gpr_log_func f) { g_log_func = f; } diff --git a/src/core/support/log_android.c b/src/core/support/log_android.c index 4c83e09914..11129e3e06 100644 --- a/src/core/support/log_android.c +++ b/src/core/support/log_android.c @@ -54,25 +54,31 @@ static android_LogPriority severity_to_log_priority(gpr_log_severity severity) { return ANDROID_LOG_DEFAULT; } -void gpr_vlog(const char *file, int line, gpr_log_severity severity, - const char *format, va_list args) { +void gpr_log(const char *file, int line, gpr_log_severity severity, + const char *format, ...) { + char *message = NULL; + va_list args; + va_start(args, format); + vasprintf(&message, format, args); + va_end(args); + gpr_log_message(file, line, severity, message); + free(message); +} + +void gpr_default_log(gpr_log_func_args *args) { char *final_slash; const char *display_file; - char *prefix = NULL; - char *suffix = NULL; char *output = NULL; - final_slash = strrchr(file, '/'); + final_slash = strrchr(args->file, '/'); if (final_slash == NULL) display_file = file; else display_file = final_slash + 1; - asprintf(&prefix, "%s:%d] ", display_file, line); - vasprintf(&suffix, format, args); - asprintf(&output, "%s%s", prefix, suffix); + asprintf(&prefix, "%s:%d] %s", display_file, args->line, args->message); - __android_log_write(severity_to_log_priority(severity), "GRPC", output); + __android_log_write(severity_to_log_priority(args->severity), "GRPC", output); /* allocated by asprintf => use free, not gpr_free */ free(prefix); diff --git a/src/core/support/log_linux.c b/src/core/support/log_linux.c index 322ff07dd9..36fb4b5051 100644 --- a/src/core/support/log_linux.c +++ b/src/core/support/log_linux.c @@ -49,17 +49,30 @@ static long gettid() { return syscall(__NR_gettid); } -void gpr_vlog(const char *file, int line, gpr_log_severity severity, - const char *format, va_list args) { +void gpr_log(const char *file, int line, gpr_log_severity severity, + const char *format, ...) { + char *message = NULL; + va_list args; + va_start(args, format); + if (vasprintf(&message, format, args) == -1) { + va_end(args); + return; + } + va_end(args); + gpr_log_message(file, line, severity, message); + free(message); +} + +void gpr_default_log(gpr_log_func_args *args) { char *final_slash; const char *display_file; char time_buffer[64]; gpr_timespec now = gpr_now(); struct tm tm; - final_slash = strrchr(file, '/'); + final_slash = strrchr(args->file, '/'); if (final_slash == NULL) - display_file = file; + display_file = args->file; else display_file = final_slash + 1; @@ -70,12 +83,10 @@ void gpr_vlog(const char *file, int line, gpr_log_severity severity, strcpy(time_buffer, "error:strftime"); } - flockfile(stderr); - fprintf(stderr, "%s%s.%09d %7ld %s:%d] ", gpr_log_severity_string(severity), - time_buffer, (int)(now.tv_nsec), gettid(), display_file, line); - vfprintf(stderr, format, args); - fputc('\n', stderr); - funlockfile(stderr); + fprintf(stderr, "%s%s.%09d %7ld %s:%d] %s\n", + gpr_log_severity_string(args->severity), time_buffer, + (int)(now.tv_nsec), gettid(), display_file, args->line, + args->message); } #endif diff --git a/src/core/support/log_posix.c b/src/core/support/log_posix.c index b47c433cd7..0420570a3e 100644 --- a/src/core/support/log_posix.c +++ b/src/core/support/log_posix.c @@ -47,17 +47,40 @@ static long gettid() { return pthread_self(); } -void gpr_vlog(const char *file, int line, gpr_log_severity severity, - const char *format, va_list args) { +void gpr_log(const char *file, int line, gpr_log_severity severity, + const char *format, ...) { + char buf[64]; + char *allocated = NULL; + char *message = NULL; + int ret; + va_list args; + va_start(args, format); + ret = vsnprintf(buf, format, args); + va_end(args); + if (ret < 0) { + message = NULL; + } else if (ret <= sizeof(buf) - 1) { + message = buf; + } else { + message = allocated = gpr_malloc(ret + 1); + va_start(args, format); + vsnprintf(message, format, args); + va_end(args); + } + gpr_log_message(file, line, severity, message); + gpr_free(allocated); +} + +void gpr_default_log(gpr_log_func_args *args) { char *final_slash; const char *display_file; char time_buffer[64]; gpr_timespec now = gpr_now(); struct tm tm; - final_slash = strrchr(file, '/'); + final_slash = strrchr(args->file, '/'); if (final_slash == NULL) - display_file = file; + display_file = args->file; else display_file = final_slash + 1; @@ -68,12 +91,10 @@ void gpr_vlog(const char *file, int line, gpr_log_severity severity, strcpy(time_buffer, "error:strftime"); } - flockfile(stderr); - fprintf(stderr, "%s%s.%09d %7ld %s:%d] ", gpr_log_severity_string(severity), - time_buffer, (int)(now.tv_nsec), gettid(), display_file, line); - vfprintf(stderr, format, args); - fputc('\n', stderr); - funlockfile(stderr); + fprintf(stderr, "%s%s.%09d %7ld %s:%d] %s\n", + gpr_log_severity_string(args->severity), time_buffer, + (int)(now.tv_nsec), gettid(), display_file, args->line, + args->message); } #endif /* defined(GPR_POSIX_LOG) */ diff --git a/src/core/support/log_win32.c b/src/core/support/log_win32.c index e6567dca7e..ae5f23a90d 100644 --- a/src/core/support/log_win32.c +++ b/src/core/support/log_win32.c @@ -39,12 +39,42 @@ #include <stdio.h> #include <stdarg.h> +void gpr_log(const char *file, int line, gpr_log_severity severity, + const char *message) { + const char *message = NULL; + va_list args; + int ret; + + /* Determine the length. */ + va_start(args, format); + ret = _vscprintf(format, args); + va_end(args); + if (!(0 <= ret && ret < ~(size_t)0)) { + message = NULL; + } else { + /* Allocate a new buffer, with space for the NUL terminator. */ + strp_buflen = (size_t)ret + 1; + message = gpr_malloc(strp_buflen); + + /* Print to the buffer. */ + va_start(args, format); + ret = vsnprintf_s(message, strp_buflen, _TRUNCATE, format, args); + va_end(args); + if (ret != strp_buflen - 1) { + /* This should never happen. */ + gpr_free(message); + message = NULL; + } + } + + gpr_log_message(file, line, severity, message); + gpr_free(message); +} + /* Simple starter implementation */ -void gpr_vlog(const char *file, int line, gpr_log_severity severity, - const char *format, va_list args) { - fprintf(stderr, "%s %s:%d: ", gpr_log_severity_string(severity), file, line); - vfprintf(stderr, format, args); - fputc('\n', stderr); +void gpr_default_log(gpr_log_func_args *args) { + fprintf(stderr, "%s %s:%d: %s\n", gpr_log_severity_string(severity), + args->file, args->line, args->message); } #endif diff --git a/src/core/support/murmur_hash.h b/src/core/support/murmur_hash.h index 5643717cd2..2ebf3e57b1 100644 --- a/src/core/support/murmur_hash.h +++ b/src/core/support/murmur_hash.h @@ -41,4 +41,4 @@ /* compute the hash of key (length len) */ gpr_uint32 gpr_murmur_hash3(const void *key, size_t len, gpr_uint32 seed); -#endif /* __GRPC_INTERNAL_SUPPORT_MURMUR_HASH_H__ */ +#endif /* __GRPC_INTERNAL_SUPPORT_MURMUR_HASH_H__ */ diff --git a/src/core/support/thd_internal.h b/src/core/support/thd_internal.h index 519177a555..190d4e3668 100644 --- a/src/core/support/thd_internal.h +++ b/src/core/support/thd_internal.h @@ -36,4 +36,4 @@ /* Internal interfaces between modules within the gpr support library. */ -#endif /* __GRPC_INTERNAL_SUPPORT_THD_INTERNAL_H__ */ +#endif /* __GRPC_INTERNAL_SUPPORT_THD_INTERNAL_H__ */ diff --git a/src/core/surface/call.c b/src/core/surface/call.c index f6d93bd957..297d9587eb 100644 --- a/src/core/surface/call.c +++ b/src/core/surface/call.c @@ -202,7 +202,7 @@ struct grpc_call { gpr_refcount internal_refcount; }; -#define CALL_STACK_FROM_CALL(call) ((grpc_call_stack *)((call)+1)) +#define CALL_STACK_FROM_CALL(call) ((grpc_call_stack *)((call) + 1)) #define CALL_FROM_CALL_STACK(call_stack) (((grpc_call *)(call_stack)) - 1) #define CALL_ELEM_FROM_CALL(call, idx) \ grpc_call_stack_element(CALL_STACK_FROM_CALL(call), idx) diff --git a/src/core/surface/call.h b/src/core/surface/call.h index 5c2ef3be18..01605bb38a 100644 --- a/src/core/surface/call.h +++ b/src/core/surface/call.h @@ -73,4 +73,4 @@ grpc_metadata_buffer *grpc_call_get_metadata_buffer(grpc_call *call); void grpc_call_add_mdelem(grpc_call *call, grpc_mdelem *mdelem, gpr_uint32 flags); -#endif /* __GRPC_INTERNAL_SURFACE_CALL_H__ */ +#endif /* __GRPC_INTERNAL_SURFACE_CALL_H__ */ diff --git a/src/core/surface/channel.c b/src/core/surface/channel.c index 8ef13675fe..a1bcea58dd 100644 --- a/src/core/surface/channel.c +++ b/src/core/surface/channel.c @@ -51,7 +51,7 @@ struct grpc_channel { grpc_mdstr *authority_string; }; -#define CHANNEL_STACK_FROM_CHANNEL(c) ((grpc_channel_stack *)((c)+1)) +#define CHANNEL_STACK_FROM_CHANNEL(c) ((grpc_channel_stack *)((c) + 1)) grpc_channel *grpc_channel_create_from_filters( const grpc_channel_filter **filters, size_t num_filters, diff --git a/src/core/surface/channel.h b/src/core/surface/channel.h index 11d4939916..b3ea2ede40 100644 --- a/src/core/surface/channel.h +++ b/src/core/surface/channel.h @@ -48,4 +48,4 @@ grpc_mdstr *grpc_channel_get_message_string(grpc_channel *channel); void grpc_channel_internal_ref(grpc_channel *channel); void grpc_channel_internal_unref(grpc_channel *channel); -#endif /* __GRPC_INTERNAL_SURFACE_CHANNEL_H__ */ +#endif /* __GRPC_INTERNAL_SURFACE_CHANNEL_H__ */ diff --git a/src/core/surface/client.c b/src/core/surface/client.c index 98cb460d63..524b0718a9 100644 --- a/src/core/surface/client.c +++ b/src/core/surface/client.c @@ -106,13 +106,12 @@ static void init_channel_elem(grpc_channel_element *elem, GPR_ASSERT(!is_last); } -static void destroy_channel_elem(grpc_channel_element *elem) { -} +static void destroy_channel_elem(grpc_channel_element *elem) {} const grpc_channel_filter grpc_client_surface_filter = { - call_op, channel_op, + call_op, channel_op, - sizeof(call_data), init_call_elem, destroy_call_elem, + sizeof(call_data), init_call_elem, destroy_call_elem, sizeof(channel_data), init_channel_elem, destroy_channel_elem, diff --git a/src/core/surface/client.h b/src/core/surface/client.h index eb567276e2..cff3d401d9 100644 --- a/src/core/surface/client.h +++ b/src/core/surface/client.h @@ -38,4 +38,4 @@ extern const grpc_channel_filter grpc_client_surface_filter; -#endif /* __GRPC_INTERNAL_SURFACE_CLIENT_H__ */ +#endif /* __GRPC_INTERNAL_SURFACE_CLIENT_H__ */ diff --git a/src/core/surface/completion_queue.h b/src/core/surface/completion_queue.h index 2e752a3fe0..5e45749396 100644 --- a/src/core/surface/completion_queue.h +++ b/src/core/surface/completion_queue.h @@ -104,4 +104,4 @@ void grpc_cq_dump_pending_ops(grpc_completion_queue *cc); grpc_pollset *grpc_cq_pollset(grpc_completion_queue *cc); -#endif /* __GRPC_INTERNAL_SURFACE_COMPLETION_QUEUE_H__ */ +#endif /* __GRPC_INTERNAL_SURFACE_COMPLETION_QUEUE_H__ */ diff --git a/src/core/surface/event_string.h b/src/core/surface/event_string.h index 30b693e95c..b34e2d152b 100644 --- a/src/core/surface/event_string.h +++ b/src/core/surface/event_string.h @@ -39,4 +39,4 @@ /* Returns a string describing an event. Must be later freed with gpr_free() */ char *grpc_event_string(grpc_event *ev); -#endif /* __GRPC_INTERNAL_SURFACE_EVENT_STRING_H__ */ +#endif /* __GRPC_INTERNAL_SURFACE_EVENT_STRING_H__ */ diff --git a/src/core/surface/lame_client.c b/src/core/surface/lame_client.c index 6a832436ca..5fa3e42362 100644 --- a/src/core/surface/lame_client.c +++ b/src/core/surface/lame_client.c @@ -33,6 +33,8 @@ #include "src/core/surface/lame_client.h" +#include <string.h> + #include "src/core/channel/channel_stack.h" #include "src/core/surface/channel.h" #include "src/core/surface/call.h" @@ -42,16 +44,28 @@ typedef struct { void *unused; } call_data; -typedef struct { void *unused; } channel_data; +typedef struct { grpc_mdelem *message; } channel_data; + +static void do_nothing(void *data, grpc_op_error error) {} static void call_op(grpc_call_element *elem, grpc_call_element *from_elem, grpc_call_op *op) { + channel_data *channeld = elem->channel_data; GRPC_CALL_LOG_OP(GPR_INFO, elem, op); switch (op->type) { - case GRPC_SEND_START: + case GRPC_SEND_START: { + grpc_call_op set_status_op; + grpc_mdelem_ref(channeld->message); + memset(&set_status_op, 0, sizeof(grpc_call_op)); + set_status_op.dir = GRPC_CALL_UP; + set_status_op.type = GRPC_RECV_METADATA; + set_status_op.done_cb = do_nothing; + set_status_op.data.metadata = channeld->message; + grpc_call_recv_metadata(elem, &set_status_op); grpc_call_recv_finish(elem, 1); break; + } case GRPC_SEND_METADATA: grpc_mdelem_unref(op->data.metadata); break; @@ -81,11 +95,20 @@ static void destroy_call_elem(grpc_call_element *elem) {} static void init_channel_elem(grpc_channel_element *elem, const grpc_channel_args *args, grpc_mdctx *mdctx, int is_first, int is_last) { + channel_data *channeld = elem->channel_data; + GPR_ASSERT(is_first); GPR_ASSERT(is_last); + + channeld->message = grpc_mdelem_from_strings(mdctx, "grpc-message", + "Rpc sent on a lame channel."); } -static void destroy_channel_elem(grpc_channel_element *elem) {} +static void destroy_channel_elem(grpc_channel_element *elem) { + channel_data *channeld = elem->channel_data; + + grpc_mdelem_unref(channeld->message); +} static const grpc_channel_filter lame_filter = { call_op, channel_op, diff --git a/src/core/surface/server.c b/src/core/surface/server.c index aa544a97f2..167bfe97d1 100644 --- a/src/core/surface/server.c +++ b/src/core/surface/server.c @@ -405,9 +405,9 @@ static void destroy_channel_elem(grpc_channel_element *elem) { } static const grpc_channel_filter server_surface_filter = { - call_op, channel_op, + call_op, channel_op, - sizeof(call_data), init_call_elem, destroy_call_elem, + sizeof(call_data), init_call_elem, destroy_call_elem, sizeof(channel_data), init_channel_elem, destroy_channel_elem, diff --git a/src/core/surface/server.h b/src/core/surface/server.h index 61292ebe4e..50574d66a4 100644 --- a/src/core/surface/server.h +++ b/src/core/surface/server.h @@ -60,4 +60,4 @@ grpc_transport_setup_result grpc_server_setup_transport( const grpc_channel_args *grpc_server_get_channel_args(grpc_server *server); -#endif /* __GRPC_INTERNAL_SURFACE_SERVER_H__ */ +#endif /* __GRPC_INTERNAL_SURFACE_SERVER_H__ */ diff --git a/src/core/surface/server_chttp2.c b/src/core/surface/server_chttp2.c index a0961bd449..47fca827f3 100644 --- a/src/core/surface/server_chttp2.c +++ b/src/core/surface/server_chttp2.c @@ -76,6 +76,8 @@ int grpc_server_add_http2_port(grpc_server *server, const char *addr) { grpc_tcp_server *tcp = NULL; size_t i; int count = 0; + int port_num = -1; + int port_temp; resolved = grpc_blocking_resolve_address(addr, "http"); if (!resolved) { @@ -88,9 +90,15 @@ int grpc_server_add_http2_port(grpc_server *server, const char *addr) { } for (i = 0; i < resolved->naddrs; i++) { - if (grpc_tcp_server_add_port(tcp, - (struct sockaddr *)&resolved->addrs[i].addr, - resolved->addrs[i].len)) { + port_temp = grpc_tcp_server_add_port( + tcp, (struct sockaddr *)&resolved->addrs[i].addr, + resolved->addrs[i].len); + if (port_temp >= 0) { + if (port_num == -1) { + port_num = port_temp; + } else { + GPR_ASSERT(port_num == port_temp); + } count++; } } @@ -108,7 +116,7 @@ int grpc_server_add_http2_port(grpc_server *server, const char *addr) { /* Register with the server only upon success */ grpc_server_add_listener(server, tcp, start, destroy); - return 1; + return port_num; /* Error path: cleanup and return */ error: diff --git a/src/core/surface/surface_trace.h b/src/core/surface/surface_trace.h index f6f9acfd9c..df1aea9669 100644 --- a/src/core/surface/surface_trace.h +++ b/src/core/surface/surface_trace.h @@ -51,4 +51,4 @@ } while (0) #endif -#endif /* __GRPC_INTERNAL_SURFACE_SURFACE_TRACE_H__ */ +#endif /* __GRPC_INTERNAL_SURFACE_SURFACE_TRACE_H__ */ diff --git a/src/core/transport/chttp2/frame.h b/src/core/transport/chttp2/frame.h index a04e442ed6..6d28638309 100644 --- a/src/core/transport/chttp2/frame.h +++ b/src/core/transport/chttp2/frame.h @@ -77,4 +77,4 @@ typedef struct { #define GRPC_CHTTP2_DATA_FLAG_PADDED 8 #define GRPC_CHTTP2_FLAG_HAS_PRIORITY 0x20 -#endif /* __GRPC_INTERNAL_TRANSPORT_CHTTP2_FRAME_H__ */ +#endif /* __GRPC_INTERNAL_TRANSPORT_CHTTP2_FRAME_H__ */ diff --git a/src/core/transport/chttp2/frame_data.c b/src/core/transport/chttp2/frame_data.c index fbd3b6cabf..c22a223737 100644 --- a/src/core/transport/chttp2/frame_data.c +++ b/src/core/transport/chttp2/frame_data.c @@ -161,4 +161,3 @@ grpc_chttp2_parse_error grpc_chttp2_data_parser_parse( abort(); return GRPC_CHTTP2_CONNECTION_ERROR; } - diff --git a/src/core/transport/chttp2/frame_data.h b/src/core/transport/chttp2/frame_data.h index abe26dab76..c260059e8b 100644 --- a/src/core/transport/chttp2/frame_data.h +++ b/src/core/transport/chttp2/frame_data.h @@ -77,4 +77,4 @@ grpc_chttp2_parse_error grpc_chttp2_data_parser_parse( /* create a slice with an empty data frame and is_last set */ gpr_slice grpc_chttp2_data_frame_create_empty_close(gpr_uint32 id); -#endif /* __GRPC_INTERNAL_TRANSPORT_CHTTP2_FRAME_DATA_H__ */ +#endif /* __GRPC_INTERNAL_TRANSPORT_CHTTP2_FRAME_DATA_H__ */ diff --git a/src/core/transport/chttp2/frame_ping.h b/src/core/transport/chttp2/frame_ping.h index a64d53644b..fa778c51b2 100644 --- a/src/core/transport/chttp2/frame_ping.h +++ b/src/core/transport/chttp2/frame_ping.h @@ -50,4 +50,4 @@ grpc_chttp2_parse_error grpc_chttp2_ping_parser_begin_frame( grpc_chttp2_parse_error grpc_chttp2_ping_parser_parse( void *parser, grpc_chttp2_parse_state *state, gpr_slice slice, int is_last); -#endif /* __GRPC_INTERNAL_TRANSPORT_CHTTP2_FRAME_PING_H__ */ +#endif /* __GRPC_INTERNAL_TRANSPORT_CHTTP2_FRAME_PING_H__ */ diff --git a/src/core/transport/chttp2/frame_rst_stream.h b/src/core/transport/chttp2/frame_rst_stream.h index 78aea0f26a..dbb262971b 100644 --- a/src/core/transport/chttp2/frame_rst_stream.h +++ b/src/core/transport/chttp2/frame_rst_stream.h @@ -38,4 +38,4 @@ gpr_slice grpc_chttp2_rst_stream_create(gpr_uint32 stream_id, gpr_uint32 code); -#endif /* __GRPC_INTERNAL_TRANSPORT_CHTTP2_FRAME_RST_STREAM_H__ */ +#endif /* __GRPC_INTERNAL_TRANSPORT_CHTTP2_FRAME_RST_STREAM_H__ */ diff --git a/src/core/transport/chttp2/frame_settings.h b/src/core/transport/chttp2/frame_settings.h index dcb8b00ca1..855f9636bb 100644 --- a/src/core/transport/chttp2/frame_settings.h +++ b/src/core/transport/chttp2/frame_settings.h @@ -96,4 +96,4 @@ grpc_chttp2_parse_error grpc_chttp2_settings_parser_begin_frame( grpc_chttp2_parse_error grpc_chttp2_settings_parser_parse( void *parser, grpc_chttp2_parse_state *state, gpr_slice slice, int is_last); -#endif /* __GRPC_INTERNAL_TRANSPORT_CHTTP2_FRAME_SETTINGS_H__ */ +#endif /* __GRPC_INTERNAL_TRANSPORT_CHTTP2_FRAME_SETTINGS_H__ */ diff --git a/src/core/transport/chttp2/frame_window_update.h b/src/core/transport/chttp2/frame_window_update.h index 4b789fcc4a..2d9e6c4dcb 100644 --- a/src/core/transport/chttp2/frame_window_update.h +++ b/src/core/transport/chttp2/frame_window_update.h @@ -52,4 +52,4 @@ grpc_chttp2_parse_error grpc_chttp2_window_update_parser_begin_frame( grpc_chttp2_parse_error grpc_chttp2_window_update_parser_parse( void *parser, grpc_chttp2_parse_state *state, gpr_slice slice, int is_last); -#endif /* __GRPC_INTERNAL_TRANSPORT_CHTTP2_FRAME_WINDOW_UPDATE_H__ */ +#endif /* __GRPC_INTERNAL_TRANSPORT_CHTTP2_FRAME_WINDOW_UPDATE_H__ */ diff --git a/src/core/transport/chttp2/hpack_parser.h b/src/core/transport/chttp2/hpack_parser.h index 799513e7ff..b0a0d76713 100644 --- a/src/core/transport/chttp2/hpack_parser.h +++ b/src/core/transport/chttp2/hpack_parser.h @@ -108,4 +108,4 @@ grpc_chttp2_parse_error grpc_chttp2_header_parser_parse( void *hpack_parser, grpc_chttp2_parse_state *state, gpr_slice slice, int is_last); -#endif /* __GRPC_INTERNAL_TRANSPORT_CHTTP2_HPACK_PARSER_H__ */ +#endif /* __GRPC_INTERNAL_TRANSPORT_CHTTP2_HPACK_PARSER_H__ */ diff --git a/src/core/transport/chttp2/hpack_table.c b/src/core/transport/chttp2/hpack_table.c index 8f2ebecfeb..ae8bfa8009 100644 --- a/src/core/transport/chttp2/hpack_table.c +++ b/src/core/transport/chttp2/hpack_table.c @@ -43,68 +43,68 @@ static struct { const char *key; const char *value; } static_table[] = { - /* 0: */ {NULL, NULL}, - /* 1: */ {":authority", ""}, - /* 2: */ {":method", "GET"}, - /* 3: */ {":method", "POST"}, - /* 4: */ {":path", "/"}, - /* 5: */ {":path", "/index.html"}, - /* 6: */ {":scheme", "http"}, - /* 7: */ {":scheme", "https"}, - /* 8: */ {":status", "200"}, - /* 9: */ {":status", "204"}, - /* 10: */ {":status", "206"}, - /* 11: */ {":status", "304"}, - /* 12: */ {":status", "400"}, - /* 13: */ {":status", "404"}, - /* 14: */ {":status", "500"}, - /* 15: */ {"accept-charset", ""}, - /* 16: */ {"accept-encoding", "gzip, deflate"}, - /* 17: */ {"accept-language", ""}, - /* 18: */ {"accept-ranges", ""}, - /* 19: */ {"accept", ""}, - /* 20: */ {"access-control-allow-origin", ""}, - /* 21: */ {"age", ""}, - /* 22: */ {"allow", ""}, - /* 23: */ {"authorization", ""}, - /* 24: */ {"cache-control", ""}, - /* 25: */ {"content-disposition", ""}, - /* 26: */ {"content-encoding", ""}, - /* 27: */ {"content-language", ""}, - /* 28: */ {"content-length", ""}, - /* 29: */ {"content-location", ""}, - /* 30: */ {"content-range", ""}, - /* 31: */ {"content-type", ""}, - /* 32: */ {"cookie", ""}, - /* 33: */ {"date", ""}, - /* 34: */ {"etag", ""}, - /* 35: */ {"expect", ""}, - /* 36: */ {"expires", ""}, - /* 37: */ {"from", ""}, - /* 38: */ {"host", ""}, - /* 39: */ {"if-match", ""}, - /* 40: */ {"if-modified-since", ""}, - /* 41: */ {"if-none-match", ""}, - /* 42: */ {"if-range", ""}, - /* 43: */ {"if-unmodified-since", ""}, - /* 44: */ {"last-modified", ""}, - /* 45: */ {"link", ""}, - /* 46: */ {"location", ""}, - /* 47: */ {"max-forwards", ""}, - /* 48: */ {"proxy-authenticate", ""}, - /* 49: */ {"proxy-authorization", ""}, - /* 50: */ {"range", ""}, - /* 51: */ {"referer", ""}, - /* 52: */ {"refresh", ""}, - /* 53: */ {"retry-after", ""}, - /* 54: */ {"server", ""}, - /* 55: */ {"set-cookie", ""}, - /* 56: */ {"strict-transport-security", ""}, - /* 57: */ {"transfer-encoding", ""}, - /* 58: */ {"user-agent", ""}, - /* 59: */ {"vary", ""}, - /* 60: */ {"via", ""}, - /* 61: */ {"www-authenticate", ""}, + /* 0: */ {NULL, NULL}, + /* 1: */ {":authority", ""}, + /* 2: */ {":method", "GET"}, + /* 3: */ {":method", "POST"}, + /* 4: */ {":path", "/"}, + /* 5: */ {":path", "/index.html"}, + /* 6: */ {":scheme", "http"}, + /* 7: */ {":scheme", "https"}, + /* 8: */ {":status", "200"}, + /* 9: */ {":status", "204"}, + /* 10: */ {":status", "206"}, + /* 11: */ {":status", "304"}, + /* 12: */ {":status", "400"}, + /* 13: */ {":status", "404"}, + /* 14: */ {":status", "500"}, + /* 15: */ {"accept-charset", ""}, + /* 16: */ {"accept-encoding", "gzip, deflate"}, + /* 17: */ {"accept-language", ""}, + /* 18: */ {"accept-ranges", ""}, + /* 19: */ {"accept", ""}, + /* 20: */ {"access-control-allow-origin", ""}, + /* 21: */ {"age", ""}, + /* 22: */ {"allow", ""}, + /* 23: */ {"authorization", ""}, + /* 24: */ {"cache-control", ""}, + /* 25: */ {"content-disposition", ""}, + /* 26: */ {"content-encoding", ""}, + /* 27: */ {"content-language", ""}, + /* 28: */ {"content-length", ""}, + /* 29: */ {"content-location", ""}, + /* 30: */ {"content-range", ""}, + /* 31: */ {"content-type", ""}, + /* 32: */ {"cookie", ""}, + /* 33: */ {"date", ""}, + /* 34: */ {"etag", ""}, + /* 35: */ {"expect", ""}, + /* 36: */ {"expires", ""}, + /* 37: */ {"from", ""}, + /* 38: */ {"host", ""}, + /* 39: */ {"if-match", ""}, + /* 40: */ {"if-modified-since", ""}, + /* 41: */ {"if-none-match", ""}, + /* 42: */ {"if-range", ""}, + /* 43: */ {"if-unmodified-since", ""}, + /* 44: */ {"last-modified", ""}, + /* 45: */ {"link", ""}, + /* 46: */ {"location", ""}, + /* 47: */ {"max-forwards", ""}, + /* 48: */ {"proxy-authenticate", ""}, + /* 49: */ {"proxy-authorization", ""}, + /* 50: */ {"range", ""}, + /* 51: */ {"referer", ""}, + /* 52: */ {"refresh", ""}, + /* 53: */ {"retry-after", ""}, + /* 54: */ {"server", ""}, + /* 55: */ {"set-cookie", ""}, + /* 56: */ {"strict-transport-security", ""}, + /* 57: */ {"transfer-encoding", ""}, + /* 58: */ {"user-agent", ""}, + /* 59: */ {"vary", ""}, + /* 60: */ {"via", ""}, + /* 61: */ {"www-authenticate", ""}, }; void grpc_chttp2_hptbl_init(grpc_chttp2_hptbl *tbl, grpc_mdctx *mdctx) { diff --git a/src/core/transport/chttp2/hpack_table.h b/src/core/transport/chttp2/hpack_table.h index a3a07ad014..84a8e2d1e0 100644 --- a/src/core/transport/chttp2/hpack_table.h +++ b/src/core/transport/chttp2/hpack_table.h @@ -94,4 +94,4 @@ typedef struct { grpc_chttp2_hptbl_find_result grpc_chttp2_hptbl_find( const grpc_chttp2_hptbl *tbl, grpc_mdelem *md); -#endif /* __GRPC_INTERNAL_TRANSPORT_CHTTP2_HPACK_TABLE_H__ */ +#endif /* __GRPC_INTERNAL_TRANSPORT_CHTTP2_HPACK_TABLE_H__ */ diff --git a/src/core/transport/chttp2/http2_errors.h b/src/core/transport/chttp2/http2_errors.h index d065422c6f..7791da6d5a 100644 --- a/src/core/transport/chttp2/http2_errors.h +++ b/src/core/transport/chttp2/http2_errors.h @@ -53,4 +53,4 @@ typedef enum { GRPC_CHTTP2__ERROR_DO_NOT_USE = -1 } grpc_chttp2_error_code; -#endif /* __GRPC_INTERNAL_TRANSPORT_CHTTP2_HTTP2_ERRORS_H__ */ +#endif /* __GRPC_INTERNAL_TRANSPORT_CHTTP2_HTTP2_ERRORS_H__ */ diff --git a/src/core/transport/chttp2/status_conversion.h b/src/core/transport/chttp2/status_conversion.h index ae9e7f2ca3..f78d81e0aa 100644 --- a/src/core/transport/chttp2/status_conversion.h +++ b/src/core/transport/chttp2/status_conversion.h @@ -47,4 +47,4 @@ grpc_status_code grpc_chttp2_http2_error_to_grpc_status( grpc_status_code grpc_chttp2_http2_status_to_grpc_status(int status); int grpc_chttp2_grpc_status_to_http2_status(grpc_status_code status); -#endif /* __GRPC_INTERNAL_TRANSPORT_CHTTP2_STATUS_CONVERSION_H__ */ +#endif /* __GRPC_INTERNAL_TRANSPORT_CHTTP2_STATUS_CONVERSION_H__ */ diff --git a/src/core/transport/chttp2/stream_encoder.c b/src/core/transport/chttp2/stream_encoder.c index 8595a59879..92a36d0c16 100644 --- a/src/core/transport/chttp2/stream_encoder.c +++ b/src/core/transport/chttp2/stream_encoder.c @@ -68,8 +68,6 @@ typedef struct { gpr_uint8 last_was_header; /* output stream id */ gpr_uint32 stream_id; - /* number of flow controlled bytes written */ - gpr_uint32 output_size; gpr_slice_buffer *output; } framer_state; @@ -464,49 +462,31 @@ void grpc_chttp2_hpack_compressor_destroy(grpc_chttp2_hpack_compressor *c) { grpc_mdstr_unref(c->timeout_key_str); } -gpr_uint32 grpc_chttp2_encode_some(grpc_stream_op *ops, size_t *ops_count, - int eof, gpr_slice_buffer *output, - gpr_uint32 max_bytes, gpr_uint32 stream_id, - grpc_chttp2_hpack_compressor *compressor) { - framer_state st; +gpr_uint32 grpc_chttp2_preencode(grpc_stream_op *inops, size_t *inops_count, + gpr_uint32 max_flow_controlled_bytes, + grpc_stream_op_buffer *outops) { gpr_slice slice; grpc_stream_op *op; gpr_uint32 max_take_size; + gpr_uint32 flow_controlled_bytes_taken = 0; gpr_uint32 curop = 0; - gpr_uint32 nops = *ops_count; gpr_uint8 *p; - GPR_ASSERT(stream_id != 0); - - st.cur_frame_type = NONE; - st.last_was_header = 0; - st.stream_id = stream_id; - st.output = output; - st.output_size = 0; - - while (curop < nops) { - GPR_ASSERT(st.output_size <= max_bytes); - op = &ops[curop]; + while (curop < *inops_count) { + GPR_ASSERT(flow_controlled_bytes_taken <= max_flow_controlled_bytes); + op = &inops[curop]; switch (op->type) { case GRPC_NO_OP: + /* skip */ curop++; break; case GRPC_OP_FLOW_CTL_CB: - op->data.flow_ctl_cb.cb(op->data.flow_ctl_cb.arg, GRPC_OP_OK); - curop++; - break; - case GRPC_OP_METADATA: - hpack_enc(compressor, op->data.metadata, &st); - curop++; - break; case GRPC_OP_DEADLINE: - deadline_enc(compressor, op->data.deadline, &st); - curop++; - break; + case GRPC_OP_METADATA: case GRPC_OP_METADATA_BOUNDARY: - ensure_frame_type(&st, HEADER, 0); - finish_frame(&st, 1, 0); - st.last_was_header = 0; /* force a new header frame */ + /* these just get copied as they don't impact the number of flow + controlled bytes */ + grpc_sopb_append(outops, op, 1); curop++; break; case GRPC_OP_BEGIN_MESSAGE: @@ -525,42 +505,100 @@ gpr_uint32 grpc_chttp2_encode_some(grpc_stream_op *ops, size_t *ops_count, case GRPC_OP_SLICE: slice = op->data.slice; if (!GPR_SLICE_LENGTH(slice)) { + /* skip zero length slices */ + gpr_slice_unref(slice); curop++; break; } - if (st.output_size == max_bytes) { + max_take_size = max_flow_controlled_bytes - flow_controlled_bytes_taken; + if (max_take_size == 0) { goto exit_loop; } + if (GPR_SLICE_LENGTH(slice) > max_take_size) { + slice = gpr_slice_split_head(&op->data.slice, max_take_size); + grpc_sopb_add_slice(outops, slice); + } else { + /* consume this op immediately */ + grpc_sopb_append(outops, op, 1); + curop++; + } + flow_controlled_bytes_taken += GPR_SLICE_LENGTH(slice); + break; + } + } +exit_loop: + *inops_count -= curop; + memmove(inops, inops + curop, *inops_count * sizeof(grpc_stream_op)); + + return flow_controlled_bytes_taken; +} + +void grpc_chttp2_encode(grpc_stream_op *ops, size_t ops_count, int eof, + gpr_uint32 stream_id, + grpc_chttp2_hpack_compressor *compressor, + gpr_slice_buffer *output) { + framer_state st; + gpr_slice slice; + grpc_stream_op *op; + gpr_uint32 max_take_size; + gpr_uint32 curop = 0; + + GPR_ASSERT(stream_id != 0); + + st.cur_frame_type = NONE; + st.last_was_header = 0; + st.stream_id = stream_id; + st.output = output; + + while (curop < ops_count) { + op = &ops[curop]; + switch (op->type) { + case GRPC_NO_OP: + case GRPC_OP_BEGIN_MESSAGE: + gpr_log( + GPR_ERROR, + "These stream ops should be filtered out by grpc_chttp2_preencode"); + abort(); + case GRPC_OP_FLOW_CTL_CB: + op->data.flow_ctl_cb.cb(op->data.flow_ctl_cb.arg, GRPC_OP_OK); + curop++; + break; + case GRPC_OP_METADATA: + hpack_enc(compressor, op->data.metadata, &st); + curop++; + break; + case GRPC_OP_DEADLINE: + deadline_enc(compressor, op->data.deadline, &st); + curop++; + break; + case GRPC_OP_METADATA_BOUNDARY: + ensure_frame_type(&st, HEADER, 0); + finish_frame(&st, 1, 0); + st.last_was_header = 0; /* force a new header frame */ + curop++; + break; + case GRPC_OP_SLICE: + slice = op->data.slice; if (st.cur_frame_type == DATA && st.output->length - st.output_length_at_start_of_frame == GRPC_CHTTP2_MAX_PAYLOAD_LENGTH) { finish_frame(&st, 0, 0); } ensure_frame_type(&st, DATA, 1); - max_take_size = - GPR_MIN(max_bytes - st.output_size, - GRPC_CHTTP2_MAX_PAYLOAD_LENGTH + - st.output_length_at_start_of_frame - st.output->length); + max_take_size = GRPC_CHTTP2_MAX_PAYLOAD_LENGTH + + st.output_length_at_start_of_frame - st.output->length; if (GPR_SLICE_LENGTH(slice) > max_take_size) { slice = gpr_slice_split_head(&op->data.slice, max_take_size); } else { /* consume this op immediately */ curop++; } - st.output_size += GPR_SLICE_LENGTH(slice); gpr_slice_buffer_add(output, slice); break; } } -exit_loop: if (eof && st.cur_frame_type == NONE) { begin_frame(&st, DATA); } - finish_frame(&st, 1, eof && curop == nops); - - nops -= curop; - *ops_count = nops; - memmove(ops, ops + curop, nops * sizeof(grpc_stream_op)); - - return st.output_size; + finish_frame(&st, 1, eof); } diff --git a/src/core/transport/chttp2/stream_encoder.h b/src/core/transport/chttp2/stream_encoder.h index dad64697a5..147b1d31ff 100644 --- a/src/core/transport/chttp2/stream_encoder.h +++ b/src/core/transport/chttp2/stream_encoder.h @@ -78,9 +78,16 @@ void grpc_chttp2_hpack_compressor_init(grpc_chttp2_hpack_compressor *c, grpc_mdctx *mdctx); void grpc_chttp2_hpack_compressor_destroy(grpc_chttp2_hpack_compressor *c); -gpr_uint32 grpc_chttp2_encode_some(grpc_stream_op *ops, size_t *ops_count, - int eof, gpr_slice_buffer *output, - gpr_uint32 max_bytes, gpr_uint32 stream_id, - grpc_chttp2_hpack_compressor *compressor); +/* select stream ops to be encoded, moving them from inops to outops, and + moving subsequent ops in inops forward in the queue */ +gpr_uint32 grpc_chttp2_preencode(grpc_stream_op *inops, size_t *inops_count, + gpr_uint32 max_flow_controlled_bytes, + grpc_stream_op_buffer *outops); -#endif /* __GRPC_INTERNAL_TRANSPORT_CHTTP2_STREAM_ENCODER_H__ */ +/* encode stream ops to output */ +void grpc_chttp2_encode(grpc_stream_op *ops, size_t ops_count, int eof, + gpr_uint32 stream_id, + grpc_chttp2_hpack_compressor *compressor, + gpr_slice_buffer *output); + +#endif /* __GRPC_INTERNAL_TRANSPORT_CHTTP2_STREAM_ENCODER_H__ */ diff --git a/src/core/transport/chttp2/stream_map.h b/src/core/transport/chttp2/stream_map.h index caaee30676..03bf719f37 100644 --- a/src/core/transport/chttp2/stream_map.h +++ b/src/core/transport/chttp2/stream_map.h @@ -78,4 +78,4 @@ void grpc_chttp2_stream_map_for_each(grpc_chttp2_stream_map *map, void *value), void *user_data); -#endif /* __GRPC_INTERNAL_TRANSPORT_CHTTP2_STREAM_MAP_H__ */ +#endif /* __GRPC_INTERNAL_TRANSPORT_CHTTP2_STREAM_MAP_H__ */ diff --git a/src/core/transport/chttp2/varint.h b/src/core/transport/chttp2/varint.h index 780390238f..940df00a99 100644 --- a/src/core/transport/chttp2/varint.h +++ b/src/core/transport/chttp2/varint.h @@ -70,4 +70,4 @@ void grpc_chttp2_hpack_write_varint_tail(gpr_uint32 tail_value, } \ } while (0) -#endif /* __GRPC_INTERNAL_TRANSPORT_CHTTP2_VARINT_H__ */ +#endif /* __GRPC_INTERNAL_TRANSPORT_CHTTP2_VARINT_H__ */ diff --git a/src/core/transport/chttp2_transport.c b/src/core/transport/chttp2_transport.c index 5bf763e76f..1b90d4715b 100644 --- a/src/core/transport/chttp2_transport.c +++ b/src/core/transport/chttp2_transport.c @@ -71,6 +71,13 @@ typedef struct stream stream; typedef enum { /* streams that have pending writes */ WRITABLE = 0, + /* streams that have been selected to be written */ + WRITING, + /* streams that have just been written, and included a close */ + WRITTEN_CLOSED, + /* streams that have been cancelled and have some pending state updates + to perform */ + CANCELLED, /* streams that want to send window updates */ WINDOW_UPDATE, /* streams that are waiting to start because there are too many concurrent @@ -258,7 +265,12 @@ struct stream { gpr_uint32 outgoing_window; gpr_uint32 incoming_window; - gpr_uint8 write_closed; + /* when the application requests writes be closed, the write_closed is + 'queued'; when the close is flow controlled into the send path, we are + 'sending' it; when the write has been performed it is 'sent' */ + gpr_uint8 queued_write_closed; + gpr_uint8 sending_write_closed; + gpr_uint8 sent_write_closed; gpr_uint8 read_closed; gpr_uint8 cancelled; gpr_uint8 allow_window_updates; @@ -267,7 +279,10 @@ struct stream { stream_link links[STREAM_LIST_COUNT]; gpr_uint8 included[STREAM_LIST_COUNT]; + /* sops from application */ grpc_stream_op_buffer outgoing_sopb; + /* sops that have passed flow control to be written */ + grpc_stream_op_buffer writing_sopb; grpc_chttp2_data_parser parser; @@ -284,7 +299,7 @@ static int prepare_callbacks(transport *t); static void run_callbacks(transport *t); static int prepare_write(transport *t); -static void finish_write(void *t, grpc_endpoint_cb_status status); +static void perform_write(transport *t, grpc_endpoint *ep); static void lock(transport *t); static void unlock(transport *t); @@ -303,6 +318,7 @@ static void cancel_stream_id(transport *t, gpr_uint32 id, static void cancel_stream(transport *t, stream *s, grpc_status_code local_status, grpc_chttp2_error_code error_code, int send_rst); +static void finalize_cancellations(transport *t); static stream *lookup_stream(transport *t, gpr_uint32 id); static void remove_from_stream_map(transport *t, stream *s); static void maybe_start_some_streams(transport *t); @@ -518,7 +534,9 @@ static int init_stream(grpc_transport *gt, grpc_stream *gs, t->settings[PEER_SETTINGS][GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE]; s->incoming_window = t->settings[SENT_SETTINGS][GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE]; - s->write_closed = 0; + s->queued_write_closed = 0; + s->sending_write_closed = 0; + s->sent_write_closed = 0; s->read_closed = 0; s->cancelled = 0; s->allow_window_updates = 0; @@ -526,8 +544,9 @@ static int init_stream(grpc_transport *gt, grpc_stream *gs, memset(&s->links, 0, sizeof(s->links)); memset(&s->included, 0, sizeof(s->included)); grpc_sopb_init(&s->outgoing_sopb); - grpc_chttp2_data_parser_init(&s->parser); + grpc_sopb_init(&s->writing_sopb); grpc_sopb_init(&s->callback_sopb); + grpc_chttp2_data_parser_init(&s->parser); if (!server_data) { unlock(t); @@ -565,8 +584,9 @@ static void destroy_stream(grpc_transport *gt, grpc_stream *gs) { gpr_mu_unlock(&t->mu); grpc_sopb_destroy(&s->outgoing_sopb); - grpc_chttp2_data_parser_destroy(&s->parser); + grpc_sopb_destroy(&s->writing_sopb); grpc_sopb_destroy(&s->callback_sopb); + grpc_chttp2_data_parser_destroy(&s->parser); unref_transport(t); } @@ -575,6 +595,10 @@ static void destroy_stream(grpc_transport *gt, grpc_stream *gs) { * LIST MANAGEMENT */ +static int stream_list_empty(transport *t, stream_list_id id) { + return t->lists[id].head == NULL; +} + static stream *stream_list_remove_head(transport *t, stream_list_id id) { stream *s = t->lists[id].head; if (s) { @@ -666,6 +690,10 @@ static void unlock(transport *t) { } } + if (!t->writing) { + finalize_cancellations(t); + } + /* gather any callbacks that need to be made */ if (!t->calling_back && t->cb) { perform_callbacks = prepare_callbacks(t); @@ -709,53 +737,9 @@ static void unlock(transport *t) { } /* write some bytes if necessary */ - while (start_write) { - switch (grpc_endpoint_write(ep, t->outbuf.slices, t->outbuf.count, - finish_write, t)) { - case GRPC_ENDPOINT_WRITE_DONE: - /* grab the lock directly without wrappers since we just want to - continue writes if we loop: no need to check read callbacks again */ - gpr_mu_lock(&t->mu); - t->outbuf.count = 0; - t->outbuf.length = 0; - t->writing = start_write = prepare_write(t); - if (!start_write) { - if (!t->reading) { - grpc_endpoint_destroy(t->ep); - t->ep = NULL; - gpr_cv_broadcast(&t->cv); - /* endpoint ref: safe because we'll still have the ref for write */ - unref_transport(t); - } - } - gpr_mu_unlock(&t->mu); - if (!start_write) { - unref_transport(t); - } - break; - case GRPC_ENDPOINT_WRITE_ERROR: - start_write = 0; - /* use the wrapper lock/unlock here as we drop_connection, causing - read callbacks to be queued (which will be cleared during unlock) */ - lock(t); - t->outbuf.count = 0; - t->outbuf.length = 0; - t->writing = 0; - drop_connection(t); - if (!t->reading) { - grpc_endpoint_destroy(t->ep); - t->ep = NULL; - gpr_cv_broadcast(&t->cv); - /* endpoint ref: safe because we'll still have the ref for write */ - unref_transport(t); - } - unlock(t); - unref_transport(t); - break; - case GRPC_ENDPOINT_WRITE_PENDING: - start_write = 0; - break; - } + if (start_write) { + /* ultimately calls unref_transport(t); and clears t->writing */ + perform_write(t, ep); } if (perform_callbacks || call_closed || num_goaways) { @@ -788,32 +772,10 @@ static void push_setting(transport *t, grpc_chttp2_setting_id id, } } -static void finish_write(void *tp, grpc_endpoint_cb_status error) { - transport *t = tp; - - lock(t); - if (error != GRPC_ENDPOINT_CB_OK) { - drop_connection(t); - } - t->outbuf.count = 0; - t->outbuf.length = 0; - /* leave the writing flag up on shutdown to prevent further writes in unlock() - from starting */ - t->writing = 0; - if (!t->reading) { - grpc_endpoint_destroy(t->ep); - t->ep = NULL; - gpr_cv_broadcast(&t->cv); - unref_transport(t); /* safe because we'll still have the ref for write */ - } - unlock(t); - - unref_transport(t); -} - static int prepare_write(transport *t) { stream *s; gpr_slice_buffer tempbuf; + gpr_uint32 window_delta; /* simple writes are queued to qbuf, and flushed here */ tempbuf = t->qbuf; @@ -834,17 +796,16 @@ static int prepare_write(transport *t) { /* for each stream that's become writable, frame it's data (according to available window sizes) and add to the output buffer */ while (t->outgoing_window && (s = stream_list_remove_head(t, WRITABLE))) { - gpr_uint32 written = grpc_chttp2_encode_some( - s->outgoing_sopb.ops, &s->outgoing_sopb.nops, s->write_closed, - &t->outbuf, GPR_MIN(t->outgoing_window, s->outgoing_window), s->id, - &t->hpack_compressor); - t->outgoing_window -= written; - s->outgoing_window -= written; - - /* if there are no more writes to do and writes are closed, we need to - queue a callback to let the application know */ - if (s->write_closed && s->outgoing_sopb.nops == 0) { - stream_list_join(t, s, PENDING_CALLBACKS); + window_delta = grpc_chttp2_preencode( + s->outgoing_sopb.ops, &s->outgoing_sopb.nops, + GPR_MIN(t->outgoing_window, s->outgoing_window), &s->writing_sopb); + t->outgoing_window -= window_delta; + s->outgoing_window -= window_delta; + + s->sending_write_closed = + s->queued_write_closed && s->outgoing_sopb.nops == 0; + if (s->writing_sopb.nops > 0 || s->sending_write_closed) { + stream_list_join(t, s, WRITING); } /* if there are still writes to do and the stream still has window @@ -857,25 +818,89 @@ static int prepare_write(transport *t) { /* for each stream that wants to update its window, add that window here */ while ((s = stream_list_remove_head(t, WINDOW_UPDATE))) { - gpr_uint32 window_add = + window_delta = t->settings[LOCAL_SETTINGS][GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE] - s->incoming_window; - if (!s->read_closed && window_add) { - gpr_slice_buffer_add(&t->outbuf, - grpc_chttp2_window_update_create(s->id, window_add)); - s->incoming_window += window_add; + if (!s->read_closed && window_delta) { + gpr_slice_buffer_add( + &t->outbuf, grpc_chttp2_window_update_create(s->id, window_delta)); + s->incoming_window += window_delta; } } /* if the transport is ready to send a window update, do so here also */ if (t->incoming_window < t->connection_window_target * 3 / 4) { - gpr_uint32 window_add = t->connection_window_target - t->incoming_window; + window_delta = t->connection_window_target - t->incoming_window; gpr_slice_buffer_add(&t->outbuf, - grpc_chttp2_window_update_create(0, window_add)); - t->incoming_window += window_add; + grpc_chttp2_window_update_create(0, window_delta)); + t->incoming_window += window_delta; } - return t->outbuf.length > 0; + return t->outbuf.length > 0 || !stream_list_empty(t, WRITING); +} + +static void finalize_outbuf(transport *t) { + stream *s; + + while ((s = stream_list_remove_head(t, WRITING))) { + grpc_chttp2_encode(s->writing_sopb.ops, s->writing_sopb.nops, + s->sending_write_closed, s->id, &t->hpack_compressor, + &t->outbuf); + s->writing_sopb.nops = 0; + if (s->sending_write_closed) { + stream_list_join(t, s, WRITTEN_CLOSED); + } + } +} + +static void finish_write_common(transport *t, int success) { + stream *s; + + lock(t); + if (!success) { + drop_connection(t); + } + while ((s = stream_list_remove_head(t, WRITTEN_CLOSED))) { + s->sent_write_closed = 1; + stream_list_join(t, s, PENDING_CALLBACKS); + } + t->outbuf.count = 0; + t->outbuf.length = 0; + /* leave the writing flag up on shutdown to prevent further writes in unlock() + from starting */ + t->writing = 0; + if (!t->reading) { + grpc_endpoint_destroy(t->ep); + t->ep = NULL; + gpr_cv_broadcast(&t->cv); + unref_transport(t); /* safe because we'll still have the ref for write */ + } + unlock(t); + + unref_transport(t); +} + +static void finish_write(void *tp, grpc_endpoint_cb_status error) { + transport *t = tp; + finish_write_common(t, error == GRPC_ENDPOINT_CB_OK); +} + +static void perform_write(transport *t, grpc_endpoint *ep) { + finalize_outbuf(t); + + GPR_ASSERT(t->outbuf.count > 0); + + switch (grpc_endpoint_write(ep, t->outbuf.slices, t->outbuf.count, + finish_write, t)) { + case GRPC_ENDPOINT_WRITE_DONE: + finish_write_common(t, 1); + break; + case GRPC_ENDPOINT_WRITE_ERROR: + finish_write_common(t, 0); + break; + case GRPC_ENDPOINT_WRITE_PENDING: + break; + } } static void maybe_start_some_streams(transport *t) { @@ -901,19 +926,14 @@ static void send_batch(grpc_transport *gt, grpc_stream *gs, grpc_stream_op *ops, lock(t); if (is_last) { - s->write_closed = 1; + s->queued_write_closed = 1; } if (!s->cancelled) { grpc_sopb_append(&s->outgoing_sopb, ops, ops_count); - if (is_last && s->outgoing_sopb.nops == 0) { - if (s->id != 0) { - gpr_slice_buffer_add(&t->qbuf, - grpc_chttp2_data_frame_create_empty_close(s->id)); - } - } else if (s->id == 0) { + if (s->id == 0) { stream_list_join(t, s, WAITING_FOR_CONCURRENCY); maybe_start_some_streams(t); - } else if (s->outgoing_window) { + } else { stream_list_join(t, s, WRITABLE); } } else { @@ -967,12 +987,22 @@ static void send_ping(grpc_transport *gt, void (*cb)(void *user_data), * INPUT PROCESSING */ +static void finalize_cancellations(transport *t) { + stream *s; + + while ((s = stream_list_remove_head(t, CANCELLED))) { + s->read_closed = 1; + s->sent_write_closed = 1; + stream_list_join(t, s, PENDING_CALLBACKS); + } +} + static void cancel_stream_inner(transport *t, stream *s, gpr_uint32 id, grpc_status_code local_status, grpc_chttp2_error_code error_code, int send_rst) { - char buffer[32]; int had_outgoing; + char buffer[32]; if (s) { /* clear out any unreported input & output: nobody cares anymore */ @@ -981,10 +1011,9 @@ static void cancel_stream_inner(transport *t, stream *s, gpr_uint32 id, grpc_sopb_reset(&s->outgoing_sopb); if (s->cancelled) { send_rst = 0; - } else if (!s->read_closed || !s->write_closed || had_outgoing) { + } else if (!s->read_closed || !s->sent_write_closed || had_outgoing) { s->cancelled = 1; - s->read_closed = 1; - s->write_closed = 1; + stream_list_join(t, s, CANCELLED); sprintf(buffer, "%d", local_status); grpc_sopb_add_metadata( @@ -1667,8 +1696,7 @@ static int prepare_callbacks(transport *t) { s->parser.incoming_sopb = s->callback_sopb; s->callback_sopb = temp_sopb; - s->callback_state = compute_state( - s->write_closed && s->outgoing_sopb.nops == 0, s->read_closed); + s->callback_state = compute_state(s->sent_write_closed, s->read_closed); if (s->callback_state == GRPC_STREAM_CLOSED) { remove_from_stream_map(t, s); if (s->published_close) { diff --git a/src/core/transport/chttp2_transport.h b/src/core/transport/chttp2_transport.h index dd4419b98d..e12357ff5e 100644 --- a/src/core/transport/chttp2_transport.h +++ b/src/core/transport/chttp2_transport.h @@ -44,4 +44,4 @@ void grpc_create_chttp2_transport(grpc_transport_setup_callback setup, size_t nslices, grpc_mdctx *metadata_context, int is_client); -#endif /* __GRPC_INTERNAL_TRANSPORT_CHTTP2_TRANSPORT_H__ */ +#endif /* __GRPC_INTERNAL_TRANSPORT_CHTTP2_TRANSPORT_H__ */ diff --git a/src/core/transport/metadata.h b/src/core/transport/metadata.h index 6c6dee5efd..943e65a981 100644 --- a/src/core/transport/metadata.h +++ b/src/core/transport/metadata.h @@ -136,4 +136,4 @@ const char *grpc_mdstr_as_c_string(grpc_mdstr *s); #define GRPC_MDSTR_KV_HASH(k_hash, v_hash) (GPR_ROTL((k_hash), 2) ^ (v_hash)) -#endif /* __GRPC_INTERNAL_TRANSPORT_METADATA_H__ */ +#endif /* __GRPC_INTERNAL_TRANSPORT_METADATA_H__ */ diff --git a/src/core/transport/stream_op.h b/src/core/transport/stream_op.h index be60bc2da6..20d609133f 100644 --- a/src/core/transport/stream_op.h +++ b/src/core/transport/stream_op.h @@ -125,4 +125,4 @@ void grpc_sopb_add_flow_ctl_cb(grpc_stream_op_buffer *sopb, void grpc_sopb_append(grpc_stream_op_buffer *sopb, grpc_stream_op *ops, size_t nops); -#endif /* __GRPC_INTERNAL_TRANSPORT_STREAM_OP_H__ */ +#endif /* __GRPC_INTERNAL_TRANSPORT_STREAM_OP_H__ */ diff --git a/src/core/transport/transport.h b/src/core/transport/transport.h index 00dacbf5b9..af12f4e700 100644 --- a/src/core/transport/transport.h +++ b/src/core/transport/transport.h @@ -254,4 +254,4 @@ void grpc_transport_setup_initiate(grpc_transport_setup *setup); used as a destruction call by setup). */ void grpc_transport_setup_cancel(grpc_transport_setup *setup); -#endif /* __GRPC_INTERNAL_TRANSPORT_TRANSPORT_H__ */ +#endif /* __GRPC_INTERNAL_TRANSPORT_TRANSPORT_H__ */ diff --git a/src/core/transport/transport_impl.h b/src/core/transport/transport_impl.h index 9f497b9cba..31e80d36ed 100644 --- a/src/core/transport/transport_impl.h +++ b/src/core/transport/transport_impl.h @@ -84,4 +84,4 @@ struct grpc_transport { const grpc_transport_vtable *vtable; }; -#endif /* __GRPC_INTERNAL_TRANSPORT_TRANSPORT_IMPL_H__ */ +#endif /* __GRPC_INTERNAL_TRANSPORT_TRANSPORT_IMPL_H__ */ diff --git a/src/core/tsi/fake_transport_security.c b/src/core/tsi/fake_transport_security.c index 7807e71949..63d0e1f788 100644 --- a/src/core/tsi/fake_transport_security.c +++ b/src/core/tsi/fake_transport_security.c @@ -83,7 +83,6 @@ typedef struct { uint32_t max_frame_size; } tsi_fake_frame_protector; - /* --- Utils. ---*/ static const char* tsi_fake_handshake_message_strings[] = { @@ -120,7 +119,7 @@ static void store32_little_endian(uint32_t value, unsigned char* buf) { buf[3] = (unsigned char)(value >> 24) & 0xFF; buf[2] = (unsigned char)(value >> 16) & 0xFF; buf[1] = (unsigned char)(value >> 8) & 0xFF; - buf[0] = (unsigned char)(value) & 0xFF; + buf[0] = (unsigned char)(value)&0xFF; } static void tsi_fake_frame_reset(tsi_fake_frame* frame, int needs_draining) { @@ -246,8 +245,8 @@ static tsi_result fake_protector_protect( /* Try to drain first. */ if (frame->needs_draining) { drained_size = saved_output_size - *num_bytes_written; - result = drain_frame_to_bytes(protected_output_frames, - &drained_size, frame); + result = + drain_frame_to_bytes(protected_output_frames, &drained_size, frame); *num_bytes_written += drained_size; protected_output_frames += drained_size; if (result != TSI_OK) { @@ -273,8 +272,8 @@ static tsi_result fake_protector_protect( return result; } } - result = fill_frame_from_bytes(unprotected_bytes, unprotected_bytes_size, - frame); + result = + fill_frame_from_bytes(unprotected_bytes, unprotected_bytes_size, frame); if (result != TSI_OK) { if (result == TSI_INCOMPLETE_DATA) result = TSI_OK; return result; @@ -301,7 +300,7 @@ static tsi_result fake_protector_protect_flush( frame->size = frame->offset; frame->offset = 0; frame->needs_draining = 1; - store32_little_endian(frame->size, frame->data); /* Overwrite header. */ + store32_little_endian(frame->size, frame->data); /* Overwrite header. */ } result = drain_frame_to_bytes(protected_output_frames, protected_output_frames_size, frame); @@ -327,8 +326,7 @@ static tsi_result fake_protector_unprotect( /* Go past the header if needed. */ if (frame->offset == 0) frame->offset = TSI_FAKE_FRAME_HEADER_SIZE; drained_size = saved_output_size - *num_bytes_written; - result = drain_frame_to_bytes(unprotected_bytes, &drained_size, - frame); + result = drain_frame_to_bytes(unprotected_bytes, &drained_size, frame); unprotected_bytes += drained_size; *num_bytes_written += drained_size; if (result != TSI_OK) { @@ -352,7 +350,7 @@ static tsi_result fake_protector_unprotect( /* Try to drain again. */ if (!frame->needs_draining) return TSI_INTERNAL_ERROR; if (frame->offset != 0) return TSI_INTERNAL_ERROR; - frame->offset = TSI_FAKE_FRAME_HEADER_SIZE; /* Go past the header. */ + frame->offset = TSI_FAKE_FRAME_HEADER_SIZE; /* Go past the header. */ drained_size = saved_output_size - *num_bytes_written; result = drain_frame_to_bytes(unprotected_bytes, &drained_size, frame); *num_bytes_written += drained_size; @@ -481,10 +479,8 @@ static void fake_handshaker_destroy(tsi_handshaker* self) { static const tsi_handshaker_vtable handshaker_vtable = { fake_handshaker_get_bytes_to_send_to_peer, - fake_handshaker_process_bytes_from_peer, - fake_handshaker_get_result, - fake_handshaker_extract_peer, - fake_handshaker_create_frame_protector, + fake_handshaker_process_bytes_from_peer, fake_handshaker_get_result, + fake_handshaker_extract_peer, fake_handshaker_create_frame_protector, fake_handshaker_destroy, }; diff --git a/src/core/tsi/fake_transport_security.h b/src/core/tsi/fake_transport_security.h index 075d51871b..a62fe81c09 100644 --- a/src/core/tsi/fake_transport_security.h +++ b/src/core/tsi/fake_transport_security.h @@ -50,7 +50,6 @@ extern "C" { cleartext data for the protector. */ tsi_handshaker* tsi_create_fake_handshaker(int is_client); - /* Creates a protector directly without going through the handshake phase. */ tsi_frame_protector* tsi_create_fake_protector( uint32_t* max_protected_frame_size); @@ -59,4 +58,4 @@ tsi_frame_protector* tsi_create_fake_protector( } #endif -#endif /* __FAKE_TRANSPORT_SECURITY_H_ */ +#endif /* __FAKE_TRANSPORT_SECURITY_H_ */ diff --git a/src/core/tsi/fake_transport_security_test.cc b/src/core/tsi/fake_transport_security_test.cc deleted file mode 100644 index 0ae88e0c9a..0000000000 --- a/src/core/tsi/fake_transport_security_test.cc +++ /dev/null @@ -1,151 +0,0 @@ -/* - * - * 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/tsi/fake_transport_security.h" - -#include "src/core/tsi/transport_security_test_lib.h" -#include <gtest/gtest.h> -#include "util/random/permute-inl.h" - -namespace { - -void CheckStringPeerProperty(const tsi_peer& peer, int property_index, - const char* expected_name, - const char* expected_value) { - EXPECT_LT(property_index, peer.property_count); - const tsi_peer_property* property = &peer.properties[property_index]; - EXPECT_EQ(TSI_PEER_PROPERTY_TYPE_STRING, property->type); - EXPECT_EQ(string(expected_name), string(property->name)); - EXPECT_EQ(string(expected_value), - string(property->value.string.data, property->value.string.length)); -} - -class FakeTransportSecurityTest : public tsi::test::TransportSecurityTest { - protected: - void SetupHandshakers() override { - client_handshaker_.reset(tsi_create_fake_handshaker(1)); - server_handshaker_.reset(tsi_create_fake_handshaker(0)); - } - - void CheckPeer(tsi_handshaker* handshaker) { - tsi_peer peer; - EXPECT_EQ(TSI_OK, tsi_handshaker_extract_peer(handshaker, &peer)); - EXPECT_EQ(1, peer.property_count); - CheckStringPeerProperty(peer, 0, TSI_CERTIFICATE_TYPE_PEER_PROPERTY, - TSI_FAKE_CERTIFICATE_TYPE); - tsi_peer_destruct(&peer); - } - - void CheckHandshakeResults() override { - CheckPeer(client_handshaker_.get()); - CheckPeer(server_handshaker_.get()); - } - - const tsi::test::TestConfig* config() { - return &config_; - } - - tsi::test::TestConfig config_; -}; - -TEST_F(FakeTransportSecurityTest, Handshake) { - PerformHandshake(); -} - -TEST_F(FakeTransportSecurityTest, HandshakeSmallBuffer) { - config_.handshake_buffer_size = 3; - PerformHandshake(); -} -TEST_F(FakeTransportSecurityTest, PingPong) { - PingPong(); -} - -TEST_F(FakeTransportSecurityTest, RoundTrip) { - config_.client_message = big_message_; - config_.server_message = small_message_; - DoRoundTrip(); -} - -TEST_F(FakeTransportSecurityTest, RoundTripSmallMessageBuffer) { - config_.message_buffer_allocated_size = 42; - config_.client_message = big_message_; - config_.server_message = small_message_; - DoRoundTrip(); -} - -TEST_F(FakeTransportSecurityTest, RoundTripSmallProtectedBufferSize) { - config_.protected_buffer_size = 37; - config_.client_message = big_message_; - config_.server_message = small_message_; - DoRoundTrip(); -} - -TEST_F(FakeTransportSecurityTest, RoundTripSmallReadBufferSize) { - config_.read_buffer_allocated_size = 41; - config_.client_message = big_message_; - config_.server_message = small_message_; - DoRoundTrip(); -} - -TEST_F(FakeTransportSecurityTest, RoundTripSmallClientFrames) { - config_.set_client_max_output_protected_frame_size(39); - config_.client_message = big_message_; - config_.server_message = small_message_; - DoRoundTrip(); -} - -TEST_F(FakeTransportSecurityTest, RoundTripSmallServerFrames) { - config_.set_server_max_output_protected_frame_size(43); - config_.client_message = small_message_; - config_.server_message = big_message_; - DoRoundTrip(); -} - -TEST_F(FakeTransportSecurityTest, RoundTripOddBufferSizes) { - int odd_sizes[] = {33, 67, 135, 271, 523}; - RandomPermutation<int> permute(odd_sizes, arraysize(odd_sizes), - random_.get()); - permute.Permute(); - LOG(ERROR) << odd_sizes[0] << "\t" << odd_sizes[1] << "\t" << odd_sizes[2] - << "\t" << odd_sizes[3] << "\t" << odd_sizes[4]; - config_.message_buffer_allocated_size = odd_sizes[0]; - config_.protected_buffer_size = odd_sizes[1]; - config_.read_buffer_allocated_size = odd_sizes[2]; - config_.set_client_max_output_protected_frame_size(odd_sizes[3]); - config_.set_server_max_output_protected_frame_size(odd_sizes[4]); - config_.client_message = big_message_; - config_.server_message = small_message_; - DoRoundTrip(); -} - -} // namespace diff --git a/src/core/tsi/ssl_transport_security.c b/src/core/tsi/ssl_transport_security.c index b9e48e7373..c98071a937 100644 --- a/src/core/tsi/ssl_transport_security.c +++ b/src/core/tsi/ssl_transport_security.c @@ -54,7 +54,6 @@ * SSL structure. This is what we would ultimately want though... */ #define TSI_SSL_MAX_PROTECTION_OVERHEAD 100 - /* --- Structure definitions. ---*/ struct tsi_ssl_handshaker_factory { @@ -100,7 +99,6 @@ typedef struct { uint32_t buffer_offset; } tsi_ssl_frame_protector; - /* --- Library Initialization. ---*/ static gpr_once init_openssl_once = GPR_ONCE_INIT; @@ -269,7 +267,7 @@ static tsi_result peer_from_x509(X509* cert, int include_certificate_type, tsi_peer* peer) { /* TODO(jboeuf): Maybe add more properties. */ uint32_t property_count = include_certificate_type ? 3 : 2; - tsi_result result = tsi_construct_peer(property_count, peer); + tsi_result result = tsi_construct_peer(property_count, peer); if (result != TSI_OK) return result; do { result = peer_property_from_x509_common_name(cert, &peer->properties[0]); @@ -299,12 +297,10 @@ static void log_ssl_error_stack(void) { } } - /* Performs an SSL_read and handle errors. */ static tsi_result do_ssl_read(SSL* ssl, unsigned char* unprotected_bytes, uint32_t* unprotected_bytes_size) { - int read_from_ssl = SSL_read(ssl, unprotected_bytes, - *unprotected_bytes_size); + int read_from_ssl = SSL_read(ssl, unprotected_bytes, *unprotected_bytes_size); if (read_from_ssl == 0) { gpr_log(GPR_ERROR, "SSL_read returned 0 unexpectedly."); return TSI_INTERNAL_ERROR; @@ -378,7 +374,7 @@ static tsi_result ssl_ctx_use_certificate_chain( X509* certificate_authority = PEM_read_bio_X509(pem, NULL, NULL, ""); if (certificate_authority == NULL) { ERR_clear_error(); - break; /* Done reading. */ + break; /* Done reading. */ } if (!SSL_CTX_add_extra_chain_cert(context, certificate_authority)) { X509_free(certificate_authority); @@ -423,8 +419,8 @@ static tsi_result ssl_ctx_use_private_key(SSL_CTX* context, /* Loads in-memory PEM verification certs into the SSL context and optionally returns the verification cert names (root_names can be NULL). */ static tsi_result ssl_ctx_load_verification_certs( - SSL_CTX* context, const unsigned char* pem_roots, - uint32_t pem_roots_size, STACK_OF(X509_NAME)** root_names) { + SSL_CTX* context, const unsigned char* pem_roots, uint32_t pem_roots_size, + STACK_OF(X509_NAME) * *root_names) { tsi_result result = TSI_OK; uint32_t num_roots = 0; X509* root = NULL; @@ -442,7 +438,7 @@ static tsi_result ssl_ctx_load_verification_certs( root = PEM_read_bio_X509_AUX(pem, NULL, NULL, ""); if (root == NULL) { ERR_clear_error(); - break; /* We're at the end of stream. */ + break; /* We're at the end of stream. */ } if (root_names != NULL) { root_name = X509_get_subject_name(root); @@ -485,13 +481,11 @@ static tsi_result ssl_ctx_load_verification_certs( return result; } - /* Populates the SSL context with a private key and a cert chain, and sets the cipher list and the ephemeral ECDH key. */ static tsi_result populate_ssl_context( SSL_CTX* context, const unsigned char* pem_private_key, - uint32_t pem_private_key_size, - const unsigned char* pem_certificate_chain, + uint32_t pem_private_key_size, const unsigned char* pem_certificate_chain, uint32_t pem_certificate_chain_size, const char* cipher_list) { tsi_result result = TSI_OK; if (pem_certificate_chain != NULL) { @@ -532,12 +526,12 @@ static tsi_result extract_x509_subject_names_from_pem_cert( tsi_result result = TSI_OK; X509* cert = NULL; BIO* pem = BIO_new_mem_buf((void*)pem_cert, pem_cert_size); - if (pem == NULL) return TSI_OUT_OF_RESOURCES; + if (pem == NULL) return TSI_OUT_OF_RESOURCES; cert = PEM_read_bio_X509(pem, NULL, NULL, ""); if (cert == NULL) { - gpr_log(GPR_ERROR, "Invalid certificate"); - result = TSI_INVALID_ARGUMENT; + gpr_log(GPR_ERROR, "Invalid certificate"); + result = TSI_INVALID_ARGUMENT; } else { result = peer_from_x509(cert, 0, peer); } @@ -581,8 +575,7 @@ static tsi_result build_alpn_protocol_name_list( static tsi_result ssl_protector_protect( tsi_frame_protector* self, const unsigned char* unprotected_bytes, - uint32_t* unprotected_bytes_size, - unsigned char* protected_output_frames, + uint32_t* unprotected_bytes_size, unsigned char* protected_output_frames, uint32_t* protected_output_frames_size) { tsi_ssl_frame_protector* impl = (tsi_ssl_frame_protector*)self; int read_from_ssl; @@ -634,8 +627,7 @@ static tsi_result ssl_protector_protect( static tsi_result ssl_protector_protect_flush( tsi_frame_protector* self, unsigned char* protected_output_frames, - uint32_t* protected_output_frames_size, - uint32_t* still_pending_size) { + uint32_t* protected_output_frames_size, uint32_t* still_pending_size) { tsi_result result = TSI_OK; tsi_ssl_frame_protector* impl = (tsi_ssl_frame_protector*)self; int read_from_ssl = 0; @@ -662,8 +654,7 @@ static tsi_result ssl_protector_protect_flush( static tsi_result ssl_protector_unprotect( tsi_frame_protector* self, const unsigned char* protected_frames_bytes, - uint32_t* protected_frames_bytes_size, - unsigned char* unprotected_bytes, + uint32_t* protected_frames_bytes_size, unsigned char* unprotected_bytes, uint32_t* unprotected_bytes_size) { tsi_result result = TSI_OK; int written_into_ssl = 0; @@ -673,7 +664,7 @@ static tsi_result ssl_protector_unprotect( /* First, try to read remaining data from ssl. */ result = do_ssl_read(impl->ssl, unprotected_bytes, unprotected_bytes_size); - if (result != TSI_OK) return result; + if (result != TSI_OK) return result; if (*unprotected_bytes_size == output_bytes_size) { /* We have read everything we could and cannot process any more input. */ *protected_frames_bytes_size = 0; @@ -684,8 +675,8 @@ static tsi_result ssl_protector_unprotect( *unprotected_bytes_size = output_bytes_size - output_bytes_offset; /* Then, try to write some data to ssl. */ - written_into_ssl = BIO_write( - impl->into_ssl, protected_frames_bytes, *protected_frames_bytes_size); + written_into_ssl = BIO_write(impl->into_ssl, protected_frames_bytes, + *protected_frames_bytes_size); if (written_into_ssl < 0) { gpr_log(GPR_ERROR, "Sending protected frame to ssl failed with %d", written_into_ssl); @@ -710,13 +701,10 @@ static void ssl_protector_destroy(tsi_frame_protector* self) { } static const tsi_frame_protector_vtable frame_protector_vtable = { - ssl_protector_protect, - ssl_protector_protect_flush, - ssl_protector_unprotect, + ssl_protector_protect, ssl_protector_protect_flush, ssl_protector_unprotect, ssl_protector_destroy, }; - /* --- tsi_handshaker methods implementation. ---*/ static tsi_result ssl_handshaker_get_bytes_to_send_to_peer( @@ -751,8 +739,7 @@ static tsi_result ssl_handshaker_get_result(tsi_handshaker* self) { } static tsi_result ssl_handshaker_process_bytes_from_peer( - tsi_handshaker* self, const unsigned char* bytes, - uint32_t* bytes_size) { + tsi_handshaker* self, const unsigned char* bytes, uint32_t* bytes_size) { tsi_ssl_handshaker* impl = (tsi_ssl_handshaker*)self; int bytes_written_into_ssl_size = 0; if (bytes == NULL || bytes_size == 0 || *bytes_size > INT_MAX) { @@ -884,14 +871,11 @@ static void ssl_handshaker_destroy(tsi_handshaker* self) { static const tsi_handshaker_vtable handshaker_vtable = { ssl_handshaker_get_bytes_to_send_to_peer, - ssl_handshaker_process_bytes_from_peer, - ssl_handshaker_get_result, - ssl_handshaker_extract_peer, - ssl_handshaker_create_frame_protector, + ssl_handshaker_process_bytes_from_peer, ssl_handshaker_get_result, + ssl_handshaker_extract_peer, ssl_handshaker_create_frame_protector, ssl_handshaker_destroy, }; - /* --- tsi_ssl_handshaker_factory common methods. --- */ tsi_result tsi_ssl_handshaker_factory_create_handshaker( @@ -971,7 +955,6 @@ static tsi_result create_tsi_ssl_handshaker(SSL_CTX* ctx, int is_client, return TSI_OK; } - /* --- tsi_ssl__client_handshaker_factory methods implementation. --- */ static tsi_result ssl_client_handshaker_factory_create_handshaker( @@ -991,7 +974,6 @@ static void ssl_client_handshaker_factory_destroy( free(impl); } - /* --- tsi_ssl_server_handshaker_factory methods implementation. --- */ static tsi_result ssl_server_handshaker_factory_create_handshaker( @@ -1031,19 +1013,19 @@ static int does_entry_match_name(const char* entry, uint32_t entry_length, const char* name_subdomain = NULL; if (entry_length == 0) return 0; if (!strncmp(name, entry, entry_length) && (strlen(name) == entry_length)) { - return 1; /* Perfect match. */ + return 1; /* Perfect match. */ } if (entry[0] != '*') return 0; /* Wildchar subdomain matching. */ - if (entry_length < 3 || entry[1] != '.') { /* At least *.x */ + if (entry_length < 3 || entry[1] != '.') { /* At least *.x */ gpr_log(GPR_ERROR, "Invalid wildchar entry."); return 0; } name_subdomain = strchr(name, '.'); if (name_subdomain == NULL || strlen(name_subdomain) < 2) return 0; - name_subdomain++; /* Starts after the dot. */ - entry += 2; /* Remove *. */ + name_subdomain++; /* Starts after the dot. */ + entry += 2; /* Remove *. */ entry_length -= 2; return (!strncmp(entry, name_subdomain, entry_length) && (strlen(name_subdomain) == entry_length)); @@ -1095,7 +1077,6 @@ static int server_handshaker_factory_alpn_callback( return SSL_TLSEXT_ERR_NOACK; } - /* --- tsi_ssl_handshaker_factory constructors. --- */ tsi_result tsi_create_ssl_client_handshaker_factory( @@ -1277,10 +1258,8 @@ int tsi_ssl_peer_matches_name(const tsi_peer* peer, const char* name) { uint32_t i = 0; const tsi_peer_property* property = tsi_peer_get_property_by_name( peer, TSI_X509_SUBJECT_COMMON_NAME_PEER_PROPERTY); - if (property == NULL || - property->type != TSI_PEER_PROPERTY_TYPE_STRING) { - gpr_log(GPR_ERROR, - "Invalid x509 subject common name property."); + if (property == NULL || property->type != TSI_PEER_PROPERTY_TYPE_STRING) { + gpr_log(GPR_ERROR, "Invalid x509 subject common name property."); return 0; } if (does_entry_match_name(property->value.string.data, @@ -1291,8 +1270,7 @@ int tsi_ssl_peer_matches_name(const tsi_peer* peer, const char* name) { property = tsi_peer_get_property_by_name( peer, TSI_X509_SUBJECT_ALTERNATIVE_NAMES_PEER_PROPERTY); if (property == NULL || property->type != TSI_PEER_PROPERTY_TYPE_LIST) { - gpr_log(GPR_ERROR, - "Invalid x509 subject alternative names property."); + gpr_log(GPR_ERROR, "Invalid x509 subject alternative names property."); return 0; } @@ -1308,5 +1286,5 @@ int tsi_ssl_peer_matches_name(const tsi_peer* peer, const char* name) { return 1; } } - return 0; /* Not found. */ + return 0; /* Not found. */ } diff --git a/src/core/tsi/ssl_transport_security.h b/src/core/tsi/ssl_transport_security.h index 2ed3ed861b..de2b1df7bf 100644 --- a/src/core/tsi/ssl_transport_security.h +++ b/src/core/tsi/ssl_transport_security.h @@ -43,6 +43,17 @@ extern "C" { /* Value for the TSI_CERTIFICATE_TYPE_PEER_PROPERTY property for X509 certs. */ #define TSI_X509_CERTIFICATE_TYPE "X509" +/* This property is of type TSI_PEER_PROPERTY_STRING. */ +#define TSI_X509_SUBJECT_COMMON_NAME_PEER_PROPERTY "x509_subject_common_name" + +/* This property is of type TSI_PEER_PROPERTY_LIST and the children contain + unnamed (name == NULL) properties of type TSI_PEER_PROPERTY_STRING. */ +#define TSI_X509_SUBJECT_ALTERNATIVE_NAMES_PEER_PROPERTY \ + "x509_subject_alternative_names" + +/* This property is of type TSI_PEER_PROPERTY_STRING. */ +#define TSI_SSL_ALPN_SELECTED_PROTOCOL "ssl_alpn_selected_protocol" + /* --- tsi_ssl_handshaker_factory object --- This object creates tsi_handshaker objects implemented in terms of the @@ -151,9 +162,8 @@ void tsi_ssl_handshaker_factory_destroy(tsi_ssl_handshaker_factory* self); /* Util that checks that an ssl peer matches a specific name. */ int tsi_ssl_peer_matches_name(const tsi_peer* peer, const char* name); - #ifdef __cplusplus } #endif -#endif /* __SSL_TRANSPORT_SECURITY_H_ */ +#endif /* __SSL_TRANSPORT_SECURITY_H_ */ diff --git a/src/core/tsi/ssl_transport_security_test.cc b/src/core/tsi/ssl_transport_security_test.cc deleted file mode 100644 index a759403126..0000000000 --- a/src/core/tsi/ssl_transport_security_test.cc +++ /dev/null @@ -1,534 +0,0 @@ -/* - * - * 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 <memory> - -#include "base/commandlineflags.h" -#include "file/base/helpers.h" -#include "file/base/options.pb.h" -#include "file/base/path.h" -#include "src/core/tsi/transport_security_test_lib.h" -#include "src/core/tsi/ssl_transport_security.h" -#include "util/random/permute-inl.h" - -namespace { - -const char kTestCredsDir[] = - "/internal/tsi/test_creds/"; - -enum AlpnMode { - NO_ALPN, - ALPN_CLIENT_NO_SERVER, - ALPN_SERVER_NO_CLIENT, - ALPN_CLIENT_SERVER_OK, - ALPN_CLIENT_SERVER_MISMATCH -}; - -class SslTestConfig : public tsi::test::TestConfig { - public: - SslTestConfig() - : do_client_authentication(false), - subject_name_indication(nullptr), - use_bad_client_cert(false), - use_bad_server_cert(false), - alpn_mode(NO_ALPN) {} - bool do_client_authentication; - const char* subject_name_indication; - bool use_bad_client_cert; - bool use_bad_server_cert; - AlpnMode alpn_mode; -}; - -struct TsiSslHandshakerFactoryDeleter { - inline void operator()(tsi_ssl_handshaker_factory* ptr) { - tsi_ssl_handshaker_factory_destroy(ptr); - } -}; -typedef std::unique_ptr<tsi_ssl_handshaker_factory, - TsiSslHandshakerFactoryDeleter> - TsiSslHandshakerFactoryUniquePtr; - -class SslTransportSecurityTest : public tsi::test::TransportSecurityTest { - protected: - void CheckSubjectAltName(const tsi_peer_property& property, - const string& expected_subject_alt_name) { - EXPECT_EQ(property.type, TSI_PEER_PROPERTY_TYPE_STRING); - EXPECT_EQ(property.name, nullptr); - EXPECT_EQ( - string(property.value.string.data, property.value.string.length), - expected_subject_alt_name); - } - - const tsi_peer_property* CheckBasicAuthenticatedPeerAndGetCommonName( - const tsi_peer* peer) { - const tsi_peer_property* property = - tsi_peer_get_property_by_name(peer, TSI_CERTIFICATE_TYPE_PEER_PROPERTY); - EXPECT_NE(property, nullptr); - EXPECT_EQ(property->type, TSI_PEER_PROPERTY_TYPE_STRING); - EXPECT_EQ( - string(property->value.string.data, property->value.string.length), - string(TSI_X509_CERTIFICATE_TYPE)); - property = tsi_peer_get_property_by_name( - peer, TSI_X509_SUBJECT_COMMON_NAME_PEER_PROPERTY); - EXPECT_EQ(property->type, TSI_PEER_PROPERTY_TYPE_STRING); - return property; - } - - void CheckServer0Peer(tsi_peer* peer) { - const tsi_peer_property* property = - CheckBasicAuthenticatedPeerAndGetCommonName(peer); - EXPECT_EQ( - string(property->value.string.data, property->value.string.length), - string("*.test.google.com.au")); - property = tsi_peer_get_property_by_name( - peer, TSI_X509_SUBJECT_ALTERNATIVE_NAMES_PEER_PROPERTY); - EXPECT_EQ(property->type, TSI_PEER_PROPERTY_TYPE_LIST); - EXPECT_EQ(property->value.list.child_count, 0); - EXPECT_EQ(1, tsi_ssl_peer_matches_name(peer, "foo.test.google.com.au")); - EXPECT_EQ(1, tsi_ssl_peer_matches_name(peer, "bar.test.google.com.au")); - EXPECT_EQ(0, tsi_ssl_peer_matches_name(peer, "bar.test.google.blah")); - EXPECT_EQ(0, tsi_ssl_peer_matches_name(peer, "foo.bar.test.google.com.au")); - EXPECT_EQ(0, tsi_ssl_peer_matches_name(peer, "test.google.com.au")); - tsi_peer_destruct(peer); - } - - void CheckServer1Peer(tsi_peer* peer) { - const tsi_peer_property* property = - CheckBasicAuthenticatedPeerAndGetCommonName(peer); - EXPECT_EQ( - string(property->value.string.data, property->value.string.length), - string("*.test.google.com")); - property = tsi_peer_get_property_by_name( - peer, TSI_X509_SUBJECT_ALTERNATIVE_NAMES_PEER_PROPERTY); - EXPECT_EQ(property->type, TSI_PEER_PROPERTY_TYPE_LIST); - EXPECT_EQ(property->value.list.child_count, 3); - CheckSubjectAltName(property->value.list.children[0], "*.test.google.fr"); - CheckSubjectAltName(property->value.list.children[1], - "waterzooi.test.google.be"); - CheckSubjectAltName(property->value.list.children[2], "*.test.youtube.com"); - EXPECT_EQ(1, tsi_ssl_peer_matches_name(peer, "foo.test.google.com")); - EXPECT_EQ(1, tsi_ssl_peer_matches_name(peer, "bar.test.google.fr")); - EXPECT_EQ(1, tsi_ssl_peer_matches_name(peer, "waterzooi.test.google.be")); - EXPECT_EQ(1, tsi_ssl_peer_matches_name(peer, "foo.test.youtube.com")); - EXPECT_EQ(0, tsi_ssl_peer_matches_name(peer, "bar.foo.test.google.com")); - EXPECT_EQ(0, tsi_ssl_peer_matches_name(peer, "test.google.fr")); - EXPECT_EQ(0, tsi_ssl_peer_matches_name(peer, "tartines.test.google.be")); - EXPECT_EQ(0, tsi_ssl_peer_matches_name(peer, "tartines.youtube.com")); - tsi_peer_destruct(peer); - } - - void CheckClientPeer(tsi_peer* peer, bool is_authenticated) { - if (!is_authenticated) { - EXPECT_EQ(peer->property_count, - config_.alpn_mode == ALPN_CLIENT_SERVER_OK ? 1 : 0); - } else { - const tsi_peer_property* property = - CheckBasicAuthenticatedPeerAndGetCommonName(peer); - EXPECT_EQ( - string(property->value.string.data, property->value.string.length), - string("testclient")); - } - tsi_peer_destruct(peer); - } - - void SetupHandshakers() override { - tsi_ssl_handshaker_factory* client_handshaker_factory; - const unsigned char* client_cert = NULL; - unsigned int client_cert_size = 0; - const unsigned char* client_key = NULL; - unsigned int client_key_size = 0; - if (config_.do_client_authentication) { - if (config_.use_bad_client_cert) { - client_cert = - reinterpret_cast<const unsigned char*>(badclient_cert_.data()); - client_cert_size = badclient_cert_.size(); - client_key = - reinterpret_cast<const unsigned char*>(badclient_key_.data()); - client_key_size = badclient_key_.size(); - } else { - client_cert = - reinterpret_cast<const unsigned char*>(client_cert_.data()); - client_cert_size = client_cert_.size(); - client_key = reinterpret_cast<const unsigned char*>(client_key_.data()); - client_key_size = client_key_.size(); - } - } - const unsigned char** client_alpn_protocols(nullptr); - const unsigned char* client_alpn_protocols_lengths(nullptr); - uint16_t num_client_alpn_protocols = 0; - if (config_.alpn_mode == ALPN_CLIENT_NO_SERVER || - config_.alpn_mode == ALPN_CLIENT_SERVER_OK || - config_.alpn_mode == ALPN_CLIENT_SERVER_MISMATCH) { - client_alpn_protocols = - reinterpret_cast<const unsigned char**>(&client_alpn_protocols_[0]); - client_alpn_protocols_lengths = &client_alpn_protocols_lengths_[0]; - num_client_alpn_protocols = client_alpn_protocols_.size(); - } - - EXPECT_EQ(tsi_create_ssl_client_handshaker_factory( - client_key, client_key_size, client_cert, client_cert_size, - reinterpret_cast<const unsigned char*>(root_certs_.data()), - root_certs_.size(), NULL, client_alpn_protocols, - client_alpn_protocols_lengths, num_client_alpn_protocols, - &client_handshaker_factory), - TSI_OK); - client_handshaker_factory_.reset(client_handshaker_factory); - - const unsigned char** server_alpn_protocols(nullptr); - const unsigned char* server_alpn_protocols_lengths(nullptr); - uint16_t num_server_alpn_protocols = 0; - if (config_.alpn_mode == ALPN_SERVER_NO_CLIENT || - config_.alpn_mode == ALPN_CLIENT_SERVER_OK || - config_.alpn_mode == ALPN_CLIENT_SERVER_MISMATCH) { - server_alpn_protocols = - reinterpret_cast<const unsigned char**>(&server_alpn_protocols_[0]); - server_alpn_protocols_lengths = &server_alpn_protocols_lengths_[0]; - num_server_alpn_protocols = server_alpn_protocols_.size(); - if (config_.alpn_mode == ALPN_CLIENT_SERVER_MISMATCH) { - // Remove the last element that is common. - num_server_alpn_protocols--; - } - } - tsi_ssl_handshaker_factory* server_handshaker_factory; - EXPECT_EQ( - tsi_create_ssl_server_handshaker_factory( - config_.use_bad_server_cert ? &badserver_keys_[0] - : &server_keys_[0], - config_.use_bad_server_cert ? &badserver_keys_sizes_[0] - : &server_keys_sizes_[0], - config_.use_bad_server_cert ? &badserver_certs_[0] - : &server_certs_[0], - config_.use_bad_server_cert ? &badserver_certs_sizes_[0] - : &server_certs_sizes_[0], - config_.use_bad_server_cert ? badserver_keys_.size() - : server_keys_.size(), - config_.do_client_authentication - ? reinterpret_cast<const unsigned char*>(root_certs_.data()) - : NULL, - config_.do_client_authentication ? root_certs_.size() : 0, NULL, - server_alpn_protocols, server_alpn_protocols_lengths, - num_server_alpn_protocols, &server_handshaker_factory), - TSI_OK); - server_handshaker_factory_.reset(server_handshaker_factory); - - tsi_handshaker* client_handshaker; - EXPECT_EQ(tsi_ssl_handshaker_factory_create_handshaker( - client_handshaker_factory, config_.subject_name_indication, - &client_handshaker), - TSI_OK); - client_handshaker_.reset(client_handshaker); - - tsi_handshaker* server_handshaker; - EXPECT_EQ(tsi_ssl_handshaker_factory_create_handshaker( - server_handshaker_factory, NULL, &server_handshaker), - TSI_OK); - server_handshaker_.reset(server_handshaker); - } - - void CheckAlpn(const tsi_peer* peer) { - const tsi_peer_property* alpn_property = - tsi_peer_get_property_by_name(peer, TSI_SSL_ALPN_SELECTED_PROTOCOL); - if (config_.alpn_mode != ALPN_CLIENT_SERVER_OK) { - EXPECT_EQ(nullptr, alpn_property); - } else { - EXPECT_NE(nullptr, alpn_property); - EXPECT_EQ(TSI_PEER_PROPERTY_TYPE_STRING, alpn_property->type); - string expected_match("baz"); - EXPECT_EQ(expected_match, string(alpn_property->value.string.data, - alpn_property->value.string.length)); - } - } - - void CheckHandshakeResults() override { - tsi_peer peer; - - bool expect_success = - !(config_.use_bad_server_cert || - (config_.use_bad_client_cert && config_.do_client_authentication)); - tsi_result result = tsi_handshaker_get_result(client_handshaker_.get()); - EXPECT_NE(result, TSI_HANDSHAKE_IN_PROGRESS); - if (expect_success) { - EXPECT_EQ(result, TSI_OK); - EXPECT_EQ(tsi_handshaker_extract_peer(client_handshaker_.get(), &peer), - TSI_OK); - CheckAlpn(&peer); - // TODO(jboeuf): This is a bit fragile. Maybe revisit. - if (config_.subject_name_indication != nullptr) { - CheckServer1Peer(&peer); - } else { - CheckServer0Peer(&peer); - } - } else { - EXPECT_NE(result, TSI_OK); - EXPECT_NE(tsi_handshaker_extract_peer(client_handshaker_.get(), &peer), - TSI_OK); - } - - result = tsi_handshaker_get_result(server_handshaker_.get()); - EXPECT_NE(result, TSI_HANDSHAKE_IN_PROGRESS); - if (expect_success) { - EXPECT_EQ(result, TSI_OK); - EXPECT_EQ(tsi_handshaker_extract_peer(server_handshaker_.get(), &peer), - TSI_OK); - CheckAlpn(&peer); - CheckClientPeer(&peer, config_.do_client_authentication); - } else { - EXPECT_NE(result, TSI_OK); - EXPECT_NE(tsi_handshaker_extract_peer(server_handshaker_.get(), &peer), - TSI_OK); - } - } - - const tsi::test::TestConfig* config() override { - return &config_; - } - - SslTransportSecurityTest() - : client_alpn_protocols_({"foo", "toto", "baz"}), - server_alpn_protocols_({"boooo", "far", "baz"}), - client_alpn_protocols_lengths_({3, 4, 3}), - server_alpn_protocols_lengths_({5, 3, 3}) { - CHECK_OK(file::GetContents( - file::JoinPath(FLAGS_test_srcdir, kTestCredsDir, "badserver.key"), - &badserver_key_, file::Options())); - CHECK_OK(file::GetContents( - file::JoinPath(FLAGS_test_srcdir, kTestCredsDir, "badserver.pem"), - &badserver_cert_, file::Options())); - CHECK_OK(file::GetContents( - file::JoinPath(FLAGS_test_srcdir, kTestCredsDir, "badclient.key"), - &badclient_key_, file::Options())); - CHECK_OK(file::GetContents( - file::JoinPath(FLAGS_test_srcdir, kTestCredsDir, "badclient.pem"), - &badclient_cert_, file::Options())); - CHECK_OK(file::GetContents( - file::JoinPath(FLAGS_test_srcdir, kTestCredsDir, "server0.key"), - &server0_key_, file::Options())); - CHECK_OK(file::GetContents( - file::JoinPath(FLAGS_test_srcdir, kTestCredsDir, "server0.pem"), - &server0_cert_, file::Options())); - CHECK_OK(file::GetContents( - file::JoinPath(FLAGS_test_srcdir, kTestCredsDir, "server1.key"), - &server1_key_, file::Options())); - CHECK_OK(file::GetContents( - file::JoinPath(FLAGS_test_srcdir, kTestCredsDir, "server1.pem"), - &server1_cert_, file::Options())); - CHECK_OK(file::GetContents( - file::JoinPath(FLAGS_test_srcdir, kTestCredsDir, "client.key"), - &client_key_, file::Options())); - CHECK_OK(file::GetContents( - file::JoinPath(FLAGS_test_srcdir, kTestCredsDir, "client.pem"), - &client_cert_, file::Options())); - CHECK_OK(file::GetContents( - file::JoinPath(FLAGS_test_srcdir, kTestCredsDir, "ca.pem"), - &root_certs_, file::Options())); - badserver_keys_.push_back( - reinterpret_cast<const unsigned char*>(badserver_key_.data())); - badserver_certs_.push_back( - reinterpret_cast<const unsigned char*>(badserver_cert_.data())); - server_keys_.push_back( - reinterpret_cast<const unsigned char*>(server0_key_.data())); - server_keys_.push_back( - reinterpret_cast<const unsigned char*>(server1_key_.data())); - server_certs_.push_back( - reinterpret_cast<const unsigned char*>(server0_cert_.data())); - server_certs_.push_back( - reinterpret_cast<const unsigned char*>(server1_cert_.data())); - badserver_keys_sizes_.push_back(badserver_key_.size()); - badserver_certs_sizes_.push_back(badserver_cert_.size()); - server_keys_sizes_.push_back(server0_key_.size()); - server_keys_sizes_.push_back(server1_key_.size()); - server_certs_sizes_.push_back(server0_cert_.size()); - server_certs_sizes_.push_back(server1_cert_.size()); - } - - string badserver_key_; - string badserver_cert_; - string badclient_key_; - string badclient_cert_; - string server0_key_; - string server0_cert_; - string server1_key_; - string server1_cert_; - string client_key_; - string client_cert_; - string root_certs_; - std::vector<const unsigned char*> badserver_keys_; - std::vector<const unsigned char*> badserver_certs_; - std::vector<const unsigned char*> server_keys_; - std::vector<const unsigned char*> server_certs_; - std::vector<unsigned int> badserver_keys_sizes_; - std::vector<unsigned int> badserver_certs_sizes_; - std::vector<unsigned int> server_keys_sizes_; - std::vector<unsigned int> server_certs_sizes_; - TsiSslHandshakerFactoryUniquePtr client_handshaker_factory_; - TsiSslHandshakerFactoryUniquePtr server_handshaker_factory_; - std::vector<const char*> client_alpn_protocols_; - std::vector<const char*> server_alpn_protocols_; - std::vector<unsigned char> client_alpn_protocols_lengths_; - std::vector<unsigned char> server_alpn_protocols_lengths_; - string matched_alpn_; - SslTestConfig config_; -}; - - -TEST_F(SslTransportSecurityTest, LoadInvalidRoots) { - tsi_ssl_handshaker_factory* client_handshaker_factory; - string invalid_roots("Invalid roots!"); - EXPECT_EQ( - TSI_INVALID_ARGUMENT, - tsi_create_ssl_client_handshaker_factory( - NULL, 0, NULL, 0, - reinterpret_cast<const unsigned char*>(invalid_roots.data()), - invalid_roots.size(), NULL, NULL, 0, 0, &client_handshaker_factory)); -} - -TEST_F(SslTransportSecurityTest, Handshake) { - PerformHandshake(); -} - -TEST_F(SslTransportSecurityTest, HandshakeClientAuthentication) { - config_.do_client_authentication = true; - PerformHandshake(); -} - -TEST_F(SslTransportSecurityTest, HandshakeSmallBuffer) { - config_.handshake_buffer_size = 128; - PerformHandshake(); -} - -TEST_F(SslTransportSecurityTest, HandshakeSNIExactDomain) { - // server1 cert contains waterzooi.test.google.be in SAN. - config_.subject_name_indication = "waterzooi.test.google.be"; - PerformHandshake(); -} - -TEST_F(SslTransportSecurityTest, HandshakeSNIWildstarDomain) { - // server1 cert contains *.test.google.fr in SAN. - config_.subject_name_indication = "juju.test.google.fr"; - PerformHandshake(); -} - -TEST_F(SslTransportSecurityTest, BadServerCertFailure) { - config_.use_bad_server_cert = true; - PerformHandshake(); -} - -TEST_F(SslTransportSecurityTest, BadClientCertFailure) { - config_.use_bad_client_cert = true; - config_.do_client_authentication = true; - PerformHandshake(); -} - -TEST_F(SslTransportSecurityTest, AlpnClientNoServer) { - config_.alpn_mode = ALPN_CLIENT_NO_SERVER; - PerformHandshake(); -} - -TEST_F(SslTransportSecurityTest, AlpnServerNoClient) { - config_.alpn_mode = ALPN_SERVER_NO_CLIENT; - PerformHandshake(); -} - -TEST_F(SslTransportSecurityTest, AlpnClientServeMismatch) { - config_.alpn_mode = ALPN_CLIENT_SERVER_MISMATCH; - PerformHandshake(); -} - -TEST_F(SslTransportSecurityTest, AlpnClientServerOk) { - config_.alpn_mode = ALPN_CLIENT_SERVER_OK; - PerformHandshake(); -} - -TEST_F(SslTransportSecurityTest, PingPong) { - PingPong(); -} - -TEST_F(SslTransportSecurityTest, RoundTrip) { - config_.client_message = big_message_; - config_.server_message = small_message_; - DoRoundTrip(); -} - -TEST_F(SslTransportSecurityTest, RoundTripSmallMessageBuffer) { - config_.message_buffer_allocated_size = 42; - config_.client_message = big_message_; - config_.server_message = small_message_; - DoRoundTrip(); -} - -TEST_F(SslTransportSecurityTest, RoundTripSmallProtectedBufferSize) { - config_.protected_buffer_size = 37; - config_.client_message = big_message_; - config_.server_message = small_message_; - DoRoundTrip(); -} - -TEST_F(SslTransportSecurityTest, RoundTripSmallReadBufferSize) { - config_.read_buffer_allocated_size = 41; - config_.client_message = big_message_; - config_.server_message = small_message_; - DoRoundTrip(); -} - -TEST_F(SslTransportSecurityTest, RoundTripSmallClientFrames) { - config_.set_client_max_output_protected_frame_size(39); - config_.client_message = big_message_; - config_.server_message = small_message_; - DoRoundTrip(); -} - -TEST_F(SslTransportSecurityTest, RoundTripSmallServerFrames) { - config_.set_server_max_output_protected_frame_size(43); - config_.client_message = small_message_; - config_.server_message = big_message_; - DoRoundTrip(); -} - -TEST_F(SslTransportSecurityTest, RoundTripOddBufferSizes) { - int odd_sizes[] = {33, 67, 135, 271, 523}; - RandomPermutation<int> permute(odd_sizes, arraysize(odd_sizes), - random_.get()); - permute.Permute(); - LOG(ERROR) << odd_sizes[0] << "\t" << odd_sizes[1] << "\t" << odd_sizes[2] - << "\t" << odd_sizes[3] << "\t" << odd_sizes[4]; - config_.message_buffer_allocated_size = odd_sizes[0]; - config_.protected_buffer_size = odd_sizes[1]; - config_.read_buffer_allocated_size = odd_sizes[2]; - config_.set_client_max_output_protected_frame_size(odd_sizes[3]); - config_.set_server_max_output_protected_frame_size(odd_sizes[4]); - config_.client_message = big_message_; - config_.server_message = small_message_; - DoRoundTrip(); -} - -} // namespace diff --git a/src/core/tsi/transport_security.c b/src/core/tsi/transport_security.c index 94252e36d0..5a42f03f5f 100644 --- a/src/core/tsi/transport_security.c +++ b/src/core/tsi/transport_security.c @@ -44,7 +44,7 @@ char* tsi_strdup(const char* src) { if (!src) return NULL; len = strlen(src) + 1; dst = malloc(len); - if (!dst) return NULL; + if (!dst) return NULL; memcpy(dst, src, len); return dst; } @@ -84,17 +84,15 @@ const char* tsi_result_to_string(tsi_result result) { } } - /* --- tsi_frame_protector common implementation. --- Calls specific implementation after state/input validation. */ -tsi_result tsi_frame_protector_protect( - tsi_frame_protector* self, - const unsigned char* unprotected_bytes, - uint32_t* unprotected_bytes_size, - unsigned char* protected_output_frames, - uint32_t* protected_output_frames_size) { +tsi_result tsi_frame_protector_protect(tsi_frame_protector* self, + const unsigned char* unprotected_bytes, + uint32_t* unprotected_bytes_size, + unsigned char* protected_output_frames, + uint32_t* protected_output_frames_size) { if (self == NULL || unprotected_bytes == NULL || unprotected_bytes_size == NULL || protected_output_frames == NULL || protected_output_frames_size == NULL) { @@ -106,10 +104,8 @@ tsi_result tsi_frame_protector_protect( } tsi_result tsi_frame_protector_protect_flush( - tsi_frame_protector* self, - unsigned char* protected_output_frames, - uint32_t* protected_output_frames_size, - uint32_t* still_pending_size) { + tsi_frame_protector* self, unsigned char* protected_output_frames, + uint32_t* protected_output_frames_size, uint32_t* still_pending_size) { if (self == NULL || protected_output_frames == NULL || protected_output_frames == NULL || still_pending_size == NULL) { return TSI_INVALID_ARGUMENT; @@ -120,10 +116,8 @@ tsi_result tsi_frame_protector_protect_flush( } tsi_result tsi_frame_protector_unprotect( - tsi_frame_protector* self, - const unsigned char* protected_frames_bytes, - uint32_t* protected_frames_bytes_size, - unsigned char* unprotected_bytes, + tsi_frame_protector* self, const unsigned char* protected_frames_bytes, + uint32_t* protected_frames_bytes_size, unsigned char* unprotected_bytes, uint32_t* unprotected_bytes_size) { if (self == NULL || protected_frames_bytes == NULL || protected_frames_bytes_size == NULL || unprotected_bytes == NULL || @@ -140,7 +134,6 @@ void tsi_frame_protector_destroy(tsi_frame_protector* self) { self->vtable->destroy(self); } - /* --- tsi_handshaker common implementation. --- Calls specific implementation after state/input validation. */ @@ -153,7 +146,6 @@ tsi_result tsi_handshaker_get_bytes_to_send_to_peer(tsi_handshaker* self, return self->vtable->get_bytes_to_send_to_peer(self, bytes, bytes_size); } - tsi_result tsi_handshaker_process_bytes_from_peer(tsi_handshaker* self, const unsigned char* bytes, uint32_t* bytes_size) { @@ -179,8 +171,7 @@ tsi_result tsi_handshaker_extract_peer(tsi_handshaker* self, tsi_peer* peer) { } tsi_result tsi_handshaker_create_frame_protector( - tsi_handshaker* self, - uint32_t* max_protected_frame_size, + tsi_handshaker* self, uint32_t* max_protected_frame_size, tsi_frame_protector** protector) { tsi_result result; if (self == NULL || protector == NULL) return TSI_INVALID_ARGUMENT; @@ -201,7 +192,6 @@ void tsi_handshaker_destroy(tsi_handshaker* self) { self->vtable->destroy(self); } - /* --- tsi_peer implementation. --- */ const tsi_peer_property* tsi_peer_get_property_by_name(const tsi_peer* self, @@ -227,7 +217,6 @@ tsi_peer_property tsi_init_peer_property(void) { return property; } - static void tsi_peer_destroy_list_property(tsi_peer_property* children, uint32_t child_count) { uint32_t i; @@ -254,7 +243,7 @@ void tsi_peer_property_destruct(tsi_peer_property* property) { /* Nothing to free. */ break; } - *property = tsi_init_peer_property(); /* Reset everything to 0. */ + *property = tsi_init_peer_property(); /* Reset everything to 0. */ } void tsi_peer_destruct(tsi_peer* self) { diff --git a/src/core/tsi/transport_security.h b/src/core/tsi/transport_security.h index cf9a2b0195..9a20fa83a5 100644 --- a/src/core/tsi/transport_security.h +++ b/src/core/tsi/transport_security.h @@ -109,10 +109,10 @@ tsi_result tsi_construct_list_peer_property(const char* name, tsi_peer_property* property); /* Utils. */ -char* tsi_strdup(const char* src); /* Sadly, no strdup in C89. */ +char* tsi_strdup(const char* src); /* Sadly, no strdup in C89. */ #ifdef __cplusplus } #endif -#endif /* __TRANSPORT_SECURITY_H_ */ +#endif /* __TRANSPORT_SECURITY_H_ */ diff --git a/src/core/tsi/transport_security_interface.h b/src/core/tsi/transport_security_interface.h index 6be72c753a..76746a4b20 100644 --- a/src/core/tsi/transport_security_interface.h +++ b/src/core/tsi/transport_security_interface.h @@ -60,7 +60,6 @@ typedef enum { const char* tsi_result_to_string(tsi_result result); - /* --- tsi_frame_protector object --- This object protects and unprotects buffers once the handshake is done. @@ -121,12 +120,11 @@ typedef struct tsi_frame_protector tsi_frame_protector; if (result != TSI_OK) HandleError(result); ------------------------------------------------------------------------ */ -tsi_result tsi_frame_protector_protect( - tsi_frame_protector* self, - const unsigned char* unprotected_bytes, - uint32_t* unprotected_bytes_size, - unsigned char* protected_output_frames, - uint32_t* protected_output_frames_size); +tsi_result tsi_frame_protector_protect(tsi_frame_protector* self, + const unsigned char* unprotected_bytes, + uint32_t* unprotected_bytes_size, + unsigned char* protected_output_frames, + uint32_t* protected_output_frames_size); /* Indicates that we need to flush the bytes buffered in the protector and get the resulting frame. @@ -137,10 +135,8 @@ tsi_result tsi_frame_protector_protect( - still_pending_bytes is an output parameter indicating the number of bytes that still need to be flushed from the protector.*/ tsi_result tsi_frame_protector_protect_flush( - tsi_frame_protector* self, - unsigned char* protected_output_frames, - uint32_t* protected_output_frames_size, - uint32_t* still_pending_size); + tsi_frame_protector* self, unsigned char* protected_output_frames, + uint32_t* protected_output_frames_size, uint32_t* still_pending_size); /* Outputs unprotected bytes. - protected_frames_bytes is an input only parameter and points to the @@ -163,16 +159,13 @@ tsi_result tsi_frame_protector_protect_flush( needs to be read before new protected data can be processed in which case protected_frames_size will be set to 0. */ tsi_result tsi_frame_protector_unprotect( - tsi_frame_protector* self, - const unsigned char* protected_frames_bytes, - uint32_t* protected_frames_bytes_size, - unsigned char* unprotected_bytes, + tsi_frame_protector* self, const unsigned char* protected_frames_bytes, + uint32_t* protected_frames_bytes_size, unsigned char* unprotected_bytes, uint32_t* unprotected_bytes_size); /* Destroys the tsi_frame_protector object. */ void tsi_frame_protector_destroy(tsi_frame_protector* self); - /* --- tsi_peer objects --- tsi_peer objects are a set of properties. The peer owns the properties. */ @@ -180,23 +173,6 @@ void tsi_frame_protector_destroy(tsi_frame_protector* self); /* This property is of type TSI_PEER_PROPERTY_STRING. */ #define TSI_CERTIFICATE_TYPE_PEER_PROPERTY "certificate_type" -/* This property is of type TSI_PEER_PROPERTY_STRING. */ -#define TSI_X509_SUBJECT_COMMON_NAME_PEER_PROPERTY "x509_subject_common_name" - -/* This property is of type TSI_PEER_PROPERTY_LIST and the children contain - unnamed (name == NULL) properties of type TSI_PEER_PROPERTY_STRING. */ -#define TSI_X509_SUBJECT_ALTERNATIVE_NAMES_PEER_PROPERTY \ - "x509_subject_alternative_names" - -/* This property is of type TSI_PEER_PROPERTY_STRING. */ -#define TSI_SSL_ALPN_SELECTED_PROTOCOL "ssl_alpn_selected_protocol" - -/* This property is of type TSI_PEER_PROPERTY_STRING. */ -#define TSI_MDB_USER_NAME_PEER_PROPERTY "mdb_user_name" - -/* This property is of type TSI_PEER_PROPERTY_SIGNED_INTEGER. */ -#define TSI_MDB_GAIA_ID_PEER_PROPERTY "mdb_gaia_id" - /* Properties of type TSI_PEER_PROPERTY_TYPE_STRING may contain NULL characters just like C++ strings. The length field gives the length of the string. */ typedef enum { @@ -350,7 +326,6 @@ tsi_result tsi_handshaker_get_result(tsi_handshaker* self); #define tsi_handshaker_is_in_progress(h) \ (tsi_handshaker_get_result((h)) == TSI_HANDSHAKE_IN_PROGRESS) - /* This method may return TSI_FAILED_PRECONDITION if tsi_handshaker_is_in_progress returns 1, it returns TSI_OK otherwise assuming the handshaker is not in a fatal error state. @@ -374,8 +349,7 @@ tsi_result tsi_handshaker_extract_peer(tsi_handshaker* self, tsi_peer* peer); the handshaker is not in a fatal error state. The caller is responsible for destroying the protector. */ tsi_result tsi_handshaker_create_frame_protector( - tsi_handshaker* self, - uint32_t* max_output_protected_frame_size, + tsi_handshaker* self, uint32_t* max_output_protected_frame_size, tsi_frame_protector** protector); /* This method releases the tsi_handshaker object. After this method is called, @@ -386,4 +360,4 @@ void tsi_handshaker_destroy(tsi_handshaker* self); } #endif -#endif /* __TRANSPORT_SECURITY_INTERFACE_H_ */ +#endif /* __TRANSPORT_SECURITY_INTERFACE_H_ */ diff --git a/src/core/tsi/transport_security_test_lib.cc b/src/core/tsi/transport_security_test_lib.cc deleted file mode 100644 index 1b630c9578..0000000000 --- a/src/core/tsi/transport_security_test_lib.cc +++ /dev/null @@ -1,363 +0,0 @@ -/* - * - * 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/tsi/transport_security_test_lib.h" - -#include <memory> - -#include "base/commandlineflags.h" -#include "src/core/tsi/transport_security_interface.h" -#include "strings/escaping.h" -#include "strings/strcat.h" -#include <gtest/gtest.h> -#include "util/random/mt_random.h" - -namespace { - -const char kPingRequest[] = "Ping"; -const char kPongResponse[] = "Pong"; -const int kBigMessageSize = 17000; - -} // namespace - -namespace tsi { -namespace test { - -TransportSecurityTest::TransportSecurityTest() : random_(new MTRandom()) { - small_message_ = "Chapi Chapo"; - big_message_ = RandomString(kBigMessageSize); -} - -string TransportSecurityTest::RandomString(int size) { - std::unique_ptr<char[]> buffer(new char[size]); - for (int i = 0; i < size; i++) { - buffer[i] = random_->Rand8(); - } - return string(buffer.get(), size); -} - -void TransportSecurityTest::SendBytesToPeer(bool is_client, unsigned char* buf, - unsigned int buf_size) { - string& channel = is_client ? to_server_channel_ : to_client_channel_; - LOG(INFO) << (is_client ? "Client:" : "Server") << " sending " << buf_size - << " bytes to peer."; - channel.append(reinterpret_cast<const char*>(buf), buf_size); -} - -void TransportSecurityTest::ReadBytesFromPeer(bool is_client, - unsigned char* buf, - unsigned int* buf_size) { - string& channel = is_client ? to_client_channel_ : to_server_channel_; - unsigned int to_read = - *buf_size < channel.size() ? *buf_size : channel.size(); - memcpy(buf, channel.data(), to_read); - *buf_size = to_read; - channel.erase(0, to_read); - LOG(INFO) << (is_client ? "Client:" : "Server") << " read " << to_read - << " bytes from peer."; -} - -void TransportSecurityTest::DoHandshakeStep(bool is_client, - unsigned int buf_allocated_size, - tsi_handshaker* handshaker, - string* remaining_bytes) { - tsi_result result = TSI_OK; - std::unique_ptr<unsigned char[]> buf(new unsigned char[buf_allocated_size]); - unsigned int buf_offset; - unsigned int buf_size; - // See if we need to send some bytes to the peer. - do { - unsigned int buf_size_to_send = buf_allocated_size; - result = tsi_handshaker_get_bytes_to_send_to_peer(handshaker, buf.get(), - &buf_size_to_send); - if (buf_size_to_send > 0) { - SendBytesToPeer(is_client, buf.get(), buf_size_to_send); - } - } while (result == TSI_INCOMPLETE_DATA); - if (!tsi_handshaker_is_in_progress(handshaker)) return; - - do { - // Read bytes from the peer. - buf_size = buf_allocated_size; - buf_offset = 0; - ReadBytesFromPeer(is_client, buf.get(), &buf_size); - if (buf_size == 0) break; - - // Process the bytes from the peer. We have to be careful as these bytes - // may contain non-handshake data (protected data). If this is the case, - // we will exit from the loop with buf_size > 0. - unsigned int consumed_by_handshaker = buf_size; - result = tsi_handshaker_process_bytes_from_peer(handshaker, buf.get(), - &consumed_by_handshaker); - buf_size -= consumed_by_handshaker; - buf_offset += consumed_by_handshaker; - } while (result == TSI_INCOMPLETE_DATA); - - if (!tsi_handshaker_is_in_progress(handshaker)) { - remaining_bytes->assign( - reinterpret_cast<const char*>(buf.get()) + buf_offset, buf_size); - } -} - -void TransportSecurityTest::PerformHandshake() { - SetupHandshakers(); - string remaining_bytes; - do { - DoHandshakeStep(true, config()->handshake_buffer_size, - client_handshaker_.get(), &remaining_bytes); - EXPECT_EQ(0, remaining_bytes.size()); - DoHandshakeStep(false, config()->handshake_buffer_size, - server_handshaker_.get(), &remaining_bytes); - EXPECT_EQ(0, remaining_bytes.size()); - } while (tsi_handshaker_is_in_progress(client_handshaker_.get()) || - tsi_handshaker_is_in_progress(server_handshaker_.get())); - CheckHandshakeResults(); -} - -void TransportSecurityTest::SendMessageToPeer( - bool is_client, tsi_frame_protector* protector, const string& message, - unsigned int protected_buffer_size) { - std::unique_ptr<unsigned char[]> protected_buffer( - new unsigned char[protected_buffer_size]); - unsigned int message_size = message.size(); - const unsigned char* message_bytes = - reinterpret_cast<const unsigned char*>(message.data()); - tsi_result result = TSI_OK; - while (message_size > 0 && result == TSI_OK) { - unsigned int protected_buffer_size_to_send = protected_buffer_size; - unsigned int processed_message_size = message_size; - result = tsi_frame_protector_protect( - protector, message_bytes, &processed_message_size, - protected_buffer.get(), &protected_buffer_size_to_send); - EXPECT_EQ(TSI_OK, result); - SendBytesToPeer(is_client, protected_buffer.get(), - protected_buffer_size_to_send); - message_bytes += processed_message_size; - message_size -= processed_message_size; - - // Flush if we're done. - if (message_size == 0) { - unsigned int still_pending_size; - do { - protected_buffer_size_to_send = protected_buffer_size; - result = tsi_frame_protector_protect_flush( - protector, protected_buffer.get(), &protected_buffer_size_to_send, - &still_pending_size); - EXPECT_EQ(TSI_OK, result); - SendBytesToPeer(is_client, protected_buffer.get(), - protected_buffer_size_to_send); - } while (still_pending_size > 0 && result == TSI_OK); - EXPECT_EQ(TSI_OK, result); - } - } - EXPECT_EQ(TSI_OK, result); -} - -void TransportSecurityTest::ReceiveMessageFromPeer( - bool is_client, tsi_frame_protector* protector, - unsigned int read_buf_allocated_size, - unsigned int message_buf_allocated_size, string* message) { - std::unique_ptr<unsigned char[]> read_buffer( - new unsigned char[read_buf_allocated_size]); - unsigned int read_offset = 0; - unsigned int read_from_peer_size = 0; - std::unique_ptr<unsigned char[]> message_buffer( - new unsigned char[message_buf_allocated_size]); - tsi_result result = TSI_OK; - bool done = false; - while (!done && result == TSI_OK) { - if (read_from_peer_size == 0) { - read_from_peer_size = read_buf_allocated_size; - ReadBytesFromPeer(is_client, read_buffer.get(), &read_from_peer_size); - read_offset = 0; - } - if (read_from_peer_size == 0) done = true; - unsigned int message_buffer_size; - do { - message_buffer_size = message_buf_allocated_size; - unsigned int processed_size = read_from_peer_size; - result = tsi_frame_protector_unprotect( - protector, read_buffer.get() + read_offset, &processed_size, - message_buffer.get(), &message_buffer_size); - EXPECT_EQ(TSI_OK, result); - if (message_buffer_size > 0) { - LOG(INFO) << "Wrote " << message_buffer_size << " bytes to message."; - message->append(reinterpret_cast<const char*>(message_buffer.get()), - message_buffer_size); - } - read_offset += processed_size; - read_from_peer_size -= processed_size; - } while ((read_from_peer_size > 0 || message_buffer_size > 0) && - result == TSI_OK); - EXPECT_EQ(TSI_OK, result); - } - EXPECT_EQ(TSI_OK, result); -} - -void TransportSecurityTest::DoRoundTrip(const string& request, - const string& response) { - PerformHandshake(); - - tsi_frame_protector* client_frame_protector; - tsi_frame_protector* server_frame_protector; - unsigned int client_max_output_protected_frame_size = - config()->client_max_output_protected_frame_size(); - EXPECT_EQ(TSI_OK, - tsi_handshaker_create_frame_protector( - client_handshaker_.get(), - config()->use_client_default_max_output_protected_frame_size() - ? nullptr - : &client_max_output_protected_frame_size, - &client_frame_protector)); - - unsigned int server_max_output_protected_frame_size = - config()->server_max_output_protected_frame_size(); - EXPECT_EQ(TSI_OK, - tsi_handshaker_create_frame_protector( - server_handshaker_.get(), - config()->use_server_default_max_output_protected_frame_size() - ? nullptr - : &server_max_output_protected_frame_size, - &server_frame_protector)); - - SendMessageToPeer(true, client_frame_protector, request, - config()->protected_buffer_size); - string retrieved_request; - ReceiveMessageFromPeer( - false, server_frame_protector, config()->read_buffer_allocated_size, - config()->message_buffer_allocated_size, &retrieved_request); - EXPECT_EQ(request.size(), retrieved_request.size()); - EXPECT_EQ(strings::b2a_hex(request), strings::b2a_hex(retrieved_request)); - - SendMessageToPeer(false, server_frame_protector, response, - config()->protected_buffer_size); - string retrieved_response; - ReceiveMessageFromPeer( - true, client_frame_protector, config()->read_buffer_allocated_size, - config()->message_buffer_allocated_size, &retrieved_response); - EXPECT_EQ(response.size(), retrieved_response.size()); - EXPECT_EQ(strings::b2a_hex(response), strings::b2a_hex(retrieved_response)); - - tsi_frame_protector_destroy(client_frame_protector); - tsi_frame_protector_destroy(server_frame_protector); -} - -void TransportSecurityTest::DoRoundTrip() { - DoRoundTrip(config()->client_message, config()->server_message); -} -void TransportSecurityTest::PingPong() { - PerformHandshake(); - - unsigned char to_server[4096]; - unsigned char to_client[4096]; - unsigned int max_frame_size = sizeof(to_client); - tsi_frame_protector* client_frame_protector; - tsi_frame_protector* server_frame_protector; - EXPECT_EQ( - tsi_handshaker_create_frame_protector( - client_handshaker_.get(), &max_frame_size, &client_frame_protector), - TSI_OK); - EXPECT_EQ(max_frame_size, sizeof(to_client)); - EXPECT_EQ( - tsi_handshaker_create_frame_protector( - server_handshaker_.get(), &max_frame_size, &server_frame_protector), - TSI_OK); - EXPECT_EQ(max_frame_size, sizeof(to_client)); - - // Send Ping. - unsigned int ping_length = strlen(kPingRequest); - unsigned int protected_size = sizeof(to_server); - EXPECT_EQ(tsi_frame_protector_protect( - client_frame_protector, - reinterpret_cast<const unsigned char*>(kPingRequest), - &ping_length, to_server, &protected_size), - TSI_OK); - EXPECT_EQ(ping_length, strlen(kPingRequest)); - EXPECT_EQ(protected_size, 0); - protected_size = sizeof(to_server); - unsigned int still_pending_size; - EXPECT_EQ( - tsi_frame_protector_protect_flush(client_frame_protector, to_server, - &protected_size, &still_pending_size), - TSI_OK); - EXPECT_EQ(still_pending_size, 0); - EXPECT_GT(protected_size, strlen(kPingRequest)); - - // Receive Ping. - unsigned int unprotected_size = sizeof(to_server); - unsigned int saved_protected_size = protected_size; - EXPECT_EQ(tsi_frame_protector_unprotect(server_frame_protector, to_server, - &protected_size, to_server, - &unprotected_size), - TSI_OK); - EXPECT_EQ(saved_protected_size, protected_size); - EXPECT_EQ(ping_length, unprotected_size); - EXPECT_EQ(string(kPingRequest), - string(reinterpret_cast<const char*>(to_server), unprotected_size)); - - // Send back Pong. - unsigned int pong_length = strlen(kPongResponse); - protected_size = sizeof(to_client); - EXPECT_EQ(tsi_frame_protector_protect( - server_frame_protector, - reinterpret_cast<const unsigned char*>(kPongResponse), - &pong_length, to_client, &protected_size), - TSI_OK); - EXPECT_EQ(pong_length, strlen(kPongResponse)); - EXPECT_EQ(protected_size, 0); - protected_size = sizeof(to_client); - EXPECT_EQ( - tsi_frame_protector_protect_flush(server_frame_protector, to_client, - &protected_size, &still_pending_size), - TSI_OK); - EXPECT_EQ(still_pending_size, 0); - EXPECT_GT(protected_size, strlen(kPongResponse)); - - // Receive Pong. - unprotected_size = sizeof(to_server); - saved_protected_size = protected_size; - EXPECT_EQ(tsi_frame_protector_unprotect(client_frame_protector, to_client, - &protected_size, to_client, - &unprotected_size), - TSI_OK); - EXPECT_EQ(saved_protected_size, protected_size); - EXPECT_EQ(pong_length, unprotected_size); - EXPECT_EQ(string(kPongResponse), - string(reinterpret_cast<const char*>(to_client), unprotected_size)); - - tsi_frame_protector_destroy(client_frame_protector); - tsi_frame_protector_destroy(server_frame_protector); -} - -} // namespace test -} // namespace tsi diff --git a/src/core/tsi/transport_security_test_lib.h b/src/core/tsi/transport_security_test_lib.h deleted file mode 100644 index 8c9c764c91..0000000000 --- a/src/core/tsi/transport_security_test_lib.h +++ /dev/null @@ -1,154 +0,0 @@ -/* - * - * 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 __TRANSPORT_SECURITY_TEST_LIB_H_ -#define __TRANSPORT_SECURITY_TEST_LIB_H_ - -#include <memory> - -#include "base/commandlineflags.h" -#include "src/core/tsi/transport_security_interface.h" -#include "strings/strcat.h" -#include <gtest/gtest.h> -#include "util/random/mt_random.h" - -namespace tsi { -namespace test { - -class TestConfig { - public: - TestConfig() - : client_message("Chapi Chapo"), - server_message("Chapi Chapo"), - handshake_buffer_size(4096), - read_buffer_allocated_size(4096), - message_buffer_allocated_size(4096), - protected_buffer_size(16384), - use_client_default_max_output_protected_frame_size_(true), - use_server_default_max_output_protected_frame_size_(true), - client_max_output_protected_frame_size_(0), - server_max_output_protected_frame_size_(0) {} - - void set_client_max_output_protected_frame_size(unsigned int size) { - use_client_default_max_output_protected_frame_size_ = false; - client_max_output_protected_frame_size_ = size; - } - void set_server_max_output_protected_frame_size(unsigned int size) { - use_server_default_max_output_protected_frame_size_ = false; - server_max_output_protected_frame_size_ = size; - } - bool use_client_default_max_output_protected_frame_size() const { - return use_client_default_max_output_protected_frame_size_; - } - bool use_server_default_max_output_protected_frame_size() const { - return use_server_default_max_output_protected_frame_size_; - } - unsigned int client_max_output_protected_frame_size() const { - return client_max_output_protected_frame_size_; - } - unsigned int server_max_output_protected_frame_size() const { - return server_max_output_protected_frame_size_; - } - - string client_message; - string server_message; - unsigned int handshake_buffer_size; - unsigned int read_buffer_allocated_size; - unsigned int message_buffer_allocated_size; - unsigned int protected_buffer_size; - - private: - bool use_client_default_max_output_protected_frame_size_; - bool use_server_default_max_output_protected_frame_size_; - unsigned int client_max_output_protected_frame_size_; - unsigned int server_max_output_protected_frame_size_; -}; - - -struct TsiHandshakerDeleter { - inline void operator()(tsi_handshaker* ptr) { tsi_handshaker_destroy(ptr); } -}; -typedef std::unique_ptr<tsi_handshaker, TsiHandshakerDeleter> - TsiHandshakerUniquePtr; - -class TransportSecurityTest : public ::testing::Test { - protected: - TransportSecurityTest(); - virtual ~TransportSecurityTest() {} - virtual const TestConfig* config() = 0; - string RandomString(int size); - virtual void SetupHandshakers() = 0; - // An implementation-specific verification of the validity of the handshake. - virtual void CheckHandshakeResults() = 0; - // Do a full handshake. - void PerformHandshake(); - // Send a protected message between the client and server. - void SendMessageToPeer(bool is_client, tsi_frame_protector* protector, - const string& message, - unsigned int protected_buffer_size); - void ReceiveMessageFromPeer(bool is_client, tsi_frame_protector* protector, - unsigned int read_buf_allocated_size, - unsigned int message_buf_allocated_size, - string* message); - - // A simple test that does a handshake and sends a message back and forth - void PingPong(); - // A complicated test that can be configured by modifying config(). - void DoRoundTrip(); - - TsiHandshakerUniquePtr client_handshaker_; - TsiHandshakerUniquePtr server_handshaker_; - - string small_message_; - string big_message_; - std::unique_ptr<RandomBase> random_; - - private: - // Functions to send raw bytes between the client and server. - void SendBytesToPeer(bool is_client, unsigned char* buf, - unsigned int buf_size); - void ReadBytesFromPeer(bool is_client, unsigned char* buf, - unsigned int* buf_size); - // Do a single step of the handshake. - void DoHandshakeStep(bool is_client, unsigned int buf_allocated_size, - tsi_handshaker* handshaker, string* remaining_bytes); - void DoRoundTrip(const string& request, const string& response); - - string to_server_channel_; - string to_client_channel_; -}; - -} // namespace test -} // namespace tsi - -#endif // __TRANSPORT_SECURITY_TEST_LIB_H_ diff --git a/src/cpp/client/channel.cc b/src/cpp/client/channel.cc index 7d95518631..ddda8c22d6 100644 --- a/src/cpp/client/channel.cc +++ b/src/cpp/client/channel.cc @@ -41,13 +41,13 @@ #include <grpc/support/log.h> #include <grpc/support/slice.h> -#include "src/cpp/rpc_method.h" #include "src/cpp/proto/proto_utils.h" #include "src/cpp/stream/stream_context.h" #include <grpc++/channel_arguments.h> #include <grpc++/client_context.h> #include <grpc++/config.h> #include <grpc++/credentials.h> +#include <grpc++/impl/rpc_method.h> #include <grpc++/status.h> #include <google/protobuf/message.h> @@ -69,8 +69,9 @@ Channel::Channel(const grpc::string& target, : args.GetSslTargetNameOverride()) { grpc_channel_args channel_args; args.SetChannelArgs(&channel_args); + grpc_credentials* c_creds = creds ? creds->GetRawCreds() : nullptr; c_channel_ = grpc_secure_channel_create( - creds->GetRawCreds(), target.c_str(), + c_creds, target.c_str(), channel_args.num_args > 0 ? &channel_args : nullptr); } @@ -118,10 +119,15 @@ Status Channel::StartBlockingRpc(const RpcMethod& method, finished_tag, GRPC_WRITE_BUFFER_HINT) == GRPC_CALL_OK); ev = grpc_completion_queue_pluck(cq, invoke_tag, gpr_inf_future); + bool success = ev->data.invoke_accepted == GRPC_OP_OK; grpc_event_finish(ev); + if (!success) { + GetFinalStatus(cq, finished_tag, &status); + return status; + } // write request grpc_byte_buffer* write_buffer = nullptr; - bool success = SerializeProto(request, &write_buffer); + success = SerializeProto(request, &write_buffer); if (!success) { grpc_call_cancel(call); status = @@ -166,10 +172,10 @@ Status Channel::StartBlockingRpc(const RpcMethod& method, return status; } -StreamContextInterface* Channel::CreateStream(const RpcMethod& method, - ClientContext* context, - const google::protobuf::Message* request, - google::protobuf::Message* result) { +StreamContextInterface* Channel::CreateStream( + const RpcMethod& method, ClientContext* context, + const google::protobuf::Message* request, + google::protobuf::Message* result) { grpc_call* call = grpc_channel_create_call( c_channel_, method.name(), target_.c_str(), context->RawDeadline()); context->set_call(call); diff --git a/src/cpp/client/channel.h b/src/cpp/client/channel.h index 621e58539b..8de1180ac2 100644 --- a/src/cpp/client/channel.h +++ b/src/cpp/client/channel.h @@ -58,10 +58,10 @@ class Channel : public ChannelInterface { const google::protobuf::Message& request, google::protobuf::Message* result) override; - StreamContextInterface* CreateStream(const RpcMethod& method, - ClientContext* context, - const google::protobuf::Message* request, - google::protobuf::Message* result) override; + StreamContextInterface* CreateStream( + const RpcMethod& method, ClientContext* context, + const google::protobuf::Message* request, + google::protobuf::Message* result) override; private: const grpc::string target_; diff --git a/src/cpp/client/credentials.cc b/src/cpp/client/credentials.cc index 986008f7bb..d81cf9f4d0 100644 --- a/src/cpp/client/credentials.cc +++ b/src/cpp/client/credentials.cc @@ -31,10 +31,10 @@ * */ - #include <string> #include <grpc/grpc_security.h> +#include <grpc/support/log.h> #include <grpc++/credentials.h> @@ -58,6 +58,9 @@ std::unique_ptr<Credentials> CredentialsFactory::SslCredentials( options.pem_root_certs.empty() ? nullptr : reinterpret_cast<const unsigned char*>( options.pem_root_certs.c_str()); + if (pem_root_certs == nullptr) { + return std::unique_ptr<Credentials>(); + } const unsigned char* pem_private_key = options.pem_private_key.empty() ? nullptr : reinterpret_cast<const unsigned char*>( @@ -71,17 +74,42 @@ std::unique_ptr<Credentials> CredentialsFactory::SslCredentials( pem_root_certs, options.pem_root_certs.size(), pem_private_key, options.pem_private_key.size(), pem_cert_chain, options.pem_cert_chain.size()); - std::unique_ptr<Credentials> cpp_creds(new Credentials(c_creds)); + std::unique_ptr<Credentials> cpp_creds( + c_creds == nullptr ? nullptr : new Credentials(c_creds)); return cpp_creds; } // Builds credentials for use when running in GCE std::unique_ptr<Credentials> CredentialsFactory::ComputeEngineCredentials() { grpc_credentials* c_creds = grpc_compute_engine_credentials_create(); - std::unique_ptr<Credentials> cpp_creds(new Credentials(c_creds)); + std::unique_ptr<Credentials> cpp_creds( + c_creds == nullptr ? nullptr : new Credentials(c_creds)); return cpp_creds; } +// Builds service account credentials. +std::unique_ptr<Credentials> CredentialsFactory::ServiceAccountCredentials( + const grpc::string& json_key, const grpc::string& scope, + std::chrono::seconds token_lifetime) { + gpr_timespec lifetime = gpr_time_from_seconds( + token_lifetime.count() > 0 ? token_lifetime.count() : 0); + grpc_credentials* c_creds = grpc_service_account_credentials_create( + json_key.c_str(), scope.c_str(), lifetime); + std::unique_ptr<Credentials> cpp_creds( + c_creds == nullptr ? nullptr : new Credentials(c_creds)); + return cpp_creds; +} + +// Builds IAM credentials. +std::unique_ptr<Credentials> CredentialsFactory::IAMCredentials( + const grpc::string& authorization_token, + const grpc::string& authority_selector) { + grpc_credentials* c_creds = grpc_iam_credentials_create( + authorization_token.c_str(), authority_selector.c_str()); + std::unique_ptr<Credentials> cpp_creds( + c_creds == nullptr ? nullptr : new Credentials(c_creds)); + return cpp_creds; +} // Combines two credentials objects into a composite credentials. std::unique_ptr<Credentials> CredentialsFactory::ComposeCredentials( @@ -93,7 +121,8 @@ std::unique_ptr<Credentials> CredentialsFactory::ComposeCredentials( // refcounts incremented. grpc_credentials* c_creds = grpc_composite_credentials_create( creds1->GetRawCreds(), creds2->GetRawCreds()); - std::unique_ptr<Credentials> cpp_creds(new Credentials(c_creds)); + std::unique_ptr<Credentials> cpp_creds( + c_creds == nullptr ? nullptr : new Credentials(c_creds)); return cpp_creds; } diff --git a/src/cpp/client/internal_stub.cc b/src/cpp/client/internal_stub.cc index ec88ba5e7e..51cb99d1b4 100644 --- a/src/cpp/client/internal_stub.cc +++ b/src/cpp/client/internal_stub.cc @@ -31,6 +31,6 @@ * */ -#include "src/cpp/client/internal_stub.h" +#include <grpc++/impl/internal_stub.h> namespace grpc {} // namespace grpc diff --git a/src/cpp/rpc_method.cc b/src/cpp/common/rpc_method.cc index 8067f42f85..c8b2ccb10e 100644 --- a/src/cpp/rpc_method.cc +++ b/src/cpp/common/rpc_method.cc @@ -31,6 +31,6 @@ * */ -#include "src/cpp/rpc_method.h" +#include <grpc++/impl/rpc_method.h> namespace grpc {} // namespace grpc diff --git a/src/cpp/proto/proto_utils.cc b/src/cpp/proto/proto_utils.cc index 255d1461a9..3b94dc3c07 100644 --- a/src/cpp/proto/proto_utils.cc +++ b/src/cpp/proto/proto_utils.cc @@ -40,7 +40,8 @@ namespace grpc { -bool SerializeProto(const google::protobuf::Message& msg, grpc_byte_buffer** bp) { +bool SerializeProto(const google::protobuf::Message& msg, + grpc_byte_buffer** bp) { grpc::string msg_str; bool success = msg.SerializeToString(&msg_str); if (success) { @@ -52,7 +53,8 @@ bool SerializeProto(const google::protobuf::Message& msg, grpc_byte_buffer** bp) return success; } -bool DeserializeProto(grpc_byte_buffer* buffer, google::protobuf::Message* msg) { +bool DeserializeProto(grpc_byte_buffer* buffer, + google::protobuf::Message* msg) { grpc::string msg_string; grpc_byte_buffer_reader* reader = grpc_byte_buffer_reader_create(buffer); gpr_slice slice; diff --git a/src/cpp/proto/proto_utils.h b/src/cpp/proto/proto_utils.h index 11471f1acb..ea472f9c51 100644 --- a/src/cpp/proto/proto_utils.h +++ b/src/cpp/proto/proto_utils.h @@ -46,7 +46,8 @@ namespace grpc { // Serialize the msg into a buffer created inside the function. The caller // should destroy the returned buffer when done with it. If serialization fails, // false is returned and buffer is left unchanged. -bool SerializeProto(const google::protobuf::Message& msg, grpc_byte_buffer** buffer); +bool SerializeProto(const google::protobuf::Message& msg, + grpc_byte_buffer** buffer); // The caller keeps ownership of buffer and msg. bool DeserializeProto(grpc_byte_buffer* buffer, google::protobuf::Message* msg); diff --git a/src/cpp/server/async_server_context.cc b/src/cpp/server/async_server_context.cc index f44678b569..298936dec9 100644 --- a/src/cpp/server/async_server_context.cc +++ b/src/cpp/server/async_server_context.cc @@ -48,8 +48,7 @@ AsyncServerContext::AsyncServerContext( host_(host), absolute_deadline_(absolute_deadline), request_(nullptr), - call_(call) { -} + call_(call) {} AsyncServerContext::~AsyncServerContext() { grpc_call_destroy(call_); } diff --git a/src/cpp/server/rpc_service_method.h b/src/cpp/server/rpc_service_method.h deleted file mode 100644 index f4fe01c06b..0000000000 --- a/src/cpp/server/rpc_service_method.h +++ /dev/null @@ -1,214 +0,0 @@ -/* - * - * 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 __GRPCPP_INTERNAL_SERVER_RPC_SERVICE_METHOD_H__ -#define __GRPCPP_INTERNAL_SERVER_RPC_SERVICE_METHOD_H__ - -#include <functional> -#include <map> -#include <memory> -#include <vector> - -#include "src/cpp/rpc_method.h" -#include <google/protobuf/message.h> -#include <grpc++/status.h> -#include <grpc++/stream.h> - -namespace grpc { -class ServerContext; -class StreamContextInterface; - -// TODO(rocking): we might need to split this file into multiple ones. - -// Base class for running an RPC handler. -class MethodHandler { - public: - virtual ~MethodHandler() {} - struct HandlerParameter { - HandlerParameter(ServerContext* context, const google::protobuf::Message* req, - google::protobuf::Message* resp) - : server_context(context), - request(req), - response(resp), - stream_context(nullptr) {} - HandlerParameter(ServerContext* context, const google::protobuf::Message* req, - google::protobuf::Message* resp, StreamContextInterface* stream) - : server_context(context), - request(req), - response(resp), - stream_context(stream) {} - ServerContext* server_context; - const google::protobuf::Message* request; - google::protobuf::Message* response; - StreamContextInterface* stream_context; - }; - virtual Status RunHandler(const HandlerParameter& param) = 0; -}; - -// A wrapper class of an application provided rpc method handler. -template <class ServiceType, class RequestType, class ResponseType> -class RpcMethodHandler : public MethodHandler { - public: - RpcMethodHandler( - std::function<Status(ServiceType*, ServerContext*, const RequestType*, - ResponseType*)> func, - ServiceType* service) - : func_(func), service_(service) {} - - Status RunHandler(const HandlerParameter& param) final { - // Invoke application function, cast proto messages to their actual types. - return func_(service_, param.server_context, - dynamic_cast<const RequestType*>(param.request), - dynamic_cast<ResponseType*>(param.response)); - } - - private: - // Application provided rpc handler function. - std::function<Status(ServiceType*, ServerContext*, const RequestType*, - ResponseType*)> func_; - // The class the above handler function lives in. - ServiceType* service_; -}; - -// A wrapper class of an application provided client streaming handler. -template <class ServiceType, class RequestType, class ResponseType> -class ClientStreamingHandler : public MethodHandler { - public: - ClientStreamingHandler( - std::function<Status(ServiceType*, ServerContext*, - ServerReader<RequestType>*, ResponseType*)> func, - ServiceType* service) - : func_(func), service_(service) {} - - Status RunHandler(const HandlerParameter& param) final { - ServerReader<RequestType> reader(param.stream_context); - return func_(service_, param.server_context, &reader, - dynamic_cast<ResponseType*>(param.response)); - } - - private: - std::function<Status(ServiceType*, ServerContext*, ServerReader<RequestType>*, - ResponseType*)> func_; - ServiceType* service_; -}; - -// A wrapper class of an application provided server streaming handler. -template <class ServiceType, class RequestType, class ResponseType> -class ServerStreamingHandler : public MethodHandler { - public: - ServerStreamingHandler( - std::function<Status(ServiceType*, ServerContext*, const RequestType*, - ServerWriter<ResponseType>*)> func, - ServiceType* service) - : func_(func), service_(service) {} - - Status RunHandler(const HandlerParameter& param) final { - ServerWriter<ResponseType> writer(param.stream_context); - return func_(service_, param.server_context, - dynamic_cast<const RequestType*>(param.request), &writer); - } - - private: - std::function<Status(ServiceType*, ServerContext*, const RequestType*, - ServerWriter<ResponseType>*)> func_; - ServiceType* service_; -}; - -// A wrapper class of an application provided bidi-streaming handler. -template <class ServiceType, class RequestType, class ResponseType> -class BidiStreamingHandler : public MethodHandler { - public: - BidiStreamingHandler( - std::function<Status(ServiceType*, ServerContext*, - ServerReaderWriter<ResponseType, RequestType>*)> - func, - ServiceType* service) - : func_(func), service_(service) {} - - Status RunHandler(const HandlerParameter& param) final { - ServerReaderWriter<ResponseType, RequestType> stream(param.stream_context); - return func_(service_, param.server_context, &stream); - } - - private: - std::function<Status(ServiceType*, ServerContext*, - ServerReaderWriter<ResponseType, RequestType>*)> func_; - ServiceType* service_; -}; - -// Server side rpc method class -class RpcServiceMethod : public RpcMethod { - public: - // Takes ownership of the handler and two prototype objects. - RpcServiceMethod(const char* name, RpcMethod::RpcType type, - MethodHandler* handler, google::protobuf::Message* request_prototype, - google::protobuf::Message* response_prototype) - : RpcMethod(name, type), - handler_(handler), - request_prototype_(request_prototype), - response_prototype_(response_prototype) {} - - MethodHandler* handler() { return handler_.get(); } - - google::protobuf::Message* AllocateRequestProto() { return request_prototype_->New(); } - google::protobuf::Message* AllocateResponseProto() { - return response_prototype_->New(); - } - - private: - std::unique_ptr<MethodHandler> handler_; - std::unique_ptr<google::protobuf::Message> request_prototype_; - std::unique_ptr<google::protobuf::Message> response_prototype_; -}; - -// This class contains all the method information for an rpc service. It is -// used for registering a service on a grpc server. -class RpcService { - public: - // Takes ownership. - void AddMethod(RpcServiceMethod* method) { - methods_.push_back(std::unique_ptr<RpcServiceMethod>(method)); - } - - RpcServiceMethod* GetMethod(int i) { - return methods_[i].get(); - } - int GetMethodCount() const { return methods_.size(); } - - private: - std::vector<std::unique_ptr<RpcServiceMethod>> methods_; -}; - -} // namespace grpc - -#endif // __GRPCPP_INTERNAL_SERVER_RPC_SERVICE_METHOD_H__ diff --git a/src/cpp/server/server.cc b/src/cpp/server/server.cc index 2130befa7d..d85748eea4 100644 --- a/src/cpp/server/server.cc +++ b/src/cpp/server/server.cc @@ -37,11 +37,11 @@ #include <grpc/grpc.h> #include <grpc/grpc_security.h> #include <grpc/support/log.h> -#include "src/cpp/server/rpc_service_method.h" #include "src/cpp/server/server_rpc_handler.h" #include "src/cpp/server/thread_pool.h" #include <grpc++/async_server_context.h> #include <grpc++/completion_queue.h> +#include <grpc++/impl/rpc_service_method.h> #include <grpc++/server_credentials.h> namespace grpc { diff --git a/src/cpp/server/server_context_impl.cc b/src/cpp/server/server_context_impl.cc index 13f2a3ae1a..467cc80e05 100644 --- a/src/cpp/server/server_context_impl.cc +++ b/src/cpp/server/server_context_impl.cc @@ -33,6 +33,4 @@ #include "src/cpp/server/server_context_impl.h" -namespace grpc { - -} // namespace grpc +namespace grpc {} // namespace grpc diff --git a/src/cpp/server/server_credentials.cc b/src/cpp/server/server_credentials.cc index f9ca1622ba..5d899b1cd9 100644 --- a/src/cpp/server/server_credentials.cc +++ b/src/cpp/server/server_credentials.cc @@ -31,7 +31,6 @@ * */ - #include <grpc/grpc_security.h> #include <grpc++/server_credentials.h> diff --git a/src/cpp/server/server_rpc_handler.cc b/src/cpp/server/server_rpc_handler.cc index 3954f04f97..42f8b755b6 100644 --- a/src/cpp/server/server_rpc_handler.cc +++ b/src/cpp/server/server_rpc_handler.cc @@ -34,10 +34,10 @@ #include "src/cpp/server/server_rpc_handler.h" #include <grpc/support/log.h> -#include "src/cpp/server/rpc_service_method.h" #include "src/cpp/server/server_context_impl.h" #include "src/cpp/stream/stream_context.h" #include <grpc++/async_server_context.h> +#include <grpc++/impl/rpc_service_method.h> namespace grpc { @@ -60,8 +60,10 @@ void ServerRpcHandler::StartRpc() { async_server_context_->Accept(cq_.cq()); // Allocate request and response. - std::unique_ptr<google::protobuf::Message> request(method_->AllocateRequestProto()); - std::unique_ptr<google::protobuf::Message> response(method_->AllocateResponseProto()); + std::unique_ptr<google::protobuf::Message> request( + method_->AllocateRequestProto()); + std::unique_ptr<google::protobuf::Message> response( + method_->AllocateResponseProto()); // Read request async_server_context_->StartRead(request.get()); @@ -86,8 +88,10 @@ void ServerRpcHandler::StartRpc() { } else { // Allocate request and response. // TODO(yangg) maybe not allocate both when not needed? - std::unique_ptr<google::protobuf::Message> request(method_->AllocateRequestProto()); - std::unique_ptr<google::protobuf::Message> response(method_->AllocateResponseProto()); + std::unique_ptr<google::protobuf::Message> request( + method_->AllocateRequestProto()); + std::unique_ptr<google::protobuf::Message> response( + method_->AllocateResponseProto()); StreamContext stream_context(*method_, async_server_context_->call(), cq_.cq(), request.get(), response.get()); diff --git a/src/cpp/stream/stream_context.cc b/src/cpp/stream/stream_context.cc index 22b7e7d494..7936a30dfd 100644 --- a/src/cpp/stream/stream_context.cc +++ b/src/cpp/stream/stream_context.cc @@ -34,11 +34,11 @@ #include "src/cpp/stream/stream_context.h" #include <grpc/support/log.h> -#include "src/cpp/rpc_method.h" #include "src/cpp/proto/proto_utils.h" #include "src/cpp/util/time.h" #include <grpc++/client_context.h> #include <grpc++/config.h> +#include <grpc++/impl/rpc_method.h> #include <google/protobuf/message.h> namespace grpc { @@ -61,7 +61,8 @@ StreamContext::StreamContext(const RpcMethod& method, ClientContext* context, // Server only ctor StreamContext::StreamContext(const RpcMethod& method, grpc_call* call, grpc_completion_queue* cq, - google::protobuf::Message* request, google::protobuf::Message* result) + google::protobuf::Message* request, + google::protobuf::Message* result) : is_client_(false), method_(&method), call_(call), @@ -85,6 +86,10 @@ void StreamContext::Start(bool buffered) { GPR_ASSERT(GRPC_CALL_OK == error); grpc_event* invoke_ev = grpc_completion_queue_pluck(cq(), invoke_tag(), gpr_inf_future); + if (invoke_ev->data.invoke_accepted != GRPC_OP_OK) { + peer_halfclosed_ = true; + self_halfclosed_ = true; + } grpc_event_finish(invoke_ev); } else { // TODO(yangg) metadata needs to be added before accept diff --git a/src/cpp/stream/stream_context.h b/src/cpp/stream/stream_context.h index 6c31095042..f70fe6daa3 100644 --- a/src/cpp/stream/stream_context.h +++ b/src/cpp/stream/stream_context.h @@ -51,7 +51,8 @@ class RpcMethod; class StreamContext : public StreamContextInterface { public: StreamContext(const RpcMethod& method, ClientContext* context, - const google::protobuf::Message* request, google::protobuf::Message* result); + const google::protobuf::Message* request, + google::protobuf::Message* result); StreamContext(const RpcMethod& method, grpc_call* call, grpc_completion_queue* cq, google::protobuf::Message* request, google::protobuf::Message* result); @@ -81,11 +82,11 @@ class StreamContext : public StreamContextInterface { grpc_completion_queue* cq() { return cq_; } bool is_client_; - const RpcMethod* method_; // not owned - grpc_call* call_; // not owned - grpc_completion_queue* cq_; // not owned - google::protobuf::Message* request_; // first request, not owned - google::protobuf::Message* result_; // last response, not owned + const RpcMethod* method_; // not owned + grpc_call* call_; // not owned + grpc_completion_queue* cq_; // not owned + google::protobuf::Message* request_; // first request, not owned + google::protobuf::Message* result_; // last response, not owned bool peer_halfclosed_; bool self_halfclosed_; diff --git a/src/cpp/util/status.cc b/src/cpp/util/status.cc index 66be26da07..e7ca41b752 100644 --- a/src/cpp/util/status.cc +++ b/src/cpp/util/status.cc @@ -31,7 +31,6 @@ * */ - #include <grpc++/status.h> namespace grpc { diff --git a/src/node/README.md b/src/node/README.md new file mode 100644 index 0000000000..55329d8cb2 --- /dev/null +++ b/src/node/README.md @@ -0,0 +1,12 @@ +# Node.js GRPC extension + +The package is built with + + node-gyp configure + node-gyp build + +or, for brevity + + node-gyp configure build + +The tests can be run with `npm test` on a dev install.
\ No newline at end of file diff --git a/src/node/binding.gyp b/src/node/binding.gyp new file mode 100644 index 0000000000..4a1fd7aaf0 --- /dev/null +++ b/src/node/binding.gyp @@ -0,0 +1,46 @@ +{ + "targets" : [ + { + 'include_dirs': [ + "<!(node -e \"require('nan')\")" + ], + 'cxxflags': [ + '-Wall', + '-pthread', + '-pedantic', + '-g', + '-zdefs' + '-Werror', + ], + 'ldflags': [ + '-g', + '-L/usr/local/google/home/mlumish/grpc_dev/lib' + ], + 'link_settings': { + 'libraries': [ + '-lgrpc', + '-levent', + '-levent_pthreads', + '-levent_core', + '-lrt', + '-lgpr', + '-lpthread' + ], + }, + "target_name": "grpc", + "sources": [ + "byte_buffer.cc", + "call.cc", + "channel.cc", + "completion_queue_async_worker.cc", + "credentials.cc", + "event.cc", + "node_grpc.cc", + "server.cc", + "server_credentials.cc", + "tag.cc", + "timeval.cc" + ] + } + ] +} diff --git a/src/node/byte_buffer.cc b/src/node/byte_buffer.cc new file mode 100644 index 0000000000..142951475a --- /dev/null +++ b/src/node/byte_buffer.cc @@ -0,0 +1,79 @@ +/* + * + * 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 <string.h> +#include <malloc.h> + +#include <node.h> +#include <nan.h> +#include "grpc/grpc.h" +#include "grpc/support/slice.h" + +namespace grpc { +namespace node { + +#include "byte_buffer.h" + +using ::node::Buffer; +using v8::Handle; +using v8::Value; + +grpc_byte_buffer *BufferToByteBuffer(Handle<Value> buffer) { + NanScope(); + int length = Buffer::Length(buffer); + char *data = Buffer::Data(buffer); + gpr_slice slice = gpr_slice_malloc(length); + memcpy(GPR_SLICE_START_PTR(slice), data, length); + grpc_byte_buffer *byte_buffer(grpc_byte_buffer_create(&slice, 1)); + gpr_slice_unref(slice); + return byte_buffer; +} + +Handle<Value> ByteBufferToBuffer(grpc_byte_buffer *buffer) { + NanEscapableScope(); + if (buffer == NULL) { + NanReturnNull(); + } + size_t length = grpc_byte_buffer_length(buffer); + char *result = reinterpret_cast<char *>(calloc(length, sizeof(char))); + size_t offset = 0; + grpc_byte_buffer_reader *reader = grpc_byte_buffer_reader_create(buffer); + gpr_slice next; + while (grpc_byte_buffer_reader_next(reader, &next) != 0) { + memcpy(result + offset, GPR_SLICE_START_PTR(next), GPR_SLICE_LENGTH(next)); + offset += GPR_SLICE_LENGTH(next); + } + return NanEscapeScope(NanNewBufferHandle(result, length)); +} +} // namespace node +} // namespace grpc diff --git a/src/node/byte_buffer.h b/src/node/byte_buffer.h new file mode 100644 index 0000000000..ee2b4c0d15 --- /dev/null +++ b/src/node/byte_buffer.h @@ -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. + * + */ + +#ifndef NET_GRPC_NODE_BYTE_BUFFER_H_ +#define NET_GRPC_NODE_BYTE_BUFFER_H_ + +#include <string.h> + +#include <node.h> +#include <nan.h> +#include "grpc/grpc.h" + +namespace grpc { +namespace node { + +/* Convert a Node.js Buffer to grpc_byte_buffer. Requires that + ::node::Buffer::HasInstance(buffer) */ +grpc_byte_buffer *BufferToByteBuffer(v8::Handle<v8::Value> buffer); + +/* Convert a grpc_byte_buffer to a Node.js Buffer */ +v8::Handle<v8::Value> ByteBufferToBuffer(grpc_byte_buffer *buffer); + +} // namespace node +} // namespace grpc + +#endif // NET_GRPC_NODE_BYTE_BUFFER_H_ diff --git a/src/node/call.cc b/src/node/call.cc new file mode 100644 index 0000000000..b8ee1786a6 --- /dev/null +++ b/src/node/call.cc @@ -0,0 +1,392 @@ +/* + * + * 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 <node.h> + +#include "grpc/grpc.h" +#include "grpc/support/time.h" +#include "byte_buffer.h" +#include "call.h" +#include "channel.h" +#include "completion_queue_async_worker.h" +#include "timeval.h" +#include "tag.h" + +namespace grpc { +namespace node { + +using ::node::Buffer; +using v8::Arguments; +using v8::Array; +using v8::Exception; +using v8::External; +using v8::Function; +using v8::FunctionTemplate; +using v8::Handle; +using v8::HandleScope; +using v8::Integer; +using v8::Local; +using v8::Number; +using v8::Object; +using v8::ObjectTemplate; +using v8::Persistent; +using v8::Uint32; +using v8::String; +using v8::Value; + +Persistent<Function> Call::constructor; +Persistent<FunctionTemplate> Call::fun_tpl; + +Call::Call(grpc_call *call) : wrapped_call(call) {} + +Call::~Call() { grpc_call_destroy(wrapped_call); } + +void Call::Init(Handle<Object> exports) { + NanScope(); + Local<FunctionTemplate> tpl = FunctionTemplate::New(New); + tpl->SetClassName(NanNew("Call")); + tpl->InstanceTemplate()->SetInternalFieldCount(1); + NanSetPrototypeTemplate(tpl, "addMetadata", + FunctionTemplate::New(AddMetadata)->GetFunction()); + NanSetPrototypeTemplate(tpl, "startInvoke", + FunctionTemplate::New(StartInvoke)->GetFunction()); + NanSetPrototypeTemplate(tpl, "serverAccept", + FunctionTemplate::New(ServerAccept)->GetFunction()); + NanSetPrototypeTemplate( + tpl, "serverEndInitialMetadata", + FunctionTemplate::New(ServerEndInitialMetadata)->GetFunction()); + NanSetPrototypeTemplate(tpl, "cancel", + FunctionTemplate::New(Cancel)->GetFunction()); + NanSetPrototypeTemplate(tpl, "startWrite", + FunctionTemplate::New(StartWrite)->GetFunction()); + NanSetPrototypeTemplate( + tpl, "startWriteStatus", + FunctionTemplate::New(StartWriteStatus)->GetFunction()); + NanSetPrototypeTemplate(tpl, "writesDone", + FunctionTemplate::New(WritesDone)->GetFunction()); + NanSetPrototypeTemplate(tpl, "startReadMetadata", + FunctionTemplate::New(WritesDone)->GetFunction()); + NanSetPrototypeTemplate(tpl, "startRead", + FunctionTemplate::New(StartRead)->GetFunction()); + NanAssignPersistent(fun_tpl, tpl); + NanAssignPersistent(constructor, tpl->GetFunction()); + constructor->Set(NanNew("WRITE_BUFFER_HINT"), + NanNew<Uint32, uint32_t>(GRPC_WRITE_BUFFER_HINT)); + constructor->Set(NanNew("WRITE_NO_COMPRESS"), + NanNew<Uint32, uint32_t>(GRPC_WRITE_NO_COMPRESS)); + exports->Set(String::NewSymbol("Call"), constructor); +} + +bool Call::HasInstance(Handle<Value> val) { + NanScope(); + return NanHasInstance(fun_tpl, val); +} + +Handle<Value> Call::WrapStruct(grpc_call *call) { + NanEscapableScope(); + if (call == NULL) { + return NanEscapeScope(NanNull()); + } + const int argc = 1; + Handle<Value> argv[argc] = {External::New(reinterpret_cast<void *>(call))}; + return NanEscapeScope(constructor->NewInstance(argc, argv)); +} + +NAN_METHOD(Call::New) { + NanScope(); + + if (args.IsConstructCall()) { + Call *call; + if (args[0]->IsExternal()) { + // This option is used for wrapping an existing call + grpc_call *call_value = + reinterpret_cast<grpc_call *>(External::Unwrap(args[0])); + call = new Call(call_value); + } else { + if (!Channel::HasInstance(args[0])) { + return NanThrowTypeError("Call's first argument must be a Channel"); + } + if (!args[1]->IsString()) { + return NanThrowTypeError("Call's second argument must be a string"); + } + if (!(args[2]->IsNumber() || args[2]->IsDate())) { + return NanThrowTypeError( + "Call's third argument must be a date or a number"); + } + Handle<Object> channel_object = args[0]->ToObject(); + Channel *channel = ObjectWrap::Unwrap<Channel>(channel_object); + if (channel->GetWrappedChannel() == NULL) { + return NanThrowError("Call cannot be created from a closed channel"); + } + NanUtf8String method(args[1]); + double deadline = args[2]->NumberValue(); + grpc_channel *wrapped_channel = channel->GetWrappedChannel(); + grpc_call *wrapped_call = + grpc_channel_create_call(wrapped_channel, *method, channel->GetHost(), + MillisecondsToTimespec(deadline)); + call = new Call(wrapped_call); + args.This()->SetHiddenValue(String::NewSymbol("channel_"), + channel_object); + } + call->Wrap(args.This()); + NanReturnValue(args.This()); + } else { + const int argc = 4; + Local<Value> argv[argc] = {args[0], args[1], args[2], args[3]}; + NanReturnValue(constructor->NewInstance(argc, argv)); + } +} + +NAN_METHOD(Call::AddMetadata) { + NanScope(); + if (!HasInstance(args.This())) { + return NanThrowTypeError("addMetadata can only be called on Call objects"); + } + Call *call = ObjectWrap::Unwrap<Call>(args.This()); + for (int i = 0; !args[i]->IsUndefined(); i++) { + if (!args[i]->IsObject()) { + return NanThrowTypeError( + "addMetadata arguments must be objects with key and value"); + } + Handle<Object> item = args[i]->ToObject(); + Handle<Value> key = item->Get(NanNew("key")); + if (!key->IsString()) { + return NanThrowTypeError( + "objects passed to addMetadata must have key->string"); + } + Handle<Value> value = item->Get(NanNew("value")); + if (!Buffer::HasInstance(value)) { + return NanThrowTypeError( + "objects passed to addMetadata must have value->Buffer"); + } + grpc_metadata metadata; + NanUtf8String utf8_key(key); + metadata.key = *utf8_key; + metadata.value = Buffer::Data(value); + metadata.value_length = Buffer::Length(value); + grpc_call_error error = + grpc_call_add_metadata(call->wrapped_call, &metadata, 0); + if (error != GRPC_CALL_OK) { + return NanThrowError("addMetadata failed", error); + } + } + NanReturnUndefined(); +} + +NAN_METHOD(Call::StartInvoke) { + NanScope(); + if (!HasInstance(args.This())) { + return NanThrowTypeError("startInvoke can only be called on Call objects"); + } + if (!args[0]->IsFunction()) { + return NanThrowTypeError("StartInvoke's first argument must be a function"); + } + if (!args[1]->IsFunction()) { + return NanThrowTypeError( + "StartInvoke's second argument must be a function"); + } + if (!args[2]->IsFunction()) { + return NanThrowTypeError("StartInvoke's third argument must be a function"); + } + if (!args[3]->IsUint32()) { + return NanThrowTypeError( + "StartInvoke's fourth argument must be integer flags"); + } + Call *call = ObjectWrap::Unwrap<Call>(args.This()); + unsigned int flags = args[3]->Uint32Value(); + grpc_call_error error = grpc_call_start_invoke( + call->wrapped_call, CompletionQueueAsyncWorker::GetQueue(), + CreateTag(args[0], args.This()), CreateTag(args[1], args.This()), + CreateTag(args[2], args.This()), flags); + if (error == GRPC_CALL_OK) { + CompletionQueueAsyncWorker::Next(); + CompletionQueueAsyncWorker::Next(); + CompletionQueueAsyncWorker::Next(); + } else { + return NanThrowError("startInvoke failed", error); + } + NanReturnUndefined(); +} + +NAN_METHOD(Call::ServerAccept) { + NanScope(); + if (!HasInstance(args.This())) { + return NanThrowTypeError("accept can only be called on Call objects"); + } + if (!args[0]->IsFunction()) { + return NanThrowTypeError("accept's first argument must be a function"); + } + Call *call = ObjectWrap::Unwrap<Call>(args.This()); + grpc_call_error error = grpc_call_server_accept( + call->wrapped_call, CompletionQueueAsyncWorker::GetQueue(), + CreateTag(args[0], args.This())); + if (error == GRPC_CALL_OK) { + CompletionQueueAsyncWorker::Next(); + } else { + return NanThrowError("serverAccept failed", error); + } + NanReturnUndefined(); +} + +NAN_METHOD(Call::ServerEndInitialMetadata) { + NanScope(); + if (!HasInstance(args.This())) { + return NanThrowTypeError( + "serverEndInitialMetadata can only be called on Call objects"); + } + if (!args[0]->IsUint32()) { + return NanThrowTypeError( + "serverEndInitialMetadata's second argument must be integer flags"); + } + Call *call = ObjectWrap::Unwrap<Call>(args.This()); + unsigned int flags = args[1]->Uint32Value(); + grpc_call_error error = + grpc_call_server_end_initial_metadata(call->wrapped_call, flags); + if (error != GRPC_CALL_OK) { + return NanThrowError("serverEndInitialMetadata failed", error); + } + NanReturnUndefined(); +} + +NAN_METHOD(Call::Cancel) { + NanScope(); + if (!HasInstance(args.This())) { + return NanThrowTypeError("startInvoke can only be called on Call objects"); + } + Call *call = ObjectWrap::Unwrap<Call>(args.This()); + grpc_call_error error = grpc_call_cancel(call->wrapped_call); + if (error != GRPC_CALL_OK) { + return NanThrowError("cancel failed", error); + } + NanReturnUndefined(); +} + +NAN_METHOD(Call::StartWrite) { + NanScope(); + if (!HasInstance(args.This())) { + return NanThrowTypeError("startWrite can only be called on Call objects"); + } + if (!Buffer::HasInstance(args[0])) { + return NanThrowTypeError("startWrite's first argument must be a Buffer"); + } + if (!args[1]->IsFunction()) { + return NanThrowTypeError("startWrite's second argument must be a function"); + } + if (!args[2]->IsUint32()) { + return NanThrowTypeError( + "startWrite's third argument must be integer flags"); + } + Call *call = ObjectWrap::Unwrap<Call>(args.This()); + grpc_byte_buffer *buffer = BufferToByteBuffer(args[0]); + unsigned int flags = args[2]->Uint32Value(); + grpc_call_error error = grpc_call_start_write( + call->wrapped_call, buffer, CreateTag(args[1], args.This()), flags); + if (error == GRPC_CALL_OK) { + CompletionQueueAsyncWorker::Next(); + } else { + return NanThrowError("startWrite failed", error); + } + NanReturnUndefined(); +} + +NAN_METHOD(Call::StartWriteStatus) { + NanScope(); + if (!HasInstance(args.This())) { + return NanThrowTypeError( + "startWriteStatus can only be called on Call objects"); + } + if (!args[0]->IsUint32()) { + return NanThrowTypeError( + "startWriteStatus's first argument must be a status code"); + } + if (!args[1]->IsString()) { + return NanThrowTypeError( + "startWriteStatus's second argument must be a string"); + } + if (!args[2]->IsFunction()) { + return NanThrowTypeError( + "startWriteStatus's third argument must be a function"); + } + Call *call = ObjectWrap::Unwrap<Call>(args.This()); + NanUtf8String details(args[1]); + grpc_call_error error = grpc_call_start_write_status( + call->wrapped_call, (grpc_status_code)args[0]->Uint32Value(), *details, + CreateTag(args[2], args.This())); + if (error == GRPC_CALL_OK) { + CompletionQueueAsyncWorker::Next(); + } else { + return NanThrowError("startWriteStatus failed", error); + } + NanReturnUndefined(); +} + +NAN_METHOD(Call::WritesDone) { + NanScope(); + if (!HasInstance(args.This())) { + return NanThrowTypeError("writesDone can only be called on Call objects"); + } + if (!args[0]->IsFunction()) { + return NanThrowTypeError("writesDone's first argument must be a function"); + } + Call *call = ObjectWrap::Unwrap<Call>(args.This()); + grpc_call_error error = grpc_call_writes_done( + call->wrapped_call, CreateTag(args[0], args.This())); + if (error == GRPC_CALL_OK) { + CompletionQueueAsyncWorker::Next(); + } else { + return NanThrowError("writesDone failed", error); + } + NanReturnUndefined(); +} + +NAN_METHOD(Call::StartRead) { + NanScope(); + if (!HasInstance(args.This())) { + return NanThrowTypeError("startRead can only be called on Call objects"); + } + if (!args[0]->IsFunction()) { + return NanThrowTypeError("startRead's first argument must be a function"); + } + Call *call = ObjectWrap::Unwrap<Call>(args.This()); + grpc_call_error error = + grpc_call_start_read(call->wrapped_call, CreateTag(args[0], args.This())); + if (error == GRPC_CALL_OK) { + CompletionQueueAsyncWorker::Next(); + } else { + return NanThrowError("startRead failed", error); + } + NanReturnUndefined(); +} + +} // namespace node +} // namespace grpc diff --git a/src/node/call.h b/src/node/call.h new file mode 100644 index 0000000000..55a6fc65b8 --- /dev/null +++ b/src/node/call.h @@ -0,0 +1,82 @@ +/* + * + * 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 NET_GRPC_NODE_CALL_H_ +#define NET_GRPC_NODE_CALL_H_ + +#include <node.h> +#include <nan.h> +#include "grpc/grpc.h" + +#include "channel.h" + +namespace grpc { +namespace node { + +/* Wrapper class for grpc_call structs. */ +class Call : public ::node::ObjectWrap { + public: + static void Init(v8::Handle<v8::Object> exports); + static bool HasInstance(v8::Handle<v8::Value> val); + /* Wrap a grpc_call struct in a javascript object */ + static v8::Handle<v8::Value> WrapStruct(grpc_call *call); + + private: + explicit Call(grpc_call *call); + ~Call(); + + // Prevent copying + Call(const Call &); + Call &operator=(const Call &); + + static NAN_METHOD(New); + static NAN_METHOD(AddMetadata); + static NAN_METHOD(StartInvoke); + static NAN_METHOD(ServerAccept); + static NAN_METHOD(ServerEndInitialMetadata); + static NAN_METHOD(Cancel); + static NAN_METHOD(StartWrite); + static NAN_METHOD(StartWriteStatus); + static NAN_METHOD(WritesDone); + static NAN_METHOD(StartRead); + static v8::Persistent<v8::Function> constructor; + // Used for typechecking instances of this javascript class + static v8::Persistent<v8::FunctionTemplate> fun_tpl; + + grpc_call *wrapped_call; +}; + +} // namespace node +} // namespace grpc + +#endif // NET_GRPC_NODE_CALL_H_ diff --git a/src/node/channel.cc b/src/node/channel.cc new file mode 100644 index 0000000000..9087d6f919 --- /dev/null +++ b/src/node/channel.cc @@ -0,0 +1,182 @@ +/* + * + * 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 <malloc.h> + +#include <vector> + +#include <node.h> +#include <nan.h> +#include "grpc/grpc.h" +#include "grpc/grpc_security.h" +#include "channel.h" +#include "credentials.h" + +namespace grpc { +namespace node { + +using v8::Arguments; +using v8::Array; +using v8::Exception; +using v8::Function; +using v8::FunctionTemplate; +using v8::Handle; +using v8::HandleScope; +using v8::Integer; +using v8::Local; +using v8::Object; +using v8::Persistent; +using v8::String; +using v8::Value; + +Persistent<Function> Channel::constructor; +Persistent<FunctionTemplate> Channel::fun_tpl; + +Channel::Channel(grpc_channel *channel, NanUtf8String *host) + : wrapped_channel(channel), host(host) {} + +Channel::~Channel() { + if (wrapped_channel != NULL) { + grpc_channel_destroy(wrapped_channel); + } + delete host; +} + +void Channel::Init(Handle<Object> exports) { + NanScope(); + Local<FunctionTemplate> tpl = FunctionTemplate::New(New); + tpl->SetClassName(NanNew("Channel")); + tpl->InstanceTemplate()->SetInternalFieldCount(1); + NanSetPrototypeTemplate(tpl, "close", + FunctionTemplate::New(Close)->GetFunction()); + NanAssignPersistent(fun_tpl, tpl); + NanAssignPersistent(constructor, tpl->GetFunction()); + exports->Set(NanNew("Channel"), constructor); +} + +bool Channel::HasInstance(Handle<Value> val) { + NanScope(); + return NanHasInstance(fun_tpl, val); +} + +grpc_channel *Channel::GetWrappedChannel() { return this->wrapped_channel; } + +char *Channel::GetHost() { return **this->host; } + +NAN_METHOD(Channel::New) { + NanScope(); + + if (args.IsConstructCall()) { + if (!args[0]->IsString()) { + return NanThrowTypeError("Channel expects a string and an object"); + } + grpc_channel *wrapped_channel; + // Owned by the Channel object + NanUtf8String *host = new NanUtf8String(args[0]); + if (args[1]->IsUndefined()) { + wrapped_channel = grpc_channel_create(**host, NULL); + } else if (args[1]->IsObject()) { + grpc_credentials *creds = NULL; + Handle<Object> args_hash(args[1]->ToObject()->Clone()); + if (args_hash->HasOwnProperty(NanNew("credentials"))) { + Handle<Value> creds_value = args_hash->Get(NanNew("credentials")); + if (!Credentials::HasInstance(creds_value)) { + return NanThrowTypeError( + "credentials arg must be a Credentials object"); + } + Credentials *creds_object = + ObjectWrap::Unwrap<Credentials>(creds_value->ToObject()); + creds = creds_object->GetWrappedCredentials(); + args_hash->Delete(NanNew("credentials")); + } + Handle<Array> keys(args_hash->GetOwnPropertyNames()); + grpc_channel_args channel_args; + channel_args.num_args = keys->Length(); + channel_args.args = reinterpret_cast<grpc_arg *>( + calloc(channel_args.num_args, sizeof(grpc_arg))); + /* These are used to keep all strings until then end of the block, then + destroy them */ + std::vector<NanUtf8String *> key_strings(keys->Length()); + std::vector<NanUtf8String *> value_strings(keys->Length()); + for (unsigned int i = 0; i < channel_args.num_args; i++) { + Handle<String> current_key(keys->Get(i)->ToString()); + Handle<Value> current_value(args_hash->Get(current_key)); + key_strings[i] = new NanUtf8String(current_key); + channel_args.args[i].key = **key_strings[i]; + if (current_value->IsInt32()) { + channel_args.args[i].type = GRPC_ARG_INTEGER; + channel_args.args[i].value.integer = current_value->Int32Value(); + } else if (current_value->IsString()) { + channel_args.args[i].type = GRPC_ARG_STRING; + value_strings[i] = new NanUtf8String(current_value); + channel_args.args[i].value.string = **value_strings[i]; + } else { + free(channel_args.args); + return NanThrowTypeError("Arg values must be strings"); + } + } + if (creds == NULL) { + wrapped_channel = grpc_channel_create(**host, &channel_args); + } else { + wrapped_channel = + grpc_secure_channel_create(creds, **host, &channel_args); + } + free(channel_args.args); + } else { + return NanThrowTypeError("Channel expects a string and an object"); + } + Channel *channel = new Channel(wrapped_channel, host); + channel->Wrap(args.This()); + NanReturnValue(args.This()); + } else { + const int argc = 2; + Local<Value> argv[argc] = {args[0], args[1]}; + NanReturnValue(constructor->NewInstance(argc, argv)); + } +} + +NAN_METHOD(Channel::Close) { + NanScope(); + if (!HasInstance(args.This())) { + return NanThrowTypeError("close can only be called on Channel objects"); + } + Channel *channel = ObjectWrap::Unwrap<Channel>(args.This()); + if (channel->wrapped_channel != NULL) { + grpc_channel_destroy(channel->wrapped_channel); + channel->wrapped_channel = NULL; + } + NanReturnUndefined(); +} + +} // namespace node +} // namespace grpc diff --git a/src/node/channel.h b/src/node/channel.h new file mode 100644 index 0000000000..140cbf201a --- /dev/null +++ b/src/node/channel.h @@ -0,0 +1,79 @@ +/* + * + * 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 NET_GRPC_NODE_CHANNEL_H_ +#define NET_GRPC_NODE_CHANNEL_H_ + +#include <node.h> +#include <nan.h> +#include "grpc/grpc.h" + +namespace grpc { +namespace node { + +/* Wrapper class for grpc_channel structs */ +class Channel : public ::node::ObjectWrap { + public: + static void Init(v8::Handle<v8::Object> exports); + static bool HasInstance(v8::Handle<v8::Value> val); + /* This is used to typecheck javascript objects before converting them to + this type */ + static v8::Persistent<v8::Value> prototype; + + /* Returns the grpc_channel struct that this object wraps */ + grpc_channel *GetWrappedChannel(); + + /* Return the hostname that this channel connects to */ + char *GetHost(); + + private: + explicit Channel(grpc_channel *channel, NanUtf8String *host); + ~Channel(); + + // Prevent copying + Channel(const Channel &); + Channel &operator=(const Channel &); + + static NAN_METHOD(New); + static NAN_METHOD(Close); + static v8::Persistent<v8::Function> constructor; + static v8::Persistent<v8::FunctionTemplate> fun_tpl; + + grpc_channel *wrapped_channel; + NanUtf8String *host; +}; + +} // namespace node +} // namespace grpc + +#endif // NET_GRPC_NODE_CHANNEL_H_ diff --git a/src/node/client.js b/src/node/client.js new file mode 100644 index 0000000000..edaa115d0f --- /dev/null +++ b/src/node/client.js @@ -0,0 +1,209 @@ +/* + * + * 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. + * + */ + +var grpc = require('bindings')('grpc.node'); + +var common = require('./common'); + +var Duplex = require('stream').Duplex; +var util = require('util'); + +util.inherits(GrpcClientStream, Duplex); + +/** + * Class for representing a gRPC client side stream as a Node stream. Extends + * from stream.Duplex. + * @constructor + * @param {grpc.Call} call Call object to proxy + * @param {object} options Stream options + */ +function GrpcClientStream(call, options) { + Duplex.call(this, options); + var self = this; + // Indicates that we can start reading and have not received a null read + var can_read = false; + // Indicates that a read is currently pending + var reading = false; + // Indicates that we can call startWrite + var can_write = false; + // Indicates that a write is currently pending + var writing = false; + this._call = call; + /** + * Callback to handle receiving a READ event. Pushes the data from that event + * onto the read queue and starts reading again if applicable. + * @param {grpc.Event} event The READ event object + */ + function readCallback(event) { + var data = event.data; + if (self.push(data)) { + if (data == null) { + // Disable starting to read after null read was received + can_read = false; + reading = false; + } else { + call.startRead(readCallback); + } + } else { + // Indicate that reading can be resumed by calling startReading + reading = false; + } + }; + /** + * Initiate a read, which continues until self.push returns false (indicating + * that reading should be paused) or data is null (indicating that there is no + * more data to read). + */ + function startReading() { + call.startRead(readCallback); + } + // TODO(mlumish): possibly change queue implementation due to shift slowness + var write_queue = []; + /** + * Write the next chunk of data in the write queue if there is one. Otherwise + * indicate that there is no pending write. When the write succeeds, this + * function is called again. + */ + function writeNext() { + if (write_queue.length > 0) { + writing = true; + var next = write_queue.shift(); + var writeCallback = function(event) { + next.callback(); + writeNext(); + }; + call.startWrite(next.chunk, writeCallback, 0); + } else { + writing = false; + } + } + call.startInvoke(function(event) { + can_read = true; + can_write = true; + startReading(); + writeNext(); + }, function(event) { + self.emit('metadata', event.data); + }, function(event) { + self.emit('status', event.data); + }, 0); + this.on('finish', function() { + call.writesDone(function() {}); + }); + /** + * Indicate that reads should start, and start them if the INVOKE_ACCEPTED + * event has been received. + */ + this._enableRead = function() { + if (!reading) { + reading = true; + if (can_read) { + startReading(); + } + } + }; + /** + * Push the chunk onto the write queue, and write from the write queue if + * there is not a pending write + * @param {Buffer} chunk The chunk of data to write + * @param {function(Error=)} callback The callback to call when the write + * completes + */ + this._tryWrite = function(chunk, callback) { + write_queue.push({chunk: chunk, callback: callback}); + if (can_write && !writing) { + writeNext(); + } + }; +} + +/** + * Start reading. This is an implementation of a method needed for implementing + * stream.Readable. + * @param {number} size Ignored + */ +GrpcClientStream.prototype._read = function(size) { + this._enableRead(); +}; + +/** + * Attempt to write the given chunk. Calls the callback when done. This is an + * implementation of a method needed for implementing stream.Writable. + * @param {Buffer} chunk The chunk to write + * @param {string} encoding Ignored + * @param {function(Error=)} callback Ignored + */ +GrpcClientStream.prototype._write = function(chunk, encoding, callback) { + this._tryWrite(chunk, callback); +}; + +/** + * Make a request on the channel to the given method with the given arguments + * @param {grpc.Channel} channel The channel on which to make the request + * @param {string} method The method to request + * @param {array=} metadata Array of metadata key/value pairs to add to the call + * @param {(number|Date)=} deadline The deadline for processing this request. + * Defaults to infinite future. + * @return {stream=} The stream of responses + */ +function makeRequest(channel, + method, + metadata, + deadline) { + if (deadline === undefined) { + deadline = Infinity; + } + var call = new grpc.Call(channel, method, deadline); + if (metadata) { + call.addMetadata(metadata); + } + return new GrpcClientStream(call); +} + +/** + * See documentation for makeRequest above + */ +exports.makeRequest = makeRequest; + +/** + * Represents a client side gRPC channel associated with a single host. + */ +exports.Channel = grpc.Channel; +/** + * Status name to code number mapping + */ +exports.status = grpc.status; +/** + * Call error name to code number mapping + */ +exports.callError = grpc.callError; diff --git a/src/node/common.js b/src/node/common.js new file mode 100644 index 0000000000..c2dc276608 --- /dev/null +++ b/src/node/common.js @@ -0,0 +1,62 @@ +/* + * + * 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. + * + */ + +var _ = require('highland'); + +/** + * When the given stream finishes without error, call the callback once. This + * will not be called until something begins to consume the stream. + * @param {function} callback The callback to call at stream end + * @param {stream} source The stream to watch + * @return {stream} The stream with the callback attached + */ +function onSuccessfulStreamEnd(callback, source) { + var error = false; + return source.consume(function(err, x, push, next) { + if (x === _.nil) { + if (!error) { + callback(); + } + push(null, x); + } else if (err) { + error = true; + push(err); + next(); + } else { + push(err, x); + next(); + } + }); +} + +exports.onSuccessfulStreamEnd = onSuccessfulStreamEnd; diff --git a/src/node/completion_queue_async_worker.cc b/src/node/completion_queue_async_worker.cc new file mode 100644 index 0000000000..8de7db66d5 --- /dev/null +++ b/src/node/completion_queue_async_worker.cc @@ -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 <node.h> +#include <nan.h> + +#include "grpc/grpc.h" +#include "grpc/support/time.h" +#include "completion_queue_async_worker.h" +#include "event.h" +#include "tag.h" + +namespace grpc { +namespace node { + +using v8::Function; +using v8::Handle; +using v8::Object; +using v8::Persistent; +using v8::Value; + +grpc_completion_queue *CompletionQueueAsyncWorker::queue; + +CompletionQueueAsyncWorker::CompletionQueueAsyncWorker() + : NanAsyncWorker(NULL) {} + +CompletionQueueAsyncWorker::~CompletionQueueAsyncWorker() {} + +void CompletionQueueAsyncWorker::Execute() { + result = grpc_completion_queue_next(queue, gpr_inf_future); +} + +grpc_completion_queue *CompletionQueueAsyncWorker::GetQueue() { return queue; } + +void CompletionQueueAsyncWorker::Next() { + NanScope(); + CompletionQueueAsyncWorker *worker = new CompletionQueueAsyncWorker(); + NanAsyncQueueWorker(worker); +} + +void CompletionQueueAsyncWorker::Init(Handle<Object> exports) { + NanScope(); + queue = grpc_completion_queue_create(); +} + +void CompletionQueueAsyncWorker::HandleOKCallback() { + NanScope(); + NanCallback event_callback(GetTagHandle(result->tag).As<Function>()); + Handle<Value> argv[] = {CreateEventObject(result)}; + + DestroyTag(result->tag); + grpc_event_finish(result); + result = NULL; + + event_callback.Call(1, argv); +} + +} // namespace node +} // namespace grpc diff --git a/src/node/completion_queue_async_worker.h b/src/node/completion_queue_async_worker.h new file mode 100644 index 0000000000..2c928b7024 --- /dev/null +++ b/src/node/completion_queue_async_worker.h @@ -0,0 +1,79 @@ +/* + * + * 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 NET_GRPC_NODE_COMPLETION_QUEUE_ASYNC_WORKER_H_ +#define NET_GRPC_NODE_COMPLETION_QUEUE_ASYNC_WORKER_H_ +#include <nan.h> + +#include "grpc/grpc.h" + +namespace grpc { +namespace node { + +/* A worker that asynchronously calls completion_queue_next, and queues onto the + node event loop a call to the function stored in the event's tag. */ +class CompletionQueueAsyncWorker : public NanAsyncWorker { + public: + CompletionQueueAsyncWorker(); + + ~CompletionQueueAsyncWorker(); + /* Calls completion_queue_next with the provided deadline, and stores the + event if there was one or sets an error message if there was not */ + void Execute(); + + /* Returns the completion queue attached to this class */ + static grpc_completion_queue *GetQueue(); + + /* Convenience function to create a worker with the given arguments and queue + it to run asynchronously */ + static void Next(); + + /* Initialize the CompletionQueueAsyncWorker class */ + static void Init(v8::Handle<v8::Object> exports); + + protected: + /* Called when Execute has succeeded (completed without setting an error + message). Calls the saved callback with the event that came from + completion_queue_next */ + void HandleOKCallback(); + + private: + grpc_event *result; + + static grpc_completion_queue *queue; +}; + +} // namespace node +} // namespace grpc + +#endif // NET_GRPC_NODE_COMPLETION_QUEUE_ASYNC_WORKER_H_ diff --git a/src/node/credentials.cc b/src/node/credentials.cc new file mode 100644 index 0000000000..d58b7eda89 --- /dev/null +++ b/src/node/credentials.cc @@ -0,0 +1,209 @@ +/* + * + * 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 <node.h> + +#include "grpc/grpc.h" +#include "grpc/grpc_security.h" +#include "grpc/support/log.h" +#include "credentials.h" + +namespace grpc { +namespace node { + +using ::node::Buffer; +using v8::Arguments; +using v8::Exception; +using v8::External; +using v8::Function; +using v8::FunctionTemplate; +using v8::Handle; +using v8::HandleScope; +using v8::Integer; +using v8::Local; +using v8::Object; +using v8::ObjectTemplate; +using v8::Persistent; +using v8::Value; + +Persistent<Function> Credentials::constructor; +Persistent<FunctionTemplate> Credentials::fun_tpl; + +Credentials::Credentials(grpc_credentials *credentials) + : wrapped_credentials(credentials) {} + +Credentials::~Credentials() { + gpr_log(GPR_DEBUG, "Destroying credentials object"); + grpc_credentials_release(wrapped_credentials); +} + +void Credentials::Init(Handle<Object> exports) { + NanScope(); + Local<FunctionTemplate> tpl = FunctionTemplate::New(New); + tpl->SetClassName(NanNew("Credentials")); + tpl->InstanceTemplate()->SetInternalFieldCount(1); + NanAssignPersistent(fun_tpl, tpl); + NanAssignPersistent(constructor, tpl->GetFunction()); + constructor->Set(NanNew("createDefault"), + FunctionTemplate::New(CreateDefault)->GetFunction()); + constructor->Set(NanNew("createSsl"), + FunctionTemplate::New(CreateSsl)->GetFunction()); + constructor->Set(NanNew("createComposite"), + FunctionTemplate::New(CreateComposite)->GetFunction()); + constructor->Set(NanNew("createGce"), + FunctionTemplate::New(CreateGce)->GetFunction()); + constructor->Set(NanNew("createFake"), + FunctionTemplate::New(CreateFake)->GetFunction()); + constructor->Set(NanNew("createIam"), + FunctionTemplate::New(CreateIam)->GetFunction()); + exports->Set(NanNew("Credentials"), constructor); +} + +bool Credentials::HasInstance(Handle<Value> val) { + NanScope(); + return NanHasInstance(fun_tpl, val); +} + +Handle<Value> Credentials::WrapStruct(grpc_credentials *credentials) { + NanEscapableScope(); + if (credentials == NULL) { + return NanEscapeScope(NanNull()); + } + const int argc = 1; + Handle<Value> argv[argc] = { + External::New(reinterpret_cast<void *>(credentials))}; + return NanEscapeScope(constructor->NewInstance(argc, argv)); +} + +grpc_credentials *Credentials::GetWrappedCredentials() { + return wrapped_credentials; +} + +NAN_METHOD(Credentials::New) { + NanScope(); + + if (args.IsConstructCall()) { + if (!args[0]->IsExternal()) { + return NanThrowTypeError( + "Credentials can only be created with the provided functions"); + } + grpc_credentials *creds_value = + reinterpret_cast<grpc_credentials *>(External::Unwrap(args[0])); + Credentials *credentials = new Credentials(creds_value); + credentials->Wrap(args.This()); + NanReturnValue(args.This()); + } else { + const int argc = 1; + Local<Value> argv[argc] = {args[0]}; + NanReturnValue(constructor->NewInstance(argc, argv)); + } +} + +NAN_METHOD(Credentials::CreateDefault) { + NanScope(); + NanReturnValue(WrapStruct(grpc_default_credentials_create())); +} + +NAN_METHOD(Credentials::CreateSsl) { + NanScope(); + char *root_certs; + char *private_key = NULL; + char *cert_chain = NULL; + int root_certs_length, private_key_length = 0, cert_chain_length = 0; + if (!Buffer::HasInstance(args[0])) { + return NanThrowTypeError("createSsl's first argument must be a Buffer"); + } + root_certs = Buffer::Data(args[0]); + root_certs_length = Buffer::Length(args[0]); + if (Buffer::HasInstance(args[1])) { + private_key = Buffer::Data(args[1]); + private_key_length = Buffer::Length(args[1]); + } else if (!(args[1]->IsNull() || args[1]->IsUndefined())) { + return NanThrowTypeError( + "createSSl's second argument must be a Buffer if provided"); + } + if (Buffer::HasInstance(args[2])) { + cert_chain = Buffer::Data(args[2]); + cert_chain_length = Buffer::Length(args[2]); + } else if (!(args[2]->IsNull() || args[2]->IsUndefined())) { + return NanThrowTypeError( + "createSSl's third argument must be a Buffer if provided"); + } + NanReturnValue(WrapStruct(grpc_ssl_credentials_create( + reinterpret_cast<unsigned char *>(root_certs), root_certs_length, + reinterpret_cast<unsigned char *>(private_key), private_key_length, + reinterpret_cast<unsigned char *>(cert_chain), cert_chain_length))); +} + +NAN_METHOD(Credentials::CreateComposite) { + NanScope(); + if (!HasInstance(args[0])) { + return NanThrowTypeError( + "createComposite's first argument must be a Credentials object"); + } + if (!HasInstance(args[1])) { + return NanThrowTypeError( + "createComposite's second argument must be a Credentials object"); + } + Credentials *creds1 = ObjectWrap::Unwrap<Credentials>(args[0]->ToObject()); + Credentials *creds2 = ObjectWrap::Unwrap<Credentials>(args[1]->ToObject()); + NanReturnValue(WrapStruct(grpc_composite_credentials_create( + creds1->wrapped_credentials, creds2->wrapped_credentials))); +} + +NAN_METHOD(Credentials::CreateGce) { + NanScope(); + NanReturnValue(WrapStruct(grpc_compute_engine_credentials_create())); +} + +NAN_METHOD(Credentials::CreateFake) { + NanScope(); + NanReturnValue(WrapStruct(grpc_fake_transport_security_credentials_create())); +} + +NAN_METHOD(Credentials::CreateIam) { + NanScope(); + if (!args[0]->IsString()) { + return NanThrowTypeError("createIam's first argument must be a string"); + } + if (!args[1]->IsString()) { + return NanThrowTypeError("createIam's second argument must be a string"); + } + NanUtf8String auth_token(args[0]); + NanUtf8String auth_selector(args[1]); + NanReturnValue( + WrapStruct(grpc_iam_credentials_create(*auth_token, *auth_selector))); +} + +} // namespace node +} // namespace grpc diff --git a/src/node/credentials.h b/src/node/credentials.h new file mode 100644 index 0000000000..981e5a99bc --- /dev/null +++ b/src/node/credentials.h @@ -0,0 +1,81 @@ +/* + * + * 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 NET_GRPC_NODE_CREDENTIALS_H_ +#define NET_GRPC_NODE_CREDENTIALS_H_ + +#include <node.h> +#include <nan.h> +#include "grpc/grpc.h" +#include "grpc/grpc_security.h" + +namespace grpc { +namespace node { + +/* Wrapper class for grpc_credentials structs */ +class Credentials : public ::node::ObjectWrap { + public: + static void Init(v8::Handle<v8::Object> exports); + static bool HasInstance(v8::Handle<v8::Value> val); + /* Wrap a grpc_credentials struct in a javascript object */ + static v8::Handle<v8::Value> WrapStruct(grpc_credentials *credentials); + + /* Returns the grpc_credentials struct that this object wraps */ + grpc_credentials *GetWrappedCredentials(); + + private: + explicit Credentials(grpc_credentials *credentials); + ~Credentials(); + + // Prevent copying + Credentials(const Credentials &); + Credentials &operator=(const Credentials &); + + static NAN_METHOD(New); + static NAN_METHOD(CreateDefault); + static NAN_METHOD(CreateSsl); + static NAN_METHOD(CreateComposite); + static NAN_METHOD(CreateGce); + static NAN_METHOD(CreateFake); + static NAN_METHOD(CreateIam); + static v8::Persistent<v8::Function> constructor; + // Used for typechecking instances of this javascript class + static v8::Persistent<v8::FunctionTemplate> fun_tpl; + + grpc_credentials *wrapped_credentials; +}; + +} // namespace node +} // namespace grpc + +#endif // NET_GRPC_NODE_CREDENTIALS_H_ diff --git a/src/node/event.cc b/src/node/event.cc new file mode 100644 index 0000000000..2ca38b7448 --- /dev/null +++ b/src/node/event.cc @@ -0,0 +1,164 @@ +/* + * + * 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 <node.h> +#include <nan.h> +#include "grpc/grpc.h" +#include "byte_buffer.h" +#include "call.h" +#include "event.h" +#include "tag.h" +#include "timeval.h" + +namespace grpc { +namespace node { + +using v8::Array; +using v8::Date; +using v8::Handle; +using v8::HandleScope; +using v8::Number; +using v8::Object; +using v8::Persistent; +using v8::String; +using v8::Value; + +Handle<Value> GetEventData(grpc_event *event) { + NanEscapableScope(); + size_t count; + grpc_metadata *items; + Handle<Array> metadata; + Handle<Object> status; + Handle<Object> rpc_new; + switch (event->type) { + case GRPC_READ: + return NanEscapeScope(ByteBufferToBuffer(event->data.read)); + case GRPC_INVOKE_ACCEPTED: + return NanEscapeScope(NanNew<Number>(event->data.invoke_accepted)); + case GRPC_WRITE_ACCEPTED: + return NanEscapeScope(NanNew<Number>(event->data.write_accepted)); + case GRPC_FINISH_ACCEPTED: + return NanEscapeScope(NanNew<Number>(event->data.finish_accepted)); + case GRPC_CLIENT_METADATA_READ: + count = event->data.client_metadata_read.count; + items = event->data.client_metadata_read.elements; + metadata = NanNew<Array>(static_cast<int>(count)); + for (unsigned int i = 0; i < count; i++) { + Handle<Object> item_obj = NanNew<Object>(); + item_obj->Set(NanNew<String, const char *>("key"), + NanNew<String, char *>(items[i].key)); + item_obj->Set( + NanNew<String, const char *>("value"), + NanNew<String, char *>(items[i].value, + static_cast<int>(items[i].value_length))); + metadata->Set(i, item_obj); + } + return NanEscapeScope(metadata); + case GRPC_FINISHED: + status = NanNew<Object>(); + status->Set(NanNew("code"), NanNew<Number>(event->data.finished.status)); + if (event->data.finished.details != NULL) { + status->Set(NanNew("details"), + String::New(event->data.finished.details)); + } + count = event->data.finished.metadata_count; + items = event->data.finished.metadata_elements; + metadata = NanNew<Array>(static_cast<int>(count)); + for (unsigned int i = 0; i < count; i++) { + Handle<Object> item_obj = NanNew<Object>(); + item_obj->Set(NanNew<String, const char *>("key"), + NanNew<String, char *>(items[i].key)); + item_obj->Set( + NanNew<String, const char *>("value"), + NanNew<String, char *>(items[i].value, + static_cast<int>(items[i].value_length))); + metadata->Set(i, item_obj); + } + status->Set(NanNew("metadata"), metadata); + return NanEscapeScope(status); + case GRPC_SERVER_RPC_NEW: + rpc_new = NanNew<Object>(); + if (event->data.server_rpc_new.method == NULL) { + return NanEscapeScope(NanNull()); + } + rpc_new->Set( + NanNew<String, const char *>("method"), + NanNew<String, const char *>(event->data.server_rpc_new.method)); + rpc_new->Set( + NanNew<String, const char *>("host"), + NanNew<String, const char *>(event->data.server_rpc_new.host)); + rpc_new->Set(NanNew<String, const char *>("absolute_deadline"), + NanNew<Date>(TimespecToMilliseconds( + event->data.server_rpc_new.deadline))); + count = event->data.server_rpc_new.metadata_count; + items = event->data.server_rpc_new.metadata_elements; + metadata = NanNew<Array>(static_cast<int>(count)); + for (unsigned int i = 0; i < count; i++) { + Handle<Object> item_obj = Object::New(); + item_obj->Set(NanNew<String, const char *>("key"), + NanNew<String, char *>(items[i].key)); + item_obj->Set( + NanNew<String, const char *>("value"), + NanNew<String, char *>(items[i].value, + static_cast<int>(items[i].value_length))); + metadata->Set(i, item_obj); + } + rpc_new->Set(NanNew<String, const char *>("metadata"), metadata); + return NanEscapeScope(rpc_new); + default: + return NanEscapeScope(NanNull()); + } +} + +Handle<Value> CreateEventObject(grpc_event *event) { + NanEscapableScope(); + if (event == NULL) { + return NanEscapeScope(NanNull()); + } + Handle<Object> event_obj = NanNew<Object>(); + Handle<Value> call; + if (TagHasCall(event->tag)) { + call = TagGetCall(event->tag); + } else { + call = Call::WrapStruct(event->call); + } + event_obj->Set(NanNew<String, const char *>("call"), call); + event_obj->Set(NanNew<String, const char *>("type"), + NanNew<Number>(event->type)); + event_obj->Set(NanNew<String, const char *>("data"), GetEventData(event)); + + return NanEscapeScope(event_obj); +} + +} // namespace node +} // namespace grpc diff --git a/src/cpp/client/internal_stub.h b/src/node/event.h index 0eaa717d0b..e06d8f0168 100644 --- a/src/cpp/client/internal_stub.h +++ b/src/node/event.h @@ -31,30 +31,18 @@ * */ -#ifndef __GRPCPP_INTERNAL_CLIENT_INTERNAL_STUB_H__ -#define __GRPCPP_INTERNAL_CLIENT_INTERNAL_STUB_H__ +#ifndef NET_GRPC_NODE_EVENT_H_ +#define NET_GRPC_NODE_EVENT_H_ -#include <memory> - -#include <grpc++/channel_interface.h> +#include <node.h> +#include "grpc/grpc.h" namespace grpc { +namespace node { -class InternalStub { - public: - InternalStub() {} - virtual ~InternalStub() {} - - void set_channel(const std::shared_ptr<ChannelInterface>& channel) { - channel_ = channel; - } - - ChannelInterface* channel() { return channel_.get(); } - - private: - std::shared_ptr<ChannelInterface> channel_; -}; +v8::Handle<v8::Value> CreateEventObject(grpc_event *event); +} // namespace node } // namespace grpc -#endif // __GRPCPP_INTERNAL_CLIENT_INTERNAL_STUB_H__ +#endif // NET_GRPC_NODE_EVENT_H_ diff --git a/src/node/examples/math.proto b/src/node/examples/math.proto new file mode 100644 index 0000000000..14eff5daaf --- /dev/null +++ b/src/node/examples/math.proto @@ -0,0 +1,25 @@ +syntax = "proto2"; + +package math; + +message DivArgs { + required int64 dividend = 1; + required int64 divisor = 2; +} + +message DivReply { + required int64 quotient = 1; + required int64 remainder = 2; +} + +message FibArgs { + optional int64 limit = 1; +} + +message Num { + required int64 num = 1; +} + +message FibReply { + required int64 count = 1; +}
\ No newline at end of file diff --git a/src/node/examples/math_server.js b/src/node/examples/math_server.js new file mode 100644 index 0000000000..87336b61e5 --- /dev/null +++ b/src/node/examples/math_server.js @@ -0,0 +1,201 @@ +/* + * + * 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. + * + */ + +var _ = require('underscore'); +var ProtoBuf = require('protobufjs'); +var fs = require('fs'); +var util = require('util'); + +var Transform = require('stream').Transform; + +var builder = ProtoBuf.loadProtoFile(__dirname + '/math.proto'); +var math = builder.build('math'); + +var makeConstructor = require('../surface_server.js').makeServerConstructor; + +/** + * Get a function that deserializes a specific type of protobuf. + * @param {function()} cls The constructor of the message type to deserialize + * @return {function(Buffer):cls} The deserialization function + */ +function deserializeCls(cls) { + /** + * Deserialize a buffer to a message object + * @param {Buffer} arg_buf The buffer to deserialize + * @return {cls} The resulting object + */ + return function deserialize(arg_buf) { + return cls.decode(arg_buf); + }; +} + +/** + * Get a function that serializes objects to a buffer by protobuf class. + * @param {function()} Cls The constructor of the message type to serialize + * @return {function(Cls):Buffer} The serialization function + */ +function serializeCls(Cls) { + /** + * Serialize an object to a Buffer + * @param {Object} arg The object to serialize + * @return {Buffer} The serialized object + */ + return function serialize(arg) { + return new Buffer(new Cls(arg).encode().toBuffer()); + }; +} + +/* This function call creates a server constructor for servers that that expose + * the four specified methods. This specifies how to serialize messages that the + * server sends and deserialize messages that the client sends, and whether the + * client or the server will send a stream of messages, for each method. This + * also specifies a prefix that will be added to method names when sending them + * on the wire. This function call and all of the preceding code in this file + * are intended to approximate what the generated code will look like for the + * math service */ +var Server = makeConstructor({ + Div: { + serialize: serializeCls(math.DivReply), + deserialize: deserializeCls(math.DivArgs), + client_stream: false, + server_stream: false + }, + Fib: { + serialize: serializeCls(math.Num), + deserialize: deserializeCls(math.FibArgs), + client_stream: false, + server_stream: true + }, + Sum: { + serialize: serializeCls(math.Num), + deserialize: deserializeCls(math.Num), + client_stream: true, + server_stream: false + }, + DivMany: { + serialize: serializeCls(math.DivReply), + deserialize: deserializeCls(math.DivArgs), + client_stream: true, + server_stream: true + } +}, '/Math/'); + +/** + * Server function for division. Provides the /Math/DivMany and /Math/Div + * functions (Div is just DivMany with only one stream element). For each + * DivArgs parameter, responds with a DivReply with the results of the division + * @param {Object} call The object containing request and cancellation info + * @param {function(Error, *)} cb Response callback + */ +function mathDiv(call, cb) { + var req = call.request; + if (req.divisor == 0) { + cb(new Error('cannot divide by zero')); + } + cb(null, { + quotient: req.dividend / req.divisor, + remainder: req.dividend % req.divisor + }); +} + +/** + * Server function for Fibonacci numbers. Provides the /Math/Fib function. Reads + * a single parameter that indicates the number of responses, and then responds + * with a stream of that many Fibonacci numbers. + * @param {stream} stream The stream for sending responses. + */ +function mathFib(stream) { + // Here, call is a standard writable Node object Stream + var previous = 0, current = 1; + for (var i = 0; i < stream.request.limit; i++) { + stream.write({num: current}); + var temp = current; + current += previous; + previous = temp; + } + stream.end(); +} + +/** + * Server function for summation. Provides the /Math/Sum function. Reads a + * stream of number parameters, then responds with their sum. + * @param {stream} call The stream of arguments. + * @param {function(Error, *)} cb Response callback + */ +function mathSum(call, cb) { + // Here, call is a standard readable Node object Stream + var sum = 0; + call.on('data', function(data) { + sum += data.num | 0; + }); + call.on('end', function() { + cb(null, {num: sum}); + }); +} + +function mathDivMany(stream) { + // Here, call is a standard duplex Node object Stream + util.inherits(DivTransform, Transform); + function DivTransform() { + var options = {objectMode: true}; + Transform.call(this, options); + } + DivTransform.prototype._transform = function(div_args, encoding, callback) { + if (div_args.divisor == 0) { + callback(new Error('cannot divide by zero')); + } + callback(null, { + quotient: div_args.dividend / div_args.divisor, + remainder: div_args.dividend % div_args.divisor + }); + }; + var transform = new DivTransform(); + stream.pipe(transform); + transform.pipe(stream); +} + +var server = new Server({ + Div: mathDiv, + Fib: mathFib, + Sum: mathSum, + DivMany: mathDivMany +}); + +if (require.main === module) { + server.bind('localhost:7070').listen(); +} + +/** + * See docs for server + */ +module.exports = server; diff --git a/src/node/node_grpc.cc b/src/node/node_grpc.cc new file mode 100644 index 0000000000..acee0386d2 --- /dev/null +++ b/src/node/node_grpc.cc @@ -0,0 +1,182 @@ +/* + * + * 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 <node.h> +#include <nan.h> +#include <v8.h> +#include "grpc/grpc.h" + +#include "call.h" +#include "channel.h" +#include "event.h" +#include "server.h" +#include "completion_queue_async_worker.h" +#include "credentials.h" +#include "server_credentials.h" + +using v8::Handle; +using v8::Value; +using v8::Object; +using v8::Uint32; +using v8::String; + +void InitStatusConstants(Handle<Object> exports) { + NanScope(); + Handle<Object> status = Object::New(); + exports->Set(NanNew("status"), status); + Handle<Value> OK(NanNew<Uint32, uint32_t>(GRPC_STATUS_OK)); + status->Set(NanNew("OK"), OK); + Handle<Value> CANCELLED(NanNew<Uint32, uint32_t>(GRPC_STATUS_CANCELLED)); + status->Set(NanNew("CANCELLED"), CANCELLED); + Handle<Value> UNKNOWN(NanNew<Uint32, uint32_t>(GRPC_STATUS_UNKNOWN)); + status->Set(NanNew("UNKNOWN"), UNKNOWN); + Handle<Value> INVALID_ARGUMENT( + NanNew<Uint32, uint32_t>(GRPC_STATUS_INVALID_ARGUMENT)); + status->Set(NanNew("INVALID_ARGUMENT"), INVALID_ARGUMENT); + Handle<Value> DEADLINE_EXCEEDED( + NanNew<Uint32, uint32_t>(GRPC_STATUS_DEADLINE_EXCEEDED)); + status->Set(NanNew("DEADLINE_EXCEEDED"), DEADLINE_EXCEEDED); + Handle<Value> NOT_FOUND(NanNew<Uint32, uint32_t>(GRPC_STATUS_NOT_FOUND)); + status->Set(NanNew("NOT_FOUND"), NOT_FOUND); + Handle<Value> ALREADY_EXISTS( + NanNew<Uint32, uint32_t>(GRPC_STATUS_ALREADY_EXISTS)); + status->Set(NanNew("ALREADY_EXISTS"), ALREADY_EXISTS); + Handle<Value> PERMISSION_DENIED( + NanNew<Uint32, uint32_t>(GRPC_STATUS_PERMISSION_DENIED)); + status->Set(NanNew("PERMISSION_DENIED"), PERMISSION_DENIED); + Handle<Value> UNAUTHENTICATED( + NanNew<Uint32, uint32_t>(GRPC_STATUS_UNAUTHENTICATED)); + status->Set(NanNew("UNAUTHENTICATED"), UNAUTHENTICATED); + Handle<Value> RESOURCE_EXHAUSTED( + NanNew<Uint32, uint32_t>(GRPC_STATUS_RESOURCE_EXHAUSTED)); + status->Set(NanNew("RESOURCE_EXHAUSTED"), RESOURCE_EXHAUSTED); + Handle<Value> FAILED_PRECONDITION( + NanNew<Uint32, uint32_t>(GRPC_STATUS_FAILED_PRECONDITION)); + status->Set(NanNew("FAILED_PRECONDITION"), FAILED_PRECONDITION); + Handle<Value> ABORTED(NanNew<Uint32, uint32_t>(GRPC_STATUS_ABORTED)); + status->Set(NanNew("ABORTED"), ABORTED); + Handle<Value> OUT_OF_RANGE( + NanNew<Uint32, uint32_t>(GRPC_STATUS_OUT_OF_RANGE)); + status->Set(NanNew("OUT_OF_RANGE"), OUT_OF_RANGE); + Handle<Value> UNIMPLEMENTED( + NanNew<Uint32, uint32_t>(GRPC_STATUS_UNIMPLEMENTED)); + status->Set(NanNew("UNIMPLEMENTED"), UNIMPLEMENTED); + Handle<Value> INTERNAL(NanNew<Uint32, uint32_t>(GRPC_STATUS_INTERNAL)); + status->Set(NanNew("INTERNAL"), INTERNAL); + Handle<Value> UNAVAILABLE(NanNew<Uint32, uint32_t>(GRPC_STATUS_UNAVAILABLE)); + status->Set(NanNew("UNAVAILABLE"), UNAVAILABLE); + Handle<Value> DATA_LOSS(NanNew<Uint32, uint32_t>(GRPC_STATUS_DATA_LOSS)); + status->Set(NanNew("DATA_LOSS"), DATA_LOSS); +} + +void InitCallErrorConstants(Handle<Object> exports) { + NanScope(); + Handle<Object> call_error = Object::New(); + exports->Set(NanNew("callError"), call_error); + Handle<Value> OK(NanNew<Uint32, uint32_t>(GRPC_CALL_OK)); + call_error->Set(NanNew("OK"), OK); + Handle<Value> ERROR(NanNew<Uint32, uint32_t>(GRPC_CALL_ERROR)); + call_error->Set(NanNew("ERROR"), ERROR); + Handle<Value> NOT_ON_SERVER( + NanNew<Uint32, uint32_t>(GRPC_CALL_ERROR_NOT_ON_SERVER)); + call_error->Set(NanNew("NOT_ON_SERVER"), NOT_ON_SERVER); + Handle<Value> NOT_ON_CLIENT( + NanNew<Uint32, uint32_t>(GRPC_CALL_ERROR_NOT_ON_CLIENT)); + call_error->Set(NanNew("NOT_ON_CLIENT"), NOT_ON_CLIENT); + Handle<Value> ALREADY_INVOKED( + NanNew<Uint32, uint32_t>(GRPC_CALL_ERROR_ALREADY_INVOKED)); + call_error->Set(NanNew("ALREADY_INVOKED"), ALREADY_INVOKED); + Handle<Value> NOT_INVOKED( + NanNew<Uint32, uint32_t>(GRPC_CALL_ERROR_NOT_INVOKED)); + call_error->Set(NanNew("NOT_INVOKED"), NOT_INVOKED); + Handle<Value> ALREADY_FINISHED( + NanNew<Uint32, uint32_t>(GRPC_CALL_ERROR_ALREADY_FINISHED)); + call_error->Set(NanNew("ALREADY_FINISHED"), ALREADY_FINISHED); + Handle<Value> TOO_MANY_OPERATIONS( + NanNew<Uint32, uint32_t>(GRPC_CALL_ERROR_TOO_MANY_OPERATIONS)); + call_error->Set(NanNew("TOO_MANY_OPERATIONS"), TOO_MANY_OPERATIONS); + Handle<Value> INVALID_FLAGS( + NanNew<Uint32, uint32_t>(GRPC_CALL_ERROR_INVALID_FLAGS)); + call_error->Set(NanNew("INVALID_FLAGS"), INVALID_FLAGS); +} + +void InitOpErrorConstants(Handle<Object> exports) { + NanScope(); + Handle<Object> op_error = Object::New(); + exports->Set(NanNew("opError"), op_error); + Handle<Value> OK(NanNew<Uint32, uint32_t>(GRPC_OP_OK)); + op_error->Set(NanNew("OK"), OK); + Handle<Value> ERROR(NanNew<Uint32, uint32_t>(GRPC_OP_ERROR)); + op_error->Set(NanNew("ERROR"), ERROR); +} + +void InitCompletionTypeConstants(Handle<Object> exports) { + NanScope(); + Handle<Object> completion_type = Object::New(); + exports->Set(NanNew("completionType"), completion_type); + Handle<Value> QUEUE_SHUTDOWN(NanNew<Uint32, uint32_t>(GRPC_QUEUE_SHUTDOWN)); + completion_type->Set(NanNew("QUEUE_SHUTDOWN"), QUEUE_SHUTDOWN); + Handle<Value> READ(NanNew<Uint32, uint32_t>(GRPC_READ)); + completion_type->Set(NanNew("READ"), READ); + Handle<Value> INVOKE_ACCEPTED(NanNew<Uint32, uint32_t>(GRPC_INVOKE_ACCEPTED)); + completion_type->Set(NanNew("INVOKE_ACCEPTED"), INVOKE_ACCEPTED); + Handle<Value> WRITE_ACCEPTED(NanNew<Uint32, uint32_t>(GRPC_WRITE_ACCEPTED)); + completion_type->Set(NanNew("WRITE_ACCEPTED"), WRITE_ACCEPTED); + Handle<Value> FINISH_ACCEPTED(NanNew<Uint32, uint32_t>(GRPC_FINISH_ACCEPTED)); + completion_type->Set(NanNew("FINISH_ACCEPTED"), FINISH_ACCEPTED); + Handle<Value> CLIENT_METADATA_READ( + NanNew<Uint32, uint32_t>(GRPC_CLIENT_METADATA_READ)); + completion_type->Set(NanNew("CLIENT_METADATA_READ"), CLIENT_METADATA_READ); + Handle<Value> FINISHED(NanNew<Uint32, uint32_t>(GRPC_FINISHED)); + completion_type->Set(NanNew("FINISHED"), FINISHED); + Handle<Value> SERVER_RPC_NEW(NanNew<Uint32, uint32_t>(GRPC_SERVER_RPC_NEW)); + completion_type->Set(NanNew("SERVER_RPC_NEW"), SERVER_RPC_NEW); +} + +void init(Handle<Object> exports) { + NanScope(); + grpc_init(); + InitStatusConstants(exports); + InitCallErrorConstants(exports); + InitOpErrorConstants(exports); + InitCompletionTypeConstants(exports); + + grpc::node::Call::Init(exports); + grpc::node::Channel::Init(exports); + grpc::node::Server::Init(exports); + grpc::node::CompletionQueueAsyncWorker::Init(exports); + grpc::node::Credentials::Init(exports); + grpc::node::ServerCredentials::Init(exports); +} + +NODE_MODULE(grpc, init) diff --git a/src/node/package.json b/src/node/package.json new file mode 100644 index 0000000000..a2940b29bb --- /dev/null +++ b/src/node/package.json @@ -0,0 +1,18 @@ +{ + "name": "grpc", + "version": "0.1.0", + "description": "gRPC Library for Node", + "scripts": { + "test": "./node_modules/mocha/bin/mocha" + }, + "dependencies": { + "bindings": "^1.2.1", + "nan": "~1.3.0", + "underscore": "^1.7.0" + }, + "devDependencies": { + "mocha": "~1.21.0", + "highland": "~2.0.0", + "protobufjs": "~3.8.0" + } +} diff --git a/src/node/port_picker.js b/src/node/port_picker.js new file mode 100644 index 0000000000..ad82f2a7f8 --- /dev/null +++ b/src/node/port_picker.js @@ -0,0 +1,52 @@ +/* + * + * 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. + * + */ + +var net = require('net'); + +/** + * Finds a free port that a server can bind to, in the format + * "address:port" + * @param {function(string)} cb The callback that should execute when the port + * is available + */ +function nextAvailablePort(cb) { + var server = net.createServer(); + server.listen(function() { + var address = server.address(); + server.close(function() { + cb(address.address + ':' + address.port.toString()); + }); + }); +} + +exports.nextAvailablePort = nextAvailablePort; diff --git a/src/node/server.cc b/src/node/server.cc new file mode 100644 index 0000000000..64826897cd --- /dev/null +++ b/src/node/server.cc @@ -0,0 +1,236 @@ +/* + * + * 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 "server.h" + +#include <node.h> +#include <nan.h> + +#include <malloc.h> + +#include <vector> +#include "grpc/grpc.h" +#include "grpc/grpc_security.h" +#include "call.h" +#include "completion_queue_async_worker.h" +#include "tag.h" +#include "server_credentials.h" + +namespace grpc { +namespace node { + +using v8::Arguments; +using v8::Array; +using v8::Boolean; +using v8::Exception; +using v8::Function; +using v8::FunctionTemplate; +using v8::Handle; +using v8::HandleScope; +using v8::Local; +using v8::Number; +using v8::Object; +using v8::Persistent; +using v8::String; +using v8::Value; + +Persistent<Function> Server::constructor; +Persistent<FunctionTemplate> Server::fun_tpl; + +Server::Server(grpc_server *server) : wrapped_server(server) {} + +Server::~Server() { grpc_server_destroy(wrapped_server); } + +void Server::Init(Handle<Object> exports) { + NanScope(); + Local<FunctionTemplate> tpl = FunctionTemplate::New(New); + tpl->SetClassName(String::NewSymbol("Server")); + tpl->InstanceTemplate()->SetInternalFieldCount(1); + NanSetPrototypeTemplate(tpl, "requestCall", + FunctionTemplate::New(RequestCall)->GetFunction()); + + NanSetPrototypeTemplate(tpl, "addHttp2Port", + FunctionTemplate::New(AddHttp2Port)->GetFunction()); + + NanSetPrototypeTemplate( + tpl, "addSecureHttp2Port", + FunctionTemplate::New(AddSecureHttp2Port)->GetFunction()); + + NanSetPrototypeTemplate(tpl, "start", + FunctionTemplate::New(Start)->GetFunction()); + + NanSetPrototypeTemplate(tpl, "shutdown", + FunctionTemplate::New(Shutdown)->GetFunction()); + + NanAssignPersistent(fun_tpl, tpl); + NanAssignPersistent(constructor, tpl->GetFunction()); + exports->Set(String::NewSymbol("Server"), constructor); +} + +bool Server::HasInstance(Handle<Value> val) { + return NanHasInstance(fun_tpl, val); +} + +NAN_METHOD(Server::New) { + NanScope(); + + /* If this is not a constructor call, make a constructor call and return + the result */ + if (!args.IsConstructCall()) { + const int argc = 1; + Local<Value> argv[argc] = {args[0]}; + NanReturnValue(constructor->NewInstance(argc, argv)); + } + grpc_server *wrapped_server; + grpc_completion_queue *queue = CompletionQueueAsyncWorker::GetQueue(); + if (args[0]->IsUndefined()) { + wrapped_server = grpc_server_create(queue, NULL); + } else if (args[0]->IsObject()) { + grpc_server_credentials *creds = NULL; + Handle<Object> args_hash(args[0]->ToObject()->Clone()); + if (args_hash->HasOwnProperty(NanNew("credentials"))) { + Handle<Value> creds_value = args_hash->Get(NanNew("credentials")); + if (!ServerCredentials::HasInstance(creds_value)) { + return NanThrowTypeError( + "credentials arg must be a ServerCredentials object"); + } + ServerCredentials *creds_object = + ObjectWrap::Unwrap<ServerCredentials>(creds_value->ToObject()); + creds = creds_object->GetWrappedServerCredentials(); + args_hash->Delete(NanNew("credentials")); + } + Handle<Array> keys(args_hash->GetOwnPropertyNames()); + grpc_channel_args channel_args; + channel_args.num_args = keys->Length(); + channel_args.args = reinterpret_cast<grpc_arg *>( + calloc(channel_args.num_args, sizeof(grpc_arg))); + /* These are used to keep all strings until then end of the block, then + destroy them */ + std::vector<NanUtf8String *> key_strings(keys->Length()); + std::vector<NanUtf8String *> value_strings(keys->Length()); + for (unsigned int i = 0; i < channel_args.num_args; i++) { + Handle<String> current_key(keys->Get(i)->ToString()); + Handle<Value> current_value(args_hash->Get(current_key)); + key_strings[i] = new NanUtf8String(current_key); + channel_args.args[i].key = **key_strings[i]; + if (current_value->IsInt32()) { + channel_args.args[i].type = GRPC_ARG_INTEGER; + channel_args.args[i].value.integer = current_value->Int32Value(); + } else if (current_value->IsString()) { + channel_args.args[i].type = GRPC_ARG_STRING; + value_strings[i] = new NanUtf8String(current_value); + channel_args.args[i].value.string = **value_strings[i]; + } else { + free(channel_args.args); + return NanThrowTypeError("Arg values must be strings"); + } + } + if (creds == NULL) { + wrapped_server = grpc_server_create(queue, &channel_args); + } else { + wrapped_server = grpc_secure_server_create(creds, queue, &channel_args); + } + free(channel_args.args); + } else { + return NanThrowTypeError("Server expects an object"); + } + Server *server = new Server(wrapped_server); + server->Wrap(args.This()); + NanReturnValue(args.This()); +} + +NAN_METHOD(Server::RequestCall) { + NanScope(); + if (!HasInstance(args.This())) { + return NanThrowTypeError("requestCall can only be called on a Server"); + } + Server *server = ObjectWrap::Unwrap<Server>(args.This()); + grpc_call_error error = grpc_server_request_call( + server->wrapped_server, CreateTag(args[0], NanNull())); + if (error == GRPC_CALL_OK) { + CompletionQueueAsyncWorker::Next(); + } else { + return NanThrowError("requestCall failed", error); + } + NanReturnUndefined(); +} + +NAN_METHOD(Server::AddHttp2Port) { + NanScope(); + if (!HasInstance(args.This())) { + return NanThrowTypeError("addHttp2Port can only be called on a Server"); + } + if (!args[0]->IsString()) { + return NanThrowTypeError("addHttp2Port's argument must be a String"); + } + Server *server = ObjectWrap::Unwrap<Server>(args.This()); + NanReturnValue(NanNew<Boolean>(grpc_server_add_http2_port( + server->wrapped_server, *NanUtf8String(args[0])))); +} + +NAN_METHOD(Server::AddSecureHttp2Port) { + NanScope(); + if (!HasInstance(args.This())) { + return NanThrowTypeError( + "addSecureHttp2Port can only be called on a Server"); + } + if (!args[0]->IsString()) { + return NanThrowTypeError("addSecureHttp2Port's argument must be a String"); + } + Server *server = ObjectWrap::Unwrap<Server>(args.This()); + NanReturnValue(NanNew<Boolean>(grpc_server_add_secure_http2_port( + server->wrapped_server, *NanUtf8String(args[0])))); +} + +NAN_METHOD(Server::Start) { + NanScope(); + if (!HasInstance(args.This())) { + return NanThrowTypeError("start can only be called on a Server"); + } + Server *server = ObjectWrap::Unwrap<Server>(args.This()); + grpc_server_start(server->wrapped_server); + NanReturnUndefined(); +} + +NAN_METHOD(Server::Shutdown) { + NanScope(); + if (!HasInstance(args.This())) { + return NanThrowTypeError("shutdown can only be called on a Server"); + } + Server *server = ObjectWrap::Unwrap<Server>(args.This()); + grpc_server_shutdown(server->wrapped_server); + NanReturnUndefined(); +} + +} // namespace node +} // namespace grpc diff --git a/src/node/server.h b/src/node/server.h new file mode 100644 index 0000000000..d50f1fb6c5 --- /dev/null +++ b/src/node/server.h @@ -0,0 +1,79 @@ +/* + * + * 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 NET_GRPC_NODE_SERVER_H_ +#define NET_GRPC_NODE_SERVER_H_ + +#include <node.h> +#include <nan.h> +#include "grpc/grpc.h" + +namespace grpc { +namespace node { + +/* Wraps grpc_server as a JavaScript object. Provides a constructor + and wrapper methods for grpc_server_create, grpc_server_request_call, + grpc_server_add_http2_port, and grpc_server_start. */ +class Server : public ::node::ObjectWrap { + public: + /* Initializes the Server class and exposes the constructor and + wrapper methods to JavaScript */ + static void Init(v8::Handle<v8::Object> exports); + /* Tests whether the given value was constructed by this class's + JavaScript constructor */ + static bool HasInstance(v8::Handle<v8::Value> val); + + private: + explicit Server(grpc_server *server); + ~Server(); + + // Prevent copying + Server(const Server &); + Server &operator=(const Server &); + + static NAN_METHOD(New); + static NAN_METHOD(RequestCall); + static NAN_METHOD(AddHttp2Port); + static NAN_METHOD(AddSecureHttp2Port); + static NAN_METHOD(Start); + static NAN_METHOD(Shutdown); + static v8::Persistent<v8::Function> constructor; + static v8::Persistent<v8::FunctionTemplate> fun_tpl; + + grpc_server *wrapped_server; +}; + +} // namespace node +} // namespace grpc + +#endif // NET_GRPC_NODE_SERVER_H_ diff --git a/src/node/server.js b/src/node/server.js new file mode 100644 index 0000000000..7f3e0259a0 --- /dev/null +++ b/src/node/server.js @@ -0,0 +1,261 @@ +/* + * + * 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. + * + */ + +var grpc = require('bindings')('grpc.node'); + +var common = require('./common'); + +var Duplex = require('stream').Duplex; +var util = require('util'); + +util.inherits(GrpcServerStream, Duplex); + +/** + * Class for representing a gRPC server side stream as a Node stream. Extends + * from stream.Duplex. + * @constructor + * @param {grpc.Call} call Call object to proxy + * @param {object} options Stream options + */ +function GrpcServerStream(call, options) { + Duplex.call(this, options); + this._call = call; + // Indicate that a status has been sent + var finished = false; + var self = this; + var status = { + 'code' : grpc.status.OK, + 'details' : 'OK' + }; + /** + * Send the pending status + */ + function sendStatus() { + call.startWriteStatus(status.code, status.details, function() { + }); + finished = true; + } + this.on('finish', sendStatus); + /** + * Set the pending status to a given error status. If the error does not have + * code or details properties, the code will be set to grpc.status.INTERNAL + * and the details will be set to 'Unknown Error'. + * @param {Error} err The error object + */ + function setStatus(err) { + var code = grpc.status.INTERNAL; + var details = 'Unknown Error'; + + if (err.hasOwnProperty('code')) { + code = err.code; + if (err.hasOwnProperty('details')) { + details = err.details; + } + } + status = {'code': code, 'details': details}; + } + /** + * Terminate the call. This includes indicating that reads are done, draining + * all pending writes, and sending the given error as a status + * @param {Error} err The error object + * @this GrpcServerStream + */ + function terminateCall(err) { + // Drain readable data + this.on('data', function() {}); + setStatus(err); + this.end(); + } + this.on('error', terminateCall); + // Indicates that a read is pending + var reading = false; + /** + * Callback to be called when a READ event is received. Pushes the data onto + * the read queue and starts reading again if applicable + * @param {grpc.Event} event READ event object + */ + function readCallback(event) { + if (finished) { + self.push(null); + return; + } + var data = event.data; + if (self.push(data) && data != null) { + self._call.startRead(readCallback); + } else { + reading = false; + } + } + /** + * Start reading if there is not already a pending read. Reading will + * continue until self.push returns false (indicating reads should slow + * down) or the read data is null (indicating that there is no more data). + */ + this.startReading = function() { + if (finished) { + self.push(null); + } else { + if (!reading) { + reading = true; + self._call.startRead(readCallback); + } + } + }; +} + +/** + * Start reading from the gRPC data source. This is an implementation of a + * method required for implementing stream.Readable + * @param {number} size Ignored + */ +GrpcServerStream.prototype._read = function(size) { + this.startReading(); +}; + +/** + * Start writing a chunk of data. This is an implementation of a method required + * for implementing stream.Writable. + * @param {Buffer} chunk The chunk of data to write + * @param {string} encoding Ignored + * @param {function(Error=)} callback Callback to indicate that the write is + * complete + */ +GrpcServerStream.prototype._write = function(chunk, encoding, callback) { + var self = this; + self._call.startWrite(chunk, function(event) { + callback(); + }, 0); +}; + +/** + * Constructs a server object that stores request handlers and delegates + * incoming requests to those handlers + * @constructor + * @param {Array} options Options that should be passed to the internal server + * implementation + */ +function Server(options) { + this.handlers = {}; + var handlers = this.handlers; + var server = new grpc.Server(options); + this._server = server; + var started = false; + /** + * Start the server and begin handling requests + * @this Server + */ + this.start = function() { + if (this.started) { + throw 'Server is already running'; + } + server.start(); + /** + * Handles the SERVER_RPC_NEW event. If there is a handler associated with + * the requested method, use that handler to respond to the request. Then + * wait for the next request + * @param {grpc.Event} event The event to handle with tag SERVER_RPC_NEW + */ + function handleNewCall(event) { + var call = event.call; + var data = event.data; + if (data == null) { + return; + } + server.requestCall(handleNewCall); + var handler = undefined; + var deadline = data.absolute_deadline; + var cancelled = false; + if (handlers.hasOwnProperty(data.method)) { + handler = handlers[data.method]; + } + call.serverAccept(function(event) { + if (event.data.code === grpc.status.CANCELLED) { + cancelled = true; + } + }, 0); + call.serverEndInitialMetadata(0); + var stream = new GrpcServerStream(call); + Object.defineProperty(stream, 'cancelled', { + get: function() { return cancelled;} + }); + try { + handler(stream, data.metadata); + } catch (e) { + stream.emit('error', e); + } + } + server.requestCall(handleNewCall); + }; + /** Shuts down the server. + */ + this.shutdown = function() { + server.shutdown(); + }; +} + +/** + * Registers a handler to handle the named method. Fails if there already is + * a handler for the given method. Returns true on success + * @param {string} name The name of the method that the provided function should + * handle/respond to. + * @param {function} handler Function that takes a stream of request values and + * returns a stream of response values + * @return {boolean} True if the handler was set. False if a handler was already + * set for that name. + */ +Server.prototype.register = function(name, handler) { + if (this.handlers.hasOwnProperty(name)) { + return false; + } + this.handlers[name] = handler; + return true; +}; + +/** + * Binds the server to the given port, with SSL enabled if secure is specified + * @param {string} port The port that the server should bind on, in the format + * "address:port" + * @param {boolean=} secure Whether the server should open a secure port + */ +Server.prototype.bind = function(port, secure) { + if (secure) { + this._server.addSecureHttp2Port(port); + } else { + this._server.addHttp2Port(port); + } +}; + +/** + * See documentation for Server + */ +module.exports = Server; diff --git a/src/node/server_credentials.cc b/src/node/server_credentials.cc new file mode 100644 index 0000000000..38df547527 --- /dev/null +++ b/src/node/server_credentials.cc @@ -0,0 +1,161 @@ +/* + * + * 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 <node.h> + +#include "grpc/grpc.h" +#include "grpc/grpc_security.h" +#include "grpc/support/log.h" +#include "server_credentials.h" + +namespace grpc { +namespace node { + +using ::node::Buffer; +using v8::Arguments; +using v8::Exception; +using v8::External; +using v8::Function; +using v8::FunctionTemplate; +using v8::Handle; +using v8::HandleScope; +using v8::Integer; +using v8::Local; +using v8::Object; +using v8::ObjectTemplate; +using v8::Persistent; +using v8::Value; + +Persistent<Function> ServerCredentials::constructor; +Persistent<FunctionTemplate> ServerCredentials::fun_tpl; + +ServerCredentials::ServerCredentials(grpc_server_credentials *credentials) + : wrapped_credentials(credentials) {} + +ServerCredentials::~ServerCredentials() { + gpr_log(GPR_DEBUG, "Destroying server credentials object"); + grpc_server_credentials_release(wrapped_credentials); +} + +void ServerCredentials::Init(Handle<Object> exports) { + NanScope(); + Local<FunctionTemplate> tpl = FunctionTemplate::New(New); + tpl->SetClassName(NanNew("ServerCredentials")); + tpl->InstanceTemplate()->SetInternalFieldCount(1); + NanAssignPersistent(fun_tpl, tpl); + NanAssignPersistent(constructor, tpl->GetFunction()); + constructor->Set(NanNew("createSsl"), + FunctionTemplate::New(CreateSsl)->GetFunction()); + constructor->Set(NanNew("createFake"), + FunctionTemplate::New(CreateFake)->GetFunction()); + exports->Set(NanNew("ServerCredentials"), constructor); +} + +bool ServerCredentials::HasInstance(Handle<Value> val) { + NanScope(); + return NanHasInstance(fun_tpl, val); +} + +Handle<Value> ServerCredentials::WrapStruct( + grpc_server_credentials *credentials) { + NanEscapableScope(); + if (credentials == NULL) { + return NanEscapeScope(NanNull()); + } + const int argc = 1; + Handle<Value> argv[argc] = { + External::New(reinterpret_cast<void *>(credentials))}; + return NanEscapeScope(constructor->NewInstance(argc, argv)); +} + +grpc_server_credentials *ServerCredentials::GetWrappedServerCredentials() { + return wrapped_credentials; +} + +NAN_METHOD(ServerCredentials::New) { + NanScope(); + + if (args.IsConstructCall()) { + if (!args[0]->IsExternal()) { + return NanThrowTypeError( + "ServerCredentials can only be created with the provide functions"); + } + grpc_server_credentials *creds_value = + reinterpret_cast<grpc_server_credentials *>(External::Unwrap(args[0])); + ServerCredentials *credentials = new ServerCredentials(creds_value); + credentials->Wrap(args.This()); + NanReturnValue(args.This()); + } else { + const int argc = 1; + Local<Value> argv[argc] = {args[0]}; + NanReturnValue(constructor->NewInstance(argc, argv)); + } +} + +NAN_METHOD(ServerCredentials::CreateSsl) { + NanScope(); + char *root_certs = NULL; + char *private_key; + char *cert_chain; + int root_certs_length = 0, private_key_length, cert_chain_length; + if (Buffer::HasInstance(args[0])) { + root_certs = Buffer::Data(args[0]); + root_certs_length = Buffer::Length(args[0]); + } else if (!(args[0]->IsNull() || args[0]->IsUndefined())) { + return NanThrowTypeError( + "createSSl's first argument must be a Buffer if provided"); + } + if (!Buffer::HasInstance(args[1])) { + return NanThrowTypeError("createSsl's second argument must be a Buffer"); + } + private_key = Buffer::Data(args[1]); + private_key_length = Buffer::Length(args[1]); + if (!Buffer::HasInstance(args[2])) { + return NanThrowTypeError("createSsl's third argument must be a Buffer"); + } + cert_chain = Buffer::Data(args[2]); + cert_chain_length = Buffer::Length(args[2]); + NanReturnValue(WrapStruct(grpc_ssl_server_credentials_create( + reinterpret_cast<unsigned char *>(root_certs), root_certs_length, + reinterpret_cast<unsigned char *>(private_key), private_key_length, + reinterpret_cast<unsigned char *>(cert_chain), cert_chain_length))); +} + +NAN_METHOD(ServerCredentials::CreateFake) { + NanScope(); + NanReturnValue( + WrapStruct(grpc_fake_transport_security_server_credentials_create())); +} + +} // namespace node +} // namespace grpc diff --git a/src/node/server_credentials.h b/src/node/server_credentials.h new file mode 100644 index 0000000000..8baae3f185 --- /dev/null +++ b/src/node/server_credentials.h @@ -0,0 +1,77 @@ +/* + * + * 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 NET_GRPC_NODE_SERVER_CREDENTIALS_H_ +#define NET_GRPC_NODE_SERVER_CREDENTIALS_H_ + +#include <node.h> +#include <nan.h> +#include "grpc/grpc.h" +#include "grpc/grpc_security.h" + +namespace grpc { +namespace node { + +/* Wrapper class for grpc_server_credentials structs */ +class ServerCredentials : public ::node::ObjectWrap { + public: + static void Init(v8::Handle<v8::Object> exports); + static bool HasInstance(v8::Handle<v8::Value> val); + /* Wrap a grpc_server_credentials struct in a javascript object */ + static v8::Handle<v8::Value> WrapStruct(grpc_server_credentials *credentials); + + /* Returns the grpc_server_credentials struct that this object wraps */ + grpc_server_credentials *GetWrappedServerCredentials(); + + private: + explicit ServerCredentials(grpc_server_credentials *credentials); + ~ServerCredentials(); + + // Prevent copying + ServerCredentials(const ServerCredentials &); + ServerCredentials &operator=(const ServerCredentials &); + + static NAN_METHOD(New); + static NAN_METHOD(CreateSsl); + static NAN_METHOD(CreateFake); + static v8::Persistent<v8::Function> constructor; + // Used for typechecking instances of this javascript class + static v8::Persistent<v8::FunctionTemplate> fun_tpl; + + grpc_server_credentials *wrapped_credentials; +}; + +} // namespace node +} // namespace grpc + +#endif // NET_GRPC_NODE_SERVER_CREDENTIALS_H_ diff --git a/src/node/surface_client.js b/src/node/surface_client.js new file mode 100644 index 0000000000..9c40b0a3a0 --- /dev/null +++ b/src/node/surface_client.js @@ -0,0 +1,339 @@ +/* + * + * 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. + * + */ + +var _ = require('underscore'); + +var client = require('./client.js'); + +var EventEmitter = require('events').EventEmitter; + +var stream = require('stream'); + +var Readable = stream.Readable; +var Writable = stream.Writable; +var Duplex = stream.Duplex; +var util = require('util'); + +function forwardEvent(fromEmitter, toEmitter, event) { + fromEmitter.on(event, function forward() { + _.partial(toEmitter.emit, event).apply(toEmitter, arguments); + }); +} + +util.inherits(ClientReadableObjectStream, Readable); + +/** + * Class for representing a gRPC server streaming call as a Node stream on the + * client side. Extends from stream.Readable. + * @constructor + * @param {stream} stream Underlying binary Duplex stream for the call + * @param {function(Buffer)} deserialize Function for deserializing binary data + * @param {object} options Stream options + */ +function ClientReadableObjectStream(stream, deserialize, options) { + options = _.extend(options, {objectMode: true}); + Readable.call(this, options); + this._stream = stream; + var self = this; + forwardEvent(stream, this, 'status'); + forwardEvent(stream, this, 'metadata'); + this._stream.on('data', function forwardData(chunk) { + if (!self.push(deserialize(chunk))) { + self._stream.pause(); + } + }); + this._stream.pause(); +} + +util.inherits(ClientWritableObjectStream, Writable); + +/** + * Class for representing a gRPC client streaming call as a Node stream on the + * client side. Extends from stream.Writable. + * @constructor + * @param {stream} stream Underlying binary Duplex stream for the call + * @param {function(*):Buffer} serialize Function for serializing objects + * @param {object} options Stream options + */ +function ClientWritableObjectStream(stream, serialize, options) { + options = _.extend(options, {objectMode: true}); + Writable.call(this, options); + this._stream = stream; + this._serialize = serialize; + forwardEvent(stream, this, 'status'); + forwardEvent(stream, this, 'metadata'); + this.on('finish', function() { + this._stream.end(); + }); +} + + +util.inherits(ClientBidiObjectStream, Duplex); + +/** + * Class for representing a gRPC bidi streaming call as a Node stream on the + * client side. Extends from stream.Duplex. + * @constructor + * @param {stream} stream Underlying binary Duplex stream for the call + * @param {function(*):Buffer} serialize Function for serializing objects + * @param {function(Buffer)} deserialize Function for deserializing binary data + * @param {object} options Stream options + */ +function ClientBidiObjectStream(stream, serialize, deserialize, options) { + options = _.extend(options, {objectMode: true}); + Duplex.call(this, options); + this._stream = stream; + this._serialize = serialize; + var self = this; + forwardEvent(stream, this, 'status'); + forwardEvent(stream, this, 'metadata'); + this._stream.on('data', function forwardData(chunk) { + if (!self.push(deserialize(chunk))) { + self._stream.pause(); + } + }); + this._stream.pause(); + this.on('finish', function() { + this._stream.end(); + }); +} + +/** + * _read implementation for both types of streams that allow reading. + * @this {ClientReadableObjectStream|ClientBidiObjectStream} + * @param {number} size Ignored + */ +function _read(size) { + this._stream.resume(); +} + +/** + * See docs for _read + */ +ClientReadableObjectStream.prototype._read = _read; +/** + * See docs for _read + */ +ClientBidiObjectStream.prototype._read = _read; + +/** + * _write implementation for both types of streams that allow writing + * @this {ClientWritableObjectStream|ClientBidiObjectStream} + * @param {*} chunk The value to write to the stream + * @param {string} encoding Ignored + * @param {function(Error)} callback Callback to call when finished writing + */ +function _write(chunk, encoding, callback) { + this._stream.write(this._serialize(chunk), encoding, callback); +} + +/** + * See docs for _write + */ +ClientWritableObjectStream.prototype._write = _write; +/** + * See docs for _write + */ +ClientBidiObjectStream.prototype._write = _write; + +/** + * Get a function that can make unary requests to the specified method. + * @param {string} method The name of the method to request + * @param {function(*):Buffer} serialize The serialization function for inputs + * @param {function(Buffer)} deserialize The deserialization function for + * outputs + * @return {Function} makeUnaryRequest + */ +function makeUnaryRequestFunction(method, serialize, deserialize) { + /** + * Make a unary request with this method on the given channel with the given + * argument, callback, etc. + * @param {client.Channel} channel The channel on which to make the request + * @param {*} argument The argument to the call. Should be serializable with + * serialize + * @param {function(?Error, value=)} callback The callback to for when the + * response is received + * @param {array=} metadata Array of metadata key/value pairs to add to the + * call + * @param {(number|Date)=} deadline The deadline for processing this request. + * Defaults to infinite future + * @return {EventEmitter} An event emitter for stream related events + */ + function makeUnaryRequest(channel, argument, callback, metadata, deadline) { + var stream = client.makeRequest(channel, method, metadata, deadline); + var emitter = new EventEmitter(); + forwardEvent(stream, emitter, 'status'); + forwardEvent(stream, emitter, 'metadata'); + stream.write(serialize(argument)); + stream.end(); + stream.on('data', function forwardData(chunk) { + try { + callback(null, deserialize(chunk)); + } catch (e) { + callback(e); + } + }); + return emitter; + } + return makeUnaryRequest; +} + +/** + * Get a function that can make client stream requests to the specified method. + * @param {string} method The name of the method to request + * @param {function(*):Buffer} serialize The serialization function for inputs + * @param {function(Buffer)} deserialize The deserialization function for + * outputs + * @return {Function} makeClientStreamRequest + */ +function makeClientStreamRequestFunction(method, serialize, deserialize) { + /** + * Make a client stream request with this method on the given channel with the + * given callback, etc. + * @param {client.Channel} channel The channel on which to make the request + * @param {function(?Error, value=)} callback The callback to for when the + * response is received + * @param {array=} metadata Array of metadata key/value pairs to add to the + * call + * @param {(number|Date)=} deadline The deadline for processing this request. + * Defaults to infinite future + * @return {EventEmitter} An event emitter for stream related events + */ + function makeClientStreamRequest(channel, callback, metadata, deadline) { + var stream = client.makeRequest(channel, method, metadata, deadline); + var obj_stream = new ClientWritableObjectStream(stream, serialize, {}); + stream.on('data', function forwardData(chunk) { + try { + callback(null, deserialize(chunk)); + } catch (e) { + callback(e); + } + }); + return obj_stream; + } + return makeClientStreamRequest; +} + +/** + * Get a function that can make server stream requests to the specified method. + * @param {string} method The name of the method to request + * @param {function(*):Buffer} serialize The serialization function for inputs + * @param {function(Buffer)} deserialize The deserialization function for + * outputs + * @return {Function} makeServerStreamRequest + */ +function makeServerStreamRequestFunction(method, serialize, deserialize) { + /** + * Make a server stream request with this method on the given channel with the + * given argument, etc. + * @param {client.Channel} channel The channel on which to make the request + * @param {*} argument The argument to the call. Should be serializable with + * serialize + * @param {array=} metadata Array of metadata key/value pairs to add to the + * call + * @param {(number|Date)=} deadline The deadline for processing this request. + * Defaults to infinite future + * @return {EventEmitter} An event emitter for stream related events + */ + function makeServerStreamRequest(channel, argument, metadata, deadline) { + var stream = client.makeRequest(channel, method, metadata, deadline); + var obj_stream = new ClientReadableObjectStream(stream, deserialize, {}); + stream.write(serialize(argument)); + stream.end(); + return obj_stream; + } + return makeServerStreamRequest; +} + +/** + * Get a function that can make bidirectional stream requests to the specified + * method. + * @param {string} method The name of the method to request + * @param {function(*):Buffer} serialize The serialization function for inputs + * @param {function(Buffer)} deserialize The deserialization function for + * outputs + * @return {Function} makeBidiStreamRequest + */ +function makeBidiStreamRequestFunction(method, serialize, deserialize) { + /** + * Make a bidirectional stream request with this method on the given channel. + * @param {client.Channel} channel The channel on which to make the request + * @param {array=} metadata Array of metadata key/value pairs to add to the + * call + * @param {(number|Date)=} deadline The deadline for processing this request. + * Defaults to infinite future + * @return {EventEmitter} An event emitter for stream related events + */ + function makeBidiStreamRequest(channel, metadata, deadline) { + var stream = client.makeRequest(channel, method, metadata, deadline); + var obj_stream = new ClientBidiObjectStream(stream, + serialize, + deserialize, + {}); + return obj_stream; + } + return makeBidiStreamRequest; +} + +/** + * See docs for makeUnaryRequestFunction + */ +exports.makeUnaryRequestFunction = makeUnaryRequestFunction; + +/** + * See docs for makeClientStreamRequestFunction + */ +exports.makeClientStreamRequestFunction = makeClientStreamRequestFunction; + +/** + * See docs for makeServerStreamRequestFunction + */ +exports.makeServerStreamRequestFunction = makeServerStreamRequestFunction; + +/** + * See docs for makeBidiStreamRequestFunction + */ +exports.makeBidiStreamRequestFunction = makeBidiStreamRequestFunction; + +/** + * See docs for client.Channel + */ +exports.Channel = client.Channel; +/** + * See docs for client.status + */ +exports.status = client.status; +/** + * See docs for client.callError + */ +exports.callError = client.callError; diff --git a/src/node/surface_server.js b/src/node/surface_server.js new file mode 100644 index 0000000000..295c1ccaff --- /dev/null +++ b/src/node/surface_server.js @@ -0,0 +1,358 @@ +/* + * + * 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. + * + */ + +var _ = require('underscore'); + +var Server = require('./server.js'); + +var stream = require('stream'); + +var Readable = stream.Readable; +var Writable = stream.Writable; +var Duplex = stream.Duplex; +var util = require('util'); + +util.inherits(ServerReadableObjectStream, Readable); + +/** + * Class for representing a gRPC client streaming call as a Node stream on the + * server side. Extends from stream.Readable. + * @constructor + * @param {stream} stream Underlying binary Duplex stream for the call + * @param {function(Buffer)} deserialize Function for deserializing binary data + * @param {object} options Stream options + */ +function ServerReadableObjectStream(stream, deserialize, options) { + options = _.extend(options, {objectMode: true}); + Readable.call(this, options); + this._stream = stream; + Object.defineProperty(this, 'cancelled', { + get: function() { return stream.cancelled; } + }); + var self = this; + this._stream.on('data', function forwardData(chunk) { + if (!self.push(deserialize(chunk))) { + self._stream.pause(); + } + }); + this._stream.on('end', function forwardEnd() { + self.push(null); + }); + this._stream.pause(); +} + +util.inherits(ServerWritableObjectStream, Writable); + +/** + * Class for representing a gRPC server streaming call as a Node stream on the + * server side. Extends from stream.Writable. + * @constructor + * @param {stream} stream Underlying binary Duplex stream for the call + * @param {function(*):Buffer} serialize Function for serializing objects + * @param {object} options Stream options + */ +function ServerWritableObjectStream(stream, serialize, options) { + options = _.extend(options, {objectMode: true}); + Writable.call(this, options); + this._stream = stream; + this._serialize = serialize; + this.on('finish', function() { + this._stream.end(); + }); +} + +util.inherits(ServerBidiObjectStream, Duplex); + +/** + * Class for representing a gRPC bidi streaming call as a Node stream on the + * server side. Extends from stream.Duplex. + * @constructor + * @param {stream} stream Underlying binary Duplex stream for the call + * @param {function(*):Buffer} serialize Function for serializing objects + * @param {function(Buffer)} deserialize Function for deserializing binary data + * @param {object} options Stream options + */ +function ServerBidiObjectStream(stream, serialize, deserialize, options) { + options = _.extend(options, {objectMode: true}); + Duplex.call(this, options); + this._stream = stream; + this._serialize = serialize; + var self = this; + this._stream.on('data', function forwardData(chunk) { + if (!self.push(deserialize(chunk))) { + self._stream.pause(); + } + }); + this._stream.on('end', function forwardEnd() { + self.push(null); + }); + this._stream.pause(); + this.on('finish', function() { + this._stream.end(); + }); +} + +/** + * _read implementation for both types of streams that allow reading. + * @this {ServerReadableObjectStream|ServerBidiObjectStream} + * @param {number} size Ignored + */ +function _read(size) { + this._stream.resume(); +} + +/** + * See docs for _read + */ +ServerReadableObjectStream.prototype._read = _read; +/** + * See docs for _read + */ +ServerBidiObjectStream.prototype._read = _read; + +/** + * _write implementation for both types of streams that allow writing + * @this {ServerWritableObjectStream|ServerBidiObjectStream} + * @param {*} chunk The value to write to the stream + * @param {string} encoding Ignored + * @param {function(Error)} callback Callback to call when finished writing + */ +function _write(chunk, encoding, callback) { + this._stream.write(this._serialize(chunk), encoding, callback); +} + +/** + * See docs for _write + */ +ServerWritableObjectStream.prototype._write = _write; +/** + * See docs for _write + */ +ServerBidiObjectStream.prototype._write = _write; + +/** + * Creates a binary stream handler function from a unary handler function + * @param {function(Object, function(Error, *))} handler Unary call handler + * @param {function(*):Buffer} serialize Serialization function + * @param {function(Buffer):*} deserialize Deserialization function + * @return {function(stream)} Binary stream handler + */ +function makeUnaryHandler(handler, serialize, deserialize) { + /** + * Handles a stream by reading a single data value, passing it to the handler, + * and writing the response back to the stream. + * @param {stream} stream Binary data stream + */ + return function handleUnaryCall(stream) { + stream.on('data', function handleUnaryData(value) { + var call = {request: deserialize(value)}; + Object.defineProperty(call, 'cancelled', { + get: function() { return stream.cancelled;} + }); + handler(call, function sendUnaryData(err, value) { + if (err) { + stream.emit('error', err); + } else { + stream.write(serialize(value)); + stream.end(); + } + }); + }); + }; +} + +/** + * Creates a binary stream handler function from a client stream handler + * function + * @param {function(Readable, function(Error, *))} handler Client stream call + * handler + * @param {function(*):Buffer} serialize Serialization function + * @param {function(Buffer):*} deserialize Deserialization function + * @return {function(stream)} Binary stream handler + */ +function makeClientStreamHandler(handler, serialize, deserialize) { + /** + * Handles a stream by passing a deserializing stream to the handler and + * writing the response back to the stream. + * @param {stream} stream Binary data stream + */ + return function handleClientStreamCall(stream) { + var object_stream = new ServerReadableObjectStream(stream, deserialize, {}); + handler(object_stream, function sendClientStreamData(err, value) { + if (err) { + stream.emit('error', err); + } else { + stream.write(serialize(value)); + stream.end(); + } + }); + }; +} + +/** + * Creates a binary stream handler function from a server stream handler + * function + * @param {function(Writable)} handler Server stream call handler + * @param {function(*):Buffer} serialize Serialization function + * @param {function(Buffer):*} deserialize Deserialization function + * @return {function(stream)} Binary stream handler + */ +function makeServerStreamHandler(handler, serialize, deserialize) { + /** + * Handles a stream by attaching it to a serializing stream, and passing it to + * the handler. + * @param {stream} stream Binary data stream + */ + return function handleServerStreamCall(stream) { + stream.on('data', function handleClientData(value) { + var object_stream = new ServerWritableObjectStream(stream, + serialize, + {}); + object_stream.request = deserialize(value); + handler(object_stream); + }); + }; +} + +/** + * Creates a binary stream handler function from a bidi stream handler function + * @param {function(Duplex)} handler Unary call handler + * @param {function(*):Buffer} serialize Serialization function + * @param {function(Buffer):*} deserialize Deserialization function + * @return {function(stream)} Binary stream handler + */ +function makeBidiStreamHandler(handler, serialize, deserialize) { + /** + * Handles a stream by wrapping it in a serializing and deserializing object + * stream, and passing it to the handler. + * @param {stream} stream Binary data stream + */ + return function handleBidiStreamCall(stream) { + var object_stream = new ServerBidiObjectStream(stream, + serialize, + deserialize, + {}); + handler(object_stream); + }; +} + +/** + * Map with short names for each of the handler maker functions. Used in + * makeServerConstructor + */ +var handler_makers = { + unary: makeUnaryHandler, + server_stream: makeServerStreamHandler, + client_stream: makeClientStreamHandler, + bidi: makeBidiStreamHandler +}; + +/** + * Creates a constructor for servers with a service defined by the methods + * object. The methods object has string keys and values of this form: + * {serialize: function, deserialize: function, client_stream: bool, + * server_stream: bool} + * @param {Object} methods Method descriptor for each method the server should + * expose + * @param {string} prefix The prefex to prepend to each method name + * @return {function(Object, Object)} New server constructor + */ +function makeServerConstructor(methods, prefix) { + /** + * Create a server with the given handlers for all of the methods. + * @constructor + * @param {Object} handlers Map from method names to method handlers. + * @param {Object} options Options to pass to the underlying server + */ + function SurfaceServer(handlers, options) { + var server = new Server(options); + this.inner_server = server; + _.each(handlers, function(handler, name) { + var method = methods[name]; + var method_type; + if (method.client_stream) { + if (method.server_stream) { + method_type = 'bidi'; + } else { + method_type = 'client_stream'; + } + } else { + if (method.server_stream) { + method_type = 'server_stream'; + } else { + method_type = 'unary'; + } + } + var binary_handler = handler_makers[method_type](handler, + method.serialize, + method.deserialize); + server.register('' + prefix + name, binary_handler); + }, this); + } + + /** + * Binds the server to the given port, with SSL enabled if secure is specified + * @param {string} port The port that the server should bind on, in the format + * "address:port" + * @param {boolean=} secure Whether the server should open a secure port + * @return {SurfaceServer} this + */ + SurfaceServer.prototype.bind = function(port, secure) { + this.inner_server.bind(port, secure); + return this; + }; + + /** + * Starts the server listening on any bound ports + * @return {SurfaceServer} this + */ + SurfaceServer.prototype.listen = function() { + this.inner_server.start(); + return this; + }; + + /** + * Shuts the server down; tells it to stop listening for new requests and to + * kill old requests. + */ + SurfaceServer.prototype.shutdown = function() { + this.inner_server.shutdown(); + }; + + return SurfaceServer; +} + +/** + * See documentation for makeServerConstructor + */ +exports.makeServerConstructor = makeServerConstructor; diff --git a/src/node/tag.cc b/src/node/tag.cc new file mode 100644 index 0000000000..dc8e523e12 --- /dev/null +++ b/src/node/tag.cc @@ -0,0 +1,101 @@ +/* + * + * 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 <stdlib.h> +#include <node.h> +#include <nan.h> +#include "tag.h" + +namespace grpc { +namespace node { + +using v8::Handle; +using v8::HandleScope; +using v8::Persistent; +using v8::Value; + +struct tag { + tag(Persistent<Value> *tag, Persistent<Value> *call) + : persist_tag(tag), persist_call(call) {} + + ~tag() { + persist_tag->Dispose(); + if (persist_call != NULL) { + persist_call->Dispose(); + } + } + Persistent<Value> *persist_tag; + Persistent<Value> *persist_call; +}; + +void *CreateTag(Handle<Value> tag, Handle<Value> call) { + NanScope(); + Persistent<Value> *persist_tag = new Persistent<Value>(); + NanAssignPersistent(*persist_tag, tag); + Persistent<Value> *persist_call; + if (call->IsNull() || call->IsUndefined()) { + persist_call = NULL; + } else { + persist_call = new Persistent<Value>(); + NanAssignPersistent(*persist_call, call); + } + struct tag *tag_struct = new struct tag(persist_tag, persist_call); + return reinterpret_cast<void *>(tag_struct); +} + +Handle<Value> GetTagHandle(void *tag) { + NanEscapableScope(); + struct tag *tag_struct = reinterpret_cast<struct tag *>(tag); + Handle<Value> tag_value = NanNew<Value>(*tag_struct->persist_tag); + return NanEscapeScope(tag_value); +} + +bool TagHasCall(void *tag) { + struct tag *tag_struct = reinterpret_cast<struct tag *>(tag); + return tag_struct->persist_call != NULL; +} + +Handle<Value> TagGetCall(void *tag) { + NanEscapableScope(); + struct tag *tag_struct = reinterpret_cast<struct tag *>(tag); + if (tag_struct->persist_call == NULL) { + return NanEscapeScope(NanNull()); + } + Handle<Value> call_value = NanNew<Value>(*tag_struct->persist_call); + return NanEscapeScope(call_value); +} + +void DestroyTag(void *tag) { delete reinterpret_cast<struct tag *>(tag); } + +} // namespace node +} // namespace grpc diff --git a/src/node/tag.h b/src/node/tag.h new file mode 100644 index 0000000000..bdb09252d9 --- /dev/null +++ b/src/node/tag.h @@ -0,0 +1,59 @@ +/* + * + * 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 NET_GRPC_NODE_TAG_H_ +#define NET_GRPC_NODE_TAG_H_ + +#include <node.h> + +namespace grpc { +namespace node { + +/* Create a void* tag that can be passed to various grpc_call functions from + a javascript value and the javascript wrapper for the call. The call can be + null. */ +void *CreateTag(v8::Handle<v8::Value> tag, v8::Handle<v8::Value> call); +/* Return the javascript value stored in the tag */ +v8::Handle<v8::Value> GetTagHandle(void *tag); +/* Returns true if the call was set (non-null) when the tag was created */ +bool TagHasCall(void *tag); +/* Returns the javascript wrapper for the call associated with this tag */ +v8::Handle<v8::Value> TagGetCall(void *call); +/* Destroy the tag and all resources it is holding. It is illegal to call any + of these other functions on a tag after it has been destroyed. */ +void DestroyTag(void *tag); + +} // namespace node +} // namespace grpc + +#endif // NET_GRPC_NODE_TAG_H_ diff --git a/src/node/test/call_test.js b/src/node/test/call_test.js new file mode 100644 index 0000000000..e6dc9664f1 --- /dev/null +++ b/src/node/test/call_test.js @@ -0,0 +1,202 @@ +/* + * + * 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. + * + */ + +var assert = require('assert'); +var grpc = require('bindings')('grpc.node'); + +var channel = new grpc.Channel('localhost:7070'); + +/** + * Helper function to return an absolute deadline given a relative timeout in + * seconds. + * @param {number} timeout_secs The number of seconds to wait before timing out + * @return {Date} A date timeout_secs in the future + */ +function getDeadline(timeout_secs) { + var deadline = new Date(); + deadline.setSeconds(deadline.getSeconds() + timeout_secs); + return deadline; +} + +describe('call', function() { + describe('constructor', function() { + it('should reject anything less than 3 arguments', function() { + assert.throws(function() { + new grpc.Call(); + }, TypeError); + assert.throws(function() { + new grpc.Call(channel); + }, TypeError); + assert.throws(function() { + new grpc.Call(channel, 'method'); + }, TypeError); + }); + it('should succeed with a Channel, a string, and a date or number', + function() { + assert.doesNotThrow(function() { + new grpc.Call(channel, 'method', new Date()); + }); + assert.doesNotThrow(function() { + new grpc.Call(channel, 'method', 0); + }); + }); + it('should fail with a closed channel', function() { + var local_channel = new grpc.Channel('hostname'); + local_channel.close(); + assert.throws(function() { + new grpc.Call(channel, 'method'); + }); + }); + it('should fail with other types', function() { + assert.throws(function() { + new grpc.Call({}, 'method', 0); + }, TypeError); + assert.throws(function() { + new grpc.Call(channel, null, 0); + }, TypeError); + assert.throws(function() { + new grpc.Call(channel, 'method', 'now'); + }, TypeError); + }); + }); + describe('addMetadata', function() { + it('should succeed with objects containing keys and values', function() { + var call = new grpc.Call(channel, 'method', getDeadline(1)); + assert.doesNotThrow(function() { + call.addMetadata(); + }); + assert.doesNotThrow(function() { + call.addMetadata({'key' : 'key', + 'value' : new Buffer('value')}); + }); + assert.doesNotThrow(function() { + call.addMetadata({'key' : 'key1', + 'value' : new Buffer('value1')}, + {'key' : 'key2', + 'value' : new Buffer('value2')}); + }); + }); + it('should fail with other parameter types', function() { + var call = new grpc.Call(channel, 'method', getDeadline(1)); + assert.throws(function() { + call.addMetadata(null); + }, TypeError); + assert.throws(function() { + call.addMetadata('value'); + }, TypeError); + assert.throws(function() { + call.addMetadata(5); + }, TypeError); + }); + it('should fail if startInvoke was already called', function(done) { + var call = new grpc.Call(channel, 'method', getDeadline(1)); + call.startInvoke(function() {}, + function() {}, + function() {done();}, + 0); + assert.throws(function() { + call.addMetadata({'key' : 'key', 'value' : new Buffer('value') }); + }, function(err) { + return err.code === grpc.callError.ALREADY_INVOKED; + }); + // Cancel to speed up the test + call.cancel(); + }); + }); + describe('startInvoke', function() { + it('should fail with fewer than 4 arguments', function() { + var call = new grpc.Call(channel, 'method', getDeadline(1)); + assert.throws(function() { + call.startInvoke(); + }, TypeError); + assert.throws(function() { + call.startInvoke(function() {}); + }, TypeError); + assert.throws(function() { + call.startInvoke(function() {}, + function() {}); + }, TypeError); + assert.throws(function() { + call.startInvoke(function() {}, + function() {}, + function() {}); + }, TypeError); + }); + it('should work with 3 args and an int', function(done) { + assert.doesNotThrow(function() { + var call = new grpc.Call(channel, 'method', getDeadline(1)); + call.startInvoke(function() {}, + function() {}, + function() {done();}, + 0); + // Cancel to speed up the test + call.cancel(); + }); + }); + it('should reject incorrectly typed arguments', function() { + var call = new grpc.Call(channel, 'method', getDeadline(1)); + assert.throws(function() { + call.startInvoke(0, 0, 0, 0); + }, TypeError); + assert.throws(function() { + call.startInvoke(function() {}, + function() {}, + function() {}, 'test'); + }); + }); + }); + describe('serverAccept', function() { + it('should fail with fewer than 1 argument1', function() { + var call = new grpc.Call(channel, 'method', getDeadline(1)); + assert.throws(function() { + call.serverAccept(); + }, TypeError); + }); + it('should return an error when called on a client Call', function() { + var call = new grpc.Call(channel, 'method', getDeadline(1)); + assert.throws(function() { + call.serverAccept(function() {}); + }, function(err) { + return err.code === grpc.callError.NOT_ON_CLIENT; + }); + }); + }); + describe('cancel', function() { + it('should succeed', function() { + var call = new grpc.Call(channel, 'method', getDeadline(1)); + assert.doesNotThrow(function() { + call.cancel(); + }); + }); + }); +}); diff --git a/src/node/test/channel_test.js b/src/node/test/channel_test.js new file mode 100644 index 0000000000..4d8cfc4d89 --- /dev/null +++ b/src/node/test/channel_test.js @@ -0,0 +1,88 @@ +/* + * + * 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. + * + */ + +var assert = require('assert'); +var grpc = require('bindings')('grpc.node'); + +describe('channel', function() { + describe('constructor', function() { + it('should require a string for the first argument', function() { + assert.doesNotThrow(function() { + new grpc.Channel('hostname'); + }); + assert.throws(function() { + new grpc.Channel(); + }, TypeError); + assert.throws(function() { + new grpc.Channel(5); + }); + }); + it('should accept an object for the second parameter', function() { + assert.doesNotThrow(function() { + new grpc.Channel('hostname', {}); + }); + assert.throws(function() { + new grpc.Channel('hostname', 5); + }); + }); + it('should only accept objects with string or int values', function() { + assert.doesNotThrow(function() { + new grpc.Channel('hostname', {'key' : 'value'}); + }); + assert.doesNotThrow(function() { + new grpc.Channel('hostname', {'key' : 5}); + }); + assert.throws(function() { + new grpc.Channel('hostname', {'key' : null}); + }); + assert.throws(function() { + new grpc.Channel('hostname', {'key' : new Date()}); + }); + }); + }); + describe('close', function() { + it('should succeed silently', function() { + var channel = new grpc.Channel('hostname', {}); + assert.doesNotThrow(function() { + channel.close(); + }); + }); + it('should be idempotent', function() { + var channel = new grpc.Channel('hostname', {}); + assert.doesNotThrow(function() { + channel.close(); + channel.close(); + }); + }); + }); +}); diff --git a/src/node/test/client_server_test.js b/src/node/test/client_server_test.js new file mode 100644 index 0000000000..534a5c464f --- /dev/null +++ b/src/node/test/client_server_test.js @@ -0,0 +1,183 @@ +/* + * + * 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. + * + */ + +var assert = require('assert'); +var fs = require('fs'); +var path = require('path'); +var grpc = require('bindings')('grpc.node'); +var Server = require('../server'); +var client = require('../client'); +var port_picker = require('../port_picker'); +var common = require('../common'); +var _ = require('highland'); + +var ca_path = path.join(__dirname, 'data/ca.pem'); + +var key_path = path.join(__dirname, 'data/server1.key'); + +var pem_path = path.join(__dirname, 'data/server1.pem'); + +/** + * Helper function to return an absolute deadline given a relative timeout in + * seconds. + * @param {number} timeout_secs The number of seconds to wait before timing out + * @return {Date} A date timeout_secs in the future + */ +function getDeadline(timeout_secs) { + var deadline = new Date(); + deadline.setSeconds(deadline.getSeconds() + timeout_secs); + return deadline; +} + +/** + * Responds to every request with the same data as a response + * @param {Stream} stream + */ +function echoHandler(stream) { + stream.pipe(stream); +} + +/** + * Responds to every request with an error status + * @param {Stream} stream + */ +function errorHandler(stream) { + throw { + 'code' : grpc.status.UNIMPLEMENTED, + 'details' : 'error details' + }; +} + +describe('echo client', function() { + it('should receive echo responses', function(done) { + port_picker.nextAvailablePort(function(port) { + var server = new Server(); + server.bind(port); + server.register('echo', echoHandler); + server.start(); + + var messages = ['echo1', 'echo2', 'echo3', 'echo4']; + var channel = new grpc.Channel(port); + var stream = client.makeRequest( + channel, + 'echo'); + _(messages).map(function(val) { + return new Buffer(val); + }).pipe(stream); + var index = 0; + stream.on('data', function(chunk) { + assert.equal(messages[index], chunk.toString()); + index += 1; + }); + stream.on('end', function() { + server.shutdown(); + done(); + }); + }); + }); + it('should get an error status that the server throws', function(done) { + port_picker.nextAvailablePort(function(port) { + var server = new Server(); + server.bind(port); + server.register('error', errorHandler); + server.start(); + + var channel = new grpc.Channel(port); + var stream = client.makeRequest( + channel, + 'error', + null, + getDeadline(1)); + + stream.on('data', function() {}); + stream.write(new Buffer('test')); + stream.end(); + stream.on('status', function(status) { + assert.equal(status.code, grpc.status.UNIMPLEMENTED); + assert.equal(status.details, 'error details'); + server.shutdown(); + done(); + }); + + }); + }); +}); +/* TODO(mlumish): explore options for reducing duplication between this test + * and the insecure echo client test */ +describe('secure echo client', function() { + it('should recieve echo responses', function(done) { + port_picker.nextAvailablePort(function(port) { + fs.readFile(ca_path, function(err, ca_data) { + assert.ifError(err); + fs.readFile(key_path, function(err, key_data) { + assert.ifError(err); + fs.readFile(pem_path, function(err, pem_data) { + assert.ifError(err); + var creds = grpc.Credentials.createSsl(ca_data); + var server_creds = grpc.ServerCredentials.createSsl(null, + key_data, + pem_data); + + var server = new Server({'credentials' : server_creds}); + server.bind(port, true); + server.register('echo', echoHandler); + server.start(); + + var messages = ['echo1', 'echo2', 'echo3', 'echo4']; + var channel = new grpc.Channel(port, { + 'grpc.ssl_target_name_override' : 'foo.test.google.com', + 'credentials' : creds + }); + var stream = client.makeRequest( + channel, + 'echo'); + + _(messages).map(function(val) { + return new Buffer(val); + }).pipe(stream); + var index = 0; + stream.on('data', function(chunk) { + assert.equal(messages[index], chunk.toString()); + index += 1; + }); + stream.on('end', function() { + server.shutdown(); + done(); + }); + }); + + }); + }); + }); + }); +}); diff --git a/src/node/test/constant_test.js b/src/node/test/constant_test.js new file mode 100644 index 0000000000..f65eea3cff --- /dev/null +++ b/src/node/test/constant_test.js @@ -0,0 +1,130 @@ +/* + * + * 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. + * + */ + +var assert = require('assert'); +var grpc = require('bindings')('grpc.node'); + +/** + * List of all status names + * @const + * @type {Array.<string>} + */ +var statusNames = [ + 'OK', + 'CANCELLED', + 'UNKNOWN', + 'INVALID_ARGUMENT', + 'DEADLINE_EXCEEDED', + 'NOT_FOUND', + 'ALREADY_EXISTS', + 'PERMISSION_DENIED', + 'UNAUTHENTICATED', + 'RESOURCE_EXHAUSTED', + 'FAILED_PRECONDITION', + 'ABORTED', + 'OUT_OF_RANGE', + 'UNIMPLEMENTED', + 'INTERNAL', + 'UNAVAILABLE', + 'DATA_LOSS' +]; + +/** + * List of all call error names + * @const + * @type {Array.<string>} + */ +var callErrorNames = [ + 'OK', + 'ERROR', + 'NOT_ON_SERVER', + 'NOT_ON_CLIENT', + 'ALREADY_INVOKED', + 'NOT_INVOKED', + 'ALREADY_FINISHED', + 'TOO_MANY_OPERATIONS', + 'INVALID_FLAGS' +]; + +/** + * List of all op error names + * @const + * @type {Array.<string>} + */ +var opErrorNames = [ + 'OK', + 'ERROR' +]; + +/** + * List of all completion type names + * @const + * @type {Array.<string>} + */ +var completionTypeNames = [ + 'QUEUE_SHUTDOWN', + 'READ', + 'INVOKE_ACCEPTED', + 'WRITE_ACCEPTED', + 'FINISH_ACCEPTED', + 'CLIENT_METADATA_READ', + 'FINISHED', + 'SERVER_RPC_NEW' +]; + +describe('constants', function() { + it('should have all of the status constants', function() { + for (var i = 0; i < statusNames.length; i++) { + assert(grpc.status.hasOwnProperty(statusNames[i]), + 'status missing: ' + statusNames[i]); + } + }); + it('should have all of the call errors', function() { + for (var i = 0; i < callErrorNames.length; i++) { + assert(grpc.callError.hasOwnProperty(callErrorNames[i]), + 'call error missing: ' + callErrorNames[i]); + } + }); + it('should have all of the op errors', function() { + for (var i = 0; i < opErrorNames.length; i++) { + assert(grpc.opError.hasOwnProperty(opErrorNames[i]), + 'op error missing: ' + opErrorNames[i]); + } + }); + it('should have all of the completion types', function() { + for (var i = 0; i < completionTypeNames.length; i++) { + assert(grpc.completionType.hasOwnProperty(completionTypeNames[i]), + 'completion type missing: ' + completionTypeNames[i]); + } + }); +}); diff --git a/src/node/test/data/README b/src/node/test/data/README new file mode 100644 index 0000000000..888d95b900 --- /dev/null +++ b/src/node/test/data/README @@ -0,0 +1 @@ +CONFIRMEDTESTKEY diff --git a/src/node/test/data/ca.pem b/src/node/test/data/ca.pem new file mode 100644 index 0000000000..6c8511a73c --- /dev/null +++ b/src/node/test/data/ca.pem @@ -0,0 +1,15 @@ +-----BEGIN CERTIFICATE----- +MIICSjCCAbOgAwIBAgIJAJHGGR4dGioHMA0GCSqGSIb3DQEBCwUAMFYxCzAJBgNV +BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX +aWRnaXRzIFB0eSBMdGQxDzANBgNVBAMTBnRlc3RjYTAeFw0xNDExMTEyMjMxMjla +Fw0yNDExMDgyMjMxMjlaMFYxCzAJBgNVBAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0 +YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxDzANBgNVBAMT +BnRlc3RjYTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAwEDfBV5MYdlHVHJ7 ++L4nxrZy7mBfAVXpOc5vMYztssUI7mL2/iYujiIXM+weZYNTEpLdjyJdu7R5gGUu +g1jSVK/EPHfc74O7AyZU34PNIP4Sh33N+/A5YexrNgJlPY+E3GdVYi4ldWJjgkAd +Qah2PH5ACLrIIC6tRka9hcaBlIECAwEAAaMgMB4wDAYDVR0TBAUwAwEB/zAOBgNV +HQ8BAf8EBAMCAgQwDQYJKoZIhvcNAQELBQADgYEAHzC7jdYlzAVmddi/gdAeKPau +sPBG/C2HCWqHzpCUHcKuvMzDVkY/MP2o6JIW2DBbY64bO/FceExhjcykgaYtCH/m +oIU63+CFOTtR7otyQAWHqXa7q4SbCDlG7DyRFxqG0txPtGvy12lgldA2+RgcigQG +Dfcog5wrJytaQ6UA0wE= +-----END CERTIFICATE----- diff --git a/src/node/test/data/server1.key b/src/node/test/data/server1.key new file mode 100644 index 0000000000..143a5b8765 --- /dev/null +++ b/src/node/test/data/server1.key @@ -0,0 +1,16 @@ +-----BEGIN PRIVATE KEY----- +MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAOHDFScoLCVJpYDD +M4HYtIdV6Ake/sMNaaKdODjDMsux/4tDydlumN+fm+AjPEK5GHhGn1BgzkWF+slf +3BxhrA/8dNsnunstVA7ZBgA/5qQxMfGAq4wHNVX77fBZOgp9VlSMVfyd9N8YwbBY +AckOeUQadTi2X1S6OgJXgQ0m3MWhAgMBAAECgYAn7qGnM2vbjJNBm0VZCkOkTIWm +V10okw7EPJrdL2mkre9NasghNXbE1y5zDshx5Nt3KsazKOxTT8d0Jwh/3KbaN+YY +tTCbKGW0pXDRBhwUHRcuRzScjli8Rih5UOCiZkhefUTcRb6xIhZJuQy71tjaSy0p +dHZRmYyBYO2YEQ8xoQJBAPrJPhMBkzmEYFtyIEqAxQ/o/A6E+E4w8i+KM7nQCK7q +K4JXzyXVAjLfyBZWHGM2uro/fjqPggGD6QH1qXCkI4MCQQDmdKeb2TrKRh5BY1LR +81aJGKcJ2XbcDu6wMZK4oqWbTX2KiYn9GB0woM6nSr/Y6iy1u145YzYxEV/iMwff +DJULAkB8B2MnyzOg0pNFJqBJuH29bKCcHa8gHJzqXhNO5lAlEbMK95p/P2Wi+4Hd +aiEIAF1BF326QJcvYKmwSmrORp85AkAlSNxRJ50OWrfMZnBgzVjDx3xG6KsFQVk2 +ol6VhqL6dFgKUORFUWBvnKSyhjJxurlPEahV6oo6+A+mPhFY8eUvAkAZQyTdupP3 +XEFQKctGz+9+gKkemDp7LBBMEMBXrGTLPhpEfcjv/7KPdnFHYmhYeBTBnuVmTVWe +F98XJ7tIFfJq +-----END PRIVATE KEY----- diff --git a/src/node/test/data/server1.pem b/src/node/test/data/server1.pem new file mode 100644 index 0000000000..8e582e571f --- /dev/null +++ b/src/node/test/data/server1.pem @@ -0,0 +1,16 @@ +-----BEGIN CERTIFICATE----- +MIICmzCCAgSgAwIBAgIBAzANBgkqhkiG9w0BAQUFADBWMQswCQYDVQQGEwJBVTET +MBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQ +dHkgTHRkMQ8wDQYDVQQDDAZ0ZXN0Y2EwHhcNMTQwNzIyMDYwMDU3WhcNMjQwNzE5 +MDYwMDU3WjBkMQswCQYDVQQGEwJVUzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNV +BAcTB0NoaWNhZ28xFDASBgNVBAoTC0dvb2dsZSBJbmMuMRowGAYDVQQDFBEqLnRl +c3QuZ29vZ2xlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA4cMVJygs +JUmlgMMzgdi0h1XoCR7+ww1pop04OMMyy7H/i0PJ2W6Y35+b4CM8QrkYeEafUGDO +RYX6yV/cHGGsD/x02ye6ey1UDtkGAD/mpDEx8YCrjAc1Vfvt8Fk6Cn1WVIxV/J30 +3xjBsFgByQ55RBp1OLZfVLo6AleBDSbcxaECAwEAAaNrMGkwCQYDVR0TBAIwADAL +BgNVHQ8EBAMCBeAwTwYDVR0RBEgwRoIQKi50ZXN0Lmdvb2dsZS5mcoIYd2F0ZXJ6 +b29pLnRlc3QuZ29vZ2xlLmJlghIqLnRlc3QueW91dHViZS5jb22HBMCoAQMwDQYJ +KoZIhvcNAQEFBQADgYEAM2Ii0LgTGbJ1j4oqX9bxVcxm+/R5Yf8oi0aZqTJlnLYS +wXcBykxTx181s7WyfJ49WwrYXo78zTDAnf1ma0fPq3e4mpspvyndLh1a+OarHa1e +aT0DIIYk7qeEa1YcVljx2KyLd0r1BBAfrwyGaEPVeJQVYWaOJRU2we/KD4ojf9s= +-----END CERTIFICATE----- diff --git a/src/node/test/end_to_end_test.js b/src/node/test/end_to_end_test.js new file mode 100644 index 0000000000..40bb5f3bbd --- /dev/null +++ b/src/node/test/end_to_end_test.js @@ -0,0 +1,201 @@ +/* + * + * 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. + * + */ + +var assert = require('assert'); +var grpc = require('bindings')('grpc.node'); +var port_picker = require('../port_picker'); + +/** + * This is used for testing functions with multiple asynchronous calls that + * can happen in different orders. This should be passed the number of async + * function invocations that can occur last, and each of those should call this + * function's return value + * @param {function()} done The function that should be called when a test is + * complete. + * @param {number} count The number of calls to the resulting function if the + * test passes. + * @return {function()} The function that should be called at the end of each + * sequence of asynchronous functions. + */ +function multiDone(done, count) { + return function() { + count -= 1; + if (count <= 0) { + done(); + } + }; +} + +describe('end-to-end', function() { + it('should start and end a request without error', function(complete) { + port_picker.nextAvailablePort(function(port) { + var server = new grpc.Server(); + var done = multiDone(function() { + complete(); + server.shutdown(); + }, 2); + server.addHttp2Port(port); + var channel = new grpc.Channel(port); + var deadline = new Date(); + deadline.setSeconds(deadline.getSeconds() + 3); + var status_text = 'xyz'; + var call = new grpc.Call(channel, + 'dummy_method', + deadline); + call.startInvoke(function(event) { + assert.strictEqual(event.type, + grpc.completionType.INVOKE_ACCEPTED); + + call.writesDone(function(event) { + assert.strictEqual(event.type, + grpc.completionType.FINISH_ACCEPTED); + assert.strictEqual(event.data, grpc.opError.OK); + }); + },function(event) { + assert.strictEqual(event.type, + grpc.completionType.CLIENT_METADATA_READ); + },function(event) { + assert.strictEqual(event.type, grpc.completionType.FINISHED); + var status = event.data; + assert.strictEqual(status.code, grpc.status.OK); + assert.strictEqual(status.details, status_text); + done(); + }, 0); + + server.start(); + server.requestCall(function(event) { + assert.strictEqual(event.type, grpc.completionType.SERVER_RPC_NEW); + var server_call = event.call; + assert.notEqual(server_call, null); + server_call.serverAccept(function(event) { + assert.strictEqual(event.type, grpc.completionType.FINISHED); + }, 0); + server_call.serverEndInitialMetadata(0); + server_call.startWriteStatus( + grpc.status.OK, + status_text, + function(event) { + assert.strictEqual(event.type, + grpc.completionType.FINISH_ACCEPTED); + assert.strictEqual(event.data, grpc.opError.OK); + done(); + }); + }); + }); + }); + + it('should send and receive data without error', function(complete) { + port_picker.nextAvailablePort(function(port) { + var req_text = 'client_request'; + var reply_text = 'server_response'; + var server = new grpc.Server(); + var done = multiDone(function() { + complete(); + server.shutdown(); + }, 6); + server.addHttp2Port(port); + var channel = new grpc.Channel(port); + var deadline = new Date(); + deadline.setSeconds(deadline.getSeconds() + 3); + var status_text = 'success'; + var call = new grpc.Call(channel, + 'dummy_method', + deadline); + call.startInvoke(function(event) { + assert.strictEqual(event.type, + grpc.completionType.INVOKE_ACCEPTED); + call.startWrite( + new Buffer(req_text), + function(event) { + assert.strictEqual(event.type, + grpc.completionType.WRITE_ACCEPTED); + assert.strictEqual(event.data, grpc.opError.OK); + call.writesDone(function(event) { + assert.strictEqual(event.type, + grpc.completionType.FINISH_ACCEPTED); + assert.strictEqual(event.data, grpc.opError.OK); + done(); + }); + }, 0); + call.startRead(function(event) { + assert.strictEqual(event.type, grpc.completionType.READ); + assert.strictEqual(event.data.toString(), reply_text); + done(); + }); + },function(event) { + assert.strictEqual(event.type, + grpc.completionType.CLIENT_METADATA_READ); + done(); + },function(event) { + assert.strictEqual(event.type, grpc.completionType.FINISHED); + var status = event.data; + assert.strictEqual(status.code, grpc.status.OK); + assert.strictEqual(status.details, status_text); + done(); + }, 0); + + server.start(); + server.requestCall(function(event) { + assert.strictEqual(event.type, grpc.completionType.SERVER_RPC_NEW); + var server_call = event.call; + assert.notEqual(server_call, null); + server_call.serverAccept(function(event) { + assert.strictEqual(event.type, grpc.completionType.FINISHED); + done(); + }); + server_call.serverEndInitialMetadata(0); + server_call.startRead(function(event) { + assert.strictEqual(event.type, grpc.completionType.READ); + assert.strictEqual(event.data.toString(), req_text); + server_call.startWrite( + new Buffer(reply_text), + function(event) { + assert.strictEqual(event.type, + grpc.completionType.WRITE_ACCEPTED); + assert.strictEqual(event.data, + grpc.opError.OK); + server_call.startWriteStatus( + grpc.status.OK, + status_text, + function(event) { + assert.strictEqual(event.type, + grpc.completionType.FINISH_ACCEPTED); + assert.strictEqual(event.data, grpc.opError.OK); + done(); + }); + }, 0); + }); + }); + }); + }); +}); diff --git a/src/node/test/math_client_test.js b/src/node/test/math_client_test.js new file mode 100644 index 0000000000..f3697aca98 --- /dev/null +++ b/src/node/test/math_client_test.js @@ -0,0 +1,209 @@ +/* + * + * 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. + * + */ + +var assert = require('assert'); +var client = require('../surface_client.js'); +var ProtoBuf = require('protobufjs'); +var port_picker = require('../port_picker'); + +var builder = ProtoBuf.loadProtoFile(__dirname + '/../examples/math.proto'); +var math = builder.build('math'); + +/** + * Get a function that deserializes a specific type of protobuf. + * @param {function()} cls The constructor of the message type to deserialize + * @return {function(Buffer):cls} The deserialization function + */ +function deserializeCls(cls) { + /** + * Deserialize a buffer to a message object + * @param {Buffer} arg_buf The buffer to deserialize + * @return {cls} The resulting object + */ + return function deserialize(arg_buf) { + return cls.decode(arg_buf); + }; +} + +/** + * Serialize an object to a buffer + * @param {*} arg The object to serialize + * @return {Buffer} The serialized object + */ +function serialize(arg) { + return new Buffer(arg.encode().toBuffer()); +} + +/** + * Sends a Div request on the channel. + * @param {client.Channel} channel The channel on which to make the request + * @param {DivArg} argument The argument to the call. Should be serializable + * with serialize + * @param {function(?Error, value=)} The callback to for when the response is + * received + * @param {array=} Array of metadata key/value pairs to add to the call + * @param {(number|Date)=} deadline The deadline for processing this request. + * Defaults to infinite future + * @return {EventEmitter} An event emitter for stream related events + */ +var div = client.makeUnaryRequestFunction( + '/Math/Div', + serialize, + deserializeCls(math.DivReply)); + +/** + * Sends a Fib request on the channel. + * @param {client.Channel} channel The channel on which to make the request + * @param {*} argument The argument to the call. Should be serializable with + * serialize + * @param {array=} Array of metadata key/value pairs to add to the call + * @param {(number|Date)=} deadline The deadline for processing this request. + * Defaults to infinite future + * @return {EventEmitter} An event emitter for stream related events + */ +var fib = client.makeServerStreamRequestFunction( + '/Math/Fib', + serialize, + deserializeCls(math.Num)); + +/** + * Sends a Sum request on the channel. + * @param {client.Channel} channel The channel on which to make the request + * @param {function(?Error, value=)} The callback to for when the response is + * received + * @param {array=} Array of metadata key/value pairs to add to the call + * @param {(number|Date)=} deadline The deadline for processing this request. + * Defaults to infinite future + * @return {EventEmitter} An event emitter for stream related events + */ +var sum = client.makeClientStreamRequestFunction( + '/Math/Sum', + serialize, + deserializeCls(math.Num)); + +/** + * Sends a DivMany request on the channel. + * @param {client.Channel} channel The channel on which to make the request + * @param {array=} Array of metadata key/value pairs to add to the call + * @param {(number|Date)=} deadline The deadline for processing this request. + * Defaults to infinite future + * @return {EventEmitter} An event emitter for stream related events + */ +var divMany = client.makeBidiStreamRequestFunction( + '/Math/DivMany', + serialize, + deserializeCls(math.DivReply)); + +/** + * Channel to use to make requests to a running server. + */ +var channel; + +/** + * Server to test against + */ +var server = require('../examples/math_server.js'); + + +describe('Math client', function() { + before(function(done) { + port_picker.nextAvailablePort(function(port) { + server.bind(port).listen(); + channel = new client.Channel(port); + done(); + }); + }); + after(function() { + server.shutdown(); + }); + it('should handle a single request', function(done) { + var arg = new math.DivArgs({dividend: 7, divisor: 4}); + var call = div(channel, arg, function handleDivResult(err, value) { + assert.ifError(err); + assert.equal(value.get('quotient'), 1); + assert.equal(value.get('remainder'), 3); + }); + call.on('status', function checkStatus(status) { + assert.strictEqual(status.code, client.status.OK); + done(); + }); + }); + it('should handle a server streaming request', function(done) { + var arg = new math.FibArgs({limit: 7}); + var call = fib(channel, arg); + var expected_results = [1, 1, 2, 3, 5, 8, 13]; + var next_expected = 0; + call.on('data', function checkResponse(value) { + assert.equal(value.get('num'), expected_results[next_expected]); + next_expected += 1; + }); + call.on('status', function checkStatus(status) { + assert.strictEqual(status.code, client.status.OK); + done(); + }); + }); + it('should handle a client streaming request', function(done) { + var call = sum(channel, function handleSumResult(err, value) { + assert.ifError(err); + assert.equal(value.get('num'), 21); + }); + for (var i = 0; i < 7; i++) { + call.write(new math.Num({'num': i})); + } + call.end(); + call.on('status', function checkStatus(status) { + assert.strictEqual(status.code, client.status.OK); + done(); + }); + }); + it('should handle a bidirectional streaming request', function(done) { + function checkResponse(index, value) { + assert.equal(value.get('quotient'), index); + assert.equal(value.get('remainder'), 1); + } + var call = divMany(channel); + var response_index = 0; + call.on('data', function(value) { + checkResponse(response_index, value); + response_index += 1; + }); + for (var i = 0; i < 7; i++) { + call.write(new math.DivArgs({dividend: 2 * i + 1, divisor: 2})); + } + call.end(); + call.on('status', function checkStatus(status) { + assert.strictEqual(status.code, client.status.OK); + done(); + }); + }); +}); diff --git a/src/node/test/server_test.js b/src/node/test/server_test.js new file mode 100644 index 0000000000..79f7b32948 --- /dev/null +++ b/src/node/test/server_test.js @@ -0,0 +1,121 @@ +/* + * + * 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. + * + */ + +var assert = require('assert'); +var grpc = require('bindings')('grpc.node'); +var Server = require('../server'); +var port_picker = require('../port_picker'); + +/** + * This is used for testing functions with multiple asynchronous calls that + * can happen in different orders. This should be passed the number of async + * function invocations that can occur last, and each of those should call this + * function's return value + * @param {function()} done The function that should be called when a test is + * complete. + * @param {number} count The number of calls to the resulting function if the + * test passes. + * @return {function()} The function that should be called at the end of each + * sequence of asynchronous functions. + */ +function multiDone(done, count) { + return function() { + count -= 1; + if (count <= 0) { + done(); + } + }; +} + +/** + * Responds to every request with the same data as a response + * @param {Stream} stream + */ +function echoHandler(stream) { + stream.pipe(stream); +} + +describe('echo server', function() { + it('should echo inputs as responses', function(done) { + done = multiDone(done, 4); + port_picker.nextAvailablePort(function(port) { + var server = new Server(); + server.bind(port); + server.register('echo', echoHandler); + server.start(); + + var req_text = 'echo test string'; + var status_text = 'OK'; + + var channel = new grpc.Channel(port); + var deadline = new Date(); + deadline.setSeconds(deadline.getSeconds() + 3); + var call = new grpc.Call(channel, + 'echo', + deadline); + call.startInvoke(function(event) { + assert.strictEqual(event.type, + grpc.completionType.INVOKE_ACCEPTED); + call.startWrite( + new Buffer(req_text), + function(event) { + assert.strictEqual(event.type, + grpc.completionType.WRITE_ACCEPTED); + assert.strictEqual(event.data, grpc.opError.OK); + call.writesDone(function(event) { + assert.strictEqual(event.type, + grpc.completionType.FINISH_ACCEPTED); + assert.strictEqual(event.data, grpc.opError.OK); + done(); + }); + }, 0); + call.startRead(function(event) { + assert.strictEqual(event.type, grpc.completionType.READ); + assert.strictEqual(event.data.toString(), req_text); + done(); + }); + },function(event) { + assert.strictEqual(event.type, + grpc.completionType.CLIENT_METADATA_READ); + done(); + },function(event) { + assert.strictEqual(event.type, grpc.completionType.FINISHED); + var status = event.data; + assert.strictEqual(status.code, grpc.status.OK); + assert.strictEqual(status.details, status_text); + server.shutdown(); + done(); + }, 0); + }); + }); +}); diff --git a/src/node/timeval.cc b/src/node/timeval.cc new file mode 100644 index 0000000000..687e33576b --- /dev/null +++ b/src/node/timeval.cc @@ -0,0 +1,66 @@ +/* + * + * 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 <limits> + +#include "grpc/grpc.h" +#include "grpc/support/time.h" +#include "timeval.h" + +namespace grpc { +namespace node { + +gpr_timespec MillisecondsToTimespec(double millis) { + if (millis == std::numeric_limits<double>::infinity()) { + return gpr_inf_future; + } else if (millis == -std::numeric_limits<double>::infinity()) { + return gpr_inf_past; + } else { + return gpr_time_from_micros(static_cast<int64_t>(millis * 1000)); + } +} + +double TimespecToMilliseconds(gpr_timespec timespec) { + if (gpr_time_cmp(timespec, gpr_inf_future) == 0) { + return std::numeric_limits<double>::infinity(); + } else if (gpr_time_cmp(timespec, gpr_inf_past) == 0) { + return -std::numeric_limits<double>::infinity(); + } else { + struct timeval time = gpr_timeval_from_timespec(timespec); + return (static_cast<double>(time.tv_sec) * 1000 + + static_cast<double>(time.tv_usec) / 1000); + } +} + +} // namespace node +} // namespace grpc diff --git a/src/node/timeval.h b/src/node/timeval.h new file mode 100644 index 0000000000..1fb0f2c690 --- /dev/null +++ b/src/node/timeval.h @@ -0,0 +1,48 @@ +/* + * + * 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 NET_GRPC_NODE_TIMEVAL_H_ +#define NET_GRPC_NODE_TIMEVAL_H_ + +#include "grpc/support/time.h" + +namespace grpc { +namespace node { + +double TimespecToMilliseconds(gpr_timespec time); +gpr_timespec MillisecondsToTimespec(double millis); + +} // namespace node +} // namespace grpc + +#endif // NET_GRPC_NODE_TIMEVAL_H_ diff --git a/src/php/ext/grpc/byte_buffer.c b/src/php/ext/grpc/byte_buffer.c index db018313a7..e2f63e3413 100755..100644 --- a/src/php/ext/grpc/byte_buffer.c +++ b/src/php/ext/grpc/byte_buffer.c @@ -21,16 +21,15 @@ grpc_byte_buffer *string_to_byte_buffer(char *string, size_t length) { return grpc_byte_buffer_create(&slice, 1); } -void byte_buffer_to_string(grpc_byte_buffer *buffer, - char **out_string, +void byte_buffer_to_string(grpc_byte_buffer *buffer, char **out_string, size_t *out_length) { size_t length = grpc_byte_buffer_length(buffer); - char *string = ecalloc(length+1, sizeof(char)); + char *string = ecalloc(length + 1, sizeof(char)); size_t offset = 0; grpc_byte_buffer_reader *reader = grpc_byte_buffer_reader_create(buffer); gpr_slice next; - while(grpc_byte_buffer_reader_next(reader, &next) != 0) { - memcpy(string+offset, GPR_SLICE_START_PTR(next), GPR_SLICE_LENGTH(next)); + while (grpc_byte_buffer_reader_next(reader, &next) != 0) { + memcpy(string + offset, GPR_SLICE_START_PTR(next), GPR_SLICE_LENGTH(next)); offset += GPR_SLICE_LENGTH(next); } *out_string = string; diff --git a/src/php/ext/grpc/byte_buffer.h b/src/php/ext/grpc/byte_buffer.h index 1dd4769de2..b83f734caf 100755..100644 --- a/src/php/ext/grpc/byte_buffer.h +++ b/src/php/ext/grpc/byte_buffer.h @@ -5,8 +5,7 @@ grpc_byte_buffer *string_to_byte_buffer(char *string, size_t length); -void byte_buffer_to_string(grpc_byte_buffer *buffer, - char **out_string, +void byte_buffer_to_string(grpc_byte_buffer *buffer, char **out_string, size_t *out_length); #endif /* NET_GRPC_PHP_GRPC_BYTE_BUFFER_H_ */ diff --git a/src/php/ext/grpc/call.c b/src/php/ext/grpc/call.c index 7f4f221caa..c01af34e95 100755..100644 --- a/src/php/ext/grpc/call.c +++ b/src/php/ext/grpc/call.c @@ -24,9 +24,9 @@ #include "byte_buffer.h" /* Frees and destroys an instance of wrapped_grpc_call */ -void free_wrapped_grpc_call(void *object TSRMLS_DC){ - wrapped_grpc_call *call = (wrapped_grpc_call*)object; - if(call->owned && call->wrapped != NULL){ +void free_wrapped_grpc_call(void *object TSRMLS_DC) { + wrapped_grpc_call *call = (wrapped_grpc_call *)object; + if (call->owned && call->wrapped != NULL) { grpc_call_destroy(call->wrapped); } efree(call); @@ -34,38 +34,36 @@ void free_wrapped_grpc_call(void *object TSRMLS_DC){ /* Initializes an instance of wrapped_grpc_call to be associated with an object * of a class specified by class_type */ -zend_object_value create_wrapped_grpc_call( - zend_class_entry *class_type TSRMLS_DC){ +zend_object_value create_wrapped_grpc_call(zend_class_entry *class_type + TSRMLS_DC) { zend_object_value retval; wrapped_grpc_call *intern; - intern = (wrapped_grpc_call*)emalloc(sizeof(wrapped_grpc_call)); + intern = (wrapped_grpc_call *)emalloc(sizeof(wrapped_grpc_call)); memset(intern, 0, sizeof(wrapped_grpc_call)); zend_object_std_init(&intern->std, class_type TSRMLS_CC); object_properties_init(&intern->std, class_type); retval.handle = zend_objects_store_put( - intern, - (zend_objects_store_dtor_t) zend_objects_destroy_object, - free_wrapped_grpc_call, - NULL TSRMLS_CC); + intern, (zend_objects_store_dtor_t)zend_objects_destroy_object, + free_wrapped_grpc_call, NULL TSRMLS_CC); retval.handlers = zend_get_std_object_handlers(); return retval; } /* Wraps a grpc_call struct in a PHP object. Owned indicates whether the struct should be destroyed at the end of the object's lifecycle */ -zval *grpc_php_wrap_call(grpc_call *wrapped, bool owned){ +zval *grpc_php_wrap_call(grpc_call *wrapped, bool owned) { zval *call_object; MAKE_STD_ZVAL(call_object); object_init_ex(call_object, grpc_ce_call); - wrapped_grpc_call *call = (wrapped_grpc_call*)zend_object_store_get_object( - call_object TSRMLS_CC); + wrapped_grpc_call *call = + (wrapped_grpc_call *)zend_object_store_get_object(call_object TSRMLS_CC); call->wrapped = wrapped; return call_object; } -zval *grpc_call_create_metadata_array(int count, grpc_metadata *elements){ +zval *grpc_call_create_metadata_array(int count, grpc_metadata *elements) { int i; zval *array; zval **data = NULL; @@ -78,18 +76,16 @@ zval *grpc_call_create_metadata_array(int count, grpc_metadata *elements){ array_init(array); array_hash = Z_ARRVAL_P(array); grpc_metadata *elem; - for(i=0; i<count; i++){ + for (i = 0; i < count; i++) { elem = &elements[i]; key_len = strlen(elem->key); - str_key = ecalloc(key_len+1, sizeof(char)); + str_key = ecalloc(key_len + 1, sizeof(char)); memcpy(str_key, elem->key, key_len); - str_val = ecalloc(elem->value_length+1, sizeof(char)); + str_val = ecalloc(elem->value_length + 1, sizeof(char)); memcpy(str_val, elem->value, elem->value_length); - if(zend_hash_find(array_hash, - str_key, - key_len, - (void**)data) == SUCCESS){ - switch(Z_TYPE_P(*data)){ + if (zend_hash_find(array_hash, str_key, key_len, (void **)data) == + SUCCESS) { + switch (Z_TYPE_P(*data)) { case IS_STRING: MAKE_STD_ZVAL(inner_array); array_init(inner_array); @@ -107,44 +103,36 @@ zval *grpc_call_create_metadata_array(int count, grpc_metadata *elements){ efree(str_val); return NULL; } - add_next_index_stringl(inner_array, - str_val, - elem->value_length, - false); + add_next_index_stringl(inner_array, str_val, elem->value_length, false); } else { - add_assoc_stringl(array, - str_key, - str_val, - elem->value_length, - false); + add_assoc_stringl(array, str_key, str_val, elem->value_length, false); } } return array; } -int php_grpc_call_add_metadata_array_walk(void *elem TSRMLS_DC, - int num_args, +int php_grpc_call_add_metadata_array_walk(void *elem TSRMLS_DC, int num_args, va_list args, - zend_hash_key *hash_key){ + zend_hash_key *hash_key) { grpc_call_error error_code; - zval **data = (zval**)elem; + zval **data = (zval **)elem; grpc_metadata metadata; - grpc_call *call = va_arg(args, grpc_call*); + grpc_call *call = va_arg(args, grpc_call *); gpr_uint32 flags = va_arg(args, gpr_uint32); const char *key; HashTable *inner_hash; /* We assume that either two args were passed, and we are in the recursive case (and the second argument is the key), or one arg was passed and hash_key is the string key. */ - if(num_args > 2){ - key = va_arg(args, const char*); + if (num_args > 2) { + key = va_arg(args, const char *); } else { /* TODO(mlumish): If possible, check that hash_key is a string */ key = hash_key->arKey; } - switch(Z_TYPE_P(*data)){ + switch (Z_TYPE_P(*data)) { case IS_STRING: - metadata.key = (char*)key; + metadata.key = (char *)key; metadata.value = Z_STRVAL_P(*data); metadata.value_length = Z_STRLEN_P(*data); error_code = grpc_call_add_metadata(call, &metadata, 0u); @@ -153,11 +141,8 @@ int php_grpc_call_add_metadata_array_walk(void *elem TSRMLS_DC, case IS_ARRAY: inner_hash = Z_ARRVAL_P(*data); zend_hash_apply_with_arguments(inner_hash TSRMLS_CC, - php_grpc_call_add_metadata_array_walk, - 3, - call, - flags, - key); + php_grpc_call_add_metadata_array_walk, 3, + call, flags, key); break; default: zend_throw_exception(zend_exception_get_default(), @@ -174,27 +159,26 @@ int php_grpc_call_add_metadata_array_walk(void *elem TSRMLS_DC, * @param string $method The method to call * @param Timeval $absolute_deadline The deadline for completing the call */ -PHP_METHOD(Call, __construct){ - wrapped_grpc_call *call = (wrapped_grpc_call*)zend_object_store_get_object( - getThis() TSRMLS_CC); +PHP_METHOD(Call, __construct) { + wrapped_grpc_call *call = + (wrapped_grpc_call *)zend_object_store_get_object(getThis() TSRMLS_CC); zval *channel_obj; char *method; int method_len; zval *deadline_obj; /* "OsO" == 1 Object, 1 string, 1 Object */ - if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, - "OsO", - &channel_obj, grpc_ce_channel, - &method, &method_len, - &deadline_obj, grpc_ce_timeval) == FAILURE){ + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "OsO", &channel_obj, + grpc_ce_channel, &method, &method_len, + &deadline_obj, grpc_ce_timeval) == FAILURE) { zend_throw_exception(spl_ce_InvalidArgumentException, "Call expects a Channel, a String, and a Timeval", 1 TSRMLS_CC); return; } wrapped_grpc_channel *channel = - (wrapped_grpc_channel*)zend_object_store_get_object(channel_obj TSRMLS_CC); - if(channel->wrapped == NULL) { + (wrapped_grpc_channel *)zend_object_store_get_object( + channel_obj TSRMLS_CC); + if (channel->wrapped == NULL) { zend_throw_exception(spl_ce_InvalidArgumentException, "Call cannot be constructed from a closed Channel", 1 TSRMLS_CC); @@ -202,11 +186,10 @@ PHP_METHOD(Call, __construct){ } add_property_zval(getThis(), "channel", channel_obj); wrapped_grpc_timeval *deadline = - (wrapped_grpc_timeval*)zend_object_store_get_object(deadline_obj TSRMLS_CC); - call->wrapped = grpc_channel_create_call(channel->wrapped, - method, - channel->target, - deadline->wrapped); + (wrapped_grpc_timeval *)zend_object_store_get_object( + deadline_obj TSRMLS_CC); + call->wrapped = grpc_channel_create_call(channel->wrapped, method, + channel->target, deadline->wrapped); } /** @@ -218,17 +201,15 @@ PHP_METHOD(Call, __construct){ * (optional) * @return Void */ -PHP_METHOD(Call, add_metadata){ - wrapped_grpc_call *call = (wrapped_grpc_call*)zend_object_store_get_object( - getThis() TSRMLS_CC); +PHP_METHOD(Call, add_metadata) { + wrapped_grpc_call *call = + (wrapped_grpc_call *)zend_object_store_get_object(getThis() TSRMLS_CC); zval *array; HashTable *array_hash; long flags = 0; /* "a|l" == 1 array, 1 optional long */ - if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, - "a|l", - &array, - &flags) == FAILURE){ + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|l", &array, &flags) == + FAILURE) { zend_throw_exception(spl_ce_InvalidArgumentException, "add_metadata expects an array and an optional long", 1 TSRMLS_CC); @@ -236,10 +217,8 @@ PHP_METHOD(Call, add_metadata){ } array_hash = Z_ARRVAL_P(array); zend_hash_apply_with_arguments(array_hash TSRMLS_CC, - php_grpc_call_add_metadata_array_walk, - 2, - call->wrapped, - (gpr_uint32)flags); + php_grpc_call_add_metadata_array_walk, 2, + call->wrapped, (gpr_uint32)flags); } /** @@ -252,7 +231,7 @@ PHP_METHOD(Call, add_metadata){ * (optional) * @return Void */ -PHP_METHOD(Call, start_invoke){ +PHP_METHOD(Call, start_invoke) { grpc_call_error error_code; long tag1; long tag2; @@ -260,31 +239,24 @@ PHP_METHOD(Call, start_invoke){ zval *queue_obj; long flags = 0; /* "Olll|l" == 1 Object, 3 mandatory longs, 1 optional long */ - if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, - "Olll|l", - &queue_obj, grpc_ce_completion_queue, - &tag1, - &tag2, - &tag3, - &flags) == FAILURE){ + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Olll|l", &queue_obj, + grpc_ce_completion_queue, &tag1, &tag2, &tag3, + &flags) == FAILURE) { zend_throw_exception( spl_ce_InvalidArgumentException, "start_invoke needs a CompletionQueue, 3 longs, and an optional long", - 1 TSRMLS_CC); + 1 TSRMLS_CC); return; } add_property_zval(getThis(), "completion_queue", queue_obj); - wrapped_grpc_call *call = (wrapped_grpc_call*)zend_object_store_get_object( - getThis() TSRMLS_CC); + wrapped_grpc_call *call = + (wrapped_grpc_call *)zend_object_store_get_object(getThis() TSRMLS_CC); wrapped_grpc_completion_queue *queue = - (wrapped_grpc_completion_queue*)zend_object_store_get_object( - queue_obj TSRMLS_CC); - error_code = grpc_call_start_invoke(call->wrapped, - queue->wrapped, - (void*)tag1, - (void*)tag2, - (void*)tag3, - (gpr_uint32)flags); + (wrapped_grpc_completion_queue *)zend_object_store_get_object( + queue_obj TSRMLS_CC); + error_code = + grpc_call_start_invoke(call->wrapped, queue->wrapped, (void *)tag1, + (void *)tag2, (void *)tag3, (gpr_uint32)flags); MAYBE_THROW_CALL_ERROR(start_invoke, error_code); } @@ -298,15 +270,13 @@ PHP_METHOD(Call, start_invoke){ * (optional) * @return Void */ -PHP_METHOD(Call, server_accept){ +PHP_METHOD(Call, server_accept) { long tag; zval *queue_obj; grpc_call_error error_code; /* "Ol|l" == 1 Object, 1 long */ - if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, - "Ol", - &queue_obj, grpc_ce_completion_queue, - &tag) == FAILURE){ + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Ol", &queue_obj, + grpc_ce_completion_queue, &tag) == FAILURE) { zend_throw_exception( spl_ce_InvalidArgumentException, "server_accept expects a CompletionQueue, a long, and an optional long", @@ -314,14 +284,13 @@ PHP_METHOD(Call, server_accept){ return; } add_property_zval(getThis(), "completion_queue", queue_obj); - wrapped_grpc_call *call = (wrapped_grpc_call*)zend_object_store_get_object( - getThis() TSRMLS_CC); + wrapped_grpc_call *call = + (wrapped_grpc_call *)zend_object_store_get_object(getThis() TSRMLS_CC); wrapped_grpc_completion_queue *queue = - (wrapped_grpc_completion_queue*)zend_object_store_get_object( - queue_obj TSRMLS_CC); - error_code = grpc_call_server_accept(call->wrapped, - queue->wrapped, - (void*)tag); + (wrapped_grpc_completion_queue *)zend_object_store_get_object( + queue_obj TSRMLS_CC); + error_code = + grpc_call_server_accept(call->wrapped, queue->wrapped, (void *)tag); MAYBE_THROW_CALL_ERROR(server_accept, error_code); } @@ -329,16 +298,14 @@ PHP_METHOD(Call, server_end_initial_metadata) { grpc_call_error error_code; long flags = 0; /* "|l" == 1 optional long */ - if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, - "|l", - &flags) == FAILURE) { - zend_throw_exception( - spl_ce_InvalidArgumentException, - "server_end_initial_metadata expects an optional long", - 1 TSRMLS_CC); + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &flags) == + FAILURE) { + zend_throw_exception(spl_ce_InvalidArgumentException, + "server_end_initial_metadata expects an optional long", + 1 TSRMLS_CC); } - wrapped_grpc_call *call = (wrapped_grpc_call*)zend_object_store_get_object( - getThis() TSRMLS_CC); + wrapped_grpc_call *call = + (wrapped_grpc_call *)zend_object_store_get_object(getThis() TSRMLS_CC); error_code = grpc_call_server_end_initial_metadata(call->wrapped, flags); MAYBE_THROW_CALL_ERROR(server_end_initial_metadata, error_code); } @@ -347,9 +314,9 @@ PHP_METHOD(Call, server_end_initial_metadata) { * Called by clients to cancel an RPC on the server. * @return Void */ -PHP_METHOD(Call, cancel){ - wrapped_grpc_call *call = (wrapped_grpc_call*)zend_object_store_get_object( - getThis() TSRMLS_CC); +PHP_METHOD(Call, cancel) { + wrapped_grpc_call *call = + (wrapped_grpc_call *)zend_object_store_get_object(getThis() TSRMLS_CC); grpc_call_error error_code = grpc_call_cancel(call->wrapped); MAYBE_THROW_CALL_ERROR(cancel, error_code); } @@ -362,30 +329,25 @@ PHP_METHOD(Call, cancel){ * (optional) * @return Void */ -PHP_METHOD(Call, start_write){ +PHP_METHOD(Call, start_write) { grpc_call_error error_code; - wrapped_grpc_call *call = (wrapped_grpc_call*)zend_object_store_get_object( - getThis() TSRMLS_CC); + wrapped_grpc_call *call = + (wrapped_grpc_call *)zend_object_store_get_object(getThis() TSRMLS_CC); char *buffer; int buffer_len; long tag; long flags = 0; /* "Ol|l" == 1 Object, 1 mandatory long, 1 optional long */ - if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, - "sl|l", - &buffer, &buffer_len, - &tag, - &flags) == FAILURE){ - zend_throw_exception( - spl_ce_InvalidArgumentException, - "start_write expects a string and an optional long", - 1 TSRMLS_CC); + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sl|l", &buffer, + &buffer_len, &tag, &flags) == FAILURE) { + zend_throw_exception(spl_ce_InvalidArgumentException, + "start_write expects a string and an optional long", + 1 TSRMLS_CC); return; } error_code = grpc_call_start_write(call->wrapped, string_to_byte_buffer(buffer, buffer_len), - (void*)tag, - (gpr_uint32)flags); + (void *)tag, (gpr_uint32)flags); MAYBE_THROW_CALL_ERROR(start_write, error_code); } @@ -396,30 +358,26 @@ PHP_METHOD(Call, start_write){ * @param long $tag The tag to associate with this status * @return Void */ -PHP_METHOD(Call, start_write_status){ +PHP_METHOD(Call, start_write_status) { grpc_call_error error_code; - wrapped_grpc_call *call = (wrapped_grpc_call*)zend_object_store_get_object( - getThis() TSRMLS_CC); + wrapped_grpc_call *call = + (wrapped_grpc_call *)zend_object_store_get_object(getThis() TSRMLS_CC); long status_code; int status_details_length; long tag; char *status_details; /* "lsl" == 1 long, 1 string, 1 long */ - if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, - "lsl", - &status_code, - &status_details, &status_details_length, - &tag) == FAILURE){ + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "lsl", &status_code, + &status_details, &status_details_length, + &tag) == FAILURE) { zend_throw_exception( spl_ce_InvalidArgumentException, - "start_write_status expects a long, a string, and a long", - 1 TSRMLS_CC); + "start_write_status expects a long, a string, and a long", 1 TSRMLS_CC); return; } - error_code = grpc_call_start_write_status(call->wrapped, - (grpc_status_code)status_code, - status_details, - (void*)tag); + error_code = + grpc_call_start_write_status(call->wrapped, (grpc_status_code)status_code, + status_details, (void *)tag); MAYBE_THROW_CALL_ERROR(start_write_status, error_code); } @@ -427,19 +385,18 @@ PHP_METHOD(Call, start_write_status){ * Indicate that there are no more messages to send * @return Void */ -PHP_METHOD(Call, writes_done){ +PHP_METHOD(Call, writes_done) { grpc_call_error error_code; - wrapped_grpc_call *call = (wrapped_grpc_call*)zend_object_store_get_object( - getThis() TSRMLS_CC); + wrapped_grpc_call *call = + (wrapped_grpc_call *)zend_object_store_get_object(getThis() TSRMLS_CC); long tag; /* "l" == 1 long */ - if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &tag) == FAILURE){ + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &tag) == FAILURE) { zend_throw_exception(spl_ce_InvalidArgumentException, - "writes_done expects a long", - 1 TSRMLS_CC); + "writes_done expects a long", 1 TSRMLS_CC); return; } - error_code = grpc_call_writes_done(call->wrapped, (void*)tag); + error_code = grpc_call_writes_done(call->wrapped, (void *)tag); MAYBE_THROW_CALL_ERROR(writes_done, error_code); } @@ -449,37 +406,35 @@ PHP_METHOD(Call, writes_done){ * @param long $tag The tag to associate with this read * @return Void */ -PHP_METHOD(Call, start_read){ +PHP_METHOD(Call, start_read) { grpc_call_error error_code; - wrapped_grpc_call *call = (wrapped_grpc_call*)zend_object_store_get_object( - getThis() TSRMLS_CC); + wrapped_grpc_call *call = + (wrapped_grpc_call *)zend_object_store_get_object(getThis() TSRMLS_CC); long tag; /* "l" == 1 long */ - if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &tag) == FAILURE){ + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &tag) == FAILURE) { zend_throw_exception(spl_ce_InvalidArgumentException, - "start_read expects a long", - 1 TSRMLS_CC); + "start_read expects a long", 1 TSRMLS_CC); return; } - error_code = grpc_call_start_read(call->wrapped, (void*)tag); + error_code = grpc_call_start_read(call->wrapped, (void *)tag); MAYBE_THROW_CALL_ERROR(start_read, error_code); } static zend_function_entry call_methods[] = { - PHP_ME(Call, __construct, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR) - PHP_ME(Call, server_accept, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Call, server_end_initial_metadata, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Call, add_metadata, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Call, cancel, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Call, start_invoke, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Call, start_read, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Call, start_write, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Call, start_write_status, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Call, writes_done, NULL, ZEND_ACC_PUBLIC) - PHP_FE_END -}; + PHP_ME(Call, __construct, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_CTOR) + PHP_ME(Call, server_accept, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Call, server_end_initial_metadata, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Call, add_metadata, NULL, ZEND_ACC_PUBLIC) PHP_ME( + Call, cancel, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Call, start_invoke, NULL, ZEND_ACC_PUBLIC) PHP_ME( + Call, start_read, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Call, start_write, NULL, ZEND_ACC_PUBLIC) PHP_ME( + Call, start_write_status, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Call, writes_done, NULL, ZEND_ACC_PUBLIC) + PHP_FE_END}; -void grpc_init_call(TSRMLS_D){ +void grpc_init_call(TSRMLS_D) { zend_class_entry ce; INIT_CLASS_ENTRY(ce, "Grpc\\Call", call_methods); ce.create_object = create_wrapped_grpc_call; diff --git a/src/php/ext/grpc/call.h b/src/php/ext/grpc/call.h index c3b18d66cb..232c5d7cf2 100755..100644 --- a/src/php/ext/grpc/call.h +++ b/src/php/ext/grpc/call.h @@ -13,15 +13,14 @@ #include "grpc/grpc.h" // Throw an exception if error_code is not OK -#define MAYBE_THROW_CALL_ERROR(func_name, error_code) \ - do{ \ - if(error_code != GRPC_CALL_OK) { \ - zend_throw_exception(spl_ce_LogicException, \ - #func_name " was called incorrectly", \ - (long)error_code TSRMLS_CC); \ - } \ - } while(0) - +#define MAYBE_THROW_CALL_ERROR(func_name, error_code) \ + do { \ + if (error_code != GRPC_CALL_OK) { \ + zend_throw_exception(spl_ce_LogicException, \ + #func_name " was called incorrectly", \ + (long)error_code TSRMLS_CC); \ + } \ + } while (0) /* Class entry for the Call PHP class */ zend_class_entry *grpc_ce_call; diff --git a/src/php/ext/grpc/channel.c b/src/php/ext/grpc/channel.c index c2847b99f1..f0e4153b22 100755..100644 --- a/src/php/ext/grpc/channel.c +++ b/src/php/ext/grpc/channel.c @@ -23,9 +23,9 @@ #include "credentials.h" /* Frees and destroys an instance of wrapped_grpc_channel */ -void free_wrapped_grpc_channel(void *object TSRMLS_DC){ - wrapped_grpc_channel *channel = (wrapped_grpc_channel*)object; - if(channel->wrapped != NULL){ +void free_wrapped_grpc_channel(void *object TSRMLS_DC) { + wrapped_grpc_channel *channel = (wrapped_grpc_channel *)object; + if (channel->wrapped != NULL) { grpc_channel_destroy(channel->wrapped); } efree(channel); @@ -33,24 +33,22 @@ void free_wrapped_grpc_channel(void *object TSRMLS_DC){ /* Initializes an instance of wrapped_grpc_channel to be associated with an * object of a class specified by class_type */ -zend_object_value create_wrapped_grpc_channel( - zend_class_entry *class_type TSRMLS_DC){ +zend_object_value create_wrapped_grpc_channel(zend_class_entry *class_type + TSRMLS_DC) { zend_object_value retval; wrapped_grpc_channel *intern; - intern = (wrapped_grpc_channel*)emalloc(sizeof(wrapped_grpc_channel)); + intern = (wrapped_grpc_channel *)emalloc(sizeof(wrapped_grpc_channel)); memset(intern, 0, sizeof(wrapped_grpc_channel)); zend_object_std_init(&intern->std, class_type TSRMLS_CC); object_properties_init(&intern->std, class_type); retval.handle = zend_objects_store_put( - intern, - (zend_objects_store_dtor_t)zend_objects_destroy_object, - free_wrapped_grpc_channel, - NULL TSRMLS_CC); + intern, (zend_objects_store_dtor_t)zend_objects_destroy_object, + free_wrapped_grpc_channel, NULL TSRMLS_CC); retval.handlers = zend_get_std_object_handlers(); return retval; } -void php_grpc_read_args_array(zval *args_array, grpc_channel_args *args){ +void php_grpc_read_args_array(zval *args_array, grpc_channel_args *args) { HashTable *array_hash; HashPosition array_pointer; int args_index; @@ -62,24 +60,18 @@ void php_grpc_read_args_array(zval *args_array, grpc_channel_args *args){ args->num_args = zend_hash_num_elements(array_hash); args->args = ecalloc(args->num_args, sizeof(grpc_arg)); args_index = 0; - for(zend_hash_internal_pointer_reset_ex(array_hash, &array_pointer); - zend_hash_get_current_data_ex(array_hash, - (void**)&data, - &array_pointer) == SUCCESS; - zend_hash_move_forward_ex(array_hash, &array_pointer)){ - if(zend_hash_get_current_key_ex(array_hash, - &key, - &key_len, - &index, - 0, - &array_pointer) != HASH_KEY_IS_STRING){ + for (zend_hash_internal_pointer_reset_ex(array_hash, &array_pointer); + zend_hash_get_current_data_ex(array_hash, (void **)&data, + &array_pointer) == SUCCESS; + zend_hash_move_forward_ex(array_hash, &array_pointer)) { + if (zend_hash_get_current_key_ex(array_hash, &key, &key_len, &index, 0, + &array_pointer) != HASH_KEY_IS_STRING) { zend_throw_exception(spl_ce_InvalidArgumentException, - "args keys must be strings", - 1 TSRMLS_CC); + "args keys must be strings", 1 TSRMLS_CC); return; } args->args[args_index].key = key; - switch(Z_TYPE_P(*data)){ + switch (Z_TYPE_P(*data)) { case IS_LONG: args->args[args_index].value.integer = (int)Z_LVAL_P(*data); break; @@ -88,8 +80,7 @@ void php_grpc_read_args_array(zval *args_array, grpc_channel_args *args){ break; default: zend_throw_exception(spl_ce_InvalidArgumentException, - "args values must be int or string", - 1 TSRMLS_CC); + "args values must be int or string", 1 TSRMLS_CC); return; } args_index++; @@ -103,9 +94,9 @@ void php_grpc_read_args_array(zval *args_array, grpc_channel_args *args){ * @param string $target The hostname to associate with this channel * @param array $args The arguments to pass to the Channel (optional) */ -PHP_METHOD(Channel, __construct){ +PHP_METHOD(Channel, __construct) { wrapped_grpc_channel *channel = - (wrapped_grpc_channel*)zend_object_store_get_object(getThis() TSRMLS_CC); + (wrapped_grpc_channel *)zend_object_store_get_object(getThis() TSRMLS_CC); char *target; int target_length; zval *args_array = NULL; @@ -114,30 +105,25 @@ PHP_METHOD(Channel, __construct){ zval **creds_obj = NULL; wrapped_grpc_credentials *creds = NULL; /* "s|a" == 1 string, 1 optional array */ - if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, - "s|a", - &target, &target_length, - &args_array) == FAILURE){ + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|a", &target, + &target_length, &args_array) == FAILURE) { zend_throw_exception(spl_ce_InvalidArgumentException, - "Channel expects a string and an array", - 1 TSRMLS_CC); + "Channel expects a string and an array", 1 TSRMLS_CC); return; } if (args_array == NULL) { channel->wrapped = grpc_channel_create(target, NULL); } else { array_hash = Z_ARRVAL_P(args_array); - if(zend_hash_find(array_hash, - "credentials", - sizeof("credentials"), - (void**)&creds_obj) == SUCCESS) { - if(zend_get_class_entry(*creds_obj TSRMLS_CC) != grpc_ce_credentials) { + if (zend_hash_find(array_hash, "credentials", sizeof("credentials"), + (void **)&creds_obj) == SUCCESS) { + if (zend_get_class_entry(*creds_obj TSRMLS_CC) != grpc_ce_credentials) { zend_throw_exception(spl_ce_InvalidArgumentException, "credentials must be a Credentials object", 1 TSRMLS_CC); return; } - creds = (wrapped_grpc_credentials*)zend_object_store_get_object( + creds = (wrapped_grpc_credentials *)zend_object_store_get_object( *creds_obj TSRMLS_CC); zend_hash_del(array_hash, "credentials", 12); } @@ -146,35 +132,32 @@ PHP_METHOD(Channel, __construct){ channel->wrapped = grpc_channel_create(target, &args); } else { gpr_log(GPR_DEBUG, "Initialized secure channel"); - channel->wrapped = grpc_secure_channel_create(creds->wrapped, - target, - &args); + channel->wrapped = + grpc_secure_channel_create(creds->wrapped, target, &args); } efree(args.args); } - channel->target = ecalloc(target_length+1, sizeof(char)); + channel->target = ecalloc(target_length + 1, sizeof(char)); memcpy(channel->target, target, target_length); } /** * Close the channel */ -PHP_METHOD(Channel, close){ +PHP_METHOD(Channel, close) { wrapped_grpc_channel *channel = - (wrapped_grpc_channel*)zend_object_store_get_object(getThis() TSRMLS_CC); - if(channel->wrapped != NULL) { + (wrapped_grpc_channel *)zend_object_store_get_object(getThis() TSRMLS_CC); + if (channel->wrapped != NULL) { grpc_channel_destroy(channel->wrapped); channel->wrapped = NULL; } } static zend_function_entry channel_methods[] = { - PHP_ME(Channel, __construct, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR) - PHP_ME(Channel, close, NULL, ZEND_ACC_PUBLIC) - PHP_FE_END -}; + PHP_ME(Channel, __construct, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_CTOR) + PHP_ME(Channel, close, NULL, ZEND_ACC_PUBLIC) PHP_FE_END}; -void grpc_init_channel(TSRMLS_D){ +void grpc_init_channel(TSRMLS_D) { zend_class_entry ce; INIT_CLASS_ENTRY(ce, "Grpc\\Channel", channel_methods); ce.create_object = create_wrapped_grpc_channel; diff --git a/src/php/ext/grpc/completion_queue.c b/src/php/ext/grpc/completion_queue.c index 0570bd5029..9785eab8cc 100755..100644 --- a/src/php/ext/grpc/completion_queue.c +++ b/src/php/ext/grpc/completion_queue.c @@ -20,15 +20,15 @@ #include "timeval.h" /* Frees and destroys a wrapped instance of grpc_completion_queue */ -void free_wrapped_grpc_completion_queue(void *object TSRMLS_DC){ +void free_wrapped_grpc_completion_queue(void *object TSRMLS_DC) { wrapped_grpc_completion_queue *queue = NULL; grpc_event *event; - queue = (wrapped_grpc_completion_queue*)object; - if(queue->wrapped != NULL){ + queue = (wrapped_grpc_completion_queue *)object; + if (queue->wrapped != NULL) { grpc_completion_queue_shutdown(queue->wrapped); event = grpc_completion_queue_next(queue->wrapped, gpr_inf_future); - while(event != NULL){ - if(event->type == GRPC_QUEUE_SHUTDOWN){ + while (event != NULL) { + if (event->type == GRPC_QUEUE_SHUTDOWN) { break; } event = grpc_completion_queue_next(queue->wrapped, gpr_inf_future); @@ -41,21 +41,19 @@ void free_wrapped_grpc_completion_queue(void *object TSRMLS_DC){ /* Initializes an instance of wrapped_grpc_channel to be associated with an * object of a class specified by class_type */ zend_object_value create_wrapped_grpc_completion_queue( - zend_class_entry *class_type TSRMLS_DC){ + zend_class_entry *class_type TSRMLS_DC) { zend_object_value retval; wrapped_grpc_completion_queue *intern; - intern = (wrapped_grpc_completion_queue*)emalloc( + intern = (wrapped_grpc_completion_queue *)emalloc( sizeof(wrapped_grpc_completion_queue)); memset(intern, 0, sizeof(wrapped_grpc_completion_queue)); zend_object_std_init(&intern->std, class_type TSRMLS_CC); object_properties_init(&intern->std, class_type); retval.handle = zend_objects_store_put( - intern, - (zend_objects_store_dtor_t) zend_objects_destroy_object, - free_wrapped_grpc_completion_queue, - NULL TSRMLS_CC); + intern, (zend_objects_store_dtor_t)zend_objects_destroy_object, + free_wrapped_grpc_completion_queue, NULL TSRMLS_CC); retval.handlers = zend_get_std_object_handlers(); return retval; } @@ -63,10 +61,10 @@ zend_object_value create_wrapped_grpc_completion_queue( /** * Construct an instance of CompletionQueue */ -PHP_METHOD(CompletionQueue, __construct){ +PHP_METHOD(CompletionQueue, __construct) { wrapped_grpc_completion_queue *queue = - (wrapped_grpc_completion_queue*)zend_object_store_get_object( - getThis() TSRMLS_CC); + (wrapped_grpc_completion_queue *)zend_object_store_get_object( + getThis() TSRMLS_CC); queue->wrapped = grpc_completion_queue_create(); } @@ -78,52 +76,46 @@ PHP_METHOD(CompletionQueue, __construct){ * @param Timeval $timeout The timeout for the event * @return Event The event that occurred */ -PHP_METHOD(CompletionQueue, next){ +PHP_METHOD(CompletionQueue, next) { zval *timeout; /* "O" == 1 Object */ - if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, - "O", - &timeout, grpc_ce_timeval)==FAILURE){ + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &timeout, + grpc_ce_timeval) == FAILURE) { zend_throw_exception(spl_ce_InvalidArgumentException, - "next needs a Timeval", - 1 TSRMLS_CC); + "next needs a Timeval", 1 TSRMLS_CC); return; } wrapped_grpc_completion_queue *completion_queue = - (wrapped_grpc_completion_queue*)zend_object_store_get_object( - getThis() TSRMLS_CC); + (wrapped_grpc_completion_queue *)zend_object_store_get_object( + getThis() TSRMLS_CC); wrapped_grpc_timeval *wrapped_timeout = - (wrapped_grpc_timeval*)zend_object_store_get_object(timeout TSRMLS_CC); + (wrapped_grpc_timeval *)zend_object_store_get_object(timeout TSRMLS_CC); grpc_event *event = grpc_completion_queue_next(completion_queue->wrapped, wrapped_timeout->wrapped); - if(event == NULL){ + if (event == NULL) { RETURN_NULL(); } zval *wrapped_event = grpc_php_convert_event(event); RETURN_DESTROY_ZVAL(wrapped_event); } -PHP_METHOD(CompletionQueue, pluck){ +PHP_METHOD(CompletionQueue, pluck) { long tag; zval *timeout; /* "lO" == 1 long, 1 Object */ - if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, - "lO", - &tag, - &timeout, grpc_ce_timeval)==FAILURE){ + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "lO", &tag, &timeout, + grpc_ce_timeval) == FAILURE) { zend_throw_exception(spl_ce_InvalidArgumentException, - "pluck needs a long and a Timeval", - 1 TSRMLS_CC); + "pluck needs a long and a Timeval", 1 TSRMLS_CC); } wrapped_grpc_completion_queue *completion_queue = - (wrapped_grpc_completion_queue*)zend_object_store_get_object( + (wrapped_grpc_completion_queue *)zend_object_store_get_object( getThis() TSRMLS_CC); wrapped_grpc_timeval *wrapped_timeout = - (wrapped_grpc_timeval*)zend_object_store_get_object(timeout TSRMLS_CC); - grpc_event *event = grpc_completion_queue_pluck(completion_queue->wrapped, - (void*)tag, - wrapped_timeout->wrapped); - if(event == NULL){ + (wrapped_grpc_timeval *)zend_object_store_get_object(timeout TSRMLS_CC); + grpc_event *event = grpc_completion_queue_pluck( + completion_queue->wrapped, (void *)tag, wrapped_timeout->wrapped); + if (event == NULL) { RETURN_NULL(); } zval *wrapped_event = grpc_php_convert_event(event); @@ -131,13 +123,11 @@ PHP_METHOD(CompletionQueue, pluck){ } static zend_function_entry completion_queue_methods[] = { - PHP_ME(CompletionQueue, __construct, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR) - PHP_ME(CompletionQueue, next, NULL, ZEND_ACC_PUBLIC) - PHP_ME(CompletionQueue, pluck, NULL, ZEND_ACC_PUBLIC) - PHP_FE_END -}; + PHP_ME(CompletionQueue, __construct, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_CTOR) + PHP_ME(CompletionQueue, next, NULL, ZEND_ACC_PUBLIC) + PHP_ME(CompletionQueue, pluck, NULL, ZEND_ACC_PUBLIC) PHP_FE_END}; -void grpc_init_completion_queue(TSRMLS_D){ +void grpc_init_completion_queue(TSRMLS_D) { zend_class_entry ce; INIT_CLASS_ENTRY(ce, "Grpc\\CompletionQueue", completion_queue_methods); ce.create_object = create_wrapped_grpc_completion_queue; diff --git a/src/php/ext/grpc/config.m4 b/src/php/ext/grpc/config.m4 index 40e4dd3379..d7d13f413e 100755 --- a/src/php/ext/grpc/config.m4 +++ b/src/php/ext/grpc/config.m4 @@ -32,6 +32,9 @@ if test "$PHP_GRPC" != "no"; then GRPC_SHARED_LIBADD="-lpthread $GRPC_SHARED_LIBADD" PHP_ADD_LIBRARY(pthread) + PHP_ADD_LIBRARY(dl,,GRPC_SHARED_LIBADD) + PHP_ADD_LIBRARY(dl) + PHP_ADD_LIBRARY(rt,,GRPC_SHARED_LIBADD) PHP_ADD_LIBRARY(rt) diff --git a/src/php/ext/grpc/credentials.c b/src/php/ext/grpc/credentials.c index ffafddae5f..f486272531 100755..100644 --- a/src/php/ext/grpc/credentials.c +++ b/src/php/ext/grpc/credentials.c @@ -17,9 +17,9 @@ #include "grpc/grpc_security.h" /* Frees and destroys an instance of wrapped_grpc_credentials */ -void free_wrapped_grpc_credentials(void *object TSRMLS_DC){ - wrapped_grpc_credentials *creds = (wrapped_grpc_credentials*)object; - if(creds->wrapped != NULL) { +void free_wrapped_grpc_credentials(void *object TSRMLS_DC) { + wrapped_grpc_credentials *creds = (wrapped_grpc_credentials *)object; + if (creds->wrapped != NULL) { grpc_credentials_release(creds->wrapped); } efree(creds); @@ -27,32 +27,31 @@ void free_wrapped_grpc_credentials(void *object TSRMLS_DC){ /* Initializes an instance of wrapped_grpc_credentials to be associated with an * object of a class specified by class_type */ -zend_object_value create_wrapped_grpc_credentials( - zend_class_entry *class_type TSRMLS_DC){ +zend_object_value create_wrapped_grpc_credentials(zend_class_entry *class_type + TSRMLS_DC) { zend_object_value retval; wrapped_grpc_credentials *intern; - intern = (wrapped_grpc_credentials*)emalloc(sizeof(wrapped_grpc_credentials)); + intern = + (wrapped_grpc_credentials *)emalloc(sizeof(wrapped_grpc_credentials)); memset(intern, 0, sizeof(wrapped_grpc_credentials)); zend_object_std_init(&intern->std, class_type TSRMLS_CC); object_properties_init(&intern->std, class_type); retval.handle = zend_objects_store_put( - intern, - (zend_objects_store_dtor_t) zend_objects_destroy_object, - free_wrapped_grpc_credentials, - NULL TSRMLS_CC); + intern, (zend_objects_store_dtor_t)zend_objects_destroy_object, + free_wrapped_grpc_credentials, NULL TSRMLS_CC); retval.handlers = zend_get_std_object_handlers(); return retval; } -zval *grpc_php_wrap_credentials(grpc_credentials *wrapped){ +zval *grpc_php_wrap_credentials(grpc_credentials *wrapped) { zval *credentials_object; MAKE_STD_ZVAL(credentials_object); object_init_ex(credentials_object, grpc_ce_credentials); wrapped_grpc_credentials *credentials = - (wrapped_grpc_credentials*)zend_object_store_get_object( - credentials_object TSRMLS_CC); + (wrapped_grpc_credentials *)zend_object_store_get_object( + credentials_object TSRMLS_CC); credentials->wrapped = wrapped; return credentials_object; } @@ -61,7 +60,7 @@ zval *grpc_php_wrap_credentials(grpc_credentials *wrapped){ * Create a default credentials object. * @return Credentials The new default credentials object */ -PHP_METHOD(Credentials, createDefault){ +PHP_METHOD(Credentials, createDefault) { grpc_credentials *creds = grpc_default_credentials_create(); zval *creds_object = grpc_php_wrap_credentials(creds); RETURN_DESTROY_ZVAL(creds_object); @@ -76,7 +75,7 @@ PHP_METHOD(Credentials, createDefault){ * (optional) * @return Credentials The new SSL credentials object */ -PHP_METHOD(Credentials, createSsl){ +PHP_METHOD(Credentials, createSsl) { char *pem_root_certs; char *pem_private_key = NULL; char *pem_cert_chain = NULL; @@ -84,20 +83,18 @@ PHP_METHOD(Credentials, createSsl){ int root_certs_length, private_key_length = 0, cert_chain_length = 0; /* "s|s!s! == 1 string, 2 optional nullable strings */ - if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, - "s|s!s!", - &pem_root_certs, &root_certs_length, - &pem_private_key, &private_key_length, - &pem_cert_chain, &cert_chain_length) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|s!s!", + &pem_root_certs, &root_certs_length, + &pem_private_key, &private_key_length, + &pem_cert_chain, &cert_chain_length) == FAILURE) { zend_throw_exception(spl_ce_InvalidArgumentException, - "createSsl expects 1 to 3 strings", - 1 TSRMLS_CC); + "createSsl expects 1 to 3 strings", 1 TSRMLS_CC); return; } grpc_credentials *creds = grpc_ssl_credentials_create( - (unsigned char*)pem_root_certs, (size_t)root_certs_length, - (unsigned char*)pem_private_key, (size_t)private_key_length, - (unsigned char*)pem_cert_chain, (size_t)cert_chain_length); + (unsigned char *)pem_root_certs, (size_t)root_certs_length, + (unsigned char *)pem_private_key, (size_t)private_key_length, + (unsigned char *)pem_cert_chain, (size_t)cert_chain_length); zval *creds_object = grpc_php_wrap_credentials(creds); RETURN_DESTROY_ZVAL(creds_object); } @@ -108,28 +105,26 @@ PHP_METHOD(Credentials, createSsl){ * @param Credentials cred2 The second credential * @return Credentials The new composite credentials object */ -PHP_METHOD(Credentials, createComposite){ +PHP_METHOD(Credentials, createComposite) { zval *cred1_obj; zval *cred2_obj; /* "OO" == 3 Objects */ - if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, - "OO", - &cred1_obj, grpc_ce_credentials, - &cred2_obj, grpc_ce_credentials) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "OO", &cred1_obj, + grpc_ce_credentials, &cred2_obj, + grpc_ce_credentials) == FAILURE) { zend_throw_exception(spl_ce_InvalidArgumentException, - "createComposite expects 2 Credentials", - 1 TSRMLS_CC); + "createComposite expects 2 Credentials", 1 TSRMLS_CC); return; } wrapped_grpc_credentials *cred1 = - (wrapped_grpc_credentials*)zend_object_store_get_object( + (wrapped_grpc_credentials *)zend_object_store_get_object( cred1_obj TSRMLS_CC); wrapped_grpc_credentials *cred2 = - (wrapped_grpc_credentials*)zend_object_store_get_object( + (wrapped_grpc_credentials *)zend_object_store_get_object( cred2_obj TSRMLS_CC); - grpc_credentials *creds = grpc_composite_credentials_create(cred1->wrapped, - cred2->wrapped); + grpc_credentials *creds = + grpc_composite_credentials_create(cred1->wrapped, cred2->wrapped); zval *creds_object = grpc_php_wrap_credentials(creds); RETURN_DESTROY_ZVAL(creds_object); } @@ -155,15 +150,16 @@ PHP_METHOD(Credentials, createFake) { } static zend_function_entry credentials_methods[] = { - PHP_ME(Credentials, createDefault, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) - PHP_ME(Credentials, createSsl, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) - PHP_ME(Credentials, createComposite, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) - PHP_ME(Credentials, createGce, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) - PHP_ME(Credentials, createFake, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) - PHP_FE_END -}; - -void grpc_init_credentials(TSRMLS_D){ + PHP_ME(Credentials, createDefault, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(Credentials, createSsl, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(Credentials, createComposite, NULL, + ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(Credentials, createGce, NULL, + ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(Credentials, createFake, NULL, + ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) PHP_FE_END}; + +void grpc_init_credentials(TSRMLS_D) { zend_class_entry ce; INIT_CLASS_ENTRY(ce, "Grpc\\Credentials", credentials_methods); ce.create_object = create_wrapped_grpc_credentials; diff --git a/src/php/ext/grpc/event.c b/src/php/ext/grpc/event.c index c15f479448..b4069f72f0 100755..100644 --- a/src/php/ext/grpc/event.c +++ b/src/php/ext/grpc/event.c @@ -32,24 +32,25 @@ zval *grpc_php_convert_event(grpc_event *event) { zval *event_object; - if(event == NULL) { + if (event == NULL) { return NULL; } MAKE_STD_ZVAL(event_object); object_init(event_object); - add_property_zval(event_object, - "call", - grpc_php_wrap_call(event->call, - event->type==GRPC_SERVER_RPC_NEW)); + add_property_zval( + event_object, "call", + grpc_php_wrap_call(event->call, event->type == GRPC_SERVER_RPC_NEW)); add_property_long(event_object, "type", event->type); add_property_long(event_object, "tag", (long)event->tag); - switch(event->type){ - case GRPC_QUEUE_SHUTDOWN: add_property_null(event_object, "data"); break; + switch (event->type) { + case GRPC_QUEUE_SHUTDOWN: + add_property_null(event_object, "data"); + break; case GRPC_READ: - if(event->data.read == NULL){ + if (event->data.read == NULL) { add_property_null(event_object, "data"); } else { byte_buffer_to_string(event->data.read, &read_string, &read_len); @@ -57,16 +58,14 @@ zval *grpc_php_convert_event(grpc_event *event) { } break; case GRPC_INVOKE_ACCEPTED: - add_property_long(event_object, - "data", + add_property_long(event_object, "data", (long)event->data.invoke_accepted); break; case GRPC_WRITE_ACCEPTED: add_property_long(event_object, "data", (long)event->data.write_accepted); break; case GRPC_FINISH_ACCEPTED: - add_property_long(event_object, - "data", + add_property_long(event_object, "data", (long)event->data.finish_accepted); break; case GRPC_CLIENT_METADATA_READ: @@ -79,19 +78,15 @@ zval *grpc_php_convert_event(grpc_event *event) { MAKE_STD_ZVAL(data_object); object_init(data_object); add_property_long(data_object, "code", event->data.finished.status); - if(event->data.finished.details == NULL){ + if (event->data.finished.details == NULL) { add_property_null(data_object, "details"); } else { detail_len = strlen(event->data.finished.details); - detail_string = ecalloc(detail_len+1, sizeof(char)); + detail_string = ecalloc(detail_len + 1, sizeof(char)); memcpy(detail_string, event->data.finished.details, detail_len); - add_property_string(data_object, - "details", - detail_string, - true); + add_property_string(data_object, "details", detail_string, true); } - add_property_zval(data_object, - "metadata", + add_property_zval(data_object, "metadata", grpc_call_create_metadata_array( event->data.finished.metadata_count, event->data.finished.metadata_elements)); @@ -101,31 +96,25 @@ zval *grpc_php_convert_event(grpc_event *event) { MAKE_STD_ZVAL(data_object); object_init(data_object); method_len = strlen(event->data.server_rpc_new.method); - method_string = ecalloc(method_len+1, sizeof(char)); + method_string = ecalloc(method_len + 1, sizeof(char)); memcpy(method_string, event->data.server_rpc_new.method, method_len); - add_property_string(data_object, - "method", - method_string, - false); + add_property_string(data_object, "method", method_string, false); host_len = strlen(event->data.server_rpc_new.host); - host_string = ecalloc(host_len+1, sizeof(char)); + host_string = ecalloc(host_len + 1, sizeof(char)); memcpy(host_string, event->data.server_rpc_new.host, host_len); - add_property_string(data_object, - "host", - host_string, - false); - add_property_zval(data_object, - "absolute_timeout", - grpc_php_wrap_timeval( - event->data.server_rpc_new.deadline)); - add_property_zval(data_object, - "metadata", + add_property_string(data_object, "host", host_string, false); + add_property_zval( + data_object, "absolute_timeout", + grpc_php_wrap_timeval(event->data.server_rpc_new.deadline)); + add_property_zval(data_object, "metadata", grpc_call_create_metadata_array( event->data.server_rpc_new.metadata_count, event->data.server_rpc_new.metadata_elements)); add_property_zval(event_object, "data", data_object); break; - default: add_property_null(event_object, "data"); break; + default: + add_property_null(event_object, "data"); + break; } grpc_event_finish(event); return event_object; diff --git a/src/php/ext/grpc/php_grpc.c b/src/php/ext/grpc/php_grpc.c index 71449bfd06..c1042293aa 100755..100644 --- a/src/php/ext/grpc/php_grpc.c +++ b/src/php/ext/grpc/php_grpc.c @@ -16,14 +16,14 @@ #include "ext/standard/info.h" #include "php_grpc.h" -//ZEND_DECLARE_MODULE_GLOBALS(grpc) +// ZEND_DECLARE_MODULE_GLOBALS(grpc) /* {{{ grpc_functions[] * * Every user visible function must have an entry in grpc_functions[]. */ const zend_function_entry grpc_functions[] = { - PHP_FE_END /* Must be the last line in grpc_functions[] */ + PHP_FE_END /* Must be the last line in grpc_functions[] */ }; /* }}} */ @@ -33,18 +33,12 @@ zend_module_entry grpc_module_entry = { #if ZEND_MODULE_API_NO >= 20010901 STANDARD_MODULE_HEADER, #endif - "grpc", - grpc_functions, - PHP_MINIT(grpc), - PHP_MSHUTDOWN(grpc), - NULL, - NULL, + "grpc", grpc_functions, PHP_MINIT(grpc), PHP_MSHUTDOWN(grpc), NULL, NULL, PHP_MINFO(grpc), #if ZEND_MODULE_API_NO >= 20010901 PHP_GRPC_VERSION, #endif - STANDARD_MODULE_PROPERTIES -}; + STANDARD_MODULE_PROPERTIES}; /* }}} */ #ifdef COMPILE_DL_GRPC @@ -55,8 +49,10 @@ ZEND_GET_MODULE(grpc) */ /* Remove comments and fill if you need to have entries in php.ini PHP_INI_BEGIN() - STD_PHP_INI_ENTRY("grpc.global_value", "42", PHP_INI_ALL, OnUpdateLong, global_value, zend_grpc_globals, grpc_globals) - STD_PHP_INI_ENTRY("grpc.global_string", "foobar", PHP_INI_ALL, OnUpdateString, global_string, zend_grpc_globals, grpc_globals) + STD_PHP_INI_ENTRY("grpc.global_value", "42", PHP_INI_ALL, OnUpdateLong, +global_value, zend_grpc_globals, grpc_globals) + STD_PHP_INI_ENTRY("grpc.global_string", "foobar", PHP_INI_ALL, +OnUpdateString, global_string, zend_grpc_globals, grpc_globals) PHP_INI_END() */ /* }}} */ @@ -74,159 +70,118 @@ static void php_grpc_init_globals(zend_grpc_globals *grpc_globals) /* {{{ PHP_MINIT_FUNCTION */ -PHP_MINIT_FUNCTION(grpc) -{ - /* If you have INI entries, uncomment these lines - REGISTER_INI_ENTRIES(); - */ - /* Register call error constants */ - grpc_init(); - REGISTER_LONG_CONSTANT("Grpc\\CALL_OK", GRPC_CALL_OK, CONST_CS); - REGISTER_LONG_CONSTANT("Grpc\\CALL_ERROR", GRPC_CALL_ERROR, CONST_CS); - REGISTER_LONG_CONSTANT("Grpc\\CALL_ERROR_NOT_ON_SERVER", - GRPC_CALL_ERROR_NOT_ON_SERVER, - CONST_CS); - REGISTER_LONG_CONSTANT("Grpc\\CALL_ERROR_NOT_ON_CLIENT", - GRPC_CALL_ERROR_NOT_ON_CLIENT, - CONST_CS); - REGISTER_LONG_CONSTANT("Grpc\\CALL_ERROR_ALREADY_INVOKED", - GRPC_CALL_ERROR_ALREADY_INVOKED, - CONST_CS); - REGISTER_LONG_CONSTANT("Grpc\\CALL_ERROR_NOT_INVOKED", - GRPC_CALL_ERROR_NOT_INVOKED, - CONST_CS); - REGISTER_LONG_CONSTANT("Grpc\\CALL_ERROR_ALREADY_FINISHED", - GRPC_CALL_ERROR_ALREADY_FINISHED, - CONST_CS); - REGISTER_LONG_CONSTANT("Grpc\\CALL_ERROR_TOO_MANY_OPERATIONS", - GRPC_CALL_ERROR_TOO_MANY_OPERATIONS, - CONST_CS); - REGISTER_LONG_CONSTANT("Grpc\\CALL_ERROR_INVALID_FLAGS", - GRPC_CALL_ERROR_INVALID_FLAGS, - CONST_CS); - - /* Register op error constants */ - REGISTER_LONG_CONSTANT("Grpc\\OP_OK", GRPC_OP_OK, CONST_CS); - REGISTER_LONG_CONSTANT("Grpc\\OP_ERROR", GRPC_OP_ERROR, CONST_CS); - - /* Register flag constants */ - REGISTER_LONG_CONSTANT("Grpc\\WRITE_BUFFER_HINT", - GRPC_WRITE_BUFFER_HINT, - CONST_CS); - REGISTER_LONG_CONSTANT("Grpc\\WRITE_NO_COMPRESS", - GRPC_WRITE_NO_COMPRESS, - CONST_CS); - - /* Register completion type constants */ - REGISTER_LONG_CONSTANT("Grpc\\QUEUE_SHUTDOWN", - GRPC_QUEUE_SHUTDOWN, - CONST_CS); - REGISTER_LONG_CONSTANT("Grpc\\READ", GRPC_READ, CONST_CS); - REGISTER_LONG_CONSTANT("Grpc\\INVOKE_ACCEPTED", - GRPC_INVOKE_ACCEPTED, - CONST_CS); - REGISTER_LONG_CONSTANT("Grpc\\WRITE_ACCEPTED", - GRPC_WRITE_ACCEPTED, - CONST_CS); - REGISTER_LONG_CONSTANT("Grpc\\FINISH_ACCEPTED", - GRPC_FINISH_ACCEPTED, - CONST_CS); - REGISTER_LONG_CONSTANT("Grpc\\CLIENT_METADATA_READ", - GRPC_CLIENT_METADATA_READ, - CONST_CS); - REGISTER_LONG_CONSTANT("Grpc\\FINISHED", GRPC_FINISHED, CONST_CS); - REGISTER_LONG_CONSTANT("Grpc\\SERVER_RPC_NEW", - GRPC_SERVER_RPC_NEW, - CONST_CS); - - /* Register status constants */ - REGISTER_LONG_CONSTANT("Grpc\\STATUS_OK", - GRPC_STATUS_OK, - CONST_CS); - REGISTER_LONG_CONSTANT("Grpc\\STATUS_CANCELLED", - GRPC_STATUS_CANCELLED, - CONST_CS); - REGISTER_LONG_CONSTANT("Grpc\\STATUS_UNKNOWN", - GRPC_STATUS_UNKNOWN, - CONST_CS); - REGISTER_LONG_CONSTANT("Grpc\\STATUS_INVALID_ARGUMENT", - GRPC_STATUS_INVALID_ARGUMENT, - CONST_CS); - REGISTER_LONG_CONSTANT("Grpc\\STATUS_DEADLINE_EXCEEDED", - GRPC_STATUS_DEADLINE_EXCEEDED, - CONST_CS); - REGISTER_LONG_CONSTANT("Grpc\\STATUS_NOT_FOUND", - GRPC_STATUS_NOT_FOUND, - CONST_CS); - REGISTER_LONG_CONSTANT("Grpc\\STATUS_ALREADY_EXISTS", - GRPC_STATUS_ALREADY_EXISTS, - CONST_CS); - REGISTER_LONG_CONSTANT("Grpc\\STATUS_PERMISSION_DENIED", - GRPC_STATUS_PERMISSION_DENIED, - CONST_CS); - REGISTER_LONG_CONSTANT("Grpc\\STATUS_UNAUTHENTICATED", - GRPC_STATUS_UNAUTHENTICATED, - CONST_CS); - REGISTER_LONG_CONSTANT("Grpc\\STATUS_RESOURCE_EXHAUSTED", - GRPC_STATUS_RESOURCE_EXHAUSTED, - CONST_CS); - REGISTER_LONG_CONSTANT("Grpc\\STATUS_FAILED_PRECONDITION", - GRPC_STATUS_FAILED_PRECONDITION, - CONST_CS); - REGISTER_LONG_CONSTANT("Grpc\\STATUS_ABORTED", - GRPC_STATUS_ABORTED, - CONST_CS); - REGISTER_LONG_CONSTANT("Grpc\\STATUS_OUT_OF_RANGE", - GRPC_STATUS_OUT_OF_RANGE, - CONST_CS); - REGISTER_LONG_CONSTANT("Grpc\\STATUS_UNIMPLEMENTED", - GRPC_STATUS_UNIMPLEMENTED, - CONST_CS); - REGISTER_LONG_CONSTANT("Grpc\\STATUS_INTERNAL", - GRPC_STATUS_INTERNAL, - CONST_CS); - REGISTER_LONG_CONSTANT("Grpc\\STATUS_UNAVAILABLE", - GRPC_STATUS_UNAVAILABLE, - CONST_CS); - REGISTER_LONG_CONSTANT("Grpc\\STATUS_DATA_LOSS", - GRPC_STATUS_DATA_LOSS, - CONST_CS); - - grpc_init_call(TSRMLS_C); - grpc_init_channel(TSRMLS_C); - grpc_init_server(TSRMLS_C); - grpc_init_completion_queue(TSRMLS_C); - grpc_init_timeval(TSRMLS_C); - grpc_init_credentials(TSRMLS_C); - grpc_init_server_credentials(TSRMLS_C); - return SUCCESS; +PHP_MINIT_FUNCTION(grpc) { + /* If you have INI entries, uncomment these lines + REGISTER_INI_ENTRIES(); + */ + /* Register call error constants */ + grpc_init(); + REGISTER_LONG_CONSTANT("Grpc\\CALL_OK", GRPC_CALL_OK, CONST_CS); + REGISTER_LONG_CONSTANT("Grpc\\CALL_ERROR", GRPC_CALL_ERROR, CONST_CS); + REGISTER_LONG_CONSTANT("Grpc\\CALL_ERROR_NOT_ON_SERVER", + GRPC_CALL_ERROR_NOT_ON_SERVER, CONST_CS); + REGISTER_LONG_CONSTANT("Grpc\\CALL_ERROR_NOT_ON_CLIENT", + GRPC_CALL_ERROR_NOT_ON_CLIENT, CONST_CS); + REGISTER_LONG_CONSTANT("Grpc\\CALL_ERROR_ALREADY_INVOKED", + GRPC_CALL_ERROR_ALREADY_INVOKED, CONST_CS); + REGISTER_LONG_CONSTANT("Grpc\\CALL_ERROR_NOT_INVOKED", + GRPC_CALL_ERROR_NOT_INVOKED, CONST_CS); + REGISTER_LONG_CONSTANT("Grpc\\CALL_ERROR_ALREADY_FINISHED", + GRPC_CALL_ERROR_ALREADY_FINISHED, CONST_CS); + REGISTER_LONG_CONSTANT("Grpc\\CALL_ERROR_TOO_MANY_OPERATIONS", + GRPC_CALL_ERROR_TOO_MANY_OPERATIONS, CONST_CS); + REGISTER_LONG_CONSTANT("Grpc\\CALL_ERROR_INVALID_FLAGS", + GRPC_CALL_ERROR_INVALID_FLAGS, CONST_CS); + + /* Register op error constants */ + REGISTER_LONG_CONSTANT("Grpc\\OP_OK", GRPC_OP_OK, CONST_CS); + REGISTER_LONG_CONSTANT("Grpc\\OP_ERROR", GRPC_OP_ERROR, CONST_CS); + + /* Register flag constants */ + REGISTER_LONG_CONSTANT("Grpc\\WRITE_BUFFER_HINT", GRPC_WRITE_BUFFER_HINT, + CONST_CS); + REGISTER_LONG_CONSTANT("Grpc\\WRITE_NO_COMPRESS", GRPC_WRITE_NO_COMPRESS, + CONST_CS); + + /* Register completion type constants */ + REGISTER_LONG_CONSTANT("Grpc\\QUEUE_SHUTDOWN", GRPC_QUEUE_SHUTDOWN, CONST_CS); + REGISTER_LONG_CONSTANT("Grpc\\READ", GRPC_READ, CONST_CS); + REGISTER_LONG_CONSTANT("Grpc\\INVOKE_ACCEPTED", GRPC_INVOKE_ACCEPTED, + CONST_CS); + REGISTER_LONG_CONSTANT("Grpc\\WRITE_ACCEPTED", GRPC_WRITE_ACCEPTED, CONST_CS); + REGISTER_LONG_CONSTANT("Grpc\\FINISH_ACCEPTED", GRPC_FINISH_ACCEPTED, + CONST_CS); + REGISTER_LONG_CONSTANT("Grpc\\CLIENT_METADATA_READ", + GRPC_CLIENT_METADATA_READ, CONST_CS); + REGISTER_LONG_CONSTANT("Grpc\\FINISHED", GRPC_FINISHED, CONST_CS); + REGISTER_LONG_CONSTANT("Grpc\\SERVER_RPC_NEW", GRPC_SERVER_RPC_NEW, CONST_CS); + + /* Register status constants */ + REGISTER_LONG_CONSTANT("Grpc\\STATUS_OK", GRPC_STATUS_OK, CONST_CS); + REGISTER_LONG_CONSTANT("Grpc\\STATUS_CANCELLED", GRPC_STATUS_CANCELLED, + CONST_CS); + REGISTER_LONG_CONSTANT("Grpc\\STATUS_UNKNOWN", GRPC_STATUS_UNKNOWN, CONST_CS); + REGISTER_LONG_CONSTANT("Grpc\\STATUS_INVALID_ARGUMENT", + GRPC_STATUS_INVALID_ARGUMENT, CONST_CS); + REGISTER_LONG_CONSTANT("Grpc\\STATUS_DEADLINE_EXCEEDED", + GRPC_STATUS_DEADLINE_EXCEEDED, CONST_CS); + REGISTER_LONG_CONSTANT("Grpc\\STATUS_NOT_FOUND", GRPC_STATUS_NOT_FOUND, + CONST_CS); + REGISTER_LONG_CONSTANT("Grpc\\STATUS_ALREADY_EXISTS", + GRPC_STATUS_ALREADY_EXISTS, CONST_CS); + REGISTER_LONG_CONSTANT("Grpc\\STATUS_PERMISSION_DENIED", + GRPC_STATUS_PERMISSION_DENIED, CONST_CS); + REGISTER_LONG_CONSTANT("Grpc\\STATUS_UNAUTHENTICATED", + GRPC_STATUS_UNAUTHENTICATED, CONST_CS); + REGISTER_LONG_CONSTANT("Grpc\\STATUS_RESOURCE_EXHAUSTED", + GRPC_STATUS_RESOURCE_EXHAUSTED, CONST_CS); + REGISTER_LONG_CONSTANT("Grpc\\STATUS_FAILED_PRECONDITION", + GRPC_STATUS_FAILED_PRECONDITION, CONST_CS); + REGISTER_LONG_CONSTANT("Grpc\\STATUS_ABORTED", GRPC_STATUS_ABORTED, CONST_CS); + REGISTER_LONG_CONSTANT("Grpc\\STATUS_OUT_OF_RANGE", GRPC_STATUS_OUT_OF_RANGE, + CONST_CS); + REGISTER_LONG_CONSTANT("Grpc\\STATUS_UNIMPLEMENTED", + GRPC_STATUS_UNIMPLEMENTED, CONST_CS); + REGISTER_LONG_CONSTANT("Grpc\\STATUS_INTERNAL", GRPC_STATUS_INTERNAL, + CONST_CS); + REGISTER_LONG_CONSTANT("Grpc\\STATUS_UNAVAILABLE", GRPC_STATUS_UNAVAILABLE, + CONST_CS); + REGISTER_LONG_CONSTANT("Grpc\\STATUS_DATA_LOSS", GRPC_STATUS_DATA_LOSS, + CONST_CS); + + grpc_init_call(TSRMLS_C); + grpc_init_channel(TSRMLS_C); + grpc_init_server(TSRMLS_C); + grpc_init_completion_queue(TSRMLS_C); + grpc_init_timeval(TSRMLS_C); + grpc_init_credentials(TSRMLS_C); + grpc_init_server_credentials(TSRMLS_C); + return SUCCESS; } /* }}} */ /* {{{ PHP_MSHUTDOWN_FUNCTION */ -PHP_MSHUTDOWN_FUNCTION(grpc) -{ - /* uncomment this line if you have INI entries - UNREGISTER_INI_ENTRIES(); - */ - grpc_shutdown_timeval(TSRMLS_C); - grpc_shutdown(); - return SUCCESS; +PHP_MSHUTDOWN_FUNCTION(grpc) { + /* uncomment this line if you have INI entries + UNREGISTER_INI_ENTRIES(); + */ + grpc_shutdown_timeval(TSRMLS_C); + grpc_shutdown(); + return SUCCESS; } /* }}} */ /* {{{ PHP_MINFO_FUNCTION */ -PHP_MINFO_FUNCTION(grpc) -{ - php_info_print_table_start(); - php_info_print_table_header(2, "grpc support", "enabled"); - php_info_print_table_end(); - - /* Remove comments if you have entries in php.ini - DISPLAY_INI_ENTRIES(); - */ +PHP_MINFO_FUNCTION(grpc) { + php_info_print_table_start(); + php_info_print_table_header(2, "grpc support", "enabled"); + php_info_print_table_end(); + + /* Remove comments if you have entries in php.ini + DISPLAY_INI_ENTRIES(); + */ } /* }}} */ /* The previous line is meant for vim and emacs, so it can correctly fold and @@ -235,7 +190,6 @@ PHP_MINFO_FUNCTION(grpc) follow this convention for the convenience of others editing your code. */ - /* * Local variables: * tab-width: 4 diff --git a/src/php/ext/grpc/php_grpc.h b/src/php/ext/grpc/php_grpc.h index 777e0c4368..53cc5dcf6e 100755..100644 --- a/src/php/ext/grpc/php_grpc.h +++ b/src/php/ext/grpc/php_grpc.h @@ -7,14 +7,15 @@ extern zend_module_entry grpc_module_entry; #define phpext_grpc_ptr &grpc_module_entry -#define PHP_GRPC_VERSION "0.1.0" /* Replace with version number for your extension */ +#define PHP_GRPC_VERSION \ + "0.1.0" /* Replace with version number for your extension */ #ifdef PHP_WIN32 -# define PHP_GRPC_API __declspec(dllexport) +#define PHP_GRPC_API __declspec(dllexport) #elif defined(__GNUC__) && __GNUC__ >= 4 -# define PHP_GRPC_API __attribute__ ((visibility("default"))) +#define PHP_GRPC_API __attribute__((visibility("default"))) #else -# define PHP_GRPC_API +#define PHP_GRPC_API #endif #ifdef ZTS @@ -25,11 +26,9 @@ extern zend_module_entry grpc_module_entry; #include "grpc/grpc.h" -#define RETURN_DESTROY_ZVAL(val) \ - RETURN_ZVAL( \ - val, \ - false /* Don't execute copy constructor */, \ - true /* Dealloc original before returning */) +#define RETURN_DESTROY_ZVAL(val) \ + RETURN_ZVAL(val, false /* Don't execute copy constructor */, \ + true /* Dealloc original before returning */) /* These are all function declarations */ /* Code that runs at module initialization */ @@ -40,8 +39,8 @@ PHP_MSHUTDOWN_FUNCTION(grpc); PHP_MINFO_FUNCTION(grpc); /* - Declare any global variables you may need between the BEGIN - and END macros here: + Declare any global variables you may need between the BEGIN + and END macros here: ZEND_BEGIN_MODULE_GLOBALS(grpc) ZEND_END_MODULE_GLOBALS(grpc) @@ -63,4 +62,4 @@ ZEND_END_MODULE_GLOBALS(grpc) #define GRPC_G(v) (grpc_globals.v) #endif -#endif /* PHP_GRPC_H */ +#endif /* PHP_GRPC_H */ diff --git a/src/php/ext/grpc/server.c b/src/php/ext/grpc/server.c index 5af42f76ee..f484375712 100755..100644 --- a/src/php/ext/grpc/server.c +++ b/src/php/ext/grpc/server.c @@ -24,9 +24,9 @@ #include "server_credentials.h" /* Frees and destroys an instance of wrapped_grpc_server */ -void free_wrapped_grpc_server(void *object TSRMLS_DC){ - wrapped_grpc_server *server = (wrapped_grpc_server*)object; - if(server->wrapped != NULL){ +void free_wrapped_grpc_server(void *object TSRMLS_DC) { + wrapped_grpc_server *server = (wrapped_grpc_server *)object; + if (server->wrapped != NULL) { grpc_server_shutdown(server->wrapped); grpc_server_destroy(server->wrapped); } @@ -35,21 +35,19 @@ void free_wrapped_grpc_server(void *object TSRMLS_DC){ /* Initializes an instance of wrapped_grpc_call to be associated with an object * of a class specified by class_type */ -zend_object_value create_wrapped_grpc_server( - zend_class_entry *class_type TSRMLS_DC){ +zend_object_value create_wrapped_grpc_server(zend_class_entry *class_type + TSRMLS_DC) { zend_object_value retval; wrapped_grpc_server *intern; - intern = (wrapped_grpc_server*)emalloc(sizeof(wrapped_grpc_server)); + intern = (wrapped_grpc_server *)emalloc(sizeof(wrapped_grpc_server)); memset(intern, 0, sizeof(wrapped_grpc_server)); zend_object_std_init(&intern->std, class_type TSRMLS_CC); object_properties_init(&intern->std, class_type); retval.handle = zend_objects_store_put( - intern, - (zend_objects_store_dtor_t) zend_objects_destroy_object, - free_wrapped_grpc_server, - NULL TSRMLS_CC); + intern, (zend_objects_store_dtor_t)zend_objects_destroy_object, + free_wrapped_grpc_server, NULL TSRMLS_CC); retval.handlers = zend_get_std_object_handlers(); return retval; } @@ -59,9 +57,9 @@ zend_object_value create_wrapped_grpc_server( * @param CompletionQueue $queue The completion queue to use with the server * @param array $args The arguments to pass to the server (optional) */ -PHP_METHOD(Server, __construct){ +PHP_METHOD(Server, __construct) { wrapped_grpc_server *server = - (wrapped_grpc_server*)zend_object_store_get_object(getThis() TSRMLS_CC); + (wrapped_grpc_server *)zend_object_store_get_object(getThis() TSRMLS_CC); zval *queue_obj; zval *args_array = NULL; grpc_channel_args args; @@ -69,10 +67,8 @@ PHP_METHOD(Server, __construct){ zval **creds_obj = NULL; wrapped_grpc_server_credentials *creds = NULL; /* "O|a" == 1 Object, 1 optional array */ - if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, - "O|a", - &queue_obj, grpc_ce_completion_queue, - &args_array) == FAILURE){ + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O|a", &queue_obj, + grpc_ce_completion_queue, &args_array) == FAILURE) { zend_throw_exception(spl_ce_InvalidArgumentException, "Server expects a CompletionQueue and an array", 1 TSRMLS_CC); @@ -80,24 +76,22 @@ PHP_METHOD(Server, __construct){ } add_property_zval(getThis(), "completion_queue", queue_obj); wrapped_grpc_completion_queue *queue = - (wrapped_grpc_completion_queue*)zend_object_store_get_object( - queue_obj TSRMLS_CC); + (wrapped_grpc_completion_queue *)zend_object_store_get_object( + queue_obj TSRMLS_CC); if (args_array == NULL) { server->wrapped = grpc_server_create(queue->wrapped, NULL); } else { array_hash = Z_ARRVAL_P(args_array); - if(zend_hash_find(array_hash, - "credentials", - sizeof("credentials"), - (void**)&creds_obj) == SUCCESS) { - if(zend_get_class_entry(*creds_obj TSRMLS_CC) != - grpc_ce_server_credentials) { + if (zend_hash_find(array_hash, "credentials", sizeof("credentials"), + (void **)&creds_obj) == SUCCESS) { + if (zend_get_class_entry(*creds_obj TSRMLS_CC) != + grpc_ce_server_credentials) { zend_throw_exception(spl_ce_InvalidArgumentException, "credentials must be a ServerCredentials object", 1 TSRMLS_CC); return; } - creds = (wrapped_grpc_server_credentials*)zend_object_store_get_object( + creds = (wrapped_grpc_server_credentials *)zend_object_store_get_object( *creds_obj TSRMLS_CC); zend_hash_del(array_hash, "credentials", sizeof("credentials")); } @@ -106,9 +100,8 @@ PHP_METHOD(Server, __construct){ server->wrapped = grpc_server_create(queue->wrapped, &args); } else { gpr_log(GPR_DEBUG, "Initialized secure server"); - server->wrapped = grpc_secure_server_create(creds->wrapped, - queue->wrapped, - &args); + server->wrapped = + grpc_secure_server_create(creds->wrapped, queue->wrapped, &args); } efree(args.args); } @@ -120,21 +113,19 @@ PHP_METHOD(Server, __construct){ * @param long $tag_cancel The tag to use if the call is cancelled * @return Void */ -PHP_METHOD(Server, request_call){ +PHP_METHOD(Server, request_call) { grpc_call_error error_code; wrapped_grpc_server *server = - (wrapped_grpc_server*)zend_object_store_get_object(getThis() TSRMLS_CC); + (wrapped_grpc_server *)zend_object_store_get_object(getThis() TSRMLS_CC); long tag_new; /* "l" == 1 long */ - if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, - "l", - &tag_new) == FAILURE){ + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &tag_new) == + FAILURE) { zend_throw_exception(spl_ce_InvalidArgumentException, - "request_call expects a long", - 1 TSRMLS_CC); + "request_call expects a long", 1 TSRMLS_CC); return; } - error_code = grpc_server_request_call(server->wrapped, (void*)tag_new); + error_code = grpc_server_request_call(server->wrapped, (void *)tag_new); MAYBE_THROW_CALL_ERROR(request_call, error_code); } @@ -143,35 +134,31 @@ PHP_METHOD(Server, request_call){ * @param string $addr The address to add * @return true on success, false on failure */ -PHP_METHOD(Server, add_http2_port){ +PHP_METHOD(Server, add_http2_port) { wrapped_grpc_server *server = - (wrapped_grpc_server*)zend_object_store_get_object(getThis() TSRMLS_CC); + (wrapped_grpc_server *)zend_object_store_get_object(getThis() TSRMLS_CC); const char *addr; int addr_len; /* "s" == 1 string */ - if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, - "s", - &addr, &addr_len) == FAILURE){ + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &addr, &addr_len) == + FAILURE) { zend_throw_exception(spl_ce_InvalidArgumentException, - "add_http2_port expects a string", - 1 TSRMLS_CC); + "add_http2_port expects a string", 1 TSRMLS_CC); return; } RETURN_BOOL(grpc_server_add_http2_port(server->wrapped, addr)); } -PHP_METHOD(Server, add_secure_http2_port){ +PHP_METHOD(Server, add_secure_http2_port) { wrapped_grpc_server *server = - (wrapped_grpc_server*)zend_object_store_get_object(getThis() TSRMLS_CC); + (wrapped_grpc_server *)zend_object_store_get_object(getThis() TSRMLS_CC); const char *addr; int addr_len; /* "s" == 1 string */ - if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, - "s", - &addr, &addr_len) == FAILURE){ + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &addr, &addr_len) == + FAILURE) { zend_throw_exception(spl_ce_InvalidArgumentException, - "add_http2_port expects a string", - 1 TSRMLS_CC); + "add_http2_port expects a string", 1 TSRMLS_CC); return; } RETURN_BOOL(grpc_server_add_secure_http2_port(server->wrapped, addr)); @@ -181,22 +168,20 @@ PHP_METHOD(Server, add_secure_http2_port){ * Start a server - tells all listeners to start listening * @return Void */ -PHP_METHOD(Server, start){ +PHP_METHOD(Server, start) { wrapped_grpc_server *server = - (wrapped_grpc_server*)zend_object_store_get_object(getThis() TSRMLS_CC); + (wrapped_grpc_server *)zend_object_store_get_object(getThis() TSRMLS_CC); grpc_server_start(server->wrapped); } static zend_function_entry server_methods[] = { - PHP_ME(Server, __construct, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR) - PHP_ME(Server, request_call, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Server, add_http2_port, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Server, add_secure_http2_port, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Server, start, NULL, ZEND_ACC_PUBLIC) - PHP_FE_END -}; - -void grpc_init_server(TSRMLS_D){ + PHP_ME(Server, __construct, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_CTOR) + PHP_ME(Server, request_call, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Server, add_http2_port, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Server, add_secure_http2_port, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Server, start, NULL, ZEND_ACC_PUBLIC) PHP_FE_END}; + +void grpc_init_server(TSRMLS_D) { zend_class_entry ce; INIT_CLASS_ENTRY(ce, "Grpc\\Server", server_methods); ce.create_object = create_wrapped_grpc_server; diff --git a/src/php/ext/grpc/server_credentials.c b/src/php/ext/grpc/server_credentials.c index b07790b4be..5b9ab3390d 100755..100644 --- a/src/php/ext/grpc/server_credentials.c +++ b/src/php/ext/grpc/server_credentials.c @@ -17,10 +17,10 @@ #include "grpc/grpc_security.h" /* Frees and destroys an instace of wrapped_grpc_server_credentials */ -void free_wrapped_grpc_server_credentials(void *object TSRMLS_DC){ +void free_wrapped_grpc_server_credentials(void *object TSRMLS_DC) { wrapped_grpc_server_credentials *creds = - (wrapped_grpc_server_credentials*)object; - if(creds->wrapped != NULL) { + (wrapped_grpc_server_credentials *)object; + if (creds->wrapped != NULL) { grpc_server_credentials_release(creds->wrapped); } efree(creds); @@ -29,32 +29,30 @@ void free_wrapped_grpc_server_credentials(void *object TSRMLS_DC){ /* Initializes an instace of wrapped_grpc_server_credentials to be associated * with an object of a class specified by class_type */ zend_object_value create_wrapped_grpc_server_credentials( - zend_class_entry *class_type TSRMLS_DC){ + zend_class_entry *class_type TSRMLS_DC) { zend_object_value retval; wrapped_grpc_server_credentials *intern; - intern = (wrapped_grpc_server_credentials*)emalloc(sizeof( - wrapped_grpc_server_credentials)); + intern = (wrapped_grpc_server_credentials *)emalloc( + sizeof(wrapped_grpc_server_credentials)); memset(intern, 0, sizeof(wrapped_grpc_server_credentials)); zend_object_std_init(&intern->std, class_type TSRMLS_CC); object_properties_init(&intern->std, class_type); retval.handle = zend_objects_store_put( - intern, - (zend_objects_store_dtor_t) zend_objects_destroy_object, - free_wrapped_grpc_server_credentials, - NULL TSRMLS_CC); + intern, (zend_objects_store_dtor_t)zend_objects_destroy_object, + free_wrapped_grpc_server_credentials, NULL TSRMLS_CC); retval.handlers = zend_get_std_object_handlers(); return retval; } -zval *grpc_php_wrap_server_credentials(grpc_server_credentials *wrapped){ +zval *grpc_php_wrap_server_credentials(grpc_server_credentials *wrapped) { zval *server_credentials_object; MAKE_STD_ZVAL(server_credentials_object); object_init_ex(server_credentials_object, grpc_ce_server_credentials); wrapped_grpc_server_credentials *server_credentials = - (wrapped_grpc_server_credentials*)zend_object_store_get_object( - server_credentials_object TSRMLS_CC); + (wrapped_grpc_server_credentials *)zend_object_store_get_object( + server_credentials_object TSRMLS_CC); server_credentials->wrapped = wrapped; return server_credentials_object; } @@ -66,7 +64,7 @@ zval *grpc_php_wrap_server_credentials(grpc_server_credentials *wrapped){ * @param string pem_cert_chain PEM encoding of the client's certificate chain * @return Credentials The new SSL credentials object */ -PHP_METHOD(ServerCredentials, createSsl){ +PHP_METHOD(ServerCredentials, createSsl) { char *pem_root_certs = 0; char *pem_private_key; char *pem_cert_chain; @@ -74,20 +72,18 @@ PHP_METHOD(ServerCredentials, createSsl){ int root_certs_length = 0, private_key_length, cert_chain_length; /* "s!ss" == 1 nullable string, 2 strings */ - if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, - "s!ss", - &pem_root_certs, &root_certs_length, - &pem_private_key, &private_key_length, - &pem_cert_chain, &cert_chain_length) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s!ss", &pem_root_certs, + &root_certs_length, &pem_private_key, + &private_key_length, &pem_cert_chain, + &cert_chain_length) == FAILURE) { zend_throw_exception(spl_ce_InvalidArgumentException, - "createSsl expects 3 strings", - 1 TSRMLS_CC); + "createSsl expects 3 strings", 1 TSRMLS_CC); return; } grpc_server_credentials *creds = grpc_ssl_server_credentials_create( - (unsigned char*)pem_root_certs, (size_t)root_certs_length, - (unsigned char*)pem_private_key, (size_t)private_key_length, - (unsigned char*)pem_cert_chain, (size_t)cert_chain_length); + (unsigned char *)pem_root_certs, (size_t)root_certs_length, + (unsigned char *)pem_private_key, (size_t)private_key_length, + (unsigned char *)pem_cert_chain, (size_t)cert_chain_length); zval *creds_object = grpc_php_wrap_server_credentials(creds); RETURN_DESTROY_ZVAL(creds_object); } @@ -96,7 +92,7 @@ PHP_METHOD(ServerCredentials, createSsl){ * Create fake credentials. Only to be used for testing. * @return ServerCredentials The new fake credentials object */ -PHP_METHOD(ServerCredentials, createFake){ +PHP_METHOD(ServerCredentials, createFake) { grpc_server_credentials *creds = grpc_fake_transport_security_server_credentials_create(); zval *creds_object = grpc_php_wrap_server_credentials(creds); @@ -104,12 +100,12 @@ PHP_METHOD(ServerCredentials, createFake){ } static zend_function_entry server_credentials_methods[] = { - PHP_ME(ServerCredentials, createSsl, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) - PHP_ME(ServerCredentials, createFake, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) - PHP_FE_END -}; + PHP_ME(ServerCredentials, createSsl, NULL, + ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(ServerCredentials, createFake, NULL, + ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) PHP_FE_END}; -void grpc_init_server_credentials(TSRMLS_D){ +void grpc_init_server_credentials(TSRMLS_D) { zend_class_entry ce; INIT_CLASS_ENTRY(ce, "Grpc\\ServerCredentials", server_credentials_methods); ce.create_object = create_wrapped_grpc_server_credentials; diff --git a/src/php/ext/grpc/timeval.c b/src/php/ext/grpc/timeval.c index 7b7e0e6443..a5508115e4 100755..100644 --- a/src/php/ext/grpc/timeval.c +++ b/src/php/ext/grpc/timeval.c @@ -18,36 +18,32 @@ #include "grpc/support/time.h" /* Frees and destroys an instance of wrapped_grpc_call */ -void free_wrapped_grpc_timeval(void *object TSRMLS_DC){ - efree(object); -} +void free_wrapped_grpc_timeval(void *object TSRMLS_DC) { efree(object); } /* Initializes an instance of wrapped_grpc_timeval to be associated with an * object of a class specified by class_type */ -zend_object_value create_wrapped_grpc_timeval( - zend_class_entry *class_type TSRMLS_DC){ +zend_object_value create_wrapped_grpc_timeval(zend_class_entry *class_type + TSRMLS_DC) { zend_object_value retval; wrapped_grpc_timeval *intern; - intern = (wrapped_grpc_timeval*)emalloc(sizeof(wrapped_grpc_timeval)); + intern = (wrapped_grpc_timeval *)emalloc(sizeof(wrapped_grpc_timeval)); memset(intern, 0, sizeof(wrapped_grpc_timeval)); zend_object_std_init(&intern->std, class_type TSRMLS_CC); object_properties_init(&intern->std, class_type); retval.handle = zend_objects_store_put( - intern, - (zend_objects_store_dtor_t)zend_objects_destroy_object, - free_wrapped_grpc_timeval, - NULL TSRMLS_CC); + intern, (zend_objects_store_dtor_t)zend_objects_destroy_object, + free_wrapped_grpc_timeval, NULL TSRMLS_CC); retval.handlers = zend_get_std_object_handlers(); return retval; } -zval *grpc_php_wrap_timeval(gpr_timespec wrapped){ +zval *grpc_php_wrap_timeval(gpr_timespec wrapped) { zval *timeval_object; MAKE_STD_ZVAL(timeval_object); object_init_ex(timeval_object, grpc_ce_timeval); wrapped_grpc_timeval *timeval = - (wrapped_grpc_timeval*)zend_object_store_get_object( - timeval_object TSRMLS_CC); + (wrapped_grpc_timeval *)zend_object_store_get_object( + timeval_object TSRMLS_CC); memcpy(&timeval->wrapped, &wrapped, sizeof(gpr_timespec)); return timeval_object; } @@ -56,17 +52,15 @@ zval *grpc_php_wrap_timeval(gpr_timespec wrapped){ * Constructs a new instance of the Timeval class * @param long $usec The number of microseconds in the interval */ -PHP_METHOD(Timeval, __construct){ +PHP_METHOD(Timeval, __construct) { wrapped_grpc_timeval *timeval = - (wrapped_grpc_timeval*)zend_object_store_get_object(getThis() TSRMLS_CC); + (wrapped_grpc_timeval *)zend_object_store_get_object(getThis() TSRMLS_CC); long microseconds; /* "l" == 1 long */ - if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, - "l", - µseconds) == FAILURE){ + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", µseconds) == + FAILURE) { zend_throw_exception(spl_ce_InvalidArgumentException, - "Timeval expects a long", - 1 TSRMLS_CC); + "Timeval expects a long", 1 TSRMLS_CC); return; } gpr_timespec time = gpr_time_from_micros(microseconds); @@ -79,23 +73,21 @@ PHP_METHOD(Timeval, __construct){ * @param Timeval $other The other Timeval object to add * @return Timeval A new Timeval object containing the sum */ -PHP_METHOD(Timeval, add){ +PHP_METHOD(Timeval, add) { zval *other_obj; /* "O" == 1 Object */ - if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, - "O", - &other_obj, grpc_ce_timeval) == FAILURE){ + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &other_obj, + grpc_ce_timeval) == FAILURE) { zend_throw_exception(spl_ce_InvalidArgumentException, - "add expects a Timeval", - 1 TSRMLS_CC); + "add expects a Timeval", 1 TSRMLS_CC); return; } wrapped_grpc_timeval *self = - (wrapped_grpc_timeval*)zend_object_store_get_object(getThis() TSRMLS_CC); + (wrapped_grpc_timeval *)zend_object_store_get_object(getThis() TSRMLS_CC); wrapped_grpc_timeval *other = - (wrapped_grpc_timeval*)zend_object_store_get_object(other_obj TSRMLS_CC); - zval *sum = grpc_php_wrap_timeval(gpr_time_add(self->wrapped, - other->wrapped)); + (wrapped_grpc_timeval *)zend_object_store_get_object(other_obj TSRMLS_CC); + zval *sum = + grpc_php_wrap_timeval(gpr_time_add(self->wrapped, other->wrapped)); RETURN_DESTROY_ZVAL(sum); } @@ -105,23 +97,21 @@ PHP_METHOD(Timeval, add){ * @param Timeval $other The other Timeval object to subtract * @param Timeval A new Timeval object containing the sum */ -PHP_METHOD(Timeval, subtract){ +PHP_METHOD(Timeval, subtract) { zval *other_obj; /* "O" == 1 Object */ - if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, - "O", - &other_obj, grpc_ce_timeval) == FAILURE){ + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &other_obj, + grpc_ce_timeval) == FAILURE) { zend_throw_exception(spl_ce_InvalidArgumentException, - "subtract expects a Timeval", - 1 TSRMLS_CC); + "subtract expects a Timeval", 1 TSRMLS_CC); return; } wrapped_grpc_timeval *self = - (wrapped_grpc_timeval*)zend_object_store_get_object(getThis() TSRMLS_CC); + (wrapped_grpc_timeval *)zend_object_store_get_object(getThis() TSRMLS_CC); wrapped_grpc_timeval *other = - (wrapped_grpc_timeval*)zend_object_store_get_object(other_obj TSRMLS_CC); - zval *diff = grpc_php_wrap_timeval(gpr_time_sub(self->wrapped, - other->wrapped)); + (wrapped_grpc_timeval *)zend_object_store_get_object(other_obj TSRMLS_CC); + zval *diff = + grpc_php_wrap_timeval(gpr_time_sub(self->wrapped, other->wrapped)); RETURN_DESTROY_ZVAL(diff); } @@ -132,22 +122,20 @@ PHP_METHOD(Timeval, subtract){ * @param Timeval $b The second time to compare * @return long */ -PHP_METHOD(Timeval, compare){ +PHP_METHOD(Timeval, compare) { zval *a_obj, *b_obj; /* "OO" == 2 Objects */ - if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, - "OO", - &a_obj, grpc_ce_timeval, - &b_obj, grpc_ce_timeval) == FAILURE){ + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "OO", &a_obj, + grpc_ce_timeval, &b_obj, + grpc_ce_timeval) == FAILURE) { zend_throw_exception(spl_ce_InvalidArgumentException, - "compare expects two Timevals", - 1 TSRMLS_CC); + "compare expects two Timevals", 1 TSRMLS_CC); return; } wrapped_grpc_timeval *a = - (wrapped_grpc_timeval*)zend_object_store_get_object(a_obj TSRMLS_CC); + (wrapped_grpc_timeval *)zend_object_store_get_object(a_obj TSRMLS_CC); wrapped_grpc_timeval *b = - (wrapped_grpc_timeval*)zend_object_store_get_object(b_obj TSRMLS_CC); + (wrapped_grpc_timeval *)zend_object_store_get_object(b_obj TSRMLS_CC); long result = gpr_time_cmp(a->wrapped, b->wrapped); RETURN_LONG(result); } @@ -159,25 +147,23 @@ PHP_METHOD(Timeval, compare){ * @param Timeval $threshold The threshold to check against * @return bool True if $a and $b are within $threshold, False otherwise */ -PHP_METHOD(Timeval, similar){ +PHP_METHOD(Timeval, similar) { zval *a_obj, *b_obj, *thresh_obj; /* "OOO" == 3 Objects */ - if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, - "OOO", - &a_obj, grpc_ce_timeval, - &b_obj, grpc_ce_timeval, - &thresh_obj, grpc_ce_timeval) == FAILURE){ + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "OOO", &a_obj, + grpc_ce_timeval, &b_obj, grpc_ce_timeval, + &thresh_obj, grpc_ce_timeval) == FAILURE) { zend_throw_exception(spl_ce_InvalidArgumentException, - "compare expects three Timevals", - 1 TSRMLS_CC); + "compare expects three Timevals", 1 TSRMLS_CC); return; } wrapped_grpc_timeval *a = - (wrapped_grpc_timeval*)zend_object_store_get_object(a_obj TSRMLS_CC); + (wrapped_grpc_timeval *)zend_object_store_get_object(a_obj TSRMLS_CC); wrapped_grpc_timeval *b = - (wrapped_grpc_timeval*)zend_object_store_get_object(b_obj TSRMLS_CC); + (wrapped_grpc_timeval *)zend_object_store_get_object(b_obj TSRMLS_CC); wrapped_grpc_timeval *thresh = - (wrapped_grpc_timeval*)zend_object_store_get_object(thresh_obj TSRMLS_CC); + (wrapped_grpc_timeval *)zend_object_store_get_object( + thresh_obj TSRMLS_CC); int result = gpr_time_similar(a->wrapped, b->wrapped, thresh->wrapped); RETURN_BOOL(result); } @@ -186,7 +172,7 @@ PHP_METHOD(Timeval, similar){ * Returns the current time as a timeval object * @return Timeval The current time */ -PHP_METHOD(Timeval, now){ +PHP_METHOD(Timeval, now) { zval *now = grpc_php_wrap_timeval(gpr_now()); RETURN_DESTROY_ZVAL(now); } @@ -195,7 +181,7 @@ PHP_METHOD(Timeval, now){ * Returns the zero time interval as a timeval object * @return Timeval Zero length time interval */ -PHP_METHOD(Timeval, zero){ +PHP_METHOD(Timeval, zero) { zval *grpc_php_timeval_zero = grpc_php_wrap_timeval(gpr_time_0); RETURN_ZVAL(grpc_php_timeval_zero, false, /* Copy original before returning? */ @@ -206,7 +192,7 @@ PHP_METHOD(Timeval, zero){ * Returns the infinite future time value as a timeval object * @return Timeval Infinite future time value */ -PHP_METHOD(Timeval, inf_future){ +PHP_METHOD(Timeval, inf_future) { zval *grpc_php_timeval_inf_future = grpc_php_wrap_timeval(gpr_inf_future); RETURN_DESTROY_ZVAL(grpc_php_timeval_inf_future); } @@ -215,7 +201,7 @@ PHP_METHOD(Timeval, inf_future){ * Returns the infinite past time value as a timeval object * @return Timeval Infinite past time value */ -PHP_METHOD(Timeval, inf_past){ +PHP_METHOD(Timeval, inf_past) { zval *grpc_php_timeval_inf_past = grpc_php_wrap_timeval(gpr_inf_past); RETURN_DESTROY_ZVAL(grpc_php_timeval_inf_past); } @@ -224,32 +210,33 @@ PHP_METHOD(Timeval, inf_past){ * Sleep until this time, interpreted as an absolute timeout * @return void */ -PHP_METHOD(Timeval, sleep_until){ +PHP_METHOD(Timeval, sleep_until) { wrapped_grpc_timeval *this = - (wrapped_grpc_timeval*)zend_object_store_get_object(getThis() TSRMLS_CC); + (wrapped_grpc_timeval *)zend_object_store_get_object(getThis() TSRMLS_CC); gpr_sleep_until(this->wrapped); } static zend_function_entry timeval_methods[] = { - PHP_ME(Timeval, __construct, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR) - PHP_ME(Timeval, add, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Timeval, compare, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) - PHP_ME(Timeval, inf_future, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) - PHP_ME(Timeval, inf_past, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) - PHP_ME(Timeval, now, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) - PHP_ME(Timeval, similar, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) - PHP_ME(Timeval, sleep_until, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Timeval, subtract, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Timeval, zero, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) - PHP_FE_END -}; - -void grpc_init_timeval(TSRMLS_D){ + PHP_ME(Timeval, __construct, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_CTOR) PHP_ME( + Timeval, add, NULL, + ZEND_ACC_PUBLIC) PHP_ME(Timeval, compare, NULL, + ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(Timeval, inf_future, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(Timeval, inf_past, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(Timeval, now, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(Timeval, similar, NULL, + ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(Timeval, sleep_until, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Timeval, subtract, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Timeval, zero, NULL, + ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_FE_END}; + +void grpc_init_timeval(TSRMLS_D) { zend_class_entry ce; INIT_CLASS_ENTRY(ce, "Grpc\\Timeval", timeval_methods); ce.create_object = create_wrapped_grpc_timeval; grpc_ce_timeval = zend_register_internal_class(&ce TSRMLS_CC); } -void grpc_shutdown_timeval(TSRMLS_D){ -} +void grpc_shutdown_timeval(TSRMLS_D) {} diff --git a/src/ruby/bin/interop/interop_client.rb b/src/ruby/bin/interop/interop_client.rb index d0478bb4d1..718b0fdb83 100644..100755 --- a/src/ruby/bin/interop/interop_client.rb +++ b/src/ruby/bin/interop/interop_client.rb @@ -1,3 +1,5 @@ +#!/usr/bin/env ruby + # Copyright 2014, Google Inc. # All rights reserved. # @@ -27,7 +29,6 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -#!/usr/bin/env ruby # interop_client is a testing tool that accesses a gRPC interop testing # server and runs a test on it. # diff --git a/src/ruby/bin/interop/interop_server.rb b/src/ruby/bin/interop/interop_server.rb index 53e271e80d..63071f3ec2 100644..100755 --- a/src/ruby/bin/interop/interop_server.rb +++ b/src/ruby/bin/interop/interop_server.rb @@ -1,3 +1,5 @@ +#!/usr/bin/env ruby + # Copyright 2014, Google Inc. # All rights reserved. # @@ -27,8 +29,6 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -#!/usr/bin/env ruby -# # interop_server is a Testing app that runs a gRPC interop testing server. # # It helps validate interoperation b/w gRPC in different environments diff --git a/src/ruby/bin/math_client.rb b/src/ruby/bin/math_client.rb index 5cba9317f4..4df333d085 100644..100755 --- a/src/ruby/bin/math_client.rb +++ b/src/ruby/bin/math_client.rb @@ -1,3 +1,5 @@ +#!/usr/bin/env ruby + # Copyright 2014, Google Inc. # All rights reserved. # @@ -27,8 +29,7 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -#!/usr/bin/env ruby -# + # Sample app that accesses a Calc service running on a Ruby gRPC server and # helps validate RpcServer as a gRPC server using proto2 serialization. # diff --git a/src/ruby/bin/math_server.rb b/src/ruby/bin/math_server.rb index a0f301c3e7..0e47f71e66 100644..100755 --- a/src/ruby/bin/math_server.rb +++ b/src/ruby/bin/math_server.rb @@ -1,3 +1,5 @@ +#!/usr/bin/env ruby + # Copyright 2014, Google Inc. # All rights reserved. # @@ -27,8 +29,6 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -#!/usr/bin/env ruby -# # Sample gRPC Ruby server that implements the Math::Calc service and helps # validate GRPC::RpcServer as GRPC implementation using proto2 serialization. # diff --git a/src/ruby/bin/noproto_client.rb b/src/ruby/bin/noproto_client.rb index 50ae9fb68f..34bdf545ee 100644..100755 --- a/src/ruby/bin/noproto_client.rb +++ b/src/ruby/bin/noproto_client.rb @@ -1,3 +1,5 @@ +#!/usr/bin/env ruby + # Copyright 2014, Google Inc. # All rights reserved. # @@ -27,7 +29,6 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -#!/usr/bin/env ruby # Sample app that helps validate RpcServer without protobuf serialization. # # Usage: $ ruby -S path/to/noproto_client.rb diff --git a/src/ruby/bin/noproto_server.rb b/src/ruby/bin/noproto_server.rb index d410827b22..1bdc192f02 100644..100755 --- a/src/ruby/bin/noproto_server.rb +++ b/src/ruby/bin/noproto_server.rb @@ -1,3 +1,5 @@ +#!/usr/bin/env ruby + # Copyright 2014, Google Inc. # All rights reserved. # @@ -27,7 +29,6 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -#!/usr/bin/env ruby # Sample app that helps validate RpcServer without protobuf serialization. # # Usage: $ path/to/noproto_server.rb diff --git a/src/ruby/ext/grpc/rb_byte_buffer.c b/src/ruby/ext/grpc/rb_byte_buffer.c index b75e853d6b..f73b12c417 100644 --- a/src/ruby/ext/grpc/rb_byte_buffer.c +++ b/src/ruby/ext/grpc/rb_byte_buffer.c @@ -49,7 +49,6 @@ typedef struct grpc_rb_byte_buffer { grpc_byte_buffer *wrapped; } grpc_rb_byte_buffer; - /* Destroys ByteBuffer instances. */ static void grpc_rb_byte_buffer_free(void *p) { grpc_rb_byte_buffer *bb = NULL; @@ -169,7 +168,6 @@ static VALUE grpc_rb_byte_buffer_to_s(VALUE self) { return output_obj; } - /* Initializes ByteBuffer instances. */ static VALUE grpc_rb_byte_buffer_init(VALUE self, VALUE src) { gpr_slice a_slice; @@ -205,8 +203,8 @@ static VALUE grpc_rb_byte_buffer_init(VALUE self, VALUE src) { VALUE rb_cByteBuffer = Qnil; void Init_google_rpc_byte_buffer() { - rb_cByteBuffer = rb_define_class_under(rb_mGoogleRpcCore, "ByteBuffer", - rb_cObject); + rb_cByteBuffer = + rb_define_class_under(rb_mGoogleRpcCore, "ByteBuffer", rb_cObject); /* Allocates an object managed by the ruby runtime */ rb_define_alloc_func(rb_cByteBuffer, grpc_rb_byte_buffer_alloc); @@ -223,7 +221,7 @@ void Init_google_rpc_byte_buffer() { id_empty = rb_intern(""); } -VALUE grpc_rb_byte_buffer_create_with_mark(VALUE mark, grpc_byte_buffer* bb) { +VALUE grpc_rb_byte_buffer_create_with_mark(VALUE mark, grpc_byte_buffer *bb) { grpc_rb_byte_buffer *byte_buffer = NULL; if (bb == NULL) { return Qnil; @@ -236,7 +234,7 @@ VALUE grpc_rb_byte_buffer_create_with_mark(VALUE mark, grpc_byte_buffer* bb) { } /* Gets the wrapped byte_buffer from the ruby wrapper */ -grpc_byte_buffer* grpc_rb_get_wrapped_byte_buffer(VALUE v) { +grpc_byte_buffer *grpc_rb_get_wrapped_byte_buffer(VALUE v) { grpc_rb_byte_buffer *wrapper = NULL; Data_Get_Struct(v, grpc_rb_byte_buffer, wrapper); return wrapper->wrapped; diff --git a/src/ruby/ext/grpc/rb_byte_buffer.h b/src/ruby/ext/grpc/rb_byte_buffer.h index 1bdcfe4019..322c268f37 100644 --- a/src/ruby/ext/grpc/rb_byte_buffer.h +++ b/src/ruby/ext/grpc/rb_byte_buffer.h @@ -51,4 +51,4 @@ VALUE grpc_rb_byte_buffer_create_with_mark(VALUE mark, grpc_byte_buffer* bb); /* Gets the wrapped byte_buffer from its ruby object. */ grpc_byte_buffer* grpc_rb_get_wrapped_byte_buffer(VALUE v); -#endif /* GRPC_RB_BYTE_BUFFER_H_ */ +#endif /* GRPC_RB_BYTE_BUFFER_H_ */ diff --git a/src/ruby/ext/grpc/rb_call.c b/src/ruby/ext/grpc/rb_call.c index bf292fac75..76b80bcaa1 100644 --- a/src/ruby/ext/grpc/rb_call.c +++ b/src/ruby/ext/grpc/rb_call.c @@ -78,7 +78,7 @@ void grpc_rb_call_destroy(void *p) { ref_count = rb_hash_aref(hash_all_calls, OFFT2NUM((VALUE)call)); if (ref_count == Qnil) { - return; /* No longer in the hash, so already deleted */ + return; /* No longer in the hash, so already deleted */ } else if (NUM2UINT(ref_count) == 1) { rb_hash_delete(hash_all_calls, OFFT2NUM((VALUE)call)); grpc_call_destroy(call); @@ -92,9 +92,9 @@ void grpc_rb_call_destroy(void *p) { VALUE rb_error_code_details; /* Obtains the error detail string for given error code */ -const char* grpc_call_error_detail_of(grpc_call_error err) { +const char *grpc_call_error_detail_of(grpc_call_error err) { VALUE detail_ref = rb_hash_aref(rb_error_code_details, UINT2NUM(err)); - const char* detail = "unknown error code!"; + const char *detail = "unknown error code!"; if (detail_ref != Qnil) { detail = StringValueCStr(detail_ref); } @@ -164,7 +164,7 @@ static VALUE grpc_rb_call_add_metadata(int argc, VALUE *argv, VALUE self) { /* "11" == 1 mandatory args, 1 (flags) is optional */ rb_scan_args(argc, argv, "11", &metadata, &flags); if (NIL_P(flags)) { - flags = UINT2NUM(0); /* Default to no flags */ + flags = UINT2NUM(0); /* Default to no flags */ } if (TYPE(metadata) != T_HASH) { rb_raise(rb_eTypeError, "add metadata failed: metadata should be a hash"); @@ -217,14 +217,13 @@ static VALUE grpc_rb_call_start_invoke(int argc, VALUE *argv, VALUE self) { rb_scan_args(argc, argv, "41", &cqueue, &invoke_accepted_tag, &metadata_read_tag, &finished_tag, &flags); if (NIL_P(flags)) { - flags = UINT2NUM(0); /* Default to no flags */ + flags = UINT2NUM(0); /* Default to no flags */ } cq = grpc_rb_get_wrapped_completion_queue(cqueue); Data_Get_Struct(self, grpc_call, call); err = grpc_call_start_invoke(call, cq, ROBJECT(invoke_accepted_tag), ROBJECT(metadata_read_tag), - ROBJECT(finished_tag), - NUM2UINT(flags)); + ROBJECT(finished_tag), NUM2UINT(flags)); if (err != GRPC_CALL_OK) { rb_raise(rb_eCallError, "invoke failed: %s (code=%d)", grpc_call_error_detail_of(err), err); @@ -329,7 +328,7 @@ static VALUE grpc_rb_call_start_write(int argc, VALUE *argv, VALUE self) { /* "21" == 2 mandatory args, 1 (flags) is optional */ rb_scan_args(argc, argv, "21", &byte_buffer, &tag, &flags); if (NIL_P(flags)) { - flags = UINT2NUM(0); /* Default to no flags */ + flags = UINT2NUM(0); /* Default to no flags */ } bfr = grpc_rb_get_wrapped_byte_buffer(byte_buffer); Data_Get_Struct(self, grpc_call, call); @@ -405,7 +404,7 @@ static VALUE grpc_rb_call_server_end_initial_metadata(int argc, VALUE *argv, /* "01" == 1 (flags) is optional */ rb_scan_args(argc, argv, "01", &flags); if (NIL_P(flags)) { - flags = UINT2NUM(0); /* Default to no flags */ + flags = UINT2NUM(0); /* Default to no flags */ } Data_Get_Struct(self, grpc_call, call); err = grpc_call_server_end_initial_metadata(call, NUM2UINT(flags)); @@ -445,7 +444,6 @@ static VALUE grpc_rb_call_server_accept(VALUE self, VALUE cqueue, return Qnil; } - /* rb_cCall is the ruby class that proxies grpc_call. */ VALUE rb_cCall = Qnil; @@ -477,8 +475,8 @@ void Init_google_rpc_error_codes() { /* Add the detail strings to a Hash */ rb_error_code_details = rb_hash_new(); - rb_hash_aset(rb_error_code_details, - UINT2NUM(GRPC_CALL_OK), rb_str_new2("ok")); + rb_hash_aset(rb_error_code_details, UINT2NUM(GRPC_CALL_OK), + rb_str_new2("ok")); rb_hash_aset(rb_error_code_details, UINT2NUM(GRPC_CALL_ERROR), rb_str_new2("unknown error")); rb_hash_aset(rb_error_code_details, UINT2NUM(GRPC_CALL_ERROR_NOT_ON_SERVER), @@ -506,8 +504,8 @@ void Init_google_rpc_error_codes() { void Init_google_rpc_call() { /* CallError inherits from Exception to signal that it is non-recoverable */ - rb_eCallError = rb_define_class_under(rb_mGoogleRpcCore, "CallError", - rb_eException); + rb_eCallError = + rb_define_class_under(rb_mGoogleRpcCore, "CallError", rb_eException); rb_cCall = rb_define_class_under(rb_mGoogleRpcCore, "Call", rb_cObject); /* Prevent allocation or inialization of the Call class */ @@ -519,8 +517,7 @@ void Init_google_rpc_call() { rb_define_method(rb_cCall, "server_accept", grpc_rb_call_server_accept, 2); rb_define_method(rb_cCall, "server_end_initial_metadata", grpc_rb_call_server_end_initial_metadata, -1); - rb_define_method(rb_cCall, "add_metadata", grpc_rb_call_add_metadata, - -1); + rb_define_method(rb_cCall, "add_metadata", grpc_rb_call_add_metadata, -1); rb_define_method(rb_cCall, "cancel", grpc_rb_call_cancel, 0); rb_define_method(rb_cCall, "start_invoke", grpc_rb_call_start_invoke, -1); rb_define_method(rb_cCall, "start_read", grpc_rb_call_start_read, 1); @@ -551,25 +548,24 @@ void Init_google_rpc_call() { } /* Gets the call from the ruby object */ -grpc_call* grpc_rb_get_wrapped_call(VALUE v) { +grpc_call *grpc_rb_get_wrapped_call(VALUE v) { grpc_call *c = NULL; Data_Get_Struct(v, grpc_call, c); return c; } /* Obtains the wrapped object for a given call */ -VALUE grpc_rb_wrap_call(grpc_call* c) { +VALUE grpc_rb_wrap_call(grpc_call *c) { VALUE obj = Qnil; if (c == NULL) { return Qnil; } obj = rb_hash_aref(hash_all_calls, OFFT2NUM((VALUE)c)); - if (obj == Qnil) { /* Not in the hash add it */ + if (obj == Qnil) { /* Not in the hash add it */ rb_hash_aset(hash_all_calls, OFFT2NUM((VALUE)c), UINT2NUM(1)); } else { rb_hash_aset(hash_all_calls, OFFT2NUM((VALUE)c), UINT2NUM(NUM2UINT(obj) + 1)); } - return Data_Wrap_Struct(rb_cCall, GC_NOT_MARKED, grpc_rb_call_destroy, - c); + return Data_Wrap_Struct(rb_cCall, GC_NOT_MARKED, grpc_rb_call_destroy, c); } diff --git a/src/ruby/ext/grpc/rb_call.h b/src/ruby/ext/grpc/rb_call.h index 422e7e7a6c..965e9eef40 100644 --- a/src/ruby/ext/grpc/rb_call.h +++ b/src/ruby/ext/grpc/rb_call.h @@ -56,4 +56,4 @@ extern VALUE rb_eCallError; /* Initializes the Call class. */ void Init_google_rpc_call(); -#endif /* GRPC_RB_CALL_H_ */ +#endif /* GRPC_RB_CALL_H_ */ diff --git a/src/ruby/ext/grpc/rb_channel.c b/src/ruby/ext/grpc/rb_channel.c index d951847662..c0187d2d11 100644 --- a/src/ruby/ext/grpc/rb_channel.c +++ b/src/ruby/ext/grpc/rb_channel.c @@ -137,7 +137,7 @@ static VALUE grpc_rb_channel_init(int argc, VALUE *argv, VALUE self) { ch = grpc_secure_channel_create(creds, target_chars, &args); } if (args.args != NULL) { - xfree(args.args); /* Allocated by grpc_rb_hash_convert_to_channel_args */ + xfree(args.args); /* Allocated by grpc_rb_hash_convert_to_channel_args */ } if (ch == NULL) { rb_raise(rb_eRuntimeError, "could not create an rpc channel to target:%s", @@ -256,7 +256,7 @@ void Init_google_rpc_channel() { } /* Gets the wrapped channel from the ruby wrapper */ -grpc_channel* grpc_rb_get_wrapped_channel(VALUE v) { +grpc_channel *grpc_rb_get_wrapped_channel(VALUE v) { grpc_rb_channel *wrapper = NULL; Data_Get_Struct(v, grpc_rb_channel, wrapper); return wrapper->wrapped; diff --git a/src/ruby/ext/grpc/rb_channel.h b/src/ruby/ext/grpc/rb_channel.h index b0a3634474..6c1210e812 100644 --- a/src/ruby/ext/grpc/rb_channel.h +++ b/src/ruby/ext/grpc/rb_channel.h @@ -46,4 +46,4 @@ void Init_google_rpc_channel(); /* Gets the wrapped channel from the ruby wrapper */ grpc_channel* grpc_rb_get_wrapped_channel(VALUE v); -#endif /* GRPC_RB_CHANNEL_H_ */ +#endif /* GRPC_RB_CHANNEL_H_ */ diff --git a/src/ruby/ext/grpc/rb_channel_args.c b/src/ruby/ext/grpc/rb_channel_args.c index eebced0bd8..b918e1264e 100644 --- a/src/ruby/ext/grpc/rb_channel_args.c +++ b/src/ruby/ext/grpc/rb_channel_args.c @@ -46,7 +46,6 @@ static int grpc_rb_channel_create_in_process_add_args_hash_cb(VALUE key, grpc_channel_args* args; switch (TYPE(key)) { - case T_STRING: the_key = StringValuePtr(key); break; @@ -68,13 +67,12 @@ static int grpc_rb_channel_create_in_process_add_args_hash_cb(VALUE key, return ST_STOP; } - args->args[args->num_args - 1].key = (char *)the_key; + args->args[args->num_args - 1].key = (char*)the_key; switch (TYPE(val)) { - case T_SYMBOL: args->args[args->num_args - 1].type = GRPC_ARG_STRING; args->args[args->num_args - 1].value.string = - (char *)rb_id2name(SYM2ID(val)); + (char*)rb_id2name(SYM2ID(val)); --args->num_args; return ST_CONTINUE; @@ -109,11 +107,10 @@ typedef struct channel_convert_params { grpc_channel_args* dst; } channel_convert_params; - static VALUE grpc_rb_hash_convert_to_channel_args0(VALUE as_value) { ID id_size = rb_intern("size"); VALUE rb_cChannelArgs = rb_define_class("TmpChannelArgs", rb_cObject); - channel_convert_params* params = (channel_convert_params *)as_value; + channel_convert_params* params = (channel_convert_params*)as_value; size_t num_args = 0; if (!NIL_P(params->src_hash) && TYPE(params->src_hash) != T_HASH) { @@ -146,7 +143,7 @@ void grpc_rb_hash_convert_to_channel_args(VALUE src_hash, /* Make a protected call to grpc_rb_hash_convert_channel_args */ params.src_hash = src_hash; params.dst = dst; - rb_protect(grpc_rb_hash_convert_to_channel_args0, (VALUE) ¶ms, &status); + rb_protect(grpc_rb_hash_convert_to_channel_args0, (VALUE)¶ms, &status); if (status != 0) { if (dst->args != NULL) { /* Free any allocated memory before propagating the error */ diff --git a/src/ruby/ext/grpc/rb_channel_args.h b/src/ruby/ext/grpc/rb_channel_args.h index bbff017c1e..07be662793 100644 --- a/src/ruby/ext/grpc/rb_channel_args.h +++ b/src/ruby/ext/grpc/rb_channel_args.h @@ -49,5 +49,4 @@ void grpc_rb_hash_convert_to_channel_args(VALUE src_hash, grpc_channel_args* dst); - -#endif /* GRPC_RB_CHANNEL_ARGS_H_ */ +#endif /* GRPC_RB_CHANNEL_ARGS_H_ */ diff --git a/src/ruby/ext/grpc/rb_completion_queue.c b/src/ruby/ext/grpc/rb_completion_queue.c index dc95838ef5..c1b74e2606 100644 --- a/src/ruby/ext/grpc/rb_completion_queue.c +++ b/src/ruby/ext/grpc/rb_completion_queue.c @@ -45,33 +45,28 @@ typedef struct next_call_stack { grpc_completion_queue *cq; grpc_event *event; gpr_timespec timeout; - void* tag; + void *tag; } next_call_stack; /* Calls grpc_completion_queue_next without holding the ruby GIL */ -static void *grpc_rb_completion_queue_next_no_gil( - next_call_stack *next_call) { - next_call->event = grpc_completion_queue_next(next_call->cq, - next_call->timeout); +static void *grpc_rb_completion_queue_next_no_gil(next_call_stack *next_call) { + next_call->event = + grpc_completion_queue_next(next_call->cq, next_call->timeout); return NULL; } /* Calls grpc_completion_queue_pluck without holding the ruby GIL */ -static void *grpc_rb_completion_queue_pluck_no_gil( - next_call_stack *next_call) { - next_call->event = grpc_completion_queue_pluck(next_call->cq, - next_call->tag, +static void *grpc_rb_completion_queue_pluck_no_gil(next_call_stack *next_call) { + next_call->event = grpc_completion_queue_pluck(next_call->cq, next_call->tag, next_call->timeout); return NULL; } - /* Shuts down and drains the completion queue if necessary. * * This is done when the ruby completion queue object is about to be GCed. */ -static void grpc_rb_completion_queue_shutdown_drain( - grpc_completion_queue* cq) { +static void grpc_rb_completion_queue_shutdown_drain(grpc_completion_queue *cq) { next_call_stack next_call; grpc_completion_type type; int drained = 0; @@ -120,13 +115,12 @@ static void grpc_rb_completion_queue_destroy(void *p) { /* Allocates a completion queue. */ static VALUE grpc_rb_completion_queue_alloc(VALUE cls) { - grpc_completion_queue* cq = grpc_completion_queue_create(); + grpc_completion_queue *cq = grpc_completion_queue_create(); if (cq == NULL) { - rb_raise(rb_eArgError, - "could not create a completion queue: not sure why"); + rb_raise(rb_eArgError, "could not create a completion queue: not sure why"); } - return Data_Wrap_Struct(cls, GC_NOT_MARKED, - grpc_rb_completion_queue_destroy, cq); + return Data_Wrap_Struct(cls, GC_NOT_MARKED, grpc_rb_completion_queue_destroy, + cq); } /* Blocks until the next event is available, and returns the event. */ @@ -166,9 +160,8 @@ static VALUE grpc_rb_completion_queue_pluck(VALUE self, VALUE tag, VALUE rb_cCompletionQueue = Qnil; void Init_google_rpc_completion_queue() { - rb_cCompletionQueue = rb_define_class_under(rb_mGoogleRpcCore, - "CompletionQueue", - rb_cObject); + rb_cCompletionQueue = + rb_define_class_under(rb_mGoogleRpcCore, "CompletionQueue", rb_cObject); /* constructor: uses an alloc func without an initializer. Using a simple alloc func works here as the grpc header does not specify any args for @@ -176,16 +169,16 @@ void Init_google_rpc_completion_queue() { rb_define_alloc_func(rb_cCompletionQueue, grpc_rb_completion_queue_alloc); /* Add the next method that waits for the next event. */ - rb_define_method(rb_cCompletionQueue, "next", - grpc_rb_completion_queue_next, 1); + rb_define_method(rb_cCompletionQueue, "next", grpc_rb_completion_queue_next, + 1); /* Add the pluck method that waits for the next event of given tag */ - rb_define_method(rb_cCompletionQueue, "pluck", - grpc_rb_completion_queue_pluck, 2); + rb_define_method(rb_cCompletionQueue, "pluck", grpc_rb_completion_queue_pluck, + 2); } /* Gets the wrapped completion queue from the ruby wrapper */ -grpc_completion_queue* grpc_rb_get_wrapped_completion_queue(VALUE v) { +grpc_completion_queue *grpc_rb_get_wrapped_completion_queue(VALUE v) { grpc_completion_queue *cq = NULL; Data_Get_Struct(v, grpc_completion_queue, cq); return cq; diff --git a/src/ruby/ext/grpc/rb_completion_queue.h b/src/ruby/ext/grpc/rb_completion_queue.h index 1ec2718ed4..c563662c2d 100644 --- a/src/ruby/ext/grpc/rb_completion_queue.h +++ b/src/ruby/ext/grpc/rb_completion_queue.h @@ -47,4 +47,4 @@ extern VALUE rb_cCompletionQueue; /* Initializes the CompletionQueue class. */ void Init_google_rpc_completion_queue(); -#endif /* GRPC_RB_COMPLETION_QUEUE_H_ */ +#endif /* GRPC_RB_COMPLETION_QUEUE_H_ */ diff --git a/src/ruby/ext/grpc/rb_credentials.c b/src/ruby/ext/grpc/rb_credentials.c index 658adacc06..5dec51824d 100644 --- a/src/ruby/ext/grpc/rb_credentials.c +++ b/src/ruby/ext/grpc/rb_credentials.c @@ -40,7 +40,6 @@ #include "rb_grpc.h" - /* grpc_rb_credentials wraps a grpc_credentials. It provides a * peer ruby object, 'mark' to minimize copying when a credential is * created from ruby. */ @@ -92,8 +91,7 @@ static VALUE grpc_rb_credentials_alloc(VALUE cls) { wrapper->wrapped = NULL; wrapper->mark = Qnil; return Data_Wrap_Struct(cls, grpc_rb_credentials_mark, - grpc_rb_credentials_free, - wrapper); + grpc_rb_credentials_free, wrapper); } /* Clones Credentials instances. @@ -111,8 +109,7 @@ static VALUE grpc_rb_credentials_init_copy(VALUE copy, VALUE orig) { /* Raise an error if orig is not a credentials object or a subclass. */ if (TYPE(orig) != T_DATA || RDATA(orig)->dfree != (RUBY_DATA_FUNC)grpc_rb_credentials_free) { - rb_raise(rb_eTypeError, "not a %s", - rb_obj_classname(rb_cCredentials)); + rb_raise(rb_eTypeError, "not a %s", rb_obj_classname(rb_cCredentials)); } Data_Get_Struct(orig, grpc_rb_credentials, orig_cred); @@ -238,14 +235,12 @@ static VALUE grpc_rb_credentials_init(int argc, VALUE *argv, VALUE self) { RSTRING_PTR(pem_cert_chain), RSTRING_LEN(pem_cert_chain)); } else if (pem_private_key == Qnil) { creds = grpc_ssl_credentials_create( - RSTRING_PTR(pem_root_certs), RSTRING_LEN(pem_root_certs), - NULL, 0, + RSTRING_PTR(pem_root_certs), RSTRING_LEN(pem_root_certs), NULL, 0, RSTRING_PTR(pem_cert_chain), RSTRING_LEN(pem_cert_chain)); } else { creds = grpc_ssl_credentials_create( RSTRING_PTR(pem_root_certs), RSTRING_LEN(pem_root_certs), - RSTRING_PTR(pem_private_key), RSTRING_LEN(pem_private_key), - NULL, 0); + RSTRING_PTR(pem_private_key), RSTRING_LEN(pem_private_key), NULL, 0); } if (creds == NULL) { rb_raise(rb_eRuntimeError, "could not create a credentials, not sure why"); @@ -265,15 +260,14 @@ static VALUE grpc_rb_credentials_init(int argc, VALUE *argv, VALUE self) { VALUE rb_cCredentials = Qnil; void Init_google_rpc_credentials() { - rb_cCredentials = rb_define_class_under(rb_mGoogleRpcCore, "Credentials", - rb_cObject); + rb_cCredentials = + rb_define_class_under(rb_mGoogleRpcCore, "Credentials", rb_cObject); /* Allocates an object managed by the ruby runtime */ rb_define_alloc_func(rb_cCredentials, grpc_rb_credentials_alloc); /* Provides a ruby constructor and support for dup/clone. */ - rb_define_method(rb_cCredentials, "initialize", - grpc_rb_credentials_init, -1); + rb_define_method(rb_cCredentials, "initialize", grpc_rb_credentials_init, -1); rb_define_method(rb_cCredentials, "initialize_copy", grpc_rb_credentials_init_copy, 1); @@ -294,7 +288,7 @@ void Init_google_rpc_credentials() { } /* Gets the wrapped grpc_credentials from the ruby wrapper */ -grpc_credentials* grpc_rb_get_wrapped_credentials(VALUE v) { +grpc_credentials *grpc_rb_get_wrapped_credentials(VALUE v) { grpc_rb_credentials *wrapper = NULL; Data_Get_Struct(v, grpc_rb_credentials, wrapper); return wrapper->wrapped; diff --git a/src/ruby/ext/grpc/rb_credentials.h b/src/ruby/ext/grpc/rb_credentials.h index d18c88ac34..fada3639d5 100644 --- a/src/ruby/ext/grpc/rb_credentials.h +++ b/src/ruby/ext/grpc/rb_credentials.h @@ -47,4 +47,4 @@ void Init_google_rpc_credentials(); /* Gets the wrapped credentials from the ruby wrapper */ grpc_credentials* grpc_rb_get_wrapped_credentials(VALUE v); -#endif /* GRPC_RB_CREDENTIALS_H_ */ +#endif /* GRPC_RB_CREDENTIALS_H_ */ diff --git a/src/ruby/ext/grpc/rb_event.c b/src/ruby/ext/grpc/rb_event.c index 9200f923cc..0fae9502c3 100644 --- a/src/ruby/ext/grpc/rb_event.c +++ b/src/ruby/ext/grpc/rb_event.c @@ -50,7 +50,6 @@ typedef struct grpc_rb_event { grpc_event *wrapped; } grpc_rb_event; - /* rb_mCompletionType is a ruby module that holds the completion type values */ VALUE rb_mCompletionType = Qnil; @@ -107,15 +106,15 @@ static VALUE grpc_rb_event_type(VALUE self) { return rb_const_get(rb_mCompletionType, rb_intern("READ")); case GRPC_INVOKE_ACCEPTED: - grpc_rb_event_result(self); /* validates the result */ + grpc_rb_event_result(self); /* validates the result */ return rb_const_get(rb_mCompletionType, rb_intern("INVOKE_ACCEPTED")); case GRPC_WRITE_ACCEPTED: - grpc_rb_event_result(self); /* validates the result */ + grpc_rb_event_result(self); /* validates the result */ return rb_const_get(rb_mCompletionType, rb_intern("WRITE_ACCEPTED")); case GRPC_FINISH_ACCEPTED: - grpc_rb_event_result(self); /* validates the result */ + grpc_rb_event_result(self); /* validates the result */ return rb_const_get(rb_mCompletionType, rb_intern("FINISH_ACCEPTED")); case GRPC_CLIENT_METADATA_READ: @@ -129,8 +128,8 @@ static VALUE grpc_rb_event_type(VALUE self) { return rb_const_get(rb_mCompletionType, rb_intern("SERVER_RPC_NEW")); default: - rb_raise(rb_eRuntimeError, - "unrecognized event code for an rpc event:%d", event->type); + rb_raise(rb_eRuntimeError, "unrecognized event code for an rpc event:%d", + event->type); } return Qnil; /* should not be reached */ } @@ -189,7 +188,6 @@ static VALUE grpc_rb_event_metadata(VALUE self) { /* Figure out which metadata to read. */ event = wrapper->wrapped; switch (event->type) { - case GRPC_CLIENT_METADATA_READ: count = event->data.client_metadata_read.count; metadata = event->data.client_metadata_read.elements; @@ -218,22 +216,18 @@ static VALUE grpc_rb_event_metadata(VALUE self) { key = rb_str_new2(metadata[i].key); value = rb_hash_aref(result, key); if (value == Qnil) { - value = rb_str_new( - metadata[i].value, - metadata[i].value_length); + value = rb_str_new(metadata[i].value, metadata[i].value_length); rb_hash_aset(result, key, value); } else if (TYPE(value) == T_ARRAY) { /* Add the string to the returned array */ - rb_ary_push(value, rb_str_new( - metadata[i].value, - metadata[i].value_length)); + rb_ary_push(value, + rb_str_new(metadata[i].value, metadata[i].value_length)); } else { /* Add the current value with this key and the new one to an array */ new_ary = rb_ary_new(); rb_ary_push(new_ary, value); - rb_ary_push(new_ary, rb_str_new( - metadata[i].value, - metadata[i].value_length)); + rb_ary_push(new_ary, + rb_str_new(metadata[i].value, metadata[i].value_length)); rb_hash_aset(result, key, new_ary); } } @@ -252,7 +246,6 @@ static VALUE grpc_rb_event_result(VALUE self) { event = wrapper->wrapped; switch (event->type) { - case GRPC_QUEUE_SHUTDOWN: return Qnil; @@ -287,29 +280,24 @@ static VALUE grpc_rb_event_result(VALUE self) { return grpc_rb_event_metadata(self); case GRPC_FINISHED: - return rb_struct_new( - rb_sStatus, - UINT2NUM(event->data.finished.status), - (event->data.finished.details == NULL ? - Qnil : rb_str_new2(event->data.finished.details)), - grpc_rb_event_metadata(self), - NULL); + return rb_struct_new(rb_sStatus, UINT2NUM(event->data.finished.status), + (event->data.finished.details == NULL + ? Qnil + : rb_str_new2(event->data.finished.details)), + grpc_rb_event_metadata(self), NULL); break; case GRPC_SERVER_RPC_NEW: return rb_struct_new( - rb_sNewServerRpc, - rb_str_new2(event->data.server_rpc_new.method), + rb_sNewServerRpc, rb_str_new2(event->data.server_rpc_new.method), rb_str_new2(event->data.server_rpc_new.host), - Data_Wrap_Struct( - rb_cTimeVal, GC_NOT_MARKED, GC_DONT_FREE, - (void *)&event->data.server_rpc_new.deadline), - grpc_rb_event_metadata(self), - NULL); + Data_Wrap_Struct(rb_cTimeVal, GC_NOT_MARKED, GC_DONT_FREE, + (void *)&event->data.server_rpc_new.deadline), + grpc_rb_event_metadata(self), NULL); default: - rb_raise(rb_eRuntimeError, - "unrecognized event code for an rpc event:%d", event->type); + rb_raise(rb_eRuntimeError, "unrecognized event code for an rpc event:%d", + event->type); } return Qfalse; @@ -319,7 +307,7 @@ static VALUE grpc_rb_event_finish(VALUE self) { grpc_event *event = NULL; grpc_rb_event *wrapper = NULL; Data_Get_Struct(self, grpc_rb_event, wrapper); - if (wrapper->wrapped == NULL) { /* already closed */ + if (wrapper->wrapped == NULL) { /* already closed */ return Qnil; } event = wrapper->wrapped; @@ -337,8 +325,8 @@ VALUE rb_cEvent = Qnil; VALUE rb_eEventError = Qnil; void Init_google_rpc_event() { - rb_eEventError = rb_define_class_under(rb_mGoogleRpcCore, "EventError", - rb_eStandardError); + rb_eEventError = + rb_define_class_under(rb_mGoogleRpcCore, "EventError", rb_eStandardError); rb_cEvent = rb_define_class_under(rb_mGoogleRpcCore, "Event", rb_cObject); /* Prevent allocation or inialization from ruby. */ @@ -355,8 +343,8 @@ void Init_google_rpc_event() { rb_define_alias(rb_cEvent, "close", "finish"); /* Constants representing the completion types */ - rb_mCompletionType = rb_define_module_under(rb_mGoogleRpcCore, - "CompletionType"); + rb_mCompletionType = + rb_define_module_under(rb_mGoogleRpcCore, "CompletionType"); rb_define_const(rb_mCompletionType, "QUEUE_SHUTDOWN", INT2NUM(GRPC_QUEUE_SHUTDOWN)); rb_define_const(rb_mCompletionType, "READ", INT2NUM(GRPC_READ)); @@ -368,8 +356,7 @@ void Init_google_rpc_event() { INT2NUM(GRPC_FINISH_ACCEPTED)); rb_define_const(rb_mCompletionType, "CLIENT_METADATA_READ", INT2NUM(GRPC_CLIENT_METADATA_READ)); - rb_define_const(rb_mCompletionType, "FINISHED", - INT2NUM(GRPC_FINISHED)); + rb_define_const(rb_mCompletionType, "FINISHED", INT2NUM(GRPC_FINISHED)); rb_define_const(rb_mCompletionType, "SERVER_RPC_NEW", INT2NUM(GRPC_SERVER_RPC_NEW)); rb_define_const(rb_mCompletionType, "RESERVED", diff --git a/src/ruby/ext/grpc/rb_event.h b/src/ruby/ext/grpc/rb_event.h index e4e4a796c2..a406e9e9f1 100644 --- a/src/ruby/ext/grpc/rb_event.h +++ b/src/ruby/ext/grpc/rb_event.h @@ -50,4 +50,4 @@ VALUE grpc_rb_new_event(grpc_event *ev); /* Initializes the Event and EventError classes. */ void Init_google_rpc_event(); -#endif /* GRPC_RB_EVENT_H_ */ +#endif /* GRPC_RB_EVENT_H_ */ diff --git a/src/ruby/ext/grpc/rb_grpc.c b/src/ruby/ext/grpc/rb_grpc.c index eae011d33b..8feefb047c 100644 --- a/src/ruby/ext/grpc/rb_grpc.c +++ b/src/ruby/ext/grpc/rb_grpc.c @@ -98,18 +98,16 @@ gpr_timespec grpc_rb_time_timeval(VALUE time, int interval) { const char *want = " want <secs from epoch>|<Time>|<GRPC::TimeConst.*>"; switch (TYPE(time)) { - case T_DATA: if (CLASS_OF(time) == rb_cTimeVal) { Data_Get_Struct(time, gpr_timespec, time_const); t = *time_const; } else if (CLASS_OF(time) == rb_cTime) { - t.tv_sec = NUM2INT(rb_funcall(time, id_tv_sec, 0)); + t.tv_sec = NUM2INT(rb_funcall(time, id_tv_sec, 0)); t.tv_nsec = NUM2INT(rb_funcall(time, id_tv_nsec, 0)); } else { - rb_raise(rb_eTypeError, - "bad input: (%s)->c_timeval, got <%s>,%s", - tstr, rb_obj_classname(time), want); + rb_raise(rb_eTypeError, "bad input: (%s)->c_timeval, got <%s>,%s", tstr, + rb_obj_classname(time), want); } break; @@ -136,7 +134,7 @@ gpr_timespec grpc_rb_time_timeval(VALUE time, int interval) { rb_raise(rb_eRangeError, "%f out of Time range", RFLOAT(time)->float_value); } - t.tv_nsec = (time_t)(d*1e9+0.5); + t.tv_nsec = (time_t)(d * 1e9 + 0.5); } break; @@ -148,9 +146,8 @@ gpr_timespec grpc_rb_time_timeval(VALUE time, int interval) { break; default: - rb_raise(rb_eTypeError, - "bad input: (%s)->c_timeval, got <%s>,%s", - tstr, rb_obj_classname(time), want); + rb_raise(rb_eTypeError, "bad input: (%s)->c_timeval, got <%s>,%s", tstr, + rb_obj_classname(time), want); break; } return t; @@ -158,8 +155,8 @@ gpr_timespec grpc_rb_time_timeval(VALUE time, int interval) { void Init_google_status_codes() { /* Constants representing the status codes or grpc_status_code in status.h */ - VALUE rb_mStatusCodes = rb_define_module_under(rb_mGoogleRpcCore, - "StatusCodes"); + VALUE rb_mStatusCodes = + rb_define_module_under(rb_mGoogleRpcCore, "StatusCodes"); rb_define_const(rb_mStatusCodes, "OK", INT2NUM(GRPC_STATUS_OK)); rb_define_const(rb_mStatusCodes, "CANCELLED", INT2NUM(GRPC_STATUS_CANCELLED)); rb_define_const(rb_mStatusCodes, "UNKNOWN", INT2NUM(GRPC_STATUS_UNKNOWN)); @@ -218,19 +215,19 @@ VALUE grpc_rb_time_val_to_s(VALUE self) { /* Adds a module with constants that map to gpr's static timeval structs. */ void Init_google_time_consts() { - VALUE rb_mTimeConsts = rb_define_module_under(rb_mGoogleRpcCore, - "TimeConsts"); - rb_cTimeVal = rb_define_class_under(rb_mGoogleRpcCore, "TimeSpec", - rb_cObject); + VALUE rb_mTimeConsts = + rb_define_module_under(rb_mGoogleRpcCore, "TimeConsts"); + rb_cTimeVal = + rb_define_class_under(rb_mGoogleRpcCore, "TimeSpec", rb_cObject); rb_define_const(rb_mTimeConsts, "ZERO", - Data_Wrap_Struct(rb_cTimeVal, GC_NOT_MARKED, - GC_DONT_FREE, (void *)&gpr_time_0)); + Data_Wrap_Struct(rb_cTimeVal, GC_NOT_MARKED, GC_DONT_FREE, + (void *)&gpr_time_0)); rb_define_const(rb_mTimeConsts, "INFINITE_FUTURE", - Data_Wrap_Struct(rb_cTimeVal, GC_NOT_MARKED, - GC_DONT_FREE, (void *)&gpr_inf_future)); + Data_Wrap_Struct(rb_cTimeVal, GC_NOT_MARKED, GC_DONT_FREE, + (void *)&gpr_inf_future)); rb_define_const(rb_mTimeConsts, "INFINITE_PAST", - Data_Wrap_Struct(rb_cTimeVal, GC_NOT_MARKED, - GC_DONT_FREE, (void *)&gpr_inf_past)); + Data_Wrap_Struct(rb_cTimeVal, GC_NOT_MARKED, GC_DONT_FREE, + (void *)&gpr_inf_past)); rb_define_method(rb_cTimeVal, "to_time", grpc_rb_time_val_to_time, 0); rb_define_method(rb_cTimeVal, "inspect", grpc_rb_time_val_inspect, 0); rb_define_method(rb_cTimeVal, "to_s", grpc_rb_time_val_to_s, 0); @@ -241,9 +238,7 @@ void Init_google_time_consts() { id_tv_nsec = rb_intern("tv_nsec"); } -void grpc_rb_shutdown(void *vm) { - grpc_shutdown(); -} +void grpc_rb_shutdown(void *vm) { grpc_shutdown(); } /* Initialize the Google RPC module structs */ diff --git a/src/ruby/ext/grpc/rb_grpc.h b/src/ruby/ext/grpc/rb_grpc.h index c2c894244f..d5e8930fca 100644 --- a/src/ruby/ext/grpc/rb_grpc.h +++ b/src/ruby/ext/grpc/rb_grpc.h @@ -74,4 +74,4 @@ VALUE grpc_rb_cannot_init_copy(VALUE copy, VALUE self); /* grpc_rb_time_timeval creates a gpr_timespec from a ruby time object. */ gpr_timespec grpc_rb_time_timeval(VALUE time, int interval); -#endif /* GRPC_RB_H_ */ +#endif /* GRPC_RB_H_ */ diff --git a/src/ruby/ext/grpc/rb_metadata.c b/src/ruby/ext/grpc/rb_metadata.c index dcacc4a976..88eb62ab73 100644 --- a/src/ruby/ext/grpc/rb_metadata.c +++ b/src/ruby/ext/grpc/rb_metadata.c @@ -48,7 +48,6 @@ typedef struct grpc_rb_metadata { grpc_metadata *wrapped; } grpc_rb_metadata; - /* Destroys Metadata instances. */ static void grpc_rb_metadata_free(void *p) { if (p == NULL) { @@ -102,7 +101,7 @@ static VALUE grpc_rb_metadata_init(VALUE self, VALUE key, VALUE value) { wrapper->wrapped = md; if (TYPE(key) == T_SYMBOL) { md->key = (char *)rb_id2name(SYM2ID(key)); - } else { /* StringValueCStr does all other type exclusions for us */ + } else { /* StringValueCStr does all other type exclusions for us */ md->key = StringValueCStr(key); } md->value = RSTRING_PTR(value); @@ -189,8 +188,8 @@ static VALUE grpc_rb_metadata_value(VALUE self) { /* rb_cMetadata is the Metadata class whose instances proxy grpc_metadata. */ VALUE rb_cMetadata = Qnil; void Init_google_rpc_metadata() { - rb_cMetadata = rb_define_class_under(rb_mGoogleRpcCore, "Metadata", - rb_cObject); + rb_cMetadata = + rb_define_class_under(rb_mGoogleRpcCore, "Metadata", rb_cObject); /* Allocates an object managed by the ruby runtime */ rb_define_alloc_func(rb_cMetadata, grpc_rb_metadata_alloc); @@ -209,7 +208,7 @@ void Init_google_rpc_metadata() { } /* Gets the wrapped metadata from the ruby wrapper */ -grpc_metadata* grpc_rb_get_wrapped_metadata(VALUE v) { +grpc_metadata *grpc_rb_get_wrapped_metadata(VALUE v) { grpc_rb_metadata *wrapper = NULL; Data_Get_Struct(v, grpc_rb_metadata, wrapper); return wrapper->wrapped; diff --git a/src/ruby/ext/grpc/rb_metadata.h b/src/ruby/ext/grpc/rb_metadata.h index 6b705914d6..329ef15c68 100644 --- a/src/ruby/ext/grpc/rb_metadata.h +++ b/src/ruby/ext/grpc/rb_metadata.h @@ -42,7 +42,7 @@ extern VALUE rb_cMetadata; /* grpc_rb_metadata_create_with_mark creates a grpc_rb_metadata with a ruby mark * object that will be kept alive while the metadata is alive. */ -extern VALUE grpc_rb_metadata_create_with_mark(VALUE mark, grpc_metadata *md); +extern VALUE grpc_rb_metadata_create_with_mark(VALUE mark, grpc_metadata* md); /* Gets the wrapped metadata from the ruby wrapper */ grpc_metadata* grpc_rb_get_wrapped_metadata(VALUE v); @@ -50,4 +50,4 @@ grpc_metadata* grpc_rb_get_wrapped_metadata(VALUE v); /* Initializes the Metadata class. */ void Init_google_rpc_metadata(); -#endif /* GRPC_RB_METADATA_H_ */ +#endif /* GRPC_RB_METADATA_H_ */ diff --git a/src/ruby/ext/grpc/rb_server.c b/src/ruby/ext/grpc/rb_server.c index 84fba5be4f..ef2a9f107b 100644 --- a/src/ruby/ext/grpc/rb_server.c +++ b/src/ruby/ext/grpc/rb_server.c @@ -128,7 +128,7 @@ static VALUE grpc_rb_server_init(int argc, VALUE *argv, VALUE self) { } if (args.args != NULL) { - xfree(args.args); /* Allocated by grpc_rb_hash_convert_to_channel_args */ + xfree(args.args); /* Allocated by grpc_rb_hash_convert_to_channel_args */ } if (srv == NULL) { rb_raise(rb_eRuntimeError, "could not create a gRPC server, not sure why"); @@ -240,8 +240,8 @@ static VALUE grpc_rb_server_add_http2_port(int argc, VALUE *argv, VALUE self) { StringValueCStr(port)); } } else if (TYPE(is_secure) != T_FALSE) { - added_ok = grpc_server_add_secure_http2_port(s->wrapped, - StringValueCStr(port)); + added_ok = + grpc_server_add_secure_http2_port(s->wrapped, StringValueCStr(port)); if (added_ok == 0) { rb_raise(rb_eRuntimeError, "could not add secure port %s to server, not sure why", @@ -271,7 +271,7 @@ void Init_google_rpc_server() { } /* Gets the wrapped server from the ruby wrapper */ -grpc_server* grpc_rb_get_wrapped_server(VALUE v) { +grpc_server *grpc_rb_get_wrapped_server(VALUE v) { grpc_rb_server *wrapper = NULL; Data_Get_Struct(v, grpc_rb_server, wrapper); return wrapper->wrapped; diff --git a/src/ruby/ext/grpc/rb_server.h b/src/ruby/ext/grpc/rb_server.h index 7fcd32c32f..92047efd18 100644 --- a/src/ruby/ext/grpc/rb_server.h +++ b/src/ruby/ext/grpc/rb_server.h @@ -47,4 +47,4 @@ void Init_google_rpc_server(); /* Gets the wrapped server from the ruby wrapper */ grpc_server* grpc_rb_get_wrapped_server(VALUE v); -#endif /* GRPC_RB_SERVER_H_ */ +#endif /* GRPC_RB_SERVER_H_ */ diff --git a/src/ruby/ext/grpc/rb_server_credentials.c b/src/ruby/ext/grpc/rb_server_credentials.c index fa0d6f2ce8..e534c11444 100644 --- a/src/ruby/ext/grpc/rb_server_credentials.c +++ b/src/ruby/ext/grpc/rb_server_credentials.c @@ -40,7 +40,6 @@ #include "rb_grpc.h" - /* grpc_rb_server_credentials wraps a grpc_server_credentials. It provides a peer ruby object, 'mark' to minimize copying when a server credential is created from ruby. */ @@ -91,8 +90,7 @@ static VALUE grpc_rb_server_credentials_alloc(VALUE cls) { wrapper->wrapped = NULL; wrapper->mark = Qnil; return Data_Wrap_Struct(cls, grpc_rb_server_credentials_mark, - grpc_rb_server_credentials_free, - wrapper); + grpc_rb_server_credentials_free, wrapper); } /* Clones ServerCredentials instances. @@ -123,7 +121,6 @@ static VALUE grpc_rb_server_credentials_init_copy(VALUE copy, VALUE orig) { return copy; } - /* The attribute used on the mark object to hold the pem_root_certs. */ static ID id_pem_root_certs; @@ -188,9 +185,8 @@ static VALUE grpc_rb_server_credentials_init(VALUE self, VALUE pem_root_certs, VALUE rb_cServerCredentials = Qnil; void Init_google_rpc_server_credentials() { - rb_cServerCredentials = rb_define_class_under(rb_mGoogleRpcCore, - "ServerCredentials", - rb_cObject); + rb_cServerCredentials = + rb_define_class_under(rb_mGoogleRpcCore, "ServerCredentials", rb_cObject); /* Allocates an object managed by the ruby runtime */ rb_define_alloc_func(rb_cServerCredentials, grpc_rb_server_credentials_alloc); @@ -199,8 +195,7 @@ void Init_google_rpc_server_credentials() { rb_define_method(rb_cServerCredentials, "initialize", grpc_rb_server_credentials_init, 3); rb_define_method(rb_cServerCredentials, "initialize_copy", - grpc_rb_server_credentials_init_copy, - 1); + grpc_rb_server_credentials_init_copy, 1); id_pem_cert_chain = rb_intern("__pem_cert_chain"); id_pem_private_key = rb_intern("__pem_private_key"); @@ -208,7 +203,7 @@ void Init_google_rpc_server_credentials() { } /* Gets the wrapped grpc_server_credentials from the ruby wrapper */ -grpc_server_credentials* grpc_rb_get_wrapped_server_credentials(VALUE v) { +grpc_server_credentials *grpc_rb_get_wrapped_server_credentials(VALUE v) { grpc_rb_server_credentials *wrapper = NULL; Data_Get_Struct(v, grpc_rb_server_credentials, wrapper); return wrapper->wrapped; diff --git a/src/ruby/ext/grpc/rb_server_credentials.h b/src/ruby/ext/grpc/rb_server_credentials.h index 39189511ab..2a2e1fbc82 100644 --- a/src/ruby/ext/grpc/rb_server_credentials.h +++ b/src/ruby/ext/grpc/rb_server_credentials.h @@ -47,4 +47,4 @@ void Init_google_rpc_server_credentials(); /* Gets the wrapped server_credentials from the ruby wrapper */ grpc_server_credentials* grpc_rb_get_wrapped_server_credentials(VALUE v); -#endif /* GRPC_RB_SERVER_CREDENTIALS_H_ */ +#endif /* GRPC_RB_SERVER_CREDENTIALS_H_ */ |