mirror of https://github.com/grpc/grpc.git
commit
3babf7c5ca
582 changed files with 50627 additions and 8568 deletions
@ -0,0 +1,7 @@ |
||||
root = true |
||||
[**] |
||||
end_of_line = LF |
||||
indent_style = space |
||||
indent_size = 2 |
||||
insert_final_newline = true |
||||
tab_width = 8 |
@ -0,0 +1,30 @@ |
||||
C++ Client implementation for Cloud Pub/Sub service |
||||
(https://developers.google.com/apis-explorer/#p/pubsub/v1beta1/). |
||||
|
||||
"Google Cloud Pub/Sub" API needs to be enabled at |
||||
https://console.developers.google.com/project to open the access for a client. |
||||
Select the project name, select the "APIs" under "APIs & auth", and turn |
||||
on "Google Cloud Pub/Sub" API. |
||||
|
||||
To run the client from Google Compute Engine (GCE), the GCE instance needs to |
||||
be created with scope "https://www.googleapis.com/auth/cloud-platform" as below: |
||||
|
||||
gcloud compute instances create instance-name |
||||
--image debian-7 --scopes https://www.googleapis.com/auth/cloud-platform |
||||
|
||||
Google TLS cert is required to run the client, which can be downloaded from |
||||
Chrome browser. |
||||
|
||||
To run the client from GCE: |
||||
make pubsub_client |
||||
GRPC_DEFAULT_SSL_ROOTS_FILE_PATH="Google TLS cert" bins/opt/pubsub_client |
||||
--project_id="your project id" |
||||
|
||||
A service account credential is required to run the client from other |
||||
environments, which can be generated as a JSON key file from |
||||
https://console.developers.google.com/project/. To run the client with a service |
||||
account credential: |
||||
|
||||
GRPC_DEFAULT_SSL_ROOTS_FILE_PATH="Google TLS cert" bins/opt/pubsub_client |
||||
--project_id="your project id" |
||||
--service_account_key_file="absolute path to the JSON key file" |
@ -1,3 +1,5 @@ |
||||
// This file will be moved to a new location. |
||||
|
||||
syntax = "proto2"; |
||||
|
||||
package proto2; |
@ -1,3 +1,5 @@ |
||||
// This file will be moved to a new location. |
||||
|
||||
// Labels provide a way to associate user-defined metadata with various |
||||
// objects. Labels may be used to organize objects into non-hierarchical |
||||
// groups; think metadata tags attached to mp3s. |
@ -0,0 +1,178 @@ |
||||
/*
|
||||
* |
||||
* 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 <chrono> |
||||
#include <fstream> |
||||
#include <memory> |
||||
#include <sstream> |
||||
#include <string> |
||||
#include <thread> |
||||
|
||||
#include <grpc/grpc.h> |
||||
#include <grpc/support/log.h> |
||||
#include <google/gflags.h> |
||||
#include <grpc++/channel_interface.h> |
||||
#include <grpc++/create_channel.h> |
||||
#include <grpc++/credentials.h> |
||||
#include <grpc++/status.h> |
||||
|
||||
#include "examples/pubsub/publisher.h" |
||||
#include "examples/pubsub/subscriber.h" |
||||
#include "test/cpp/util/create_test_channel.h" |
||||
|
||||
DEFINE_int32(server_port, 443, "Server port."); |
||||
DEFINE_string(server_host, |
||||
"pubsub-staging.googleapis.com", "Server host to connect to"); |
||||
DEFINE_string(project_id, "", "GCE project id such as stoked-keyword-656"); |
||||
DEFINE_string(service_account_key_file, "", |
||||
"Path to service account json key file."); |
||||
DEFINE_string(oauth_scope, |
||||
"https://www.googleapis.com/auth/cloud-platform", |
||||
"Scope for OAuth tokens."); |
||||
|
||||
namespace { |
||||
|
||||
const char kTopic[] = "testtopics"; |
||||
const char kSubscriptionName[] = "testsubscription"; |
||||
const char kMessageData[] = "Test Data"; |
||||
|
||||
} // namespace
|
||||
|
||||
grpc::string GetServiceAccountJsonKey() { |
||||
grpc::string json_key; |
||||
if (json_key.empty()) { |
||||
std::ifstream json_key_file(FLAGS_service_account_key_file); |
||||
std::stringstream key_stream; |
||||
key_stream << json_key_file.rdbuf(); |
||||
json_key = key_stream.str(); |
||||
} |
||||
return json_key; |
||||
} |
||||
|
||||
int main(int argc, char** argv) { |
||||
grpc_init(); |
||||
google::ParseCommandLineFlags(&argc, &argv, true); |
||||
gpr_log(GPR_INFO, "Start PUBSUB client"); |
||||
|
||||
std::ostringstream ss; |
||||
|
||||
std::unique_ptr<grpc::Credentials> creds; |
||||
if (FLAGS_service_account_key_file != "") { |
||||
grpc::string json_key = GetServiceAccountJsonKey(); |
||||
creds = grpc::CredentialsFactory::ServiceAccountCredentials( |
||||
json_key, FLAGS_oauth_scope, std::chrono::hours(1)); |
||||
} else { |
||||
creds = grpc::CredentialsFactory::ComputeEngineCredentials(); |
||||
} |
||||
|
||||
ss << FLAGS_server_host << ":" << FLAGS_server_port; |
||||
std::shared_ptr<grpc::ChannelInterface> channel( |
||||
grpc::CreateTestChannel( |
||||
ss.str(), |
||||
FLAGS_server_host, |
||||
true, // enable SSL
|
||||
true, // use prod roots
|
||||
creds)); |
||||
|
||||
grpc::examples::pubsub::Publisher publisher(channel); |
||||
grpc::examples::pubsub::Subscriber subscriber(channel); |
||||
|
||||
GPR_ASSERT(FLAGS_project_id != ""); |
||||
ss.str(""); |
||||
ss << "/topics/" << FLAGS_project_id << "/" << kTopic; |
||||
grpc::string topic = ss.str(); |
||||
|
||||
ss.str(""); |
||||
ss << FLAGS_project_id << "/" << kSubscriptionName; |
||||
grpc::string subscription_name = ss.str(); |
||||
|
||||
// Clean up test topic and subcription if they exist before.
|
||||
grpc::string subscription_topic; |
||||
if (subscriber.GetSubscription( |
||||
subscription_name, &subscription_topic).IsOk()) { |
||||
subscriber.DeleteSubscription(subscription_name); |
||||
} |
||||
if (publisher.GetTopic(topic).IsOk()) publisher.DeleteTopic(topic); |
||||
|
||||
grpc::Status s = publisher.CreateTopic(topic); |
||||
gpr_log(GPR_INFO, "Create topic returns code %d, %s", |
||||
s.code(), s.details().c_str()); |
||||
GPR_ASSERT(s.IsOk()); |
||||
|
||||
s = publisher.GetTopic(topic); |
||||
gpr_log(GPR_INFO, "Get topic returns code %d, %s", |
||||
s.code(), s.details().c_str()); |
||||
GPR_ASSERT(s.IsOk()); |
||||
|
||||
std::vector<grpc::string> topics; |
||||
s = publisher.ListTopics(FLAGS_project_id, &topics); |
||||
gpr_log(GPR_INFO, "List topic returns code %d, %s", |
||||
s.code(), s.details().c_str()); |
||||
bool topic_found = false; |
||||
for (unsigned int i = 0; i < topics.size(); i++) { |
||||
if (topics[i] == topic) topic_found = true; |
||||
gpr_log(GPR_INFO, "topic: %s", topics[i].c_str()); |
||||
} |
||||
GPR_ASSERT(s.IsOk()); |
||||
GPR_ASSERT(topic_found); |
||||
|
||||
s = subscriber.CreateSubscription(topic, subscription_name); |
||||
gpr_log(GPR_INFO, "create subscrption returns code %d, %s", |
||||
s.code(), s.details().c_str()); |
||||
GPR_ASSERT(s.IsOk()); |
||||
|
||||
s = publisher.Publish(topic, kMessageData); |
||||
gpr_log(GPR_INFO, "Publish %s returns code %d, %s", |
||||
kMessageData, s.code(), s.details().c_str()); |
||||
GPR_ASSERT(s.IsOk()); |
||||
|
||||
grpc::string data; |
||||
s = subscriber.Pull(subscription_name, &data); |
||||
gpr_log(GPR_INFO, "Pull %s", data.c_str()); |
||||
|
||||
s = subscriber.DeleteSubscription(subscription_name); |
||||
gpr_log(GPR_INFO, "Delete subscription returns code %d, %s", |
||||
s.code(), s.details().c_str()); |
||||
GPR_ASSERT(s.IsOk()); |
||||
|
||||
s = publisher.DeleteTopic(topic); |
||||
gpr_log(GPR_INFO, "Delete topic returns code %d, %s", |
||||
s.code(), s.details().c_str()); |
||||
GPR_ASSERT(s.IsOk()); |
||||
|
||||
subscriber.Shutdown(); |
||||
publisher.Shutdown(); |
||||
channel.reset(); |
||||
grpc_shutdown(); |
||||
return 0; |
||||
} |
@ -0,0 +1,124 @@ |
||||
/*
|
||||
* |
||||
* 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 <sstream> |
||||
|
||||
#include <grpc++/client_context.h> |
||||
|
||||
#include "examples/pubsub/publisher.h" |
||||
|
||||
using tech::pubsub::Topic; |
||||
using tech::pubsub::DeleteTopicRequest; |
||||
using tech::pubsub::GetTopicRequest; |
||||
using tech::pubsub::PublisherService; |
||||
using tech::pubsub::ListTopicsRequest; |
||||
using tech::pubsub::ListTopicsResponse; |
||||
using tech::pubsub::PublishRequest; |
||||
using tech::pubsub::PubsubMessage; |
||||
|
||||
namespace grpc { |
||||
namespace examples { |
||||
namespace pubsub { |
||||
|
||||
Publisher::Publisher(std::shared_ptr<ChannelInterface> channel) |
||||
: stub_(PublisherService::NewStub(channel)) { |
||||
} |
||||
|
||||
void Publisher::Shutdown() { |
||||
stub_.reset(); |
||||
} |
||||
|
||||
Status Publisher::CreateTopic(const grpc::string& topic) { |
||||
Topic request; |
||||
Topic response; |
||||
request.set_name(topic); |
||||
ClientContext context; |
||||
|
||||
return stub_->CreateTopic(&context, request, &response); |
||||
} |
||||
|
||||
Status Publisher::ListTopics(const grpc::string& project_id, |
||||
std::vector<grpc::string>* topics) { |
||||
ListTopicsRequest request; |
||||
ListTopicsResponse response; |
||||
ClientContext context; |
||||
|
||||
std::ostringstream ss; |
||||
ss << "cloud.googleapis.com/project in (/projects/" << project_id << ")"; |
||||
request.set_query(ss.str()); |
||||
|
||||
Status s = stub_->ListTopics(&context, request, &response); |
||||
|
||||
tech::pubsub::Topic topic; |
||||
for (int i = 0; i < response.topic_size(); i++) { |
||||
topic = response.topic(i); |
||||
topics->push_back(topic.name()); |
||||
} |
||||
|
||||
return s; |
||||
} |
||||
|
||||
Status Publisher::GetTopic(const grpc::string& topic) { |
||||
GetTopicRequest request; |
||||
Topic response; |
||||
ClientContext context; |
||||
|
||||
request.set_topic(topic); |
||||
|
||||
return stub_->GetTopic(&context, request, &response); |
||||
} |
||||
|
||||
Status Publisher::DeleteTopic(const grpc::string& topic) { |
||||
DeleteTopicRequest request; |
||||
proto2::Empty response; |
||||
ClientContext context; |
||||
|
||||
request.set_topic(topic); |
||||
|
||||
return stub_->DeleteTopic(&context, request, &response); |
||||
} |
||||
|
||||
Status Publisher::Publish(const grpc::string& topic, const grpc::string& data) { |
||||
PublishRequest request; |
||||
proto2::Empty response; |
||||
ClientContext context; |
||||
|
||||
request.mutable_message()->set_data(data); |
||||
request.set_topic(topic); |
||||
|
||||
return stub_->Publish(&context, request, &response); |
||||
} |
||||
|
||||
} // namespace pubsub
|
||||
} // namespace examples
|
||||
} // namespace grpc
|
@ -1,9 +1,11 @@ |
||||
// This file will be moved to a new location. |
||||
|
||||
// Specification of the Pubsub API. |
||||
|
||||
syntax = "proto2"; |
||||
|
||||
import "examples/tips/empty.proto"; |
||||
import "examples/tips/label.proto"; |
||||
import "examples/pubsub/empty.proto"; |
||||
import "examples/pubsub/label.proto"; |
||||
|
||||
package tech.pubsub; |
||||
|
@ -0,0 +1,118 @@ |
||||
/*
|
||||
* |
||||
* Copyright 2014, Google Inc. |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* * Redistributions in binary form must reproduce the above |
||||
* copyright notice, this list of conditions and the following disclaimer |
||||
* in the documentation and/or other materials provided with the |
||||
* distribution. |
||||
* * Neither the name of Google Inc. nor the names of its |
||||
* contributors may be used to endorse or promote products derived from |
||||
* this software without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
* |
||||
*/ |
||||
|
||||
#include <grpc++/client_context.h> |
||||
|
||||
#include "examples/pubsub/subscriber.h" |
||||
|
||||
using tech::pubsub::Topic; |
||||
using tech::pubsub::DeleteTopicRequest; |
||||
using tech::pubsub::GetTopicRequest; |
||||
using tech::pubsub::SubscriberService; |
||||
using tech::pubsub::ListTopicsRequest; |
||||
using tech::pubsub::ListTopicsResponse; |
||||
using tech::pubsub::PublishRequest; |
||||
using tech::pubsub::PubsubMessage; |
||||
|
||||
namespace grpc { |
||||
namespace examples { |
||||
namespace pubsub { |
||||
|
||||
Subscriber::Subscriber(std::shared_ptr<ChannelInterface> channel) |
||||
: stub_(SubscriberService::NewStub(channel)) { |
||||
} |
||||
|
||||
void Subscriber::Shutdown() { |
||||
stub_.reset(); |
||||
} |
||||
|
||||
Status Subscriber::CreateSubscription(const grpc::string& topic, |
||||
const grpc::string& name) { |
||||
tech::pubsub::Subscription request; |
||||
tech::pubsub::Subscription response; |
||||
ClientContext context; |
||||
|
||||
request.set_topic(topic); |
||||
request.set_name(name); |
||||
|
||||
return stub_->CreateSubscription(&context, request, &response); |
||||
} |
||||
|
||||
Status Subscriber::GetSubscription(const grpc::string& name, |
||||
grpc::string* topic) { |
||||
tech::pubsub::GetSubscriptionRequest request; |
||||
tech::pubsub::Subscription response; |
||||
ClientContext context; |
||||
|
||||
request.set_subscription(name); |
||||
|
||||
Status s = stub_->GetSubscription(&context, request, &response); |
||||
*topic = response.topic(); |
||||
return s; |
||||
} |
||||
|
||||
Status Subscriber::DeleteSubscription(const grpc::string& name) { |
||||
tech::pubsub::DeleteSubscriptionRequest request; |
||||
proto2::Empty response; |
||||
ClientContext context; |
||||
|
||||
request.set_subscription(name); |
||||
|
||||
return stub_->DeleteSubscription(&context, request, &response); |
||||
} |
||||
|
||||
Status Subscriber::Pull(const grpc::string& name, grpc::string* data) { |
||||
tech::pubsub::PullRequest request; |
||||
tech::pubsub::PullResponse response; |
||||
ClientContext context; |
||||
|
||||
request.set_subscription(name); |
||||
Status s = stub_->Pull(&context, request, &response); |
||||
if (s.IsOk()) { |
||||
tech::pubsub::PubsubEvent event = response.pubsub_event(); |
||||
if (event.has_message()) { |
||||
*data = event.message().data(); |
||||
} |
||||
tech::pubsub::AcknowledgeRequest ack; |
||||
proto2::Empty empty; |
||||
ClientContext ack_context; |
||||
ack.set_subscription(name); |
||||
ack.add_ack_id(response.ack_id()); |
||||
stub_->Acknowledge(&ack_context, ack, &empty); |
||||
} |
||||
return s; |
||||
} |
||||
|
||||
} // namespace pubsub
|
||||
} // namespace examples
|
||||
} // namespace grpc
|
@ -0,0 +1,68 @@ |
||||
/*
|
||||
* |
||||
* 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_EXAMPLES_PUBSUB_SUBSCRIBER_H_ |
||||
#define __GRPCPP_EXAMPLES_PUBSUB_SUBSCRIBER_H_ |
||||
|
||||
#include <grpc++/channel_interface.h> |
||||
#include <grpc++/status.h> |
||||
|
||||
#include "examples/pubsub/pubsub.pb.h" |
||||
|
||||
namespace grpc { |
||||
namespace examples { |
||||
namespace pubsub { |
||||
|
||||
class Subscriber { |
||||
public: |
||||
Subscriber(std::shared_ptr<ChannelInterface> channel); |
||||
void Shutdown(); |
||||
|
||||
Status CreateSubscription(const grpc::string& topic, |
||||
const grpc::string& name); |
||||
|
||||
Status GetSubscription(const grpc::string& name, grpc::string* topic); |
||||
|
||||
Status DeleteSubscription(const grpc::string& name); |
||||
|
||||
Status Pull(const grpc::string& name, grpc::string* data); |
||||
|
||||
private: |
||||
std::unique_ptr<tech::pubsub::SubscriberService::Stub> stub_; |
||||
}; |
||||
|
||||
} // namespace pubsub
|
||||
} // namespace examples
|
||||
} // namespace grpc
|
||||
|
||||
#endif // __GRPCPP_EXAMPLES_PUBSUB_SUBSCRIBER_H_
|
@ -0,0 +1,159 @@ |
||||
/*
|
||||
* |
||||
* 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 <google/protobuf/stubs/common.h> |
||||
|
||||
#include <grpc++/channel_arguments.h> |
||||
#include <grpc++/channel_interface.h> |
||||
#include <grpc++/client_context.h> |
||||
#include <grpc++/create_channel.h> |
||||
#include <grpc++/server.h> |
||||
#include <grpc++/server_builder.h> |
||||
#include <grpc++/server_context.h> |
||||
#include <grpc++/status.h> |
||||
#include <gtest/gtest.h> |
||||
|
||||
#include "examples/pubsub/subscriber.h" |
||||
#include "test/core/util/port.h" |
||||
#include "test/core/util/test_config.h" |
||||
|
||||
namespace grpc { |
||||
namespace testing { |
||||
namespace { |
||||
|
||||
const char kTopic[] = "test topic"; |
||||
const char kSubscriptionName[] = "subscription name"; |
||||
const char kData[] = "Message data"; |
||||
|
||||
class SubscriberServiceImpl : public tech::pubsub::SubscriberService::Service { |
||||
public: |
||||
Status CreateSubscription(ServerContext* context, |
||||
const tech::pubsub::Subscription* request, |
||||
tech::pubsub::Subscription* response) override { |
||||
EXPECT_EQ(request->topic(), kTopic); |
||||
EXPECT_EQ(request->name(), kSubscriptionName); |
||||
return Status::OK; |
||||
} |
||||
|
||||
Status GetSubscription(ServerContext* context, |
||||
const tech::pubsub::GetSubscriptionRequest* request, |
||||
tech::pubsub::Subscription* response) override { |
||||
EXPECT_EQ(request->subscription(), kSubscriptionName); |
||||
response->set_topic(kTopic); |
||||
return Status::OK; |
||||
} |
||||
|
||||
Status DeleteSubscription( |
||||
ServerContext* context, |
||||
const tech::pubsub::DeleteSubscriptionRequest* request, |
||||
proto2::Empty* response) override { |
||||
EXPECT_EQ(request->subscription(), kSubscriptionName); |
||||
return Status::OK; |
||||
} |
||||
|
||||
Status Pull(ServerContext* context, |
||||
const tech::pubsub::PullRequest* request, |
||||
tech::pubsub::PullResponse* response) override { |
||||
EXPECT_EQ(request->subscription(), kSubscriptionName); |
||||
response->set_ack_id("1"); |
||||
response->mutable_pubsub_event()->mutable_message()->set_data(kData); |
||||
return Status::OK; |
||||
} |
||||
|
||||
Status Acknowledge(ServerContext* context, |
||||
const tech::pubsub::AcknowledgeRequest* request, |
||||
proto2::Empty* response) override { |
||||
return Status::OK; |
||||
} |
||||
|
||||
}; |
||||
|
||||
class SubscriberTest : public ::testing::Test { |
||||
protected: |
||||
// Setup a server and a client for SubscriberService.
|
||||
void SetUp() override { |
||||
int port = grpc_pick_unused_port_or_die(); |
||||
server_address_ << "localhost:" << port; |
||||
ServerBuilder builder; |
||||
builder.AddPort(server_address_.str()); |
||||
builder.RegisterService(service_.service()); |
||||
server_ = builder.BuildAndStart(); |
||||
|
||||
channel_ = CreateChannel(server_address_.str(), ChannelArguments()); |
||||
|
||||
subscriber_.reset(new grpc::examples::pubsub::Subscriber(channel_)); |
||||
} |
||||
|
||||
void TearDown() override { |
||||
server_->Shutdown(); |
||||
subscriber_->Shutdown(); |
||||
} |
||||
|
||||
std::ostringstream server_address_; |
||||
std::unique_ptr<Server> server_; |
||||
SubscriberServiceImpl service_; |
||||
|
||||
std::shared_ptr<ChannelInterface> channel_; |
||||
|
||||
std::unique_ptr<grpc::examples::pubsub::Subscriber> subscriber_; |
||||
}; |
||||
|
||||
TEST_F(SubscriberTest, TestSubscriber) { |
||||
EXPECT_TRUE(subscriber_->CreateSubscription(kTopic, |
||||
kSubscriptionName).IsOk()); |
||||
|
||||
grpc::string topic; |
||||
EXPECT_TRUE(subscriber_->GetSubscription(kSubscriptionName, |
||||
&topic).IsOk()); |
||||
EXPECT_EQ(topic, kTopic); |
||||
|
||||
grpc::string data; |
||||
EXPECT_TRUE(subscriber_->Pull(kSubscriptionName, |
||||
&data).IsOk()); |
||||
|
||||
EXPECT_TRUE(subscriber_->DeleteSubscription(kSubscriptionName).IsOk()); |
||||
} |
||||
|
||||
} // namespace
|
||||
} // namespace testing
|
||||
} // namespace grpc
|
||||
|
||||
int main(int argc, char** argv) { |
||||
grpc_test_init(argc, argv); |
||||
grpc_init(); |
||||
::testing::InitGoogleTest(&argc, argv); |
||||
gpr_log(GPR_INFO, "Start test ..."); |
||||
int result = RUN_ALL_TESTS(); |
||||
grpc_shutdown(); |
||||
return result; |
||||
} |
@ -0,0 +1,53 @@ |
||||
/*
|
||||
* |
||||
* Copyright 2014, Google Inc. |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* * Redistributions in binary form must reproduce the above |
||||
* copyright notice, this list of conditions and the following disclaimer |
||||
* in the documentation and/or other materials provided with the |
||||
* distribution. |
||||
* * Neither the name of Google Inc. nor the names of its |
||||
* contributors may be used to endorse or promote products derived from |
||||
* this software without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
* |
||||
*/ |
||||
|
||||
#ifndef __GRPC_SUPPORT_LOG_WIN32_H__ |
||||
#define __GRPC_SUPPORT_LOG_WIN32_H__ |
||||
|
||||
#include <windows.h> |
||||
|
||||
#ifdef __cplusplus |
||||
extern "C" { |
||||
#endif |
||||
|
||||
/* Returns a string allocated with gpr_malloc that contains a UTF-8
|
||||
* formatted error message, corresponding to the error messageid. |
||||
* Use in conjunction with GetLastError() et al. |
||||
*/ |
||||
char *gpr_format_message(DWORD messageid); |
||||
|
||||
#ifdef __cplusplus |
||||
} |
||||
#endif |
||||
|
||||
#endif /* __GRPC_SUPPORT_LOG_H__ */ |
@ -0,0 +1,200 @@ |
||||
/*
|
||||
* |
||||
* Copyright 2014, Google Inc. |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* * Redistributions in binary form must reproduce the above |
||||
* copyright notice, this list of conditions and the following disclaimer |
||||
* in the documentation and/or other materials provided with the |
||||
* distribution. |
||||
* * Neither the name of Google Inc. nor the names of its |
||||
* contributors may be used to endorse or promote products derived from |
||||
* this software without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
* |
||||
*/ |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#ifdef GPR_WINSOCK_SOCKET |
||||
|
||||
#include <winsock2.h> |
||||
|
||||
#include <grpc/support/log.h> |
||||
#include <grpc/support/log_win32.h> |
||||
#include <grpc/support/alloc.h> |
||||
#include <grpc/support/thd.h> |
||||
|
||||
#include "src/core/iomgr/alarm_internal.h" |
||||
#include "src/core/iomgr/iocp_windows.h" |
||||
#include "src/core/iomgr/iomgr_internal.h" |
||||
#include "src/core/iomgr/socket_windows.h" |
||||
|
||||
static ULONG g_iocp_kick_token; |
||||
static OVERLAPPED g_iocp_custom_overlap; |
||||
|
||||
static gpr_event g_shutdown_iocp; |
||||
static gpr_event g_iocp_done; |
||||
|
||||
static HANDLE g_iocp; |
||||
|
||||
static int do_iocp_work() { |
||||
BOOL success; |
||||
DWORD bytes = 0; |
||||
DWORD flags = 0; |
||||
ULONG_PTR completion_key; |
||||
LPOVERLAPPED overlapped; |
||||
gpr_timespec wait_time = gpr_inf_future; |
||||
grpc_winsocket *socket; |
||||
grpc_winsocket_callback_info *info; |
||||
void(*f)(void *, int) = NULL; |
||||
void *opaque = NULL; |
||||
success = GetQueuedCompletionStatus(g_iocp, &bytes, |
||||
&completion_key, &overlapped, |
||||
gpr_time_to_millis(wait_time)); |
||||
if (!success && !overlapped) { |
||||
/* The deadline got attained. */ |
||||
return 0; |
||||
} |
||||
GPR_ASSERT(completion_key && overlapped); |
||||
if (overlapped == &g_iocp_custom_overlap) { |
||||
if (completion_key == (ULONG_PTR) &g_iocp_kick_token) { |
||||
/* We were awoken from a kick. */ |
||||
gpr_log(GPR_DEBUG, "do_iocp_work - got a kick"); |
||||
return 1; |
||||
} |
||||
gpr_log(GPR_ERROR, "Unknown custom completion key."); |
||||
abort(); |
||||
} |
||||
|
||||
socket = (grpc_winsocket*) completion_key; |
||||
if (overlapped == &socket->write_info.overlapped) { |
||||
gpr_log(GPR_DEBUG, "do_iocp_work - got write packet"); |
||||
info = &socket->write_info; |
||||
} else if (overlapped == &socket->read_info.overlapped) { |
||||
gpr_log(GPR_DEBUG, "do_iocp_work - got read packet"); |
||||
info = &socket->read_info; |
||||
} else { |
||||
gpr_log(GPR_ERROR, "Unknown IOCP operation"); |
||||
abort(); |
||||
} |
||||
success = WSAGetOverlappedResult(socket->socket, &info->overlapped, &bytes, |
||||
FALSE, &flags); |
||||
gpr_log(GPR_DEBUG, "bytes: %u, flags: %u - op %s", bytes, flags, |
||||
success ? "succeeded" : "failed"); |
||||
info->bytes_transfered = bytes; |
||||
info->wsa_error = success ? 0 : WSAGetLastError(); |
||||
GPR_ASSERT(overlapped == &info->overlapped); |
||||
gpr_mu_lock(&socket->state_mu); |
||||
GPR_ASSERT(!info->has_pending_iocp); |
||||
if (info->cb) { |
||||
f = info->cb; |
||||
opaque = info->opaque; |
||||
info->cb = NULL; |
||||
} else { |
||||
info->has_pending_iocp = 1; |
||||
} |
||||
gpr_mu_unlock(&socket->state_mu); |
||||
if (f) f(opaque, 1); |
||||
|
||||
return 1; |
||||
} |
||||
|
||||
static void iocp_loop(void *p) { |
||||
while (!gpr_event_get(&g_shutdown_iocp)) { |
||||
grpc_maybe_call_delayed_callbacks(NULL, 1); |
||||
do_iocp_work(); |
||||
} |
||||
|
||||
gpr_event_set(&g_iocp_done, (void *)1); |
||||
} |
||||
|
||||
void grpc_iocp_init(void) { |
||||
gpr_thd_id id; |
||||
|
||||
g_iocp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, |
||||
(ULONG_PTR)NULL, 0); |
||||
GPR_ASSERT(g_iocp); |
||||
|
||||
gpr_event_init(&g_iocp_done); |
||||
gpr_event_init(&g_shutdown_iocp); |
||||
gpr_thd_new(&id, iocp_loop, NULL, NULL); |
||||
} |
||||
|
||||
void grpc_iocp_shutdown(void) { |
||||
BOOL success; |
||||
gpr_event_set(&g_shutdown_iocp, (void *)1); |
||||
success = PostQueuedCompletionStatus(g_iocp, 0, |
||||
(ULONG_PTR) &g_iocp_kick_token, |
||||
&g_iocp_custom_overlap); |
||||
GPR_ASSERT(success); |
||||
gpr_event_wait(&g_iocp_done, gpr_inf_future); |
||||
success = CloseHandle(g_iocp); |
||||
GPR_ASSERT(success); |
||||
} |
||||
|
||||
void grpc_iocp_add_socket(grpc_winsocket *socket) { |
||||
HANDLE ret; |
||||
if (socket->added_to_iocp) return; |
||||
ret = CreateIoCompletionPort((HANDLE)socket->socket, |
||||
g_iocp, (gpr_uintptr) socket, 0); |
||||
if (!ret) { |
||||
char *utf8_message = gpr_format_message(WSAGetLastError()); |
||||
gpr_log(GPR_ERROR, "Unable to add socket to iocp: %s", utf8_message); |
||||
gpr_free(utf8_message); |
||||
__debugbreak(); |
||||
abort(); |
||||
} |
||||
socket->added_to_iocp = 1; |
||||
GPR_ASSERT(ret == g_iocp); |
||||
} |
||||
|
||||
static void socket_notify_on_iocp(grpc_winsocket *socket, |
||||
void(*cb)(void *, int), void *opaque, |
||||
grpc_winsocket_callback_info *info) { |
||||
int run_now = 0; |
||||
GPR_ASSERT(!info->cb); |
||||
gpr_mu_lock(&socket->state_mu); |
||||
if (info->has_pending_iocp) { |
||||
run_now = 1; |
||||
info->has_pending_iocp = 0; |
||||
gpr_log(GPR_DEBUG, "socket_notify_on_iocp - runs now"); |
||||
} else { |
||||
info->cb = cb; |
||||
info->opaque = opaque; |
||||
gpr_log(GPR_DEBUG, "socket_notify_on_iocp - queued"); |
||||
} |
||||
gpr_mu_unlock(&socket->state_mu); |
||||
if (run_now) cb(opaque, 1); |
||||
} |
||||
|
||||
void grpc_socket_notify_on_write(grpc_winsocket *socket, |
||||
void(*cb)(void *, int), void *opaque) { |
||||
gpr_log(GPR_DEBUG, "grpc_socket_notify_on_write"); |
||||
socket_notify_on_iocp(socket, cb, opaque, &socket->write_info); |
||||
} |
||||
|
||||
void grpc_socket_notify_on_read(grpc_winsocket *socket, |
||||
void(*cb)(void *, int), void *opaque) { |
||||
gpr_log(GPR_DEBUG, "grpc_socket_notify_on_read"); |
||||
socket_notify_on_iocp(socket, cb, opaque, &socket->read_info); |
||||
} |
||||
|
||||
#endif /* GPR_WINSOCK_SOCKET */ |
@ -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. |
||||
* |
||||
*/ |
||||
|
||||
#ifndef __GRPC_INTERNAL_IOMGR_IOCP_WINDOWS_H_ |
||||
#define __GRPC_INTERNAL_IOMGR_IOCP_WINDOWS_H_ |
||||
|
||||
#include <windows.h> |
||||
#include <grpc/support/sync.h> |
||||
|
||||
#include "src/core/iomgr/socket_windows.h" |
||||
|
||||
void grpc_iocp_init(void); |
||||
void grpc_iocp_shutdown(void); |
||||
void grpc_iocp_add_socket(grpc_winsocket *); |
||||
|
||||
void grpc_socket_notify_on_write(grpc_winsocket *, void(*cb)(void *, int success), |
||||
void *opaque); |
||||
|
||||
void grpc_socket_notify_on_read(grpc_winsocket *, void(*cb)(void *, int success), |
||||
void *opaque); |
||||
|
||||
#endif /* __GRPC_INTERNAL_IOMGR_IOCP_WINDOWS_H_ */ |
@ -0,0 +1,67 @@ |
||||
/*
|
||||
* |
||||
* Copyright 2014, Google Inc. |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* * Redistributions in binary form must reproduce the above |
||||
* copyright notice, this list of conditions and the following disclaimer |
||||
* in the documentation and/or other materials provided with the |
||||
* distribution. |
||||
* * Neither the name of Google Inc. nor the names of its |
||||
* contributors may be used to endorse or promote products derived from |
||||
* this software without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
* |
||||
*/ |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#ifdef GPR_WINSOCK_SOCKET |
||||
|
||||
#include "src/core/iomgr/sockaddr_win32.h" |
||||
|
||||
#include <grpc/support/log.h> |
||||
|
||||
#include "src/core/iomgr/socket_windows.h" |
||||
#include "src/core/iomgr/iocp_windows.h" |
||||
#include "src/core/iomgr/iomgr.h" |
||||
|
||||
static void winsock_init(void) { |
||||
WSADATA wsaData; |
||||
int status = WSAStartup(MAKEWORD(2, 0), &wsaData); |
||||
GPR_ASSERT(status == 0); |
||||
} |
||||
|
||||
static void winsock_shutdown(void) { |
||||
int status = WSACleanup(); |
||||
GPR_ASSERT(status == 0); |
||||
} |
||||
|
||||
void grpc_iomgr_platform_init(void) { |
||||
winsock_init(); |
||||
grpc_iocp_init(); |
||||
} |
||||
|
||||
void grpc_iomgr_platform_shutdown(void) { |
||||
grpc_iocp_shutdown(); |
||||
winsock_shutdown(); |
||||
} |
||||
|
||||
#endif /* GRPC_WINSOCK_SOCKET */ |
@ -0,0 +1,197 @@ |
||||
/*
|
||||
* |
||||
* Copyright 2015, Google Inc. |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* * Redistributions in binary form must reproduce the above |
||||
* copyright notice, this list of conditions and the following disclaimer |
||||
* in the documentation and/or other materials provided with the |
||||
* distribution. |
||||
* * Neither the name of Google Inc. nor the names of its |
||||
* contributors may be used to endorse or promote products derived from |
||||
* this software without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
* |
||||
*/ |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#ifdef GPR_LINUX_MULTIPOLL_WITH_EPOLL |
||||
|
||||
#include <errno.h> |
||||
#include <string.h> |
||||
#include <sys/epoll.h> |
||||
#include <unistd.h> |
||||
|
||||
#include "src/core/iomgr/fd_posix.h" |
||||
#include <grpc/support/alloc.h> |
||||
#include <grpc/support/log.h> |
||||
|
||||
typedef struct { |
||||
int epoll_fd; |
||||
grpc_wakeup_fd_info wakeup_fd; |
||||
} pollset_hdr; |
||||
|
||||
static void multipoll_with_epoll_pollset_add_fd(grpc_pollset *pollset, |
||||
grpc_fd *fd) { |
||||
pollset_hdr *h = pollset->data.ptr; |
||||
struct epoll_event ev; |
||||
int err; |
||||
|
||||
ev.events = EPOLLIN | EPOLLOUT | EPOLLET; |
||||
ev.data.ptr = fd; |
||||
err = epoll_ctl(h->epoll_fd, EPOLL_CTL_ADD, fd->fd, &ev); |
||||
if (err < 0) { |
||||
/* FDs may be added to a pollset multiple times, so EEXIST is normal. */ |
||||
if (errno != EEXIST) { |
||||
gpr_log(GPR_ERROR, "epoll_ctl add for %d failed: %s", fd->fd, |
||||
strerror(errno)); |
||||
} |
||||
} |
||||
} |
||||
|
||||
static void multipoll_with_epoll_pollset_del_fd(grpc_pollset *pollset, |
||||
grpc_fd *fd) { |
||||
pollset_hdr *h = pollset->data.ptr; |
||||
int err; |
||||
/* Note that this can race with concurrent poll, but that should be fine since
|
||||
* at worst it creates a spurious read event on a reused grpc_fd object. */ |
||||
err = epoll_ctl(h->epoll_fd, EPOLL_CTL_DEL, fd->fd, NULL); |
||||
if (err < 0) { |
||||
gpr_log(GPR_ERROR, "epoll_ctl del for %d failed: %s", fd->fd, |
||||
strerror(errno)); |
||||
} |
||||
} |
||||
|
||||
/* TODO(klempner): We probably want to turn this down a bit */ |
||||
#define GRPC_EPOLL_MAX_EVENTS 1000 |
||||
|
||||
static int multipoll_with_epoll_pollset_maybe_work( |
||||
grpc_pollset *pollset, gpr_timespec deadline, gpr_timespec now, |
||||
int allow_synchronous_callback) { |
||||
struct epoll_event ep_ev[GRPC_EPOLL_MAX_EVENTS]; |
||||
int ep_rv; |
||||
pollset_hdr *h = pollset->data.ptr; |
||||
int timeout_ms; |
||||
|
||||
/* If you want to ignore epoll's ability to sanely handle parallel pollers,
|
||||
* for a more apples-to-apples performance comparison with poll, add a |
||||
* if (pollset->counter == 0) { return 0 } |
||||
* here. |
||||
*/ |
||||
|
||||
if (gpr_time_cmp(deadline, gpr_inf_future) == 0) { |
||||
timeout_ms = -1; |
||||
} else { |
||||
timeout_ms = gpr_time_to_millis(gpr_time_sub(deadline, now)); |
||||
if (timeout_ms <= 0) { |
||||
return 1; |
||||
} |
||||
} |
||||
pollset->counter += 1; |
||||
gpr_mu_unlock(&pollset->mu); |
||||
|
||||
do { |
||||
ep_rv = epoll_wait(h->epoll_fd, ep_ev, GRPC_EPOLL_MAX_EVENTS, timeout_ms); |
||||
if (ep_rv < 0) { |
||||
if (errno != EINTR) { |
||||
gpr_log(GPR_ERROR, "epoll_wait() failed: %s", strerror(errno)); |
||||
} |
||||
} else { |
||||
int i; |
||||
for (i = 0; i < ep_rv; ++i) { |
||||
if (ep_ev[i].data.ptr == 0) { |
||||
grpc_wakeup_fd_consume_wakeup(&h->wakeup_fd); |
||||
} else { |
||||
grpc_fd *fd = ep_ev[i].data.ptr; |
||||
/* TODO(klempner): We might want to consider making err and pri
|
||||
* separate events */ |
||||
int cancel = ep_ev[i].events & (EPOLLERR | EPOLLHUP); |
||||
int read = ep_ev[i].events & (EPOLLIN | EPOLLPRI); |
||||
int write = ep_ev[i].events & EPOLLOUT; |
||||
if (read || cancel) { |
||||
grpc_fd_become_readable(fd, allow_synchronous_callback); |
||||
} |
||||
if (write || cancel) { |
||||
grpc_fd_become_writable(fd, allow_synchronous_callback); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
timeout_ms = 0; |
||||
} while (ep_rv == GRPC_EPOLL_MAX_EVENTS); |
||||
|
||||
gpr_mu_lock(&pollset->mu); |
||||
pollset->counter -= 1; |
||||
/* TODO(klempner): This should signal once per event rather than broadcast,
|
||||
* although it probably doesn't matter because threads will generally be |
||||
* blocked in epoll_wait rather than being blocked on the cv. */ |
||||
gpr_cv_broadcast(&pollset->cv); |
||||
return 1; |
||||
} |
||||
|
||||
static void multipoll_with_epoll_pollset_destroy(grpc_pollset *pollset) { |
||||
pollset_hdr *h = pollset->data.ptr; |
||||
grpc_wakeup_fd_destroy(&h->wakeup_fd); |
||||
close(h->epoll_fd); |
||||
gpr_free(h); |
||||
} |
||||
|
||||
static void epoll_kick(grpc_pollset *pollset) { |
||||
pollset_hdr *h = pollset->data.ptr; |
||||
grpc_wakeup_fd_wakeup(&h->wakeup_fd); |
||||
} |
||||
|
||||
static const grpc_pollset_vtable multipoll_with_epoll_pollset = { |
||||
multipoll_with_epoll_pollset_add_fd, multipoll_with_epoll_pollset_del_fd, |
||||
multipoll_with_epoll_pollset_maybe_work, epoll_kick, |
||||
multipoll_with_epoll_pollset_destroy}; |
||||
|
||||
void grpc_platform_become_multipoller(grpc_pollset *pollset, grpc_fd **fds, |
||||
size_t nfds) { |
||||
size_t i; |
||||
pollset_hdr *h = gpr_malloc(sizeof(pollset_hdr)); |
||||
struct epoll_event ev; |
||||
int err; |
||||
|
||||
pollset->vtable = &multipoll_with_epoll_pollset; |
||||
pollset->data.ptr = h; |
||||
h->epoll_fd = epoll_create1(EPOLL_CLOEXEC); |
||||
if (h->epoll_fd < 0) { |
||||
/* TODO(klempner): Fall back to poll here, especially on ENOSYS */ |
||||
gpr_log(GPR_ERROR, "epoll_create1 failed: %s", strerror(errno)); |
||||
abort(); |
||||
} |
||||
for (i = 0; i < nfds; i++) { |
||||
multipoll_with_epoll_pollset_add_fd(pollset, fds[i]); |
||||
} |
||||
|
||||
grpc_wakeup_fd_create(&h->wakeup_fd); |
||||
ev.events = EPOLLIN; |
||||
ev.data.ptr = 0; |
||||
err = epoll_ctl(h->epoll_fd, EPOLL_CTL_ADD, |
||||
GRPC_WAKEUP_FD_GET_READ_FD(&h->wakeup_fd), &ev); |
||||
if (err < 0) { |
||||
gpr_log(GPR_ERROR, "Wakeup fd epoll_ctl failed: %s", strerror(errno)); |
||||
abort(); |
||||
} |
||||
} |
||||
|
||||
#endif /* GPR_LINUX_MULTIPOLL_WITH_EPOLL */ |
@ -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. |
||||
* |
||||
*/ |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
#include <grpc/support/alloc.h> |
||||
#include <grpc/support/log.h> |
||||
|
||||
#ifdef GPR_WINSOCK_SOCKET |
||||
|
||||
#include "src/core/iomgr/iocp_windows.h" |
||||
#include "src/core/iomgr/iomgr.h" |
||||
#include "src/core/iomgr/iomgr_internal.h" |
||||
#include "src/core/iomgr/socket_windows.h" |
||||
#include "src/core/iomgr/pollset.h" |
||||
#include "src/core/iomgr/pollset_windows.h" |
||||
|
||||
grpc_winsocket *grpc_winsocket_create(SOCKET socket) { |
||||
grpc_winsocket *r = gpr_malloc(sizeof(grpc_winsocket)); |
||||
gpr_log(GPR_DEBUG, "grpc_winsocket_create"); |
||||
memset(r, 0, sizeof(grpc_winsocket)); |
||||
r->socket = socket; |
||||
gpr_mu_init(&r->state_mu); |
||||
grpc_iomgr_ref(); |
||||
grpc_iocp_add_socket(r); |
||||
return r; |
||||
} |
||||
|
||||
void shutdown_op(grpc_winsocket_callback_info *info) { |
||||
if (!info->cb) return; |
||||
grpc_iomgr_add_delayed_callback(info->cb, info->opaque, 0); |
||||
} |
||||
|
||||
void grpc_winsocket_shutdown(grpc_winsocket *socket) { |
||||
gpr_log(GPR_DEBUG, "grpc_winsocket_shutdown"); |
||||
shutdown_op(&socket->read_info); |
||||
shutdown_op(&socket->write_info); |
||||
} |
||||
|
||||
void grpc_winsocket_orphan(grpc_winsocket *socket) { |
||||
gpr_log(GPR_DEBUG, "grpc_winsocket_orphan"); |
||||
grpc_iomgr_unref(); |
||||
closesocket(socket->socket); |
||||
gpr_mu_destroy(&socket->state_mu); |
||||
gpr_free(socket); |
||||
} |
||||
|
||||
#endif /* GPR_WINSOCK_SOCKET */ |
@ -0,0 +1,215 @@ |
||||
/*
|
||||
* |
||||
* Copyright 2014, Google Inc. |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* * Redistributions in binary form must reproduce the above |
||||
* copyright notice, this list of conditions and the following disclaimer |
||||
* in the documentation and/or other materials provided with the |
||||
* distribution. |
||||
* * Neither the name of Google Inc. nor the names of its |
||||
* contributors may be used to endorse or promote products derived from |
||||
* this software without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
* |
||||
*/ |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#ifdef GPR_WINSOCK_SOCKET |
||||
|
||||
#include "src/core/iomgr/sockaddr_win32.h" |
||||
|
||||
#include <grpc/support/alloc.h> |
||||
#include <grpc/support/log.h> |
||||
#include <grpc/support/log_win32.h> |
||||
#include <grpc/support/slice_buffer.h> |
||||
#include <grpc/support/useful.h> |
||||
|
||||
#include "src/core/iomgr/tcp_client.h" |
||||
#include "src/core/iomgr/tcp_windows.h" |
||||
#include "src/core/iomgr/socket_windows.h" |
||||
#include "src/core/iomgr/alarm.h" |
||||
#include "src/core/iomgr/sockaddr.h" |
||||
#include "src/core/iomgr/sockaddr_utils.h" |
||||
|
||||
typedef struct { |
||||
void(*cb)(void *arg, grpc_endpoint *tcp); |
||||
void *cb_arg; |
||||
gpr_mu mu; |
||||
grpc_winsocket *socket; |
||||
gpr_timespec deadline; |
||||
grpc_alarm alarm; |
||||
int refs; |
||||
} async_connect; |
||||
|
||||
static void async_connect_cleanup(async_connect *ac) { |
||||
int done = (--ac->refs == 0); |
||||
gpr_mu_unlock(&ac->mu); |
||||
if (done) { |
||||
gpr_mu_destroy(&ac->mu); |
||||
gpr_free(ac); |
||||
} |
||||
} |
||||
|
||||
static void on_alarm(void *acp, int success) { |
||||
async_connect *ac = acp; |
||||
gpr_mu_lock(&ac->mu); |
||||
if (ac->socket != NULL && success) { |
||||
grpc_winsocket_shutdown(ac->socket); |
||||
} |
||||
async_connect_cleanup(ac); |
||||
} |
||||
|
||||
static void on_connect(void *acp, int success) { |
||||
async_connect *ac = acp; |
||||
SOCKET sock = ac->socket->socket; |
||||
grpc_endpoint *ep = NULL; |
||||
grpc_winsocket_callback_info *info = &ac->socket->write_info; |
||||
void(*cb)(void *arg, grpc_endpoint *tcp) = ac->cb; |
||||
void *cb_arg = ac->cb_arg; |
||||
|
||||
grpc_alarm_cancel(&ac->alarm); |
||||
|
||||
if (success) { |
||||
DWORD transfered_bytes = 0; |
||||
DWORD flags; |
||||
BOOL wsa_success = WSAGetOverlappedResult(sock, &info->overlapped, |
||||
&transfered_bytes, FALSE, |
||||
&flags); |
||||
GPR_ASSERT(transfered_bytes == 0); |
||||
if (!wsa_success) { |
||||
char *utf8_message = gpr_format_message(WSAGetLastError()); |
||||
gpr_log(GPR_ERROR, "on_connect error: %s", utf8_message); |
||||
gpr_free(utf8_message); |
||||
goto finish; |
||||
} else { |
||||
gpr_log(GPR_DEBUG, "on_connect: connection established"); |
||||
ep = grpc_tcp_create(ac->socket); |
||||
goto finish; |
||||
} |
||||
} else { |
||||
gpr_log(GPR_ERROR, "on_connect is shutting down"); |
||||
goto finish; |
||||
} |
||||
|
||||
abort(); |
||||
|
||||
finish: |
||||
gpr_mu_lock(&ac->mu); |
||||
if (!ep) { |
||||
grpc_winsocket_orphan(ac->socket); |
||||
} |
||||
async_connect_cleanup(ac); |
||||
cb(cb_arg, ep); |
||||
} |
||||
|
||||
void grpc_tcp_client_connect(void(*cb)(void *arg, grpc_endpoint *tcp), |
||||
void *arg, const struct sockaddr *addr, |
||||
int addr_len, gpr_timespec deadline) { |
||||
SOCKET sock = INVALID_SOCKET; |
||||
BOOL success; |
||||
int status; |
||||
struct sockaddr_in6 addr6_v4mapped; |
||||
struct sockaddr_in6 local_address; |
||||
async_connect *ac; |
||||
grpc_winsocket *socket = NULL; |
||||
LPFN_CONNECTEX ConnectEx; |
||||
GUID guid = WSAID_CONNECTEX; |
||||
DWORD ioctl_num_bytes; |
||||
const char *message = NULL; |
||||
char *utf8_message; |
||||
grpc_winsocket_callback_info *info; |
||||
|
||||
/* Use dualstack sockets where available. */ |
||||
if (grpc_sockaddr_to_v4mapped(addr, &addr6_v4mapped)) { |
||||
addr = (const struct sockaddr *)&addr6_v4mapped; |
||||
addr_len = sizeof(addr6_v4mapped); |
||||
} |
||||
|
||||
sock = WSASocket(AF_INET6, SOCK_STREAM, IPPROTO_TCP, NULL, 0, |
||||
WSA_FLAG_OVERLAPPED); |
||||
if (sock == INVALID_SOCKET) { |
||||
message = "Unable to create socket: %s"; |
||||
goto failure; |
||||
} |
||||
|
||||
if (!grpc_tcp_prepare_socket(sock)) { |
||||
message = "Unable to set socket options: %s"; |
||||
goto failure; |
||||
} |
||||
|
||||
status = WSAIoctl(sock, SIO_GET_EXTENSION_FUNCTION_POINTER, |
||||
&guid, sizeof(guid), &ConnectEx, sizeof(ConnectEx), |
||||
&ioctl_num_bytes, NULL, NULL); |
||||
|
||||
if (status != 0) { |
||||
message = "Unable to retreive ConnectEx pointer: %s"; |
||||
goto failure; |
||||
} |
||||
|
||||
grpc_sockaddr_make_wildcard6(0, &local_address); |
||||
|
||||
status = bind(sock, (struct sockaddr *) &local_address, |
||||
sizeof(local_address)); |
||||
if (status != 0) { |
||||
message = "Unable to bind socket: %s"; |
||||
goto failure; |
||||
} |
||||
|
||||
socket = grpc_winsocket_create(sock); |
||||
info = &socket->write_info; |
||||
success = ConnectEx(sock, addr, addr_len, NULL, 0, NULL, &info->overlapped); |
||||
|
||||
if (success) { |
||||
gpr_log(GPR_DEBUG, "connected immediately - but we still go to sleep"); |
||||
} else { |
||||
int error = WSAGetLastError(); |
||||
if (error != ERROR_IO_PENDING) { |
||||
message = "ConnectEx failed: %s"; |
||||
goto failure; |
||||
} |
||||
} |
||||
|
||||
gpr_log(GPR_DEBUG, "grpc_tcp_client_connect: connection pending"); |
||||
ac = gpr_malloc(sizeof(async_connect)); |
||||
ac->cb = cb; |
||||
ac->cb_arg = arg; |
||||
ac->socket = socket; |
||||
gpr_mu_init(&ac->mu); |
||||
ac->refs = 2; |
||||
|
||||
grpc_alarm_init(&ac->alarm, deadline, on_alarm, ac, gpr_now()); |
||||
grpc_socket_notify_on_write(socket, on_connect, ac); |
||||
return; |
||||
|
||||
failure: |
||||
utf8_message = gpr_format_message(WSAGetLastError()); |
||||
gpr_log(GPR_ERROR, message, utf8_message); |
||||
gpr_free(utf8_message); |
||||
if (socket) { |
||||
grpc_winsocket_orphan(socket); |
||||
} else if (sock != INVALID_SOCKET) { |
||||
closesocket(sock); |
||||
} |
||||
cb(arg, NULL); |
||||
} |
||||
|
||||
#endif /* GPR_WINSOCK_SOCKET */ |
@ -0,0 +1,374 @@ |
||||
/*
|
||||
* |
||||
* Copyright 2014, Google Inc. |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* * Redistributions in binary form must reproduce the above |
||||
* copyright notice, this list of conditions and the following disclaimer |
||||
* in the documentation and/or other materials provided with the |
||||
* distribution. |
||||
* * Neither the name of Google Inc. nor the names of its |
||||
* contributors may be used to endorse or promote products derived from |
||||
* this software without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
* |
||||
*/ |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#ifdef GPR_WINSOCK_SOCKET |
||||
|
||||
#define _GNU_SOURCE |
||||
#include "src/core/iomgr/sockaddr_utils.h" |
||||
|
||||
#include <grpc/support/alloc.h> |
||||
#include <grpc/support/log.h> |
||||
#include <grpc/support/log_win32.h> |
||||
#include <grpc/support/sync.h> |
||||
#include <grpc/support/time.h> |
||||
|
||||
#include "src/core/iomgr/iocp_windows.h" |
||||
#include "src/core/iomgr/pollset_windows.h" |
||||
#include "src/core/iomgr/socket_windows.h" |
||||
#include "src/core/iomgr/tcp_server.h" |
||||
#include "src/core/iomgr/tcp_windows.h" |
||||
|
||||
#define INIT_PORT_CAP 2 |
||||
#define MIN_SAFE_ACCEPT_QUEUE_SIZE 100 |
||||
|
||||
static gpr_once s_init_max_accept_queue_size; |
||||
static int s_max_accept_queue_size; |
||||
|
||||
/* one listening port */ |
||||
typedef struct server_port { |
||||
gpr_uint8 addresses[sizeof(struct sockaddr_in6) * 2 + 32]; |
||||
SOCKET new_socket; |
||||
grpc_winsocket *socket; |
||||
grpc_tcp_server *server; |
||||
LPFN_ACCEPTEX AcceptEx; |
||||
} server_port; |
||||
|
||||
/* the overall server */ |
||||
struct grpc_tcp_server { |
||||
grpc_tcp_server_cb cb; |
||||
void *cb_arg; |
||||
|
||||
gpr_mu mu; |
||||
gpr_cv cv; |
||||
|
||||
/* active port count: how many ports are actually still listening */ |
||||
int active_ports; |
||||
|
||||
/* all listening ports */ |
||||
server_port *ports; |
||||
size_t nports; |
||||
size_t port_capacity; |
||||
}; |
||||
|
||||
grpc_tcp_server *grpc_tcp_server_create(void) { |
||||
grpc_tcp_server *s = gpr_malloc(sizeof(grpc_tcp_server)); |
||||
gpr_mu_init(&s->mu); |
||||
gpr_cv_init(&s->cv); |
||||
s->active_ports = 0; |
||||
s->cb = NULL; |
||||
s->cb_arg = NULL; |
||||
s->ports = gpr_malloc(sizeof(server_port) * INIT_PORT_CAP); |
||||
s->nports = 0; |
||||
s->port_capacity = INIT_PORT_CAP; |
||||
return s; |
||||
} |
||||
|
||||
void grpc_tcp_server_destroy(grpc_tcp_server *s) { |
||||
size_t i; |
||||
gpr_mu_lock(&s->mu); |
||||
/* shutdown all fd's */ |
||||
for (i = 0; i < s->nports; i++) { |
||||
grpc_winsocket_shutdown(s->ports[i].socket); |
||||
} |
||||
/* wait while that happens */ |
||||
while (s->active_ports) { |
||||
gpr_cv_wait(&s->cv, &s->mu, gpr_inf_future); |
||||
} |
||||
gpr_mu_unlock(&s->mu); |
||||
|
||||
/* delete ALL the things */ |
||||
for (i = 0; i < s->nports; i++) { |
||||
server_port *sp = &s->ports[i]; |
||||
grpc_winsocket_orphan(sp->socket); |
||||
} |
||||
gpr_free(s->ports); |
||||
gpr_free(s); |
||||
} |
||||
|
||||
/* Prepare a recently-created socket for listening. */ |
||||
static int prepare_socket(SOCKET sock, |
||||
const struct sockaddr *addr, int addr_len) { |
||||
struct sockaddr_storage sockname_temp; |
||||
socklen_t sockname_len; |
||||
|
||||
if (sock == INVALID_SOCKET) goto error; |
||||
|
||||
if (!grpc_tcp_prepare_socket(sock)) { |
||||
char *utf8_message = gpr_format_message(WSAGetLastError()); |
||||
gpr_log(GPR_ERROR, "Unable to prepare socket: %s", utf8_message); |
||||
gpr_free(utf8_message); |
||||
goto error; |
||||
} |
||||
|
||||
if (bind(sock, addr, addr_len) == SOCKET_ERROR) { |
||||
char *addr_str; |
||||
char *utf8_message = gpr_format_message(WSAGetLastError()); |
||||
grpc_sockaddr_to_string(&addr_str, addr, 0); |
||||
gpr_log(GPR_ERROR, "bind addr=%s: %s", addr_str, utf8_message); |
||||
gpr_free(utf8_message); |
||||
gpr_free(addr_str); |
||||
goto error; |
||||
} |
||||
|
||||
if (listen(sock, SOMAXCONN) == SOCKET_ERROR) { |
||||
char *utf8_message = gpr_format_message(WSAGetLastError()); |
||||
gpr_log(GPR_ERROR, "listen: %s", utf8_message); |
||||
gpr_free(utf8_message); |
||||
goto error; |
||||
} |
||||
|
||||
sockname_len = sizeof(sockname_temp); |
||||
if (getsockname(sock, (struct sockaddr *) &sockname_temp, &sockname_len) |
||||
== SOCKET_ERROR) { |
||||
char *utf8_message = gpr_format_message(WSAGetLastError()); |
||||
gpr_log(GPR_ERROR, "getsockname: %s", utf8_message); |
||||
gpr_free(utf8_message); |
||||
goto error; |
||||
} |
||||
|
||||
return grpc_sockaddr_get_port((struct sockaddr *) &sockname_temp); |
||||
|
||||
error: |
||||
if (sock != INVALID_SOCKET) closesocket(sock); |
||||
return -1; |
||||
} |
||||
|
||||
static void on_accept(void *arg, int success); |
||||
|
||||
static void start_accept(server_port *port) { |
||||
SOCKET sock = INVALID_SOCKET; |
||||
char *message; |
||||
char *utf8_message; |
||||
BOOL success; |
||||
DWORD addrlen = sizeof(struct sockaddr_in6) + 16; |
||||
DWORD bytes_received = 0; |
||||
|
||||
sock = WSASocket(AF_INET6, SOCK_STREAM, IPPROTO_TCP, NULL, 0, |
||||
WSA_FLAG_OVERLAPPED); |
||||
|
||||
if (sock == INVALID_SOCKET) { |
||||
message = "Unable to create socket: %s"; |
||||
goto failure; |
||||
} |
||||
|
||||
if (!grpc_tcp_prepare_socket(sock)) { |
||||
message = "Unable to prepare socket: %s"; |
||||
goto failure; |
||||
} |
||||
|
||||
success = port->AcceptEx(port->socket->socket, sock, port->addresses, 0, |
||||
addrlen, addrlen, &bytes_received, |
||||
&port->socket->read_info.overlapped); |
||||
|
||||
if (success) { |
||||
gpr_log(GPR_DEBUG, "accepted immediately - but we still go to sleep"); |
||||
} else { |
||||
int error = WSAGetLastError(); |
||||
if (error != ERROR_IO_PENDING) { |
||||
message = "AcceptEx failed: %s"; |
||||
goto failure; |
||||
} |
||||
} |
||||
|
||||
port->new_socket = sock; |
||||
grpc_socket_notify_on_read(port->socket, on_accept, port); |
||||
return; |
||||
|
||||
failure: |
||||
utf8_message = gpr_format_message(WSAGetLastError()); |
||||
gpr_log(GPR_ERROR, message, utf8_message); |
||||
gpr_free(utf8_message); |
||||
if (sock != INVALID_SOCKET) closesocket(sock); |
||||
} |
||||
|
||||
/* event manager callback when reads are ready */ |
||||
static void on_accept(void *arg, int success) { |
||||
server_port *sp = arg; |
||||
SOCKET sock = sp->new_socket; |
||||
grpc_winsocket_callback_info *info = &sp->socket->read_info; |
||||
grpc_endpoint *ep = NULL; |
||||
|
||||
if (success) { |
||||
DWORD transfered_bytes = 0; |
||||
DWORD flags; |
||||
BOOL wsa_success = WSAGetOverlappedResult(sock, &info->overlapped, |
||||
&transfered_bytes, FALSE, |
||||
&flags); |
||||
if (!wsa_success) { |
||||
char *utf8_message = gpr_format_message(WSAGetLastError()); |
||||
gpr_log(GPR_ERROR, "on_accept error: %s", utf8_message); |
||||
gpr_free(utf8_message); |
||||
closesocket(sock); |
||||
} else { |
||||
gpr_log(GPR_DEBUG, "on_accept: accepted connection"); |
||||
ep = grpc_tcp_create(grpc_winsocket_create(sock)); |
||||
} |
||||
} else { |
||||
gpr_log(GPR_DEBUG, "on_accept: shutting down"); |
||||
closesocket(sock); |
||||
gpr_mu_lock(&sp->server->mu); |
||||
if (0 == --sp->server->active_ports) { |
||||
gpr_cv_broadcast(&sp->server->cv); |
||||
} |
||||
gpr_mu_unlock(&sp->server->mu); |
||||
} |
||||
|
||||
if (ep) sp->server->cb(sp->server->cb_arg, ep); |
||||
start_accept(sp); |
||||
} |
||||
|
||||
static int add_socket_to_server(grpc_tcp_server *s, SOCKET sock, |
||||
const struct sockaddr *addr, int addr_len) { |
||||
server_port *sp; |
||||
int port; |
||||
int status; |
||||
GUID guid = WSAID_ACCEPTEX; |
||||
DWORD ioctl_num_bytes; |
||||
LPFN_ACCEPTEX AcceptEx; |
||||
|
||||
if (sock == INVALID_SOCKET) return -1; |
||||
|
||||
status = WSAIoctl(sock, SIO_GET_EXTENSION_FUNCTION_POINTER, |
||||
&guid, sizeof(guid), &AcceptEx, sizeof(AcceptEx), |
||||
&ioctl_num_bytes, NULL, NULL); |
||||
|
||||
if (status != 0) { |
||||
char *utf8_message = gpr_format_message(WSAGetLastError()); |
||||
gpr_log(GPR_ERROR, "on_connect error: %s", utf8_message); |
||||
gpr_free(utf8_message); |
||||
closesocket(sock); |
||||
return -1; |
||||
} |
||||
|
||||
port = prepare_socket(sock, 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->socket = grpc_winsocket_create(sock); |
||||
sp->AcceptEx = AcceptEx; |
||||
GPR_ASSERT(sp->socket); |
||||
gpr_mu_unlock(&s->mu); |
||||
} |
||||
|
||||
return port; |
||||
} |
||||
|
||||
int grpc_tcp_server_add_port(grpc_tcp_server *s, const void *addr, |
||||
int addr_len) { |
||||
int allocated_port = -1; |
||||
unsigned i; |
||||
SOCKET sock; |
||||
struct sockaddr_in6 addr6_v4mapped; |
||||
struct sockaddr_in6 wildcard; |
||||
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].socket->socket, |
||||
(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); |
||||
} |
||||
|
||||
/* Treat :: or 0.0.0.0 as a family-agnostic wildcard. */ |
||||
if (grpc_sockaddr_is_wildcard(addr, &port)) { |
||||
grpc_sockaddr_make_wildcard6(port, &wildcard); |
||||
|
||||
addr = (struct sockaddr *) &wildcard; |
||||
addr_len = sizeof(wildcard); |
||||
} |
||||
|
||||
sock = WSASocket(AF_INET6, SOCK_STREAM, IPPROTO_TCP, NULL, 0, |
||||
WSA_FLAG_OVERLAPPED); |
||||
if (sock == INVALID_SOCKET) { |
||||
char *utf8_message = gpr_format_message(WSAGetLastError()); |
||||
gpr_log(GPR_ERROR, "unable to create socket: %s", utf8_message); |
||||
gpr_free(utf8_message); |
||||
} |
||||
|
||||
allocated_port = add_socket_to_server(s, sock, addr, addr_len); |
||||
gpr_free(allocated_addr); |
||||
|
||||
return allocated_port; |
||||
} |
||||
|
||||
SOCKET grpc_tcp_server_get_socket(grpc_tcp_server *s, unsigned index) { |
||||
return (index < s->nports) ? s->ports[index].socket->socket : INVALID_SOCKET; |
||||
} |
||||
|
||||
void grpc_tcp_server_start(grpc_tcp_server *s, grpc_pollset *pollset, |
||||
grpc_tcp_server_cb cb, void *cb_arg) { |
||||
size_t i; |
||||
GPR_ASSERT(cb); |
||||
gpr_mu_lock(&s->mu); |
||||
GPR_ASSERT(!s->cb); |
||||
GPR_ASSERT(s->active_ports == 0); |
||||
s->cb = cb; |
||||
s->cb_arg = cb_arg; |
||||
for (i = 0; i < s->nports; i++) { |
||||
start_accept(s->ports + i); |
||||
s->active_ports++; |
||||
} |
||||
gpr_mu_unlock(&s->mu); |
||||
} |
||||
|
||||
#endif /* GPR_WINSOCK_SOCKET */ |
@ -0,0 +1,373 @@ |
||||
/*
|
||||
* |
||||
* Copyright 2014, Google Inc. |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* * Redistributions in binary form must reproduce the above |
||||
* copyright notice, this list of conditions and the following disclaimer |
||||
* in the documentation and/or other materials provided with the |
||||
* distribution. |
||||
* * Neither the name of Google Inc. nor the names of its |
||||
* contributors may be used to endorse or promote products derived from |
||||
* this software without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
* |
||||
*/ |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#ifdef GPR_WINSOCK_SOCKET |
||||
|
||||
#include "src/core/iomgr/sockaddr_win32.h" |
||||
|
||||
#include <grpc/support/alloc.h> |
||||
#include <grpc/support/log.h> |
||||
#include <grpc/support/log_win32.h> |
||||
#include <grpc/support/slice_buffer.h> |
||||
#include <grpc/support/useful.h> |
||||
|
||||
#include "src/core/iomgr/alarm.h" |
||||
#include "src/core/iomgr/iocp_windows.h" |
||||
#include "src/core/iomgr/sockaddr.h" |
||||
#include "src/core/iomgr/sockaddr_utils.h" |
||||
#include "src/core/iomgr/socket_windows.h" |
||||
#include "src/core/iomgr/tcp_client.h" |
||||
|
||||
static int set_non_block(SOCKET sock) { |
||||
int status; |
||||
unsigned long param = 1; |
||||
DWORD ret; |
||||
status = WSAIoctl(sock, FIONBIO, ¶m, sizeof(param), NULL, 0, &ret, |
||||
NULL, NULL); |
||||
return status == 0; |
||||
} |
||||
|
||||
static int set_dualstack(SOCKET sock) { |
||||
int status; |
||||
unsigned long param = 0; |
||||
status = setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, |
||||
(const char *) ¶m, sizeof(param)); |
||||
return status == 0; |
||||
} |
||||
|
||||
int grpc_tcp_prepare_socket(SOCKET sock) { |
||||
if (!set_non_block(sock)) |
||||
return 0; |
||||
if (!set_dualstack(sock)) |
||||
return 0; |
||||
return 1; |
||||
} |
||||
|
||||
typedef struct grpc_tcp { |
||||
grpc_endpoint base; |
||||
grpc_winsocket *socket; |
||||
gpr_refcount refcount; |
||||
|
||||
grpc_endpoint_read_cb read_cb; |
||||
void *read_user_data; |
||||
gpr_slice read_slice; |
||||
int outstanding_read; |
||||
|
||||
grpc_endpoint_write_cb write_cb; |
||||
void *write_user_data; |
||||
gpr_slice_buffer write_slices; |
||||
int outstanding_write; |
||||
|
||||
} grpc_tcp; |
||||
|
||||
static void tcp_ref(grpc_tcp *tcp) { |
||||
gpr_log(GPR_DEBUG, "tcp_ref"); |
||||
gpr_ref(&tcp->refcount); |
||||
} |
||||
|
||||
static void tcp_unref(grpc_tcp *tcp) { |
||||
gpr_log(GPR_DEBUG, "tcp_unref"); |
||||
if (gpr_unref(&tcp->refcount)) { |
||||
gpr_log(GPR_DEBUG, "tcp_unref: destroying"); |
||||
gpr_slice_buffer_destroy(&tcp->write_slices); |
||||
grpc_winsocket_orphan(tcp->socket); |
||||
gpr_free(tcp); |
||||
} |
||||
} |
||||
|
||||
static void on_read(void *tcpp, int success) { |
||||
grpc_tcp *tcp = (grpc_tcp *) tcpp; |
||||
grpc_winsocket *socket = tcp->socket; |
||||
gpr_slice sub; |
||||
gpr_slice *slice = NULL; |
||||
size_t nslices = 0; |
||||
grpc_endpoint_cb_status status; |
||||
grpc_endpoint_read_cb cb = tcp->read_cb; |
||||
grpc_winsocket_callback_info *info = &socket->read_info; |
||||
void *opaque = tcp->read_user_data; |
||||
|
||||
GPR_ASSERT(tcp->outstanding_read); |
||||
|
||||
if (!success) { |
||||
tcp_unref(tcp); |
||||
cb(opaque, NULL, 0, GRPC_ENDPOINT_CB_SHUTDOWN); |
||||
return; |
||||
} |
||||
|
||||
gpr_log(GPR_DEBUG, "on_read"); |
||||
tcp->outstanding_read = 0; |
||||
|
||||
if (socket->read_info.wsa_error != 0) { |
||||
char *utf8_message = gpr_format_message(info->wsa_error); |
||||
__debugbreak(); |
||||
gpr_log(GPR_ERROR, "ReadFile overlapped error: %s", utf8_message); |
||||
gpr_free(utf8_message); |
||||
status = GRPC_ENDPOINT_CB_ERROR; |
||||
} else { |
||||
if (info->bytes_transfered != 0) { |
||||
sub = gpr_slice_sub(tcp->read_slice, 0, info->bytes_transfered); |
||||
gpr_log(GPR_DEBUG, "on_read: calling callback"); |
||||
status = GRPC_ENDPOINT_CB_OK; |
||||
slice = ⊂ |
||||
nslices = 1; |
||||
} else { |
||||
gpr_log(GPR_DEBUG, "on_read: closed socket"); |
||||
gpr_slice_unref(tcp->read_slice); |
||||
status = GRPC_ENDPOINT_CB_EOF; |
||||
} |
||||
} |
||||
tcp_unref(tcp); |
||||
cb(opaque, slice, nslices, status); |
||||
} |
||||
|
||||
static void win_notify_on_read(grpc_endpoint *ep, |
||||
grpc_endpoint_read_cb cb, void *arg) { |
||||
grpc_tcp *tcp = (grpc_tcp *) ep; |
||||
grpc_winsocket *handle = tcp->socket; |
||||
grpc_winsocket_callback_info *info = &handle->read_info; |
||||
int status; |
||||
DWORD bytes_read = 0; |
||||
DWORD flags = 0; |
||||
int error; |
||||
WSABUF buffer; |
||||
|
||||
GPR_ASSERT(!tcp->outstanding_read); |
||||
tcp_ref(tcp); |
||||
tcp->outstanding_read = 1; |
||||
tcp->read_cb = cb; |
||||
tcp->read_user_data = arg; |
||||
|
||||
tcp->read_slice = gpr_slice_malloc(8192); |
||||
|
||||
buffer.len = GPR_SLICE_LENGTH(tcp->read_slice); |
||||
buffer.buf = GPR_SLICE_START_PTR(tcp->read_slice); |
||||
|
||||
gpr_log(GPR_DEBUG, "win_notify_on_read: calling WSARecv without overlap"); |
||||
status = WSARecv(tcp->socket->socket, &buffer, 1, &bytes_read, &flags, |
||||
NULL, NULL); |
||||
info->wsa_error = status == 0 ? 0 : WSAGetLastError(); |
||||
|
||||
if (info->wsa_error != WSAEWOULDBLOCK) { |
||||
gpr_log(GPR_DEBUG, "got response immediately, calling on_read"); |
||||
info->bytes_transfered = bytes_read; |
||||
/* This might heavily recurse. */ |
||||
on_read(tcp, 1); |
||||
return; |
||||
} |
||||
|
||||
gpr_log(GPR_DEBUG, "got WSAEWOULDBLOCK - calling WSARecv with overlap"); |
||||
|
||||
memset(&tcp->socket->read_info.overlapped, 0, sizeof(OVERLAPPED)); |
||||
status = WSARecv(tcp->socket->socket, &buffer, 1, &bytes_read, &flags, |
||||
&info->overlapped, NULL); |
||||
|
||||
if (status == 0) { |
||||
gpr_log(GPR_DEBUG, "got response immediately, but we're going to sleep"); |
||||
grpc_socket_notify_on_read(tcp->socket, on_read, tcp); |
||||
return; |
||||
} |
||||
|
||||
error = WSAGetLastError(); |
||||
|
||||
if (error != WSA_IO_PENDING) { |
||||
char *utf8_message = gpr_format_message(WSAGetLastError()); |
||||
__debugbreak(); |
||||
gpr_log(GPR_ERROR, "WSARecv error: %s", utf8_message); |
||||
gpr_free(utf8_message); |
||||
/* would the IO completion port be called anyway... ? Let's assume not. */ |
||||
tcp->outstanding_read = 0; |
||||
tcp_unref(tcp); |
||||
cb(arg, NULL, 0, GRPC_ENDPOINT_CB_ERROR); |
||||
return; |
||||
} |
||||
|
||||
gpr_log(GPR_DEBUG, "waiting on the IO completion port now"); |
||||
grpc_socket_notify_on_read(tcp->socket, on_read, tcp); |
||||
} |
||||
|
||||
static void on_write(void *tcpp, int success) { |
||||
grpc_tcp *tcp = (grpc_tcp *) tcpp; |
||||
grpc_winsocket *handle = tcp->socket; |
||||
grpc_winsocket_callback_info *info = &handle->write_info; |
||||
grpc_endpoint_cb_status status = GRPC_ENDPOINT_CB_OK; |
||||
grpc_endpoint_write_cb cb = tcp->write_cb; |
||||
void *opaque = tcp->write_user_data; |
||||
|
||||
GPR_ASSERT(tcp->outstanding_write); |
||||
|
||||
gpr_log(GPR_DEBUG, "on_write"); |
||||
|
||||
if (!success) { |
||||
tcp_unref(tcp); |
||||
cb(opaque, GRPC_ENDPOINT_CB_SHUTDOWN); |
||||
return; |
||||
} |
||||
|
||||
if (info->wsa_error != 0) { |
||||
char *utf8_message = gpr_format_message(info->wsa_error); |
||||
gpr_log(GPR_ERROR, "WSASend overlapped error: %s", utf8_message); |
||||
gpr_free(utf8_message); |
||||
status = GRPC_ENDPOINT_CB_ERROR; |
||||
} else { |
||||
GPR_ASSERT(info->bytes_transfered == tcp->write_slices.length); |
||||
} |
||||
|
||||
gpr_slice_buffer_reset_and_unref(&tcp->write_slices); |
||||
tcp->outstanding_write = 0; |
||||
|
||||
tcp_unref(tcp); |
||||
cb(opaque, status); |
||||
} |
||||
|
||||
static grpc_endpoint_write_status win_write(grpc_endpoint *ep, |
||||
gpr_slice *slices, size_t nslices, |
||||
grpc_endpoint_write_cb cb, |
||||
void *arg) { |
||||
grpc_tcp *tcp = (grpc_tcp *) ep; |
||||
grpc_winsocket *socket = tcp->socket; |
||||
grpc_winsocket_callback_info *info = &socket->write_info; |
||||
unsigned i; |
||||
DWORD bytes_sent; |
||||
int status; |
||||
WSABUF local_buffers[16]; |
||||
WSABUF *allocated = NULL; |
||||
WSABUF *buffers = local_buffers; |
||||
|
||||
GPR_ASSERT(nslices != 0); |
||||
GPR_ASSERT(GPR_SLICE_LENGTH(slices[0]) != 0); |
||||
GPR_ASSERT(!tcp->outstanding_write); |
||||
tcp_ref(tcp); |
||||
|
||||
gpr_log(GPR_DEBUG, "win_write"); |
||||
|
||||
tcp->outstanding_write = 1; |
||||
tcp->write_cb = cb; |
||||
tcp->write_user_data = arg; |
||||
gpr_slice_buffer_addn(&tcp->write_slices, slices, nslices); |
||||
|
||||
if (tcp->write_slices.count > GPR_ARRAY_SIZE(local_buffers)) { |
||||
buffers = (WSABUF *) gpr_malloc(sizeof(WSABUF) * tcp->write_slices.count); |
||||
allocated = buffers; |
||||
} |
||||
|
||||
for (i = 0; i < tcp->write_slices.count; i++) { |
||||
buffers[i].len = GPR_SLICE_LENGTH(tcp->write_slices.slices[i]); |
||||
buffers[i].buf = GPR_SLICE_START_PTR(tcp->write_slices.slices[i]); |
||||
} |
||||
|
||||
gpr_log(GPR_DEBUG, "win_write: calling WSASend without overlap"); |
||||
status = WSASend(socket->socket, buffers, tcp->write_slices.count, |
||||
&bytes_sent, 0, NULL, NULL); |
||||
info->wsa_error = status == 0 ? 0 : WSAGetLastError(); |
||||
|
||||
if (info->wsa_error != WSAEWOULDBLOCK) { |
||||
grpc_endpoint_write_status ret = GRPC_ENDPOINT_WRITE_ERROR; |
||||
gpr_log(GPR_DEBUG, "got response immediately, cleaning up and leaving"); |
||||
if (status == 0) { |
||||
ret = GRPC_ENDPOINT_WRITE_DONE; |
||||
GPR_ASSERT(bytes_sent == tcp->write_slices.length); |
||||
} else { |
||||
char *utf8_message = gpr_format_message(info->wsa_error); |
||||
gpr_log(GPR_ERROR, "WSASend error: %s", utf8_message); |
||||
gpr_free(utf8_message); |
||||
} |
||||
if (allocated) gpr_free(allocated); |
||||
gpr_slice_buffer_reset_and_unref(&tcp->write_slices); |
||||
tcp->outstanding_write = 0; |
||||
tcp_unref(tcp); |
||||
return ret; |
||||
} |
||||
|
||||
gpr_log(GPR_DEBUG, "got WSAEWOULDBLOCK - calling WSASend with overlap"); |
||||
|
||||
memset(&socket->write_info, 0, sizeof(OVERLAPPED)); |
||||
status = WSASend(socket->socket, buffers, tcp->write_slices.count, |
||||
&bytes_sent, 0, &socket->write_info.overlapped, NULL); |
||||
if (allocated) gpr_free(allocated); |
||||
|
||||
if (status != 0) { |
||||
int error = WSAGetLastError(); |
||||
if (error != WSA_IO_PENDING) { |
||||
char *utf8_message = gpr_format_message(WSAGetLastError()); |
||||
__debugbreak(); |
||||
gpr_log(GPR_ERROR, "WSASend error: %s", utf8_message); |
||||
gpr_free(utf8_message); |
||||
/* would the IO completion port be called anyway ? Let's assume not. */ |
||||
tcp->outstanding_write = 0; |
||||
tcp_unref(tcp); |
||||
return GRPC_ENDPOINT_WRITE_ERROR; |
||||
} |
||||
gpr_log(GPR_DEBUG, "win_write: got pending op"); |
||||
} else { |
||||
gpr_log(GPR_DEBUG, "wrote data immediately - but we're going to sleep"); |
||||
} |
||||
|
||||
grpc_socket_notify_on_write(socket, on_write, tcp); |
||||
return GRPC_ENDPOINT_WRITE_PENDING; |
||||
} |
||||
|
||||
static void win_add_to_pollset(grpc_endpoint *ep, grpc_pollset *pollset) { |
||||
grpc_tcp *tcp = (grpc_tcp *) ep; |
||||
gpr_log(GPR_DEBUG, "win_add_to_pollset"); |
||||
grpc_iocp_add_socket(tcp->socket); |
||||
} |
||||
|
||||
static void win_shutdown(grpc_endpoint *ep) { |
||||
grpc_tcp *tcp = (grpc_tcp *) ep; |
||||
gpr_log(GPR_DEBUG, "win_shutdown"); |
||||
grpc_winsocket_shutdown(tcp->socket); |
||||
} |
||||
|
||||
static void win_destroy(grpc_endpoint *ep) { |
||||
grpc_tcp *tcp = (grpc_tcp *) ep; |
||||
gpr_log(GPR_DEBUG, "win_destroy"); |
||||
tcp_unref(tcp); |
||||
} |
||||
|
||||
static grpc_endpoint_vtable vtable = { |
||||
win_notify_on_read, win_write, win_add_to_pollset, win_shutdown, win_destroy |
||||
}; |
||||
|
||||
grpc_endpoint *grpc_tcp_create(grpc_winsocket *socket) { |
||||
grpc_tcp *tcp = (grpc_tcp *) gpr_malloc(sizeof(grpc_tcp)); |
||||
memset(tcp, 0, sizeof(grpc_tcp)); |
||||
tcp->base.vtable = &vtable; |
||||
tcp->socket = socket; |
||||
gpr_slice_buffer_init(&tcp->write_slices); |
||||
gpr_ref_init(&tcp->refcount, 1); |
||||
return &tcp->base; |
||||
} |
||||
|
||||
#endif /* GPR_WINSOCK_SOCKET */ |
@ -0,0 +1,57 @@ |
||||
/*
|
||||
* |
||||
* Copyright 2014, Google Inc. |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* * Redistributions in binary form must reproduce the above |
||||
* copyright notice, this list of conditions and the following disclaimer |
||||
* in the documentation and/or other materials provided with the |
||||
* distribution. |
||||
* * Neither the name of Google Inc. nor the names of its |
||||
* contributors may be used to endorse or promote products derived from |
||||
* this software without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
* |
||||
*/ |
||||
|
||||
#ifndef __GRPC_INTERNAL_IOMGR_TCP_WINDOWS_H__ |
||||
#define __GRPC_INTERNAL_IOMGR_TCP_WINDOWS_H__ |
||||
/*
|
||||
Low level TCP "bottom half" implementation, for use by transports built on |
||||
top of a TCP connection. |
||||
|
||||
Note that this file does not (yet) include APIs for creating the socket in |
||||
the first place. |
||||
|
||||
All calls passing slice transfer ownership of a slice refcount unless |
||||
otherwise specified. |
||||
*/ |
||||
|
||||
#include "src/core/iomgr/endpoint.h" |
||||
#include "src/core/iomgr/socket_windows.h" |
||||
|
||||
/* Create a tcp endpoint given a winsock handle.
|
||||
* Takes ownership of the handle. |
||||
*/ |
||||
grpc_endpoint *grpc_tcp_create(grpc_winsocket *socket); |
||||
|
||||
int grpc_tcp_prepare_socket(SOCKET sock); |
||||
|
||||
#endif /* __GRPC_INTERNAL_IOMGR_TCP_WINDOWS_H__ */ |
@ -0,0 +1,82 @@ |
||||
/*
|
||||
* |
||||
* Copyright 2015, Google Inc. |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* * Redistributions in binary form must reproduce the above |
||||
* copyright notice, this list of conditions and the following disclaimer |
||||
* in the documentation and/or other materials provided with the |
||||
* distribution. |
||||
* * Neither the name of Google Inc. nor the names of its |
||||
* contributors may be used to endorse or promote products derived from |
||||
* this software without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
* |
||||
*/ |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#ifdef GPR_LINUX_EVENTFD |
||||
|
||||
#include <errno.h> |
||||
#include <sys/eventfd.h> |
||||
#include <unistd.h> |
||||
|
||||
#include "src/core/iomgr/wakeup_fd_posix.h" |
||||
#include <grpc/support/log.h> |
||||
|
||||
static void eventfd_create(grpc_wakeup_fd_info *fd_info) { |
||||
int efd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC); |
||||
/* TODO(klempner): Handle failure more gracefully */ |
||||
GPR_ASSERT(efd >= 0); |
||||
fd_info->read_fd = efd; |
||||
fd_info->write_fd = -1; |
||||
} |
||||
|
||||
static void eventfd_consume(grpc_wakeup_fd_info *fd_info) { |
||||
eventfd_t value; |
||||
int err; |
||||
do { |
||||
err = eventfd_read(fd_info->read_fd, &value); |
||||
} while (err < 0 && errno == EINTR); |
||||
} |
||||
|
||||
static void eventfd_wakeup(grpc_wakeup_fd_info *fd_info) { |
||||
int err; |
||||
do { |
||||
err = eventfd_write(fd_info->read_fd, 1); |
||||
} while (err < 0 && errno == EINTR); |
||||
} |
||||
|
||||
static void eventfd_destroy(grpc_wakeup_fd_info *fd_info) { |
||||
close(fd_info->read_fd); |
||||
} |
||||
|
||||
static int eventfd_check_availability(void) { |
||||
/* TODO(klempner): Actually check if eventfd is available */ |
||||
return 1; |
||||
} |
||||
|
||||
const grpc_wakeup_fd_vtable grpc_specialized_wakeup_fd_vtable = { |
||||
eventfd_create, eventfd_consume, eventfd_wakeup, eventfd_destroy, |
||||
eventfd_check_availability |
||||
}; |
||||
|
||||
#endif /* GPR_LINUX_EVENTFD */ |
@ -0,0 +1,54 @@ |
||||
/*
|
||||
* |
||||
* Copyright 2015, 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. |
||||
* |
||||
*/ |
||||
|
||||
/*
|
||||
* This is a dummy file to provide an invalid specialized_wakeup_fd_vtable on |
||||
* systems without anything better than pipe. |
||||
*/ |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#ifdef GPR_POSIX_NO_SPECIAL_WAKEUP_FD |
||||
|
||||
#include "src/core/iomgr/wakeup_fd_posix.h" |
||||
#include <stddef.h> |
||||
|
||||
static int check_availability_invalid(void) { |
||||
return 0; |
||||
} |
||||
|
||||
const grpc_wakeup_fd_vtable grpc_specialized_wakeup_fd_vtable = { |
||||
NULL, NULL, NULL, NULL, check_availability_invalid |
||||
}; |
||||
|
||||
#endif /* GPR_POSIX_NO_SPECIAL_WAKEUP_FD */ |
@ -0,0 +1,97 @@ |
||||
/*
|
||||
* |
||||
* Copyright 2015, Google Inc. |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* * Redistributions in binary form must reproduce the above |
||||
* copyright notice, this list of conditions and the following disclaimer |
||||
* in the documentation and/or other materials provided with the |
||||
* distribution. |
||||
* * Neither the name of Google Inc. nor the names of its |
||||
* contributors may be used to endorse or promote products derived from |
||||
* this software without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
* |
||||
*/ |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#ifdef GPR_POSIX_WAKEUP_FD |
||||
|
||||
#include "src/core/iomgr/wakeup_fd_posix.h" |
||||
|
||||
#include <errno.h> |
||||
#include <string.h> |
||||
#include <unistd.h> |
||||
|
||||
#include "src/core/iomgr/socket_utils_posix.h" |
||||
#include <grpc/support/log.h> |
||||
|
||||
static void pipe_create(grpc_wakeup_fd_info *fd_info) { |
||||
int pipefd[2]; |
||||
/* TODO(klempner): Make this nonfatal */ |
||||
GPR_ASSERT(0 == pipe(pipefd)); |
||||
GPR_ASSERT(grpc_set_socket_nonblocking(pipefd[0], 1)); |
||||
GPR_ASSERT(grpc_set_socket_nonblocking(pipefd[1], 1)); |
||||
fd_info->read_fd = pipefd[0]; |
||||
fd_info->write_fd = pipefd[1]; |
||||
} |
||||
|
||||
static void pipe_consume(grpc_wakeup_fd_info *fd_info) { |
||||
char buf[128]; |
||||
int r; |
||||
|
||||
for (;;) { |
||||
r = read(fd_info->read_fd, buf, sizeof(buf)); |
||||
if (r > 0) continue; |
||||
if (r == 0) return; |
||||
switch (errno) { |
||||
case EAGAIN: |
||||
return; |
||||
case EINTR: |
||||
continue; |
||||
default: |
||||
gpr_log(GPR_ERROR, "error reading pipe: %s", strerror(errno)); |
||||
return; |
||||
} |
||||
} |
||||
} |
||||
|
||||
static void pipe_wakeup(grpc_wakeup_fd_info *fd_info) { |
||||
char c = 0; |
||||
while (write(fd_info->write_fd, &c, 1) != 1 && errno == EINTR) |
||||
; |
||||
} |
||||
|
||||
static void pipe_destroy(grpc_wakeup_fd_info *fd_info) { |
||||
close(fd_info->read_fd); |
||||
close(fd_info->write_fd); |
||||
} |
||||
|
||||
static int pipe_check_availability(void) { |
||||
/* Assume that pipes are always available. */ |
||||
return 1; |
||||
} |
||||
|
||||
const grpc_wakeup_fd_vtable grpc_pipe_wakeup_fd_vtable = { |
||||
pipe_create, pipe_consume, pipe_wakeup, pipe_destroy, pipe_check_availability |
||||
}; |
||||
|
||||
#endif /* GPR_POSIX_WAKUP_FD */ |
@ -0,0 +1,76 @@ |
||||
/*
|
||||
* |
||||
* Copyright 2015, Google Inc. |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* * Redistributions in binary form must reproduce the above |
||||
* copyright notice, this list of conditions and the following disclaimer |
||||
* in the documentation and/or other materials provided with the |
||||
* distribution. |
||||
* * Neither the name of Google Inc. nor the names of its |
||||
* contributors may be used to endorse or promote products derived from |
||||
* this software without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
* |
||||
*/ |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#ifdef GPR_POSIX_WAKEUP_FD |
||||
|
||||
#include "src/core/iomgr/wakeup_fd_posix.h" |
||||
#include "src/core/iomgr/wakeup_fd_pipe.h" |
||||
#include <stddef.h> |
||||
|
||||
static const grpc_wakeup_fd_vtable *wakeup_fd_vtable = NULL; |
||||
|
||||
void grpc_wakeup_fd_global_init(void) { |
||||
if (grpc_specialized_wakeup_fd_vtable.check_availability()) { |
||||
wakeup_fd_vtable = &grpc_specialized_wakeup_fd_vtable; |
||||
} else { |
||||
wakeup_fd_vtable = &grpc_pipe_wakeup_fd_vtable; |
||||
} |
||||
} |
||||
|
||||
void grpc_wakeup_fd_global_init_force_fallback(void) { |
||||
wakeup_fd_vtable = &grpc_pipe_wakeup_fd_vtable; |
||||
} |
||||
|
||||
void grpc_wakeup_fd_global_destroy(void) { |
||||
wakeup_fd_vtable = NULL; |
||||
} |
||||
|
||||
void grpc_wakeup_fd_create(grpc_wakeup_fd_info *fd_info) { |
||||
wakeup_fd_vtable->create(fd_info); |
||||
} |
||||
|
||||
void grpc_wakeup_fd_consume_wakeup(grpc_wakeup_fd_info *fd_info) { |
||||
wakeup_fd_vtable->consume(fd_info); |
||||
} |
||||
|
||||
void grpc_wakeup_fd_wakeup(grpc_wakeup_fd_info *fd_info) { |
||||
wakeup_fd_vtable->wakeup(fd_info); |
||||
} |
||||
|
||||
void grpc_wakeup_fd_destroy(grpc_wakeup_fd_info *fd_info) { |
||||
wakeup_fd_vtable->destroy(fd_info); |
||||
} |
||||
|
||||
#endif /* GPR_POSIX_WAKEUP_FD */ |
@ -0,0 +1,99 @@ |
||||
/*
|
||||
* |
||||
* Copyright 2015, 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. |
||||
* |
||||
*/ |
||||
|
||||
/*
|
||||
* wakeup_fd abstracts the concept of a file descriptor for the purpose of |
||||
* waking up a thread in select()/poll()/epoll_wait()/etc. |
||||
|
||||
* The poll() family of system calls provide a way for a thread to block until |
||||
* there is activity on one (or more) of a set of file descriptors. An |
||||
* application may wish to wake up this thread to do non file related work. The |
||||
* typical way to do this is to add a pipe to the set of file descriptors, then |
||||
* write to the pipe to wake up the thread in poll(). |
||||
* |
||||
* Linux has a lighter weight eventfd specifically designed for this purpose. |
||||
* wakeup_fd abstracts the difference between the two. |
||||
* |
||||
* Setup: |
||||
* 1. Before calling anything, call global_init() at least once. |
||||
* 1. Call grpc_wakeup_fd_create() to get a wakeup_fd. |
||||
* 2. Add the result of GRPC_WAKEUP_FD_FD to the set of monitored file |
||||
* descriptors for the poll() style API you are using. Monitor the file |
||||
* descriptor for readability. |
||||
* 3. To tear down, call grpc_wakeup_fd_destroy(). This closes the underlying |
||||
* file descriptor. |
||||
* |
||||
* Usage: |
||||
* 1. To wake up a polling thread, call grpc_wakeup_fd_wakeup() on a wakeup_fd |
||||
* it is monitoring. |
||||
* 2. If the polling thread was awakened by a wakeup_fd event, call |
||||
* grpc_wakeup_fd_consume_wakeup() on it. |
||||
*/ |
||||
#ifndef __GRPC_INTERNAL_IOMGR_WAKEUP_FD_POSIX_H_ |
||||
#define __GRPC_INTERNAL_IOMGR_WAKEUP_FD_POSIX_H_ |
||||
|
||||
void grpc_wakeup_fd_global_init(void); |
||||
void grpc_wakeup_fd_global_destroy(void); |
||||
|
||||
/* Force using the fallback implementation. This is intended for testing
|
||||
* purposes only.*/ |
||||
void grpc_wakeup_fd_global_init_force_fallback(void); |
||||
|
||||
typedef struct grpc_wakeup_fd_info grpc_wakeup_fd_info; |
||||
|
||||
typedef struct grpc_wakeup_fd_vtable { |
||||
void (*create)(grpc_wakeup_fd_info *fd_info); |
||||
void (*consume)(grpc_wakeup_fd_info *fd_info); |
||||
void (*wakeup)(grpc_wakeup_fd_info *fd_info); |
||||
void (*destroy)(grpc_wakeup_fd_info *fd_info); |
||||
/* Must be called before calling any other functions */ |
||||
int (*check_availability)(void); |
||||
} grpc_wakeup_fd_vtable; |
||||
|
||||
struct grpc_wakeup_fd_info { |
||||
int read_fd; |
||||
int write_fd; |
||||
}; |
||||
|
||||
#define GRPC_WAKEUP_FD_GET_READ_FD(fd_info) ((fd_info)->read_fd) |
||||
|
||||
void grpc_wakeup_fd_create(grpc_wakeup_fd_info *fd_info); |
||||
void grpc_wakeup_fd_consume_wakeup(grpc_wakeup_fd_info *fd_info); |
||||
void grpc_wakeup_fd_wakeup(grpc_wakeup_fd_info *fd_info); |
||||
void grpc_wakeup_fd_destroy(grpc_wakeup_fd_info *fd_info); |
||||
|
||||
/* Defined in some specialized implementation's .c file, or by
|
||||
* wakeup_fd_nospecial.c if no such implementation exists. */ |
||||
extern const grpc_wakeup_fd_vtable grpc_specialized_wakeup_fd_vtable; |
||||
|
||||
#endif /* __GRPC_INTERNAL_IOMGR_WAKEUP_FD_POSIX_H_ */ |
@ -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. |
||||
* |
||||
*/ |
||||
|
||||
#ifndef __GRPC_SRC_CORE_JSON_JSON_H__ |
||||
#define __GRPC_SRC_CORE_JSON_JSON_H__ |
||||
|
||||
#include <stdlib.h> |
||||
|
||||
#include "src/core/json/json_common.h" |
||||
|
||||
/* A tree-like structure to hold json values. The key and value pointers
|
||||
* are not owned by it. |
||||
*/ |
||||
typedef struct grpc_json { |
||||
struct grpc_json* next; |
||||
struct grpc_json* prev; |
||||
struct grpc_json* child; |
||||
struct grpc_json* parent; |
||||
|
||||
grpc_json_type type; |
||||
const char* key; |
||||
const char* value; |
||||
} grpc_json; |
||||
|
||||
/* The next two functions are going to parse the input string, and
|
||||
* destroy it in the process, in order to use its space to store |
||||
* all of the keys and values for the returned object tree. |
||||
* |
||||
* They assume UTF-8 input stream, and will output UTF-8 encoded |
||||
* strings in the tree. The input stream's UTF-8 isn't validated, |
||||
* as in, what you input is what you get as an output. |
||||
* |
||||
* All the keys and values in the grpc_json_t objects will be strings |
||||
* pointing at your input buffer. |
||||
* |
||||
* Delete the allocated tree afterward using grpc_json_destroy(). |
||||
*/ |
||||
grpc_json* grpc_json_parse_string_with_len(char* input, size_t size); |
||||
grpc_json* grpc_json_parse_string(char* input); |
||||
|
||||
/* This function will create a new string using gpr_realloc, and will
|
||||
* deserialize the grpc_json tree into it. It'll be zero-terminated, |
||||
* but will be allocated in chunks of 256 bytes. |
||||
* |
||||
* The indent parameter controls the way the output is formatted. |
||||
* If indent is 0, then newlines will be suppressed as well, and the |
||||
* output will be condensed at its maximum. |
||||
*/ |
||||
char* grpc_json_dump_to_string(grpc_json* json, int indent); |
||||
|
||||
/* Use these to create or delete a grpc_json object.
|
||||
* Deletion is recursive. We will not attempt to free any of the strings |
||||
* in any of the objects of that tree. |
||||
*/ |
||||
grpc_json* grpc_json_create(grpc_json_type type); |
||||
void grpc_json_destroy(grpc_json* json); |
||||
|
||||
#endif /* __GRPC_SRC_CORE_JSON_JSON_H__ */ |
@ -0,0 +1,49 @@ |
||||
/*
|
||||
* |
||||
* Copyright 2014, Google Inc. |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* * Redistributions in binary form must reproduce the above |
||||
* copyright notice, this list of conditions and the following disclaimer |
||||
* in the documentation and/or other materials provided with the |
||||
* distribution. |
||||
* * Neither the name of Google Inc. nor the names of its |
||||
* contributors may be used to endorse or promote products derived from |
||||
* this software without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
* |
||||
*/ |
||||
|
||||
#ifndef __GRPC_SRC_CORE_JSON_JSON_COMMON_H__ |
||||
#define __GRPC_SRC_CORE_JSON_JSON_COMMON_H__ |
||||
|
||||
/* The various json types. */ |
||||
typedef enum { |
||||
GRPC_JSON_OBJECT, |
||||
GRPC_JSON_ARRAY, |
||||
GRPC_JSON_STRING, |
||||
GRPC_JSON_NUMBER, |
||||
GRPC_JSON_TRUE, |
||||
GRPC_JSON_FALSE, |
||||
GRPC_JSON_NULL, |
||||
GRPC_JSON_TOP_LEVEL |
||||
} grpc_json_type; |
||||
|
||||
#endif /* __GRPC_SRC_CORE_JSON_JSON_COMMON_H__ */ |
@ -0,0 +1,653 @@ |
||||
/*
|
||||
* |
||||
* 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 <grpc/support/port_platform.h> |
||||
|
||||
#include "src/core/json/json_reader.h" |
||||
|
||||
static void json_reader_string_clear(grpc_json_reader* reader) { |
||||
reader->vtable->string_clear(reader->userdata); |
||||
} |
||||
|
||||
static void json_reader_string_add_char(grpc_json_reader* reader, |
||||
gpr_uint32 c) { |
||||
reader->vtable->string_add_char(reader->userdata, c); |
||||
} |
||||
|
||||
static void json_reader_string_add_utf32(grpc_json_reader* reader, |
||||
gpr_uint32 utf32) { |
||||
reader->vtable->string_add_utf32(reader->userdata, utf32); |
||||
} |
||||
|
||||
static gpr_uint32 |
||||
grpc_json_reader_read_char(grpc_json_reader* reader) { |
||||
return reader->vtable->read_char(reader->userdata); |
||||
} |
||||
|
||||
static void json_reader_container_begins(grpc_json_reader* reader, |
||||
grpc_json_type type) { |
||||
reader->vtable->container_begins(reader->userdata, type); |
||||
} |
||||
|
||||
static grpc_json_type |
||||
grpc_json_reader_container_ends(grpc_json_reader* reader) { |
||||
return reader->vtable->container_ends(reader->userdata); |
||||
} |
||||
|
||||
static void json_reader_set_key(grpc_json_reader* reader) { |
||||
reader->vtable->set_key(reader->userdata); |
||||
} |
||||
|
||||
static void json_reader_set_string(grpc_json_reader* reader) { |
||||
reader->vtable->set_string(reader->userdata); |
||||
} |
||||
|
||||
static int json_reader_set_number(grpc_json_reader* reader) { |
||||
return reader->vtable->set_number(reader->userdata); |
||||
} |
||||
|
||||
static void json_reader_set_true(grpc_json_reader* reader) { |
||||
reader->vtable->set_true(reader->userdata); |
||||
} |
||||
|
||||
static void json_reader_set_false(grpc_json_reader* reader) { |
||||
reader->vtable->set_false(reader->userdata); |
||||
} |
||||
|
||||
static void json_reader_set_null(grpc_json_reader* reader) { |
||||
reader->vtable->set_null(reader->userdata); |
||||
} |
||||
|
||||
/* Call this function to initialize the reader structure. */ |
||||
void grpc_json_reader_init(grpc_json_reader* reader, |
||||
grpc_json_reader_vtable* vtable, void* userdata) { |
||||
memset(reader, 0, sizeof(grpc_json_reader)); |
||||
reader->vtable = vtable; |
||||
reader->userdata = userdata; |
||||
json_reader_string_clear(reader); |
||||
reader->state = GRPC_JSON_STATE_VALUE_BEGIN; |
||||
} |
||||
|
||||
int grpc_json_reader_is_complete(grpc_json_reader* reader) { |
||||
return ((reader->depth == 0) && ((reader->state == GRPC_JSON_STATE_END) || |
||||
(reader->state == GRPC_JSON_STATE_VALUE_END))); |
||||
} |
||||
|
||||
grpc_json_reader_status grpc_json_reader_run(grpc_json_reader* reader) { |
||||
gpr_uint32 c, success; |
||||
|
||||
/* This state-machine is a strict implementation of ECMA-404 */ |
||||
for (;;) { |
||||
c = grpc_json_reader_read_char(reader); |
||||
switch (c) { |
||||
/* Let's process the error cases first. */ |
||||
case GRPC_JSON_READ_CHAR_ERROR: |
||||
return GRPC_JSON_READ_ERROR; |
||||
|
||||
case GRPC_JSON_READ_CHAR_EAGAIN: |
||||
return GRPC_JSON_EAGAIN; |
||||
|
||||
case GRPC_JSON_READ_CHAR_EOF: |
||||
if (grpc_json_reader_is_complete(reader)) { |
||||
return GRPC_JSON_DONE; |
||||
} else { |
||||
return GRPC_JSON_PARSE_ERROR; |
||||
} |
||||
break; |
||||
|
||||
/* Processing whitespaces. */ |
||||
case ' ': |
||||
case '\t': |
||||
case '\n': |
||||
case '\r': |
||||
switch (reader->state) { |
||||
case GRPC_JSON_STATE_OBJECT_KEY_BEGIN: |
||||
case GRPC_JSON_STATE_OBJECT_KEY_END: |
||||
case GRPC_JSON_STATE_VALUE_BEGIN: |
||||
case GRPC_JSON_STATE_VALUE_END: |
||||
case GRPC_JSON_STATE_END: |
||||
break; |
||||
|
||||
case GRPC_JSON_STATE_OBJECT_KEY_STRING: |
||||
case GRPC_JSON_STATE_VALUE_STRING: |
||||
if (c != ' ') return GRPC_JSON_PARSE_ERROR; |
||||
if (reader->unicode_high_surrogate != 0) return GRPC_JSON_PARSE_ERROR; |
||||
json_reader_string_add_char(reader, c); |
||||
break; |
||||
|
||||
case GRPC_JSON_STATE_VALUE_NUMBER: |
||||
case GRPC_JSON_STATE_VALUE_NUMBER_WITH_DECIMAL: |
||||
case GRPC_JSON_STATE_VALUE_NUMBER_ZERO: |
||||
case GRPC_JSON_STATE_VALUE_NUMBER_EPM: |
||||
success = json_reader_set_number(reader); |
||||
if (!success) return GRPC_JSON_PARSE_ERROR; |
||||
json_reader_string_clear(reader); |
||||
reader->state = GRPC_JSON_STATE_VALUE_END; |
||||
break; |
||||
|
||||
default: |
||||
return GRPC_JSON_PARSE_ERROR; |
||||
} |
||||
break; |
||||
|
||||
/* Value, object or array terminations. */ |
||||
case ',': |
||||
case '}': |
||||
case ']': |
||||
switch (reader->state) { |
||||
case GRPC_JSON_STATE_OBJECT_KEY_STRING: |
||||
case GRPC_JSON_STATE_VALUE_STRING: |
||||
if (reader->unicode_high_surrogate != 0) return GRPC_JSON_PARSE_ERROR; |
||||
json_reader_string_add_char(reader, c); |
||||
break; |
||||
|
||||
case GRPC_JSON_STATE_VALUE_NUMBER: |
||||
case GRPC_JSON_STATE_VALUE_NUMBER_WITH_DECIMAL: |
||||
case GRPC_JSON_STATE_VALUE_NUMBER_ZERO: |
||||
case GRPC_JSON_STATE_VALUE_NUMBER_EPM: |
||||
success = json_reader_set_number(reader); |
||||
if (!success) return GRPC_JSON_PARSE_ERROR; |
||||
json_reader_string_clear(reader); |
||||
reader->state = GRPC_JSON_STATE_VALUE_END; |
||||
/* The missing break here is intentional. */ |
||||
|
||||
case GRPC_JSON_STATE_VALUE_END: |
||||
case GRPC_JSON_STATE_OBJECT_KEY_BEGIN: |
||||
case GRPC_JSON_STATE_VALUE_BEGIN: |
||||
if (c == ',') { |
||||
if (reader->state != GRPC_JSON_STATE_VALUE_END) { |
||||
return GRPC_JSON_PARSE_ERROR; |
||||
} |
||||
if (reader->in_object) { |
||||
reader->state = GRPC_JSON_STATE_OBJECT_KEY_BEGIN; |
||||
} else { |
||||
reader->state = GRPC_JSON_STATE_VALUE_BEGIN; |
||||
} |
||||
} else { |
||||
if (reader->depth-- == 0) return GRPC_JSON_PARSE_ERROR; |
||||
if ((c == '}') && !reader->in_object) { |
||||
return GRPC_JSON_PARSE_ERROR; |
||||
} |
||||
if ((c == '}') && |
||||
(reader->state == GRPC_JSON_STATE_OBJECT_KEY_BEGIN) && |
||||
!reader->container_just_begun) { |
||||
return GRPC_JSON_PARSE_ERROR; |
||||
} |
||||
if ((c == ']') && !reader->in_array) return GRPC_JSON_PARSE_ERROR; |
||||
if ((c == ']') && |
||||
(reader->state == GRPC_JSON_STATE_VALUE_BEGIN) && |
||||
!reader->container_just_begun) { |
||||
return GRPC_JSON_PARSE_ERROR; |
||||
} |
||||
reader->state = GRPC_JSON_STATE_VALUE_END; |
||||
switch (grpc_json_reader_container_ends(reader)) { |
||||
case GRPC_JSON_OBJECT: |
||||
reader->in_object = 1; |
||||
reader->in_array = 0; |
||||
break; |
||||
case GRPC_JSON_ARRAY: |
||||
reader->in_object = 0; |
||||
reader->in_array = 1; |
||||
break; |
||||
case GRPC_JSON_TOP_LEVEL: |
||||
if (reader->depth != 0) return GRPC_JSON_INTERNAL_ERROR; |
||||
reader->in_object = 0; |
||||
reader->in_array = 0; |
||||
reader->state = GRPC_JSON_STATE_END; |
||||
break; |
||||
default: |
||||
return GRPC_JSON_INTERNAL_ERROR; |
||||
} |
||||
} |
||||
break; |
||||
|
||||
default: |
||||
return GRPC_JSON_PARSE_ERROR; |
||||
} |
||||
break; |
||||
|
||||
/* In-string escaping. */ |
||||
case '\\': |
||||
switch (reader->state) { |
||||
case GRPC_JSON_STATE_OBJECT_KEY_STRING: |
||||
reader->escaped_string_was_key = 1; |
||||
reader->state = GRPC_JSON_STATE_STRING_ESCAPE; |
||||
break; |
||||
|
||||
case GRPC_JSON_STATE_VALUE_STRING: |
||||
reader->escaped_string_was_key = 0; |
||||
reader->state = GRPC_JSON_STATE_STRING_ESCAPE; |
||||
break; |
||||
|
||||
/* This is the \\ case. */ |
||||
case GRPC_JSON_STATE_STRING_ESCAPE: |
||||
if (reader->unicode_high_surrogate != 0) return GRPC_JSON_PARSE_ERROR; |
||||
json_reader_string_add_char(reader, '\\'); |
||||
if (reader->escaped_string_was_key) { |
||||
reader->state = GRPC_JSON_STATE_OBJECT_KEY_STRING; |
||||
} else { |
||||
reader->state = GRPC_JSON_STATE_VALUE_STRING; |
||||
} |
||||
break; |
||||
|
||||
default: |
||||
return GRPC_JSON_PARSE_ERROR; |
||||
} |
||||
break; |
||||
|
||||
default: |
||||
reader->container_just_begun = 0; |
||||
switch (reader->state) { |
||||
case GRPC_JSON_STATE_OBJECT_KEY_BEGIN: |
||||
if (c != '"') return GRPC_JSON_PARSE_ERROR; |
||||
reader->state = GRPC_JSON_STATE_OBJECT_KEY_STRING; |
||||
break; |
||||
|
||||
case GRPC_JSON_STATE_OBJECT_KEY_STRING: |
||||
if (reader->unicode_high_surrogate != 0) return GRPC_JSON_PARSE_ERROR; |
||||
if (c == '"') { |
||||
reader->state = GRPC_JSON_STATE_OBJECT_KEY_END; |
||||
json_reader_set_key(reader); |
||||
json_reader_string_clear(reader); |
||||
} else { |
||||
if (c <= 0x001f) return GRPC_JSON_PARSE_ERROR; |
||||
json_reader_string_add_char(reader, c); |
||||
} |
||||
break; |
||||
|
||||
case GRPC_JSON_STATE_VALUE_STRING: |
||||
if (reader->unicode_high_surrogate != 0) return GRPC_JSON_PARSE_ERROR; |
||||
if (c == '"') { |
||||
reader->state = GRPC_JSON_STATE_VALUE_END; |
||||
json_reader_set_string(reader); |
||||
json_reader_string_clear(reader); |
||||
} else { |
||||
if (c < 32) return GRPC_JSON_PARSE_ERROR; |
||||
json_reader_string_add_char(reader, c); |
||||
} |
||||
break; |
||||
|
||||
case GRPC_JSON_STATE_OBJECT_KEY_END: |
||||
if (c != ':') return GRPC_JSON_PARSE_ERROR; |
||||
reader->state = GRPC_JSON_STATE_VALUE_BEGIN; |
||||
break; |
||||
|
||||
case GRPC_JSON_STATE_VALUE_BEGIN: |
||||
switch (c) { |
||||
case 't': |
||||
reader->state = GRPC_JSON_STATE_VALUE_TRUE_R; |
||||
break; |
||||
|
||||
case 'f': |
||||
reader->state = GRPC_JSON_STATE_VALUE_FALSE_A; |
||||
break; |
||||
|
||||
case 'n': |
||||
reader->state = GRPC_JSON_STATE_VALUE_NULL_U; |
||||
break; |
||||
|
||||
case '"': |
||||
reader->state = GRPC_JSON_STATE_VALUE_STRING; |
||||
break; |
||||
|
||||
case '0': |
||||
json_reader_string_add_char(reader, c); |
||||
reader->state = GRPC_JSON_STATE_VALUE_NUMBER_ZERO; |
||||
break; |
||||
|
||||
case '1': |
||||
case '2': |
||||
case '3': |
||||
case '4': |
||||
case '5': |
||||
case '6': |
||||
case '7': |
||||
case '8': |
||||
case '9': |
||||
case '-': |
||||
json_reader_string_add_char(reader, c); |
||||
reader->state = GRPC_JSON_STATE_VALUE_NUMBER; |
||||
break; |
||||
|
||||
case '{': |
||||
reader->container_just_begun = 1; |
||||
json_reader_container_begins(reader, GRPC_JSON_OBJECT); |
||||
reader->depth++; |
||||
reader->state = GRPC_JSON_STATE_OBJECT_KEY_BEGIN; |
||||
reader->in_object = 1; |
||||
reader->in_array = 0; |
||||
break; |
||||
|
||||
case '[': |
||||
reader->container_just_begun = 1; |
||||
json_reader_container_begins(reader, GRPC_JSON_ARRAY); |
||||
reader->depth++; |
||||
reader->in_object = 0; |
||||
reader->in_array = 1; |
||||
break; |
||||
} |
||||
break; |
||||
|
||||
case GRPC_JSON_STATE_STRING_ESCAPE: |
||||
if (reader->escaped_string_was_key) { |
||||
reader->state = GRPC_JSON_STATE_OBJECT_KEY_STRING; |
||||
} else { |
||||
reader->state = GRPC_JSON_STATE_VALUE_STRING; |
||||
} |
||||
if (reader->unicode_high_surrogate && c != 'u') |
||||
return GRPC_JSON_PARSE_ERROR; |
||||
switch (c) { |
||||
case '"': |
||||
case '/': |
||||
json_reader_string_add_char(reader, c); |
||||
break; |
||||
case 'b': |
||||
json_reader_string_add_char(reader, '\b'); |
||||
break; |
||||
case 'f': |
||||
json_reader_string_add_char(reader, '\f'); |
||||
break; |
||||
case 'n': |
||||
json_reader_string_add_char(reader, '\n'); |
||||
break; |
||||
case 'r': |
||||
json_reader_string_add_char(reader, '\r'); |
||||
break; |
||||
case 't': |
||||
json_reader_string_add_char(reader, '\t'); |
||||
break; |
||||
case 'u': |
||||
reader->state = GRPC_JSON_STATE_STRING_ESCAPE_U1; |
||||
reader->unicode_char = 0; |
||||
break; |
||||
default: |
||||
return GRPC_JSON_PARSE_ERROR; |
||||
} |
||||
break; |
||||
|
||||
case GRPC_JSON_STATE_STRING_ESCAPE_U1: |
||||
case GRPC_JSON_STATE_STRING_ESCAPE_U2: |
||||
case GRPC_JSON_STATE_STRING_ESCAPE_U3: |
||||
case GRPC_JSON_STATE_STRING_ESCAPE_U4: |
||||
if ((c >= '0') && (c <= '9')) { |
||||
c -= '0'; |
||||
} else if ((c >= 'A') && (c <= 'F')) { |
||||
c -= 'A' - 10; |
||||
} else if ((c >= 'a') && (c <= 'f')) { |
||||
c -= 'a' - 10; |
||||
} else { |
||||
return GRPC_JSON_PARSE_ERROR; |
||||
} |
||||
reader->unicode_char <<= 4; |
||||
reader->unicode_char |= c; |
||||
|
||||
switch (reader->state) { |
||||
case GRPC_JSON_STATE_STRING_ESCAPE_U1: |
||||
reader->state = GRPC_JSON_STATE_STRING_ESCAPE_U2; |
||||
break; |
||||
case GRPC_JSON_STATE_STRING_ESCAPE_U2: |
||||
reader->state = GRPC_JSON_STATE_STRING_ESCAPE_U3; |
||||
break; |
||||
case GRPC_JSON_STATE_STRING_ESCAPE_U3: |
||||
reader->state = GRPC_JSON_STATE_STRING_ESCAPE_U4; |
||||
break; |
||||
case GRPC_JSON_STATE_STRING_ESCAPE_U4: |
||||
/* See grpc_json_writer_escape_string to have a description
|
||||
* of what's going on here. |
||||
*/ |
||||
if ((reader->unicode_char & 0xfc00) == 0xd800) { |
||||
/* high surrogate utf-16 */ |
||||
if (reader->unicode_high_surrogate != 0) |
||||
return GRPC_JSON_PARSE_ERROR; |
||||
reader->unicode_high_surrogate = reader->unicode_char; |
||||
} else if ((reader->unicode_char & 0xfc00) == 0xdc00) { |
||||
/* low surrogate utf-16 */ |
||||
gpr_uint32 utf32; |
||||
if (reader->unicode_high_surrogate == 0) |
||||
return GRPC_JSON_PARSE_ERROR; |
||||
utf32 = 0x10000; |
||||
utf32 += (reader->unicode_high_surrogate - 0xd800) * 0x400; |
||||
utf32 += reader->unicode_char - 0xdc00; |
||||
json_reader_string_add_utf32(reader, utf32); |
||||
reader->unicode_high_surrogate = 0; |
||||
} else { |
||||
/* anything else */ |
||||
if (reader->unicode_high_surrogate != 0) |
||||
return GRPC_JSON_PARSE_ERROR; |
||||
json_reader_string_add_utf32(reader, reader->unicode_char); |
||||
} |
||||
if (reader->escaped_string_was_key) { |
||||
reader->state = GRPC_JSON_STATE_OBJECT_KEY_STRING; |
||||
} else { |
||||
reader->state = GRPC_JSON_STATE_VALUE_STRING; |
||||
} |
||||
break; |
||||
default: |
||||
return GRPC_JSON_INTERNAL_ERROR; |
||||
} |
||||
break; |
||||
|
||||
case GRPC_JSON_STATE_VALUE_NUMBER: |
||||
json_reader_string_add_char(reader, c); |
||||
switch (c) { |
||||
case '0': |
||||
case '1': |
||||
case '2': |
||||
case '3': |
||||
case '4': |
||||
case '5': |
||||
case '6': |
||||
case '7': |
||||
case '8': |
||||
case '9': |
||||
break; |
||||
case 'e': |
||||
case 'E': |
||||
reader->state = GRPC_JSON_STATE_VALUE_NUMBER_E; |
||||
break; |
||||
case '.': |
||||
reader->state = GRPC_JSON_STATE_VALUE_NUMBER_DOT; |
||||
break; |
||||
default: |
||||
return GRPC_JSON_PARSE_ERROR; |
||||
} |
||||
break; |
||||
|
||||
case GRPC_JSON_STATE_VALUE_NUMBER_WITH_DECIMAL: |
||||
json_reader_string_add_char(reader, c); |
||||
switch (c) { |
||||
case '0': |
||||
case '1': |
||||
case '2': |
||||
case '3': |
||||
case '4': |
||||
case '5': |
||||
case '6': |
||||
case '7': |
||||
case '8': |
||||
case '9': |
||||
break; |
||||
case 'e': |
||||
case 'E': |
||||
reader->state = GRPC_JSON_STATE_VALUE_NUMBER_E; |
||||
break; |
||||
default: |
||||
return GRPC_JSON_PARSE_ERROR; |
||||
} |
||||
break; |
||||
|
||||
case GRPC_JSON_STATE_VALUE_NUMBER_ZERO: |
||||
if (c != '.') return GRPC_JSON_PARSE_ERROR; |
||||
json_reader_string_add_char(reader, c); |
||||
reader->state = GRPC_JSON_STATE_VALUE_NUMBER_DOT; |
||||
break; |
||||
|
||||
case GRPC_JSON_STATE_VALUE_NUMBER_DOT: |
||||
json_reader_string_add_char(reader, c); |
||||
switch (c) { |
||||
case '0': |
||||
case '1': |
||||
case '2': |
||||
case '3': |
||||
case '4': |
||||
case '5': |
||||
case '6': |
||||
case '7': |
||||
case '8': |
||||
case '9': |
||||
reader->state = GRPC_JSON_STATE_VALUE_NUMBER_WITH_DECIMAL; |
||||
break; |
||||
default: |
||||
return GRPC_JSON_PARSE_ERROR; |
||||
} |
||||
break; |
||||
|
||||
case GRPC_JSON_STATE_VALUE_NUMBER_E: |
||||
json_reader_string_add_char(reader, c); |
||||
switch (c) { |
||||
case '0': |
||||
case '1': |
||||
case '2': |
||||
case '3': |
||||
case '4': |
||||
case '5': |
||||
case '6': |
||||
case '7': |
||||
case '8': |
||||
case '9': |
||||
case '+': |
||||
case '-': |
||||
reader->state = GRPC_JSON_STATE_VALUE_NUMBER_EPM; |
||||
break; |
||||
default: |
||||
return GRPC_JSON_PARSE_ERROR; |
||||
} |
||||
break; |
||||
|
||||
case GRPC_JSON_STATE_VALUE_NUMBER_EPM: |
||||
json_reader_string_add_char(reader, c); |
||||
switch (c) { |
||||
case '0': |
||||
case '1': |
||||
case '2': |
||||
case '3': |
||||
case '4': |
||||
case '5': |
||||
case '6': |
||||
case '7': |
||||
case '8': |
||||
case '9': |
||||
break; |
||||
default: |
||||
return GRPC_JSON_PARSE_ERROR; |
||||
} |
||||
break; |
||||
|
||||
case GRPC_JSON_STATE_VALUE_TRUE_R: |
||||
if (c != 'r') return GRPC_JSON_PARSE_ERROR; |
||||
reader->state = GRPC_JSON_STATE_VALUE_TRUE_U; |
||||
break; |
||||
|
||||
case GRPC_JSON_STATE_VALUE_TRUE_U: |
||||
if (c != 'u') return GRPC_JSON_PARSE_ERROR; |
||||
reader->state = GRPC_JSON_STATE_VALUE_TRUE_E; |
||||
break; |
||||
|
||||
case GRPC_JSON_STATE_VALUE_TRUE_E: |
||||
if (c != 'e') return GRPC_JSON_PARSE_ERROR; |
||||
json_reader_set_true(reader); |
||||
reader->state = GRPC_JSON_STATE_VALUE_END; |
||||
break; |
||||
|
||||
case GRPC_JSON_STATE_VALUE_FALSE_A: |
||||
if (c != 'a') return GRPC_JSON_PARSE_ERROR; |
||||
reader->state = GRPC_JSON_STATE_VALUE_FALSE_L; |
||||
break; |
||||
|
||||
case GRPC_JSON_STATE_VALUE_FALSE_L: |
||||
if (c != 'l') return GRPC_JSON_PARSE_ERROR; |
||||
reader->state = GRPC_JSON_STATE_VALUE_FALSE_S; |
||||
break; |
||||
|
||||
case GRPC_JSON_STATE_VALUE_FALSE_S: |
||||
if (c != 's') return GRPC_JSON_PARSE_ERROR; |
||||
reader->state = GRPC_JSON_STATE_VALUE_FALSE_E; |
||||
break; |
||||
|
||||
case GRPC_JSON_STATE_VALUE_FALSE_E: |
||||
if (c != 'e') return GRPC_JSON_PARSE_ERROR; |
||||
json_reader_set_false(reader); |
||||
reader->state = GRPC_JSON_STATE_VALUE_END; |
||||
break; |
||||
|
||||
case GRPC_JSON_STATE_VALUE_NULL_U: |
||||
if (c != 'u') return GRPC_JSON_PARSE_ERROR; |
||||
reader->state = GRPC_JSON_STATE_VALUE_NULL_L1; |
||||
break; |
||||
|
||||
case GRPC_JSON_STATE_VALUE_NULL_L1: |
||||
if (c != 'l') return GRPC_JSON_PARSE_ERROR; |
||||
reader->state = GRPC_JSON_STATE_VALUE_NULL_L2; |
||||
break; |
||||
|
||||
case GRPC_JSON_STATE_VALUE_NULL_L2: |
||||
if (c != 'l') return GRPC_JSON_PARSE_ERROR; |
||||
json_reader_set_null(reader); |
||||
reader->state = GRPC_JSON_STATE_VALUE_END; |
||||
break; |
||||
|
||||
/* All of the VALUE_END cases are handled in the specialized case
|
||||
* above. */ |
||||
case GRPC_JSON_STATE_VALUE_END: |
||||
switch (c) { |
||||
case ',': |
||||
case '}': |
||||
case ']': |
||||
return GRPC_JSON_INTERNAL_ERROR; |
||||
break; |
||||
|
||||
default: |
||||
return GRPC_JSON_PARSE_ERROR; |
||||
} |
||||
break; |
||||
|
||||
case GRPC_JSON_STATE_END: |
||||
return GRPC_JSON_PARSE_ERROR; |
||||
} |
||||
} |
||||
} |
||||
|
||||
return GRPC_JSON_INTERNAL_ERROR; |
||||
} |
@ -0,0 +1,160 @@ |
||||
/*
|
||||
* |
||||
* Copyright 2014, Google Inc. |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* * Redistributions in binary form must reproduce the above |
||||
* copyright notice, this list of conditions and the following disclaimer |
||||
* in the documentation and/or other materials provided with the |
||||
* distribution. |
||||
* * Neither the name of Google Inc. nor the names of its |
||||
* contributors may be used to endorse or promote products derived from |
||||
* this software without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
* |
||||
*/ |
||||
|
||||
#ifndef __GRPC_SRC_CORE_JSON_JSON_READER_H__ |
||||
#define __GRPC_SRC_CORE_JSON_JSON_READER_H__ |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
#include "src/core/json/json_common.h" |
||||
|
||||
typedef enum { |
||||
GRPC_JSON_STATE_OBJECT_KEY_BEGIN, |
||||
GRPC_JSON_STATE_OBJECT_KEY_STRING, |
||||
GRPC_JSON_STATE_OBJECT_KEY_END, |
||||
GRPC_JSON_STATE_VALUE_BEGIN, |
||||
GRPC_JSON_STATE_VALUE_STRING, |
||||
GRPC_JSON_STATE_STRING_ESCAPE, |
||||
GRPC_JSON_STATE_STRING_ESCAPE_U1, |
||||
GRPC_JSON_STATE_STRING_ESCAPE_U2, |
||||
GRPC_JSON_STATE_STRING_ESCAPE_U3, |
||||
GRPC_JSON_STATE_STRING_ESCAPE_U4, |
||||
GRPC_JSON_STATE_VALUE_NUMBER, |
||||
GRPC_JSON_STATE_VALUE_NUMBER_WITH_DECIMAL, |
||||
GRPC_JSON_STATE_VALUE_NUMBER_ZERO, |
||||
GRPC_JSON_STATE_VALUE_NUMBER_DOT, |
||||
GRPC_JSON_STATE_VALUE_NUMBER_E, |
||||
GRPC_JSON_STATE_VALUE_NUMBER_EPM, |
||||
GRPC_JSON_STATE_VALUE_TRUE_R, |
||||
GRPC_JSON_STATE_VALUE_TRUE_U, |
||||
GRPC_JSON_STATE_VALUE_TRUE_E, |
||||
GRPC_JSON_STATE_VALUE_FALSE_A, |
||||
GRPC_JSON_STATE_VALUE_FALSE_L, |
||||
GRPC_JSON_STATE_VALUE_FALSE_S, |
||||
GRPC_JSON_STATE_VALUE_FALSE_E, |
||||
GRPC_JSON_STATE_VALUE_NULL_U, |
||||
GRPC_JSON_STATE_VALUE_NULL_L1, |
||||
GRPC_JSON_STATE_VALUE_NULL_L2, |
||||
GRPC_JSON_STATE_VALUE_END, |
||||
GRPC_JSON_STATE_END |
||||
} grpc_json_reader_state; |
||||
|
||||
enum { |
||||
/* The first non-unicode value is 0x110000. But let's pick
|
||||
* a value high enough to start our error codes from. These |
||||
* values are safe to return from the read_char function. |
||||
*/ |
||||
GRPC_JSON_READ_CHAR_EOF = 0x7ffffff0, |
||||
GRPC_JSON_READ_CHAR_EAGAIN, |
||||
GRPC_JSON_READ_CHAR_ERROR |
||||
}; |
||||
|
||||
struct grpc_json_reader; |
||||
|
||||
typedef struct grpc_json_reader_vtable { |
||||
/* Clears your internal string scratchpad. */ |
||||
void (*string_clear)(void* userdata); |
||||
/* Adds a char to the string scratchpad. */ |
||||
void (*string_add_char)(void* userdata, gpr_uint32 c); |
||||
/* Adds a utf32 char to the string scratchpad. */ |
||||
void (*string_add_utf32)(void* userdata, gpr_uint32 c); |
||||
/* Reads a character from your input. May be utf-8, 16 or 32. */ |
||||
gpr_uint32 (*read_char)(void* userdata); |
||||
/* Starts a container of type GRPC_JSON_ARRAY or GRPC_JSON_OBJECT. */ |
||||
void (*container_begins)(void* userdata, grpc_json_type type); |
||||
/* Ends the current container. Must return the type of its parent. */ |
||||
grpc_json_type (*container_ends)(void* userdata); |
||||
/* Your internal string scratchpad is an object's key. */ |
||||
void (*set_key)(void* userdata); |
||||
/* Your internal string scratchpad is a string value. */ |
||||
void (*set_string)(void* userdata); |
||||
/* Your internal string scratchpad is a numerical value. Return 1 if valid. */ |
||||
int (*set_number)(void* userdata); |
||||
/* Sets the values true, false or null. */ |
||||
void (*set_true)(void* userdata); |
||||
void (*set_false)(void* userdata); |
||||
void (*set_null)(void* userdata); |
||||
} grpc_json_reader_vtable; |
||||
|
||||
typedef struct grpc_json_reader { |
||||
/* That structure is fully private, and initialized by grpc_json_reader_init.
|
||||
* The definition is public so you can put it on your stack. |
||||
*/ |
||||
|
||||
void* userdata; |
||||
grpc_json_reader_vtable* vtable; |
||||
int depth; |
||||
int in_object; |
||||
int in_array; |
||||
int escaped_string_was_key; |
||||
int container_just_begun; |
||||
gpr_uint16 unicode_char, unicode_high_surrogate; |
||||
grpc_json_reader_state state; |
||||
} grpc_json_reader; |
||||
|
||||
/* The return type of the parser. */ |
||||
typedef enum { |
||||
GRPC_JSON_DONE, /* The parser finished successfully. */ |
||||
GRPC_JSON_EAGAIN, /* The parser yields to get more data. */ |
||||
GRPC_JSON_READ_ERROR, /* The parser passes through a read error. */ |
||||
GRPC_JSON_PARSE_ERROR, /* The parser found an error in the json stream. */ |
||||
GRPC_JSON_INTERNAL_ERROR /* The parser got an internal error. */ |
||||
} grpc_json_reader_status; |
||||
|
||||
/* Call this function to start parsing the input. It will return the following:
|
||||
* . GRPC_JSON_DONE if the input got eof, and the parsing finished |
||||
* successfully. |
||||
* . GRPC_JSON_EAGAIN if the read_char function returned again. Call the |
||||
* parser again as needed. It is okay to call the parser in polling mode, |
||||
* although a bit dull. |
||||
* . GRPC_JSON_READ_ERROR if the read_char function returned an error. The |
||||
* state isn't broken however, and the function can be called again if the |
||||
* error has been corrected. But please use the EAGAIN feature instead for |
||||
* consistency. |
||||
* . GRPC_JSON_PARSE_ERROR if the input was somehow invalid. |
||||
* . GRPC_JSON_INTERNAL_ERROR if the parser somehow ended into an invalid |
||||
* internal state. |
||||
*/ |
||||
grpc_json_reader_status grpc_json_reader_run(grpc_json_reader* reader); |
||||
|
||||
/* Call this function to initialize the reader structure. */ |
||||
void grpc_json_reader_init(grpc_json_reader* reader, |
||||
grpc_json_reader_vtable* vtable, void* userdata); |
||||
|
||||
/* You may call this from the read_char callback if you don't know where is the
|
||||
* end of your input stream, and you'd like the json reader to hint you that it |
||||
* has completed reading its input, so you can return an EOF to it. Note that |
||||
* there might still be trailing whitespaces after that point. |
||||
*/ |
||||
int grpc_json_reader_is_complete(grpc_json_reader* reader); |
||||
|
||||
#endif /* __GRPC_SRC_CORE_JSON_JSON_READER_H__ */ |
@ -0,0 +1,391 @@ |
||||
/*
|
||||
* |
||||
* 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 <stdlib.h> |
||||
|
||||
#include <grpc/support/alloc.h> |
||||
#include <grpc/support/log.h> |
||||
|
||||
#include "src/core/json/json.h" |
||||
#include "src/core/json/json_reader.h" |
||||
#include "src/core/json/json_writer.h" |
||||
|
||||
/* The json reader will construct a bunch of grpc_json objects and
|
||||
* link them all up together in a tree-like structure that will represent |
||||
* the json data in memory. |
||||
* |
||||
* It also uses its own input as a scratchpad to store all of the decoded, |
||||
* unescaped strings. So we need to keep track of all these pointers in |
||||
* that opaque structure the reader will carry for us. |
||||
* |
||||
* Note that this works because the act of parsing json always reduces its |
||||
* input size, and never expands it. |
||||
*/ |
||||
typedef struct { |
||||
grpc_json* top; |
||||
grpc_json* current_container; |
||||
grpc_json* current_value; |
||||
gpr_uint8* input; |
||||
gpr_uint8* key; |
||||
gpr_uint8* string; |
||||
gpr_uint8* string_ptr; |
||||
size_t remaining_input; |
||||
} json_reader_userdata; |
||||
|
||||
/* This json writer will put everything in a big string.
|
||||
* The point is that we allocate that string in chunks of 256 bytes. |
||||
*/ |
||||
typedef struct { |
||||
char* output; |
||||
size_t free_space; |
||||
size_t string_len; |
||||
size_t allocated; |
||||
} json_writer_userdata; |
||||
|
||||
|
||||
/* This function checks if there's enough space left in the output buffer,
|
||||
* and will enlarge it if necessary. We're only allocating chunks of 256 |
||||
* bytes at a time (or multiples thereof). |
||||
*/ |
||||
static void json_writer_output_check(void* userdata, size_t needed) { |
||||
json_writer_userdata* state = userdata; |
||||
if (state->free_space >= needed) return; |
||||
needed -= state->free_space; |
||||
/* Round up by 256 bytes. */ |
||||
needed = (needed + 0xff) & ~0xff; |
||||
state->output = gpr_realloc(state->output, state->allocated + needed); |
||||
state->free_space += needed; |
||||
state->allocated += needed; |
||||
} |
||||
|
||||
/* These are needed by the writer's implementation. */ |
||||
static void json_writer_output_char(void* userdata, char c) { |
||||
json_writer_userdata* state = userdata; |
||||
json_writer_output_check(userdata, 1); |
||||
state->output[state->string_len++] = c; |
||||
state->free_space--; |
||||
} |
||||
|
||||
static void json_writer_output_string_with_len(void* userdata, |
||||
const char* str, size_t len) { |
||||
json_writer_userdata* state = userdata; |
||||
json_writer_output_check(userdata, len); |
||||
memcpy(state->output + state->string_len, str, len); |
||||
state->string_len += len; |
||||
state->free_space -= len; |
||||
} |
||||
|
||||
static void json_writer_output_string(void* userdata, |
||||
const char* str) { |
||||
size_t len = strlen(str); |
||||
json_writer_output_string_with_len(userdata, str, len); |
||||
} |
||||
|
||||
/* The reader asks us to clear our scratchpad. In our case, we'll simply mark
|
||||
* the end of the current string, and advance our output pointer. |
||||
*/ |
||||
static void json_reader_string_clear(void* userdata) { |
||||
json_reader_userdata* state = userdata; |
||||
if (state->string) { |
||||
GPR_ASSERT(state->string_ptr < state->input); |
||||
*state->string_ptr++ = 0; |
||||
} |
||||
state->string = state->string_ptr; |
||||
} |
||||
|
||||
static void json_reader_string_add_char(void* userdata, gpr_uint32 c) { |
||||
json_reader_userdata* state = userdata; |
||||
GPR_ASSERT(state->string_ptr < state->input); |
||||
GPR_ASSERT(c <= 0xff); |
||||
*state->string_ptr++ = (char)c; |
||||
} |
||||
|
||||
/* We are converting a UTF-32 character into UTF-8 here,
|
||||
* as described by RFC3629. |
||||
*/ |
||||
static void json_reader_string_add_utf32(void* userdata, gpr_uint32 c) { |
||||
if (c <= 0x7f) { |
||||
json_reader_string_add_char(userdata, c); |
||||
} else if (c <= 0x7ff) { |
||||
int b1 = 0xc0 | ((c >> 6) & 0x1f); |
||||
int b2 = 0x80 | (c & 0x3f); |
||||
json_reader_string_add_char(userdata, b1); |
||||
json_reader_string_add_char(userdata, b2); |
||||
} else if (c <= 0xffff) { |
||||
int b1 = 0xe0 | ((c >> 12) & 0x0f); |
||||
int b2 = 0x80 | ((c >> 6) & 0x3f); |
||||
int b3 = 0x80 | (c & 0x3f); |
||||
json_reader_string_add_char(userdata, b1); |
||||
json_reader_string_add_char(userdata, b2); |
||||
json_reader_string_add_char(userdata, b3); |
||||
} else if (c <= 0x1fffff) { |
||||
int b1 = 0xf0 | ((c >> 18) & 0x07); |
||||
int b2 = 0x80 | ((c >> 12) & 0x3f); |
||||
int b3 = 0x80 | ((c >> 6) & 0x3f); |
||||
int b4 = 0x80 | (c & 0x3f); |
||||
json_reader_string_add_char(userdata, b1); |
||||
json_reader_string_add_char(userdata, b2); |
||||
json_reader_string_add_char(userdata, b3); |
||||
json_reader_string_add_char(userdata, b4); |
||||
} |
||||
} |
||||
|
||||
/* We consider that the input may be a zero-terminated string. So we
|
||||
* can end up hitting eof before the end of the alleged string length. |
||||
*/ |
||||
static gpr_uint32 json_reader_read_char(void* userdata) { |
||||
gpr_uint32 r; |
||||
json_reader_userdata* state = userdata; |
||||
|
||||
if (state->remaining_input == 0) return GRPC_JSON_READ_CHAR_EOF; |
||||
|
||||
r = *state->input++; |
||||
state->remaining_input--; |
||||
|
||||
if (r == 0) { |
||||
state->remaining_input = 0; |
||||
return GRPC_JSON_READ_CHAR_EOF; |
||||
} |
||||
|
||||
return r; |
||||
} |
||||
|
||||
/* Helper function to create a new grpc_json object and link it into
|
||||
* our tree-in-progress inside our opaque structure. |
||||
*/ |
||||
static grpc_json* json_create_and_link(void* userdata, |
||||
grpc_json_type type) { |
||||
json_reader_userdata* state = userdata; |
||||
grpc_json* json = grpc_json_create(type); |
||||
|
||||
json->parent = state->current_container; |
||||
json->prev = state->current_value; |
||||
state->current_value = json; |
||||
|
||||
if (json->prev) { |
||||
json->prev->next = json; |
||||
} |
||||
if (json->parent) { |
||||
if (!json->parent->child) { |
||||
json->parent->child = json; |
||||
} |
||||
if (json->parent->type == GRPC_JSON_OBJECT) { |
||||
json->key = (char*) state->key; |
||||
} |
||||
} |
||||
if (!state->top) { |
||||
state->top = json; |
||||
} |
||||
|
||||
return json; |
||||
} |
||||
|
||||
static void json_reader_container_begins(void* userdata, grpc_json_type type) { |
||||
json_reader_userdata* state = userdata; |
||||
grpc_json* container; |
||||
|
||||
GPR_ASSERT(type == GRPC_JSON_ARRAY || type == GRPC_JSON_OBJECT); |
||||
|
||||
container = json_create_and_link(userdata, type); |
||||
state->current_container = container; |
||||
state->current_value = NULL; |
||||
} |
||||
|
||||
/* It's important to remember that the reader is mostly stateless, so it
|
||||
* isn't trying to remember what the container was prior the one that just |
||||
* ends. Since we're keeping track of these for our own purpose, we are |
||||
* able to return that information back, which is useful for it to validate |
||||
* the input json stream. |
||||
* |
||||
* Also note that if we're at the top of the tree, and the last container |
||||
* ends, we have to return GRPC_JSON_TOP_LEVEL. |
||||
*/ |
||||
static grpc_json_type json_reader_container_ends(void* userdata) { |
||||
grpc_json_type container_type = GRPC_JSON_TOP_LEVEL; |
||||
json_reader_userdata* state = userdata; |
||||
|
||||
GPR_ASSERT(state->current_container); |
||||
|
||||
state->current_value = state->current_container; |
||||
state->current_container = state->current_container->parent; |
||||
|
||||
if (state->current_container) { |
||||
container_type = state->current_container->type; |
||||
} |
||||
|
||||
return container_type; |
||||
} |
||||
|
||||
/* The next 3 functions basically are the reader asking us to use our string
|
||||
* scratchpad for one of these 3 purposes. |
||||
* |
||||
* Note that in the set_number case, we're not going to try interpreting it. |
||||
* We'll keep it as a string, and leave it to the caller to evaluate it. |
||||
*/ |
||||
static void json_reader_set_key(void* userdata) { |
||||
json_reader_userdata* state = userdata; |
||||
state->key = state->string; |
||||
} |
||||
|
||||
static void json_reader_set_string(void* userdata) { |
||||
json_reader_userdata* state = userdata; |
||||
grpc_json* json = json_create_and_link(userdata, GRPC_JSON_STRING); |
||||
json->value = (char*) state->string; |
||||
} |
||||
|
||||
static int json_reader_set_number(void* userdata) { |
||||
json_reader_userdata* state = userdata; |
||||
grpc_json* json = json_create_and_link(userdata, GRPC_JSON_NUMBER); |
||||
json->value = (char*) state->string; |
||||
return 1; |
||||
} |
||||
|
||||
/* The object types true, false and null are self-sufficient, and don't need
|
||||
* any more information beside their type. |
||||
*/ |
||||
static void json_reader_set_true(void* userdata) { |
||||
json_create_and_link(userdata, GRPC_JSON_TRUE); |
||||
} |
||||
|
||||
static void json_reader_set_false(void* userdata) { |
||||
json_create_and_link(userdata, GRPC_JSON_FALSE); |
||||
} |
||||
|
||||
static void json_reader_set_null(void* userdata) { |
||||
json_create_and_link(userdata, GRPC_JSON_NULL); |
||||
} |
||||
|
||||
static grpc_json_reader_vtable reader_vtable = { |
||||
json_reader_string_clear, |
||||
json_reader_string_add_char, |
||||
json_reader_string_add_utf32, |
||||
json_reader_read_char, |
||||
json_reader_container_begins, |
||||
json_reader_container_ends, |
||||
json_reader_set_key, |
||||
json_reader_set_string, |
||||
json_reader_set_number, |
||||
json_reader_set_true, |
||||
json_reader_set_false, |
||||
json_reader_set_null |
||||
}; |
||||
|
||||
/* And finally, let's define our public API. */ |
||||
grpc_json* grpc_json_parse_string_with_len(char* input, size_t size) { |
||||
grpc_json_reader reader; |
||||
json_reader_userdata state; |
||||
grpc_json *json = NULL; |
||||
grpc_json_reader_status status; |
||||
|
||||
if (!input) return NULL; |
||||
|
||||
state.top = state.current_container = state.current_value = NULL; |
||||
state.string = state.key = NULL; |
||||
state.string_ptr = state.input = (gpr_uint8*) input; |
||||
state.remaining_input = size; |
||||
grpc_json_reader_init(&reader, &reader_vtable, &state); |
||||
|
||||
status = grpc_json_reader_run(&reader); |
||||
json = state.top; |
||||
|
||||
if ((status != GRPC_JSON_DONE) && json) { |
||||
grpc_json_destroy(json); |
||||
json = NULL; |
||||
} |
||||
|
||||
return json; |
||||
} |
||||
|
||||
#define UNBOUND_JSON_STRING_LENGTH 0x7fffffff |
||||
|
||||
grpc_json* grpc_json_parse_string(char* input) { |
||||
return grpc_json_parse_string_with_len(input, UNBOUND_JSON_STRING_LENGTH); |
||||
} |
||||
|
||||
static void json_dump_recursive(grpc_json_writer* writer, |
||||
grpc_json* json, int in_object) { |
||||
while (json) { |
||||
if (in_object) grpc_json_writer_object_key(writer, json->key); |
||||
|
||||
switch (json->type) { |
||||
case GRPC_JSON_OBJECT: |
||||
case GRPC_JSON_ARRAY: |
||||
grpc_json_writer_container_begins(writer, json->type); |
||||
if (json->child) |
||||
json_dump_recursive(writer, json->child, |
||||
json->type == GRPC_JSON_OBJECT); |
||||
grpc_json_writer_container_ends(writer, json->type); |
||||
break; |
||||
case GRPC_JSON_STRING: |
||||
grpc_json_writer_value_string(writer, json->value); |
||||
break; |
||||
case GRPC_JSON_NUMBER: |
||||
grpc_json_writer_value_raw(writer, json->value); |
||||
break; |
||||
case GRPC_JSON_TRUE: |
||||
grpc_json_writer_value_raw_with_len(writer, "true", 4); |
||||
break; |
||||
case GRPC_JSON_FALSE: |
||||
grpc_json_writer_value_raw_with_len(writer, "false", 5); |
||||
break; |
||||
case GRPC_JSON_NULL: |
||||
grpc_json_writer_value_raw_with_len(writer, "null", 4); |
||||
break; |
||||
default: |
||||
abort(); |
||||
} |
||||
json = json->next; |
||||
} |
||||
} |
||||
|
||||
static grpc_json_writer_vtable writer_vtable = { |
||||
json_writer_output_char, |
||||
json_writer_output_string, |
||||
json_writer_output_string_with_len |
||||
}; |
||||
|
||||
char* grpc_json_dump_to_string(grpc_json* json, int indent) { |
||||
grpc_json_writer writer; |
||||
json_writer_userdata state; |
||||
|
||||
state.output = NULL; |
||||
state.free_space = state.string_len = state.allocated = 0; |
||||
grpc_json_writer_init(&writer, indent, &writer_vtable, &state); |
||||
|
||||
json_dump_recursive(&writer, json, 0); |
||||
|
||||
json_writer_output_char(&state, 0); |
||||
|
||||
return state.output; |
||||
} |
@ -0,0 +1,252 @@ |
||||
/*
|
||||
* |
||||
* 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 <grpc/support/port_platform.h> |
||||
|
||||
#include "src/core/json/json_writer.h" |
||||
|
||||
static void json_writer_output_char(grpc_json_writer* writer, char c) { |
||||
writer->vtable->output_char(writer->userdata, c); |
||||
} |
||||
|
||||
static void json_writer_output_string(grpc_json_writer* writer, const char* str) { |
||||
writer->vtable->output_string(writer->userdata, str); |
||||
} |
||||
|
||||
static void json_writer_output_string_with_len(grpc_json_writer* writer, const char* str, size_t len) { |
||||
writer->vtable->output_string_with_len(writer->userdata, str, len); |
||||
} |
||||
|
||||
void grpc_json_writer_init(grpc_json_writer* writer, int indent, |
||||
grpc_json_writer_vtable* vtable, void* userdata) { |
||||
memset(writer, 0, sizeof(grpc_json_writer)); |
||||
writer->container_empty = 1; |
||||
writer->indent = indent; |
||||
writer->vtable = vtable; |
||||
writer->userdata = userdata; |
||||
} |
||||
|
||||
static void json_writer_output_indent( |
||||
grpc_json_writer* writer) { |
||||
static const char spacesstr[] = |
||||
" " |
||||
" " |
||||
" " |
||||
" "; |
||||
|
||||
unsigned spaces = writer->depth * writer->indent; |
||||
|
||||
if (writer->indent == 0) return; |
||||
|
||||
if (writer->got_key) { |
||||
json_writer_output_char(writer, ' '); |
||||
return; |
||||
} |
||||
|
||||
while (spaces >= (sizeof(spacesstr) - 1)) { |
||||
json_writer_output_string_with_len(writer, spacesstr, |
||||
sizeof(spacesstr) - 1); |
||||
spaces -= (sizeof(spacesstr) - 1); |
||||
} |
||||
|
||||
if (spaces == 0) return; |
||||
|
||||
json_writer_output_string_with_len( |
||||
writer, spacesstr + sizeof(spacesstr) - 1 - spaces, spaces); |
||||
} |
||||
|
||||
static void json_writer_value_end(grpc_json_writer* writer) { |
||||
if (writer->container_empty) { |
||||
writer->container_empty = 0; |
||||
if ((writer->indent == 0) || (writer->depth == 0)) return; |
||||
json_writer_output_char(writer, '\n'); |
||||
} else { |
||||
json_writer_output_char(writer, ','); |
||||
if (writer->indent == 0) return; |
||||
json_writer_output_char(writer, '\n'); |
||||
} |
||||
} |
||||
|
||||
static void json_writer_escape_utf16(grpc_json_writer* writer, gpr_uint16 utf16) { |
||||
static const char hex[] = "0123456789abcdef"; |
||||
|
||||
json_writer_output_string_with_len(writer, "\\u", 2); |
||||
json_writer_output_char(writer, hex[(utf16 >> 12) & 0x0f]); |
||||
json_writer_output_char(writer, hex[(utf16 >> 8) & 0x0f]); |
||||
json_writer_output_char(writer, hex[(utf16 >> 4) & 0x0f]); |
||||
json_writer_output_char(writer, hex[(utf16) & 0x0f]); |
||||
} |
||||
|
||||
static void json_writer_escape_string(grpc_json_writer* writer, |
||||
const char* string) { |
||||
json_writer_output_char(writer, '"'); |
||||
|
||||
for (;;) { |
||||
gpr_uint8 c = (gpr_uint8)*string++; |
||||
if (c == 0) { |
||||
break; |
||||
} else if ((c >= 32) && (c <= 127)) { |
||||
if ((c == '\\') || (c == '"')) json_writer_output_char(writer, '\\'); |
||||
json_writer_output_char(writer, c); |
||||
} else if (c < 32) { |
||||
switch (c) { |
||||
case '\b': |
||||
json_writer_output_string_with_len(writer, "\\b", 2); |
||||
break; |
||||
case '\f': |
||||
json_writer_output_string_with_len(writer, "\\f", 2); |
||||
break; |
||||
case '\n': |
||||
json_writer_output_string_with_len(writer, "\\n", 2); |
||||
break; |
||||
case '\r': |
||||
json_writer_output_string_with_len(writer, "\\r", 2); |
||||
break; |
||||
case '\t': |
||||
json_writer_output_string_with_len(writer, "\\t", 2); |
||||
break; |
||||
default: |
||||
json_writer_escape_utf16(writer, c); |
||||
break; |
||||
} |
||||
} else { |
||||
gpr_uint32 utf32 = 0; |
||||
int extra = 0; |
||||
int i; |
||||
int valid = 1; |
||||
if ((c & 0xe0) == 0xc0) { |
||||
utf32 = c & 0x1f; |
||||
extra = 1; |
||||
} else if ((c & 0xf0) == 0xe0) { |
||||
utf32 = c & 0x0f; |
||||
extra = 2; |
||||
} else if ((c & 0xf8) == 0xf0) { |
||||
utf32 = c & 0x07; |
||||
extra = 3; |
||||
} else { |
||||
break; |
||||
} |
||||
for (i = 0; i < extra; i++) { |
||||
utf32 <<= 6; |
||||
c = *string++; |
||||
if ((c & 0xc0) != 0x80) { |
||||
valid = 0; |
||||
break; |
||||
} |
||||
utf32 |= c & 0x3f; |
||||
} |
||||
if (!valid) break; |
||||
/* The range 0xd800 - 0xdfff is reserved by the surrogates ad vitam.
|
||||
* Any other range is technically reserved for future usage, so if we |
||||
* don't want the software to break in the future, we have to allow |
||||
* anything else. The first non-unicode character is 0x110000. */ |
||||
if (((utf32 >= 0xd800) && (utf32 <= 0xdfff)) || |
||||
(utf32 >= 0x110000)) break; |
||||
if (utf32 >= 0x10000) { |
||||
/* If utf32 contains a character that is above 0xffff, it needs to be
|
||||
* broken down into a utf-16 surrogate pair. A surrogate pair is first |
||||
* a high surrogate, followed by a low surrogate. Each surrogate holds |
||||
* 10 bits of usable data, thus allowing a total of 20 bits of data. |
||||
* The high surrogate marker is 0xd800, while the low surrogate marker |
||||
* is 0xdc00. The low 10 bits of each will be the usable data. |
||||
* |
||||
* After re-combining the 20 bits of data, one has to add 0x10000 to |
||||
* the resulting value, in order to obtain the original character. |
||||
* This is obviously because the range 0x0000 - 0xffff can be written |
||||
* without any special trick. |
||||
* |
||||
* Since 0x10ffff is the highest allowed character, we're working in |
||||
* the range 0x00000 - 0xfffff after we decrement it by 0x10000. |
||||
* That range is exactly 20 bits. |
||||
*/ |
||||
utf32 -= 0x10000; |
||||
json_writer_escape_utf16(writer, 0xd800 | (utf32 >> 10)); |
||||
json_writer_escape_utf16(writer, 0xdc00 | (utf32 & 0x3ff)); |
||||
} else { |
||||
json_writer_escape_utf16(writer, utf32); |
||||
} |
||||
} |
||||
} |
||||
|
||||
json_writer_output_char(writer, '"'); |
||||
} |
||||
|
||||
void grpc_json_writer_container_begins(grpc_json_writer* writer, grpc_json_type type) { |
||||
if (!writer->got_key) json_writer_value_end(writer); |
||||
json_writer_output_indent(writer); |
||||
json_writer_output_char(writer, type == GRPC_JSON_OBJECT ? '{' : '['); |
||||
writer->container_empty = 1; |
||||
writer->got_key = 0; |
||||
writer->depth++; |
||||
} |
||||
|
||||
void grpc_json_writer_container_ends(grpc_json_writer* writer, grpc_json_type type) { |
||||
if (writer->indent && !writer->container_empty) |
||||
json_writer_output_char(writer, '\n'); |
||||
writer->depth--; |
||||
if (!writer->container_empty) json_writer_output_indent(writer); |
||||
json_writer_output_char(writer, type == GRPC_JSON_OBJECT ? '}' : ']'); |
||||
writer->container_empty = 0; |
||||
writer->got_key = 0; |
||||
} |
||||
|
||||
void grpc_json_writer_object_key(grpc_json_writer* writer, const char* string) { |
||||
json_writer_value_end(writer); |
||||
json_writer_output_indent(writer); |
||||
json_writer_escape_string(writer, string); |
||||
json_writer_output_char(writer, ':'); |
||||
writer->got_key = 1; |
||||
} |
||||
|
||||
void grpc_json_writer_value_raw(grpc_json_writer* writer, const char* string) { |
||||
if (!writer->got_key) json_writer_value_end(writer); |
||||
json_writer_output_indent(writer); |
||||
json_writer_output_string(writer, string); |
||||
writer->got_key = 0; |
||||
} |
||||
|
||||
void grpc_json_writer_value_raw_with_len(grpc_json_writer* writer, const char* string, size_t len) { |
||||
if (!writer->got_key) json_writer_value_end(writer); |
||||
json_writer_output_indent(writer); |
||||
json_writer_output_string_with_len(writer, string, len); |
||||
writer->got_key = 0; |
||||
} |
||||
|
||||
void grpc_json_writer_value_string(grpc_json_writer* writer, const char* string) { |
||||
if (!writer->got_key) json_writer_value_end(writer); |
||||
json_writer_output_indent(writer); |
||||
json_writer_escape_string(writer, string); |
||||
writer->got_key = 0; |
||||
} |
@ -0,0 +1,93 @@ |
||||
/*
|
||||
* |
||||
* 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. |
||||
* |
||||
*/ |
||||
|
||||
/* The idea of the writer is basically symmetrical of the reader. While the
|
||||
* reader emits various calls to your code, the writer takes basically the |
||||
* same calls and emit json out of it. It doesn't try to make any check on |
||||
* the order of the calls you do on it. Meaning you can theorically force |
||||
* it to generate invalid json. |
||||
* |
||||
* Also, unlike the reader, the writer expects UTF-8 encoded input strings. |
||||
* These strings will be UTF-8 validated, and any invalid character will |
||||
* cut the conversion short, before any invalid UTF-8 sequence, thus forming |
||||
* a valid UTF-8 string overall. |
||||
*/ |
||||
|
||||
#ifndef __GRPC_SRC_CORE_JSON_JSON_WRITER_H__ |
||||
#define __GRPC_SRC_CORE_JSON_JSON_WRITER_H__ |
||||
|
||||
#include <stdlib.h> |
||||
|
||||
#include "src/core/json/json_common.h" |
||||
|
||||
typedef struct grpc_json_writer_vtable { |
||||
/* Adds a character to the output stream. */ |
||||
void (*output_char)(void* userdata, char); |
||||
/* Adds a zero-terminated string to the output stream. */ |
||||
void (*output_string)(void* userdata, const char* str); |
||||
/* Adds a fixed-length string to the output stream. */ |
||||
void (*output_string_with_len)(void* userdata, const char* str, size_t len); |
||||
|
||||
} grpc_json_writer_vtable; |
||||
|
||||
typedef struct grpc_json_writer { |
||||
void* userdata; |
||||
grpc_json_writer_vtable* vtable; |
||||
int indent; |
||||
int depth; |
||||
int container_empty; |
||||
int got_key; |
||||
} grpc_json_writer; |
||||
|
||||
/* Call this to initialize your writer structure. The indent parameter is
|
||||
* specifying the number of spaces to use for indenting the output. If you |
||||
* use indent=0, then the output will not have any newlines either, thus |
||||
* emitting a condensed json output. |
||||
*/ |
||||
void grpc_json_writer_init(grpc_json_writer* writer, int indent, |
||||
grpc_json_writer_vtable* vtable, void* userdata); |
||||
|
||||
/* Signals the beginning of a container. */ |
||||
void grpc_json_writer_container_begins(grpc_json_writer* writer, grpc_json_type type); |
||||
/* Signals the end of a container. */ |
||||
void grpc_json_writer_container_ends(grpc_json_writer* writer, grpc_json_type type); |
||||
/* Writes down an object key for the next value. */ |
||||
void grpc_json_writer_object_key(grpc_json_writer* writer, const char* string); |
||||
/* Sets a raw value. Useful for numbers. */ |
||||
void grpc_json_writer_value_raw(grpc_json_writer* writer, const char* string); |
||||
/* Sets a raw value with its length. Useful for values like true or false. */ |
||||
void grpc_json_writer_value_raw_with_len(grpc_json_writer* writer, const char* string, size_t len); |
||||
/* Sets a string value. It'll be escaped, and utf-8 validated. */ |
||||
void grpc_json_writer_value_string(grpc_json_writer* writer, const char* string); |
||||
|
||||
#endif /* __GRPC_SRC_CORE_JSON_JSON_WRITER_H__ */ |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue