blob: 6a3b7b9e4272a182e054261730fc14ace0b7368a [file]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
// brpc - A framework to host and access services throughout Baidu.
// Date: 2015/10/22 16:28:44
#include <gtest/gtest.h>
#include <atomic>
#include "brpc/server.h"
#include "brpc/controller.h"
#include "brpc/channel.h"
#include "brpc/callback.h"
#include "brpc/socket.h"
#include "brpc/stream_impl.h"
#include "brpc/policy/streaming_rpc_protocol.h"
#include "echo.pb.h"
class AfterAcceptStream {
public:
virtual void action(brpc::StreamId) = 0;
};
class MyServiceWithStream : public test::EchoService {
public:
MyServiceWithStream(const brpc::StreamOptions& options)
: _options(options)
, _after_accept_stream(NULL)
{}
MyServiceWithStream(const brpc::StreamOptions& options,
AfterAcceptStream* after_accept_stream)
: _options(options)
, _after_accept_stream(after_accept_stream)
{}
MyServiceWithStream()
: _options()
, _after_accept_stream(NULL)
{}
void Echo(::google::protobuf::RpcController* controller,
const ::test::EchoRequest* request,
::test::EchoResponse* response,
::google::protobuf::Closure* done) {
brpc::ClosureGuard done_guard(done);
response->set_message(request->message());
brpc::Controller* cntl = (brpc::Controller*)controller;
brpc::StreamId response_stream;
ASSERT_EQ(0, StreamAccept(&response_stream, *cntl, &_options));
LOG(INFO) << "Created response_stream=" << response_stream;
if (_after_accept_stream) {
_after_accept_stream->action(response_stream);
}
}
private:
brpc::StreamOptions _options;
AfterAcceptStream* _after_accept_stream;
};
class StreamingRpcTest : public testing::Test {
protected:
void SetUp() { request.set_message("hello world"); }
void TearDown() {}
test::EchoRequest request;
test::EchoResponse response;
};
struct BatchStreamFeedbackRaceState {
brpc::StreamId server_first_stream_id{brpc::INVALID_STREAM_ID};
brpc::StreamId server_extra_stream_id{brpc::INVALID_STREAM_ID};
brpc::StreamId client_extra_stream_id{brpc::INVALID_STREAM_ID};
std::atomic<int> server_first_write_rc{-1};
std::atomic<int> server_second_write_rc{-1};
std::atomic<bool> client_got_first_msg{false};
std::atomic<bool> client_got_second_msg{false};
std::atomic<bool> server_write_done{false};
std::atomic<bool> rpc_done{false};
std::atomic<int> client_closed_count{0};
bthread_t server_send_tid{0};
std::atomic<bool> server_send_started{false};
};
class BatchStreamClientHandler : public brpc::StreamInputHandler {
public:
explicit BatchStreamClientHandler(BatchStreamFeedbackRaceState* state)
: _state(state) {}
int on_received_messages(brpc::StreamId id,
butil::IOBuf* const messages[],
size_t size) override {
if (id != _state->client_extra_stream_id) {
// This test only cares about extra stream in batch creation.
return 0;
}
for (size_t i = 0; i < size; ++i) {
const size_t len = messages[i]->length();
messages[i]->clear();
// First payload: 64 bytes. Second payload: 1 byte.
if (len == 64) {
_state->client_got_first_msg.store(true, std::memory_order_release);
} else if (len == 1) {
_state->client_got_second_msg.store(true, std::memory_order_release);
}
}
return 0;
}
void on_idle_timeout(brpc::StreamId /*id*/) override {}
void on_closed(brpc::StreamId /*id*/) override {
_state->client_closed_count.fetch_add(1, std::memory_order_release);
}
void on_failed(brpc::StreamId /*id*/, int /*error_code*/, const std::string& /*error_text*/) override {}
private:
BatchStreamFeedbackRaceState* _state;
};
static void* SendTwoMessagesOnServerExtraStream(void* arg) {
auto* state = static_cast<BatchStreamFeedbackRaceState*>(arg);
const brpc::StreamId sid = state->server_extra_stream_id;
// Wait until server-side stream is connected.
const int64_t connect_deadline_us = butil::gettimeofday_us() + 2 * 1000 * 1000L;
bool connected = false;
while (butil::gettimeofday_us() < connect_deadline_us) {
brpc::SocketUniquePtr ptr;
if (brpc::Socket::Address(sid, &ptr) == 0) {
brpc::Stream* s = static_cast<brpc::Stream*>(ptr->conn());
if (s->_host_socket != NULL && s->_connected) {
connected = true;
break;
}
}
usleep(1000);
}
if (!connected) {
state->server_first_write_rc.store(ETIMEDOUT, std::memory_order_relaxed);
state->server_second_write_rc.store(ETIMEDOUT, std::memory_order_relaxed);
state->server_write_done.store(true, std::memory_order_release);
return NULL;
}
// 1) Send a payload exactly equal to max_buf_size(64).
{
std::string payload(64, 'a');
butil::IOBuf out;
out.append(payload);
state->server_first_write_rc.store(brpc::StreamWrite(sid, out), std::memory_order_relaxed);
}
// 2) Then send another byte. This write should become writable only after
// client sends FEEDBACK with consumed_size >= 64.
const int64_t write_deadline_us = butil::gettimeofday_us() + 2 * 1000 * 1000L;
int rc = -1;
while (butil::gettimeofday_us() < write_deadline_us) {
butil::IOBuf out;
out.append("b", 1);
rc = brpc::StreamWrite(sid, out);
if (rc == 0) {
break;
}
if (rc != EAGAIN) {
break;
}
const timespec duetime = butil::milliseconds_from_now(100);
(void)brpc::StreamWait(sid, &duetime);
}
state->server_second_write_rc.store(rc, std::memory_order_relaxed);
state->server_write_done.store(true, std::memory_order_release);
return NULL;
}
class MyServiceWithBatchStream : public test::EchoService {
public:
MyServiceWithBatchStream(const brpc::StreamOptions& options,
BatchStreamFeedbackRaceState* state)
: _options(options), _state(state) {}
void Echo(::google::protobuf::RpcController* controller,
const ::test::EchoRequest* request,
::test::EchoResponse* response,
::google::protobuf::Closure* done) override {
brpc::ClosureGuard done_guard(done);
response->set_message(request->message());
brpc::Controller* cntl = static_cast<brpc::Controller*>(controller);
brpc::StreamIds response_streams;
ASSERT_EQ(0, brpc::StreamAccept(response_streams, *cntl, &_options));
ASSERT_EQ(2u, response_streams.size());
_state->server_first_stream_id = response_streams[0];
_state->server_extra_stream_id = response_streams[1];
bthread_t tid;
ASSERT_EQ(0, bthread_start_background(
&tid, &BTHREAD_ATTR_NORMAL,
SendTwoMessagesOnServerExtraStream, _state));
_state->server_send_tid = tid;
_state->server_send_started.store(true, std::memory_order_release);
}
private:
brpc::StreamOptions _options;
BatchStreamFeedbackRaceState* _state;
};
static void SetAtomicTrue(std::atomic<bool>* f) {
f->store(true, std::memory_order_release);
}
template <typename Pred>
static bool WaitForTrue(Pred pred, int timeout_ms) {
const int64_t deadline_us = butil::gettimeofday_us() + (int64_t)timeout_ms * 1000L;
while (!pred() && butil::gettimeofday_us() < deadline_us) {
usleep(1000);
}
return pred();
}
static bool WaitForTrue(const std::atomic<bool>& f, int timeout_ms) {
return WaitForTrue([&f]() { return f.load(std::memory_order_acquire); }, timeout_ms);
}
TEST_F(StreamingRpcTest, sanity) {
brpc::Server server;
MyServiceWithStream service;
ASSERT_EQ(0, server.AddService(&service, brpc::SERVER_DOESNT_OWN_SERVICE));
ASSERT_EQ(0, server.Start(9007, NULL));
brpc::Channel channel;
ASSERT_EQ(0, channel.Init("127.0.0.1:9007", NULL));
brpc::Controller cntl;
brpc::StreamId request_stream;
ASSERT_EQ(0, StreamCreate(&request_stream, cntl, NULL));
brpc::ScopedStream stream_guard(request_stream);
test::EchoService_Stub stub(&channel);
stub.Echo(&cntl, &request, &response, NULL);
ASSERT_FALSE(cntl.Failed()) << cntl.ErrorText() << " request_stream=" << request_stream;
usleep(10);
brpc::StreamClose(request_stream);
server.Stop(0);
server.Join();
}
TEST_F(StreamingRpcTest, batch_create_stream_feedback_race) {
BatchStreamFeedbackRaceState state;
BatchStreamClientHandler client_handler(&state);
brpc::StreamOptions server_stream_opt;
// Make server-side sender sensitive to FEEDBACK quickly.
server_stream_opt.max_buf_size = 16;
brpc::Server server;
MyServiceWithBatchStream service(server_stream_opt, &state);
ASSERT_EQ(0, server.AddService(&service, brpc::SERVER_DOESNT_OWN_SERVICE));
ASSERT_EQ(0, server.Start(9007, NULL));
brpc::Channel channel;
ASSERT_EQ(0, channel.Init("127.0.0.1:9007", NULL));
brpc::Controller cntl;
brpc::StreamIds request_streams;
brpc::StreamOptions client_stream_opt;
client_stream_opt.handler = &client_handler;
client_stream_opt.max_buf_size = 0;
ASSERT_EQ(0, brpc::StreamCreate(request_streams, 2, cntl, &client_stream_opt));
ASSERT_EQ(2u, request_streams.size());
state.client_extra_stream_id = request_streams[1];
// Block SetConnected() on the extra stream to enlarge the race window.
brpc::SocketUniquePtr client_extra_ptr;
ASSERT_EQ(0, brpc::Socket::Address(state.client_extra_stream_id, &client_extra_ptr));
brpc::Stream* client_extra_stream = static_cast<brpc::Stream*>(client_extra_ptr->conn());
bthread_mutex_lock(&client_extra_stream->_connect_mutex);
struct UnlockGuard {
bthread_mutex_t* m;
~UnlockGuard() {
if (m) {
bthread_mutex_unlock(m);
}
}
} unlock_guard{&client_extra_stream->_connect_mutex};
BRPC_SCOPE_EXIT {
if (state.server_extra_stream_id != brpc::INVALID_STREAM_ID) {
brpc::StreamClose(state.server_extra_stream_id);
}
if (state.server_first_stream_id != brpc::INVALID_STREAM_ID) {
brpc::StreamClose(state.server_first_stream_id);
}
for (auto sid : request_streams) {
brpc::StreamClose(sid);
}
if (state.server_send_tid) {
bthread_join(state.server_send_tid, NULL);
}
server.Stop(0);
server.Join();
// Release the SocketUniquePtr held above so the fake socket can be
// recycled. Otherwise BeforeRecycle / on_closed for the extra stream
// is deferred until `client_extra_ptr` destructs at scope exit, which
// happens *after* `client_handler` and `state` are destroyed -> UAF
// inside Stream::Consume on Linux.
client_extra_ptr.reset();
// on_closed() runs asynchronously on each client stream's consumer
// bthread. Wait for both before letting handler/state go out of
// scope, otherwise Stream::Consume will dereference freed memory.
int expected_closed = request_streams.size();
WaitForTrue([&state, expected_closed]() {
return state.client_closed_count.load(std::memory_order_acquire)
>= expected_closed;
}, 2000);
};
test::EchoService_Stub stub(&channel);
stub.Echo(&cntl, &request, &response, brpc::NewCallback(SetAtomicTrue, &state.rpc_done));
// Wait until client consumes the first 64B payload on extra stream.
ASSERT_TRUE(WaitForTrue(state.client_got_first_msg, 2000));
// Unblock SetConnected(); the fix in PR 3215 should send the first FEEDBACK
// with consumed_size=64 here, making server-side stream writable again.
bthread_mutex_unlock(&client_extra_stream->_connect_mutex);
unlock_guard.m = NULL;
ASSERT_TRUE(WaitForTrue(state.rpc_done, 2000));
ASSERT_FALSE(cntl.Failed()) << cntl.ErrorText();
// Wait for server-side send thread to be started.
ASSERT_TRUE(WaitForTrue(state.server_send_started, 2000));
ASSERT_TRUE(WaitForTrue(state.server_write_done, 2000));
ASSERT_EQ(0, state.server_first_write_rc.load(std::memory_order_relaxed));
ASSERT_EQ(0, state.server_second_write_rc.load(std::memory_order_relaxed));
ASSERT_TRUE(WaitForTrue(state.client_got_second_msg, 2000));
}
struct HandlerControl {
HandlerControl()
: block(false)
{}
bool block;
};
class OrderedInputHandler : public brpc::StreamInputHandler {
public:
explicit OrderedInputHandler(HandlerControl *cntl = NULL)
: _expected_next_value(0)
, _failed(false)
, _stopped(false)
, _idle_times(0)
, _cntl(cntl)
{}
int on_received_messages(brpc::StreamId /*id*/,
butil::IOBuf *const messages[],
size_t size) override {
if (_cntl && _cntl->block) {
while (_cntl->block) {
usleep(100);
}
}
for (size_t i = 0; i < size; ++i) {
CHECK(messages[i]->length() == sizeof(int));
int network = 0;
messages[i]->cutn(&network, sizeof(int));
EXPECT_EQ((int)ntohl(network), _expected_next_value++);
}
return 0;
}
void on_idle_timeout(brpc::StreamId /*id*/) override {
++_idle_times;
}
void on_closed(brpc::StreamId /*id*/) override {
ASSERT_FALSE(_stopped);
_stopped = true;
}
void on_failed(brpc::StreamId id, int error_code,
const std::string& /*error_text*/) override {
ASSERT_FALSE(_failed);
ASSERT_NE(0, error_code);
_failed = true;
}
bool failed() const { return _failed; }
bool stopped() const { return _stopped; }
int idle_times() const { return _idle_times; }
private:
int _expected_next_value;
bool _failed;
bool _stopped;
int _idle_times;
HandlerControl* _cntl;
};
TEST_F(StreamingRpcTest, received_in_order) {
OrderedInputHandler handler;
brpc::StreamOptions opt;
opt.handler = &handler;
opt.messages_in_batch = 100;
brpc::Server server;
MyServiceWithStream service(opt);
ASSERT_EQ(0, server.AddService(&service, brpc::SERVER_DOESNT_OWN_SERVICE));
ASSERT_EQ(0, server.Start(9007, NULL));
brpc::Channel channel;
ASSERT_EQ(0, channel.Init("127.0.0.1:9007", NULL));
brpc::Controller cntl;
brpc::StreamId request_stream;
brpc::StreamOptions request_stream_options;
request_stream_options.max_buf_size = 0;
ASSERT_EQ(0, StreamCreate(&request_stream, cntl, &request_stream_options));
brpc::ScopedStream stream_guard(request_stream);
test::EchoService_Stub stub(&channel);
stub.Echo(&cntl, &request, &response, NULL);
ASSERT_FALSE(cntl.Failed()) << cntl.ErrorText() << " request_stream=" << request_stream;
const int N = 10000;
for (int i = 0; i < N; ++i) {
int network = htonl(i);
butil::IOBuf out;
out.append(&network, sizeof(network));
ASSERT_EQ(0, brpc::StreamWrite(request_stream, out)) << "i=" << i;
}
ASSERT_EQ(0, brpc::StreamClose(request_stream));
server.Stop(0);
server.Join();
while (!handler.stopped()) {
usleep(100);
}
ASSERT_FALSE(handler.failed());
ASSERT_EQ(0, handler.idle_times());
ASSERT_EQ(N, handler._expected_next_value);
}
void on_writable(brpc::StreamId, void* arg, int error_code) {
std::pair<bool, int>* p = (std::pair<bool, int>*)arg;
p->first = true;
p->second = error_code;
LOG(INFO) << "error_code=" << error_code;
}
TEST_F(StreamingRpcTest, block) {
HandlerControl hc;
hc.block = true;
OrderedInputHandler handler(&hc);
brpc::StreamOptions opt;
opt.handler = &handler;
const int N = 10000;
opt.max_buf_size = sizeof(uint32_t) * N;
brpc::Server server;
MyServiceWithStream service(opt);
ASSERT_EQ(0, server.AddService(&service, brpc::SERVER_DOESNT_OWN_SERVICE));
ASSERT_EQ(0, server.Start(9007, NULL));
brpc::Channel channel;
ASSERT_EQ(0, channel.Init("127.0.0.1:9007", NULL));
brpc::Controller cntl;
brpc::StreamId request_stream;
brpc::ScopedStream stream_guard(request_stream);
brpc::StreamOptions request_stream_options;
request_stream_options.max_buf_size = sizeof(uint32_t) * N;
ASSERT_EQ(0, StreamCreate(&request_stream, cntl, &request_stream_options));
test::EchoService_Stub stub(&channel);
stub.Echo(&cntl, &request, &response, NULL);
ASSERT_FALSE(cntl.Failed()) << cntl.ErrorText() << " request_stream="
<< request_stream;
for (int i = 0; i < N; ++i) {
int network = htonl(i);
butil::IOBuf out;
out.append(&network, sizeof(network));
ASSERT_EQ(0, brpc::StreamWrite(request_stream, out)) << "i=" << i;
}
// sync wait
int dummy = 102030123;
butil::IOBuf out;
out.append(&dummy, sizeof(dummy));
ASSERT_EQ(EAGAIN, brpc::StreamWrite(request_stream, out));
hc.block = false;
// wait flushing all the pending messages
while (handler._expected_next_value != N) {
usleep(100);
}
// block hanlder again to test async wait
hc.block = true;
// async wait
for (int i = N; i < N + N; ++i) {
ASSERT_EQ(0, brpc::StreamWait(request_stream, NULL));
int network = htonl(i);
butil::IOBuf out;
out.append(&network, sizeof(network));
ASSERT_EQ(0, brpc::StreamWrite(request_stream, out)) << "i=" << i;
}
out.clear();
out.append(&dummy, sizeof(dummy));
ASSERT_EQ(EAGAIN, brpc::StreamWrite(request_stream, out));
hc.block = false;
std::pair<bool, int> p = std::make_pair(false, 0);
usleep(10);
brpc::StreamWait(request_stream, NULL, on_writable, &p);
while (!p.first) {
usleep(100);
}
ASSERT_EQ(0, p.second);
// wait flushing all the pending messages
while (handler._expected_next_value != N + N) {
usleep(100);
}
usleep(1000);
LOG(INFO) << "Starting block";
hc.block = true;
for (int i = N + N; i < N + N + N; ++i) {
int network = htonl(i);
butil::IOBuf out;
out.append(&network, sizeof(network));
ASSERT_EQ(0, brpc::StreamWrite(request_stream, out)) << "i=" << i - N - N;
}
out.clear();
out.append(&dummy, sizeof(dummy));
ASSERT_EQ(EAGAIN, brpc::StreamWrite(request_stream, out));
timespec duetime = butil::microseconds_from_now(1);
p.first = false;
LOG(INFO) << "Start wait";
brpc::StreamWait(request_stream, &duetime, on_writable, &p);
while (!p.first) {
usleep(100);
}
ASSERT_TRUE(p.first);
EXPECT_EQ(ETIMEDOUT, p.second);
hc.block = false;
ASSERT_EQ(0, brpc::StreamClose(request_stream));
while (!handler.stopped()) {
usleep(100);
}
ASSERT_FALSE(handler.failed());
ASSERT_EQ(0, handler.idle_times());
ASSERT_EQ(N + N + N, handler._expected_next_value);
}
TEST_F(StreamingRpcTest, auto_close_if_host_socket_closed) {
HandlerControl hc;
hc.block = true;
OrderedInputHandler handler(&hc);
brpc::StreamOptions opt;
opt.handler = &handler;
const int N = 10000;
opt.max_buf_size = sizeof(uint32_t) * N;
brpc::Server server;
MyServiceWithStream service(opt);
ASSERT_EQ(0, server.AddService(&service, brpc::SERVER_DOESNT_OWN_SERVICE));
ASSERT_EQ(0, server.Start(9007, NULL));
brpc::Channel channel;
ASSERT_EQ(0, channel.Init("127.0.0.1:9007", NULL));
brpc::Controller cntl;
brpc::StreamId request_stream;
brpc::StreamOptions request_stream_options;
request_stream_options.max_buf_size = sizeof(uint32_t) * N;
ASSERT_EQ(0, StreamCreate(&request_stream, cntl, &request_stream_options));
brpc::ScopedStream stream_guard(request_stream);
test::EchoService_Stub stub(&channel);
stub.Echo(&cntl, &request, &response, NULL);
ASSERT_FALSE(cntl.Failed()) << cntl.ErrorText() << " request_stream=" << request_stream;
{
brpc::SocketUniquePtr ptr;
ASSERT_EQ(0, brpc::Socket::Address(request_stream, &ptr));
brpc::Stream* s = (brpc::Stream*)ptr->conn();
ASSERT_TRUE(s->_host_socket != NULL);
s->_host_socket->SetFailed();
}
usleep(100);
butil::IOBuf out;
out.append("test");
ASSERT_EQ(EINVAL, brpc::StreamWrite(request_stream, out));
while (!handler.stopped()) {
usleep(100);
}
ASSERT_TRUE(handler.failed());
ASSERT_EQ(0, handler.idle_times());
ASSERT_EQ(0, handler._expected_next_value);
}
TEST_F(StreamingRpcTest, failed_when_rst) {
OrderedInputHandler handler;
brpc::StreamOptions opt;
opt.handler = &handler;
opt.messages_in_batch = 100;
brpc::Server server;
MyServiceWithStream service(opt);
ASSERT_EQ(0, server.AddService(&service, brpc::SERVER_DOESNT_OWN_SERVICE));
ASSERT_EQ(0, server.Start(9007, NULL));
brpc::Channel channel;
ASSERT_EQ(0, channel.Init("127.0.0.1:9007", NULL));
brpc::Controller cntl;
brpc::StreamId request_stream;
brpc::StreamOptions request_stream_options;
request_stream_options.max_buf_size = 0;
ASSERT_EQ(0, StreamCreate(&request_stream, cntl, &request_stream_options));
brpc::ScopedStream stream_guard(request_stream);
test::EchoService_Stub stub(&channel);
stub.Echo(&cntl, &request, &response, NULL);
ASSERT_FALSE(cntl.Failed()) << cntl.ErrorText() << " request_stream=" << request_stream;
const int N = 10000;
for (int i = 0; i < N; ++i) {
int network = htonl(i);
butil::IOBuf out;
out.append(&network, sizeof(network));
ASSERT_EQ(0, brpc::StreamWrite(request_stream, out)) << "i=" << i;
}
while (handler._expected_next_value != N) {
usleep(100);
}
{
brpc::SocketUniquePtr ptr;
ASSERT_EQ(0, brpc::Socket::Address(request_stream, &ptr));
brpc::Stream* s = (brpc::Stream*)ptr->conn();
ASSERT_TRUE(s->_host_socket != NULL);
brpc::policy::SendStreamRst(s->_host_socket,
s->_remote_settings.stream_id());
}
// ASSERT_EQ(0, brpc::StreamClose(request_stream));
server.Stop(0);
server.Join();
while (!handler.stopped() && !handler.failed()) {
usleep(100);
}
ASSERT_TRUE(handler.failed());
ASSERT_EQ(0, handler.idle_times());
ASSERT_EQ(N, handler._expected_next_value);
}
TEST_F(StreamingRpcTest, idle_timeout) {
HandlerControl hc;
hc.block = true;
OrderedInputHandler handler(&hc);
brpc::StreamOptions opt;
opt.handler = &handler;
opt.idle_timeout_ms = 2;
const int N = 10000;
opt.max_buf_size = sizeof(uint32_t) * N;
brpc::Server server;
MyServiceWithStream service(opt);
ASSERT_EQ(0, server.AddService(&service, brpc::SERVER_DOESNT_OWN_SERVICE));
ASSERT_EQ(0, server.Start(9007, NULL));
brpc::Channel channel;
ASSERT_EQ(0, channel.Init("127.0.0.1:9007", NULL));
brpc::Controller cntl;
brpc::StreamId request_stream;
brpc::StreamOptions request_stream_options;
request_stream_options.max_buf_size = sizeof(uint32_t) * N;
ASSERT_EQ(0, StreamCreate(&request_stream, cntl, &request_stream_options));
brpc::ScopedStream stream_guard(request_stream);
test::EchoService_Stub stub(&channel);
stub.Echo(&cntl, &request, &response, NULL);
ASSERT_FALSE(cntl.Failed()) << cntl.ErrorText() << " request_stream=" << request_stream;
usleep(10 * 1000 + 800);
ASSERT_EQ(0, brpc::StreamClose(request_stream));
while (!handler.stopped()) {
usleep(100);
}
ASSERT_FALSE(handler.failed());
// ASSERT_TRUE(handler.idle_times() >= 4 && handler.idle_times() <= 6)
// << handler.idle_times();
ASSERT_EQ(0, handler._expected_next_value);
}
class PingPongHandler : public brpc::StreamInputHandler {
public:
int on_received_messages(brpc::StreamId id,
butil::IOBuf *const messages[],
size_t size) override {
if (size != 1) {
LOG(INFO) << "size=" << size;
_error = true;
return 0;
}
for (size_t i = 0; i < size; ++i) {
CHECK(messages[i]->length() == sizeof(int));
int network = 0;
messages[i]->cutn(&network, sizeof(int));
if ((int)ntohl(network) != _expected_next_value) {
_error = true;
}
int send_back = ntohl(network) + 1;
_expected_next_value = send_back + 1;
butil::IOBuf out;
network = htonl(send_back);
out.append(&network, sizeof(network));
// don't care the return value
brpc::StreamWrite(id, out);
}
return 0;
}
void on_idle_timeout(brpc::StreamId /*id*/) override {
++_idle_times;
}
void on_closed(brpc::StreamId /*id*/) override {
ASSERT_FALSE(_stopped);
_stopped = true;
}
void on_failed(brpc::StreamId id, int error_code,
const std::string& /*error_text*/) override {
ASSERT_FALSE(_failed);
ASSERT_NE(0, error_code);
_failed = true;
}
bool error() const { return _error; }
bool failed() const { return _failed; }
bool stopped() const { return _stopped; }
int idle_times() const { return _idle_times; }
private:
int _expected_next_value{0};
bool _error{false};
bool _failed{false};
bool _stopped{false};
int _idle_times{0};
};
TEST_F(StreamingRpcTest, ping_pong) {
PingPongHandler resh;
brpc::StreamOptions opt;
opt.handler = &resh;
const int N = 10000;
opt.max_buf_size = sizeof(uint32_t) * N;
brpc::Server server;
MyServiceWithStream service(opt);
ASSERT_EQ(0, server.AddService(&service, brpc::SERVER_DOESNT_OWN_SERVICE));
ASSERT_EQ(0, server.Start(9007, NULL));
brpc::Channel channel;
ASSERT_EQ(0, channel.Init("127.0.0.1:9007", NULL));
brpc::Controller cntl;
brpc::StreamId request_stream;
brpc::StreamOptions request_stream_options;
PingPongHandler reqh;
reqh._expected_next_value = 1;
request_stream_options.handler = &reqh;
request_stream_options.max_buf_size = sizeof(uint32_t) * N;
ASSERT_EQ(0, StreamCreate(&request_stream, cntl, &request_stream_options));
brpc::ScopedStream stream_guard(request_stream);
test::EchoService_Stub stub(&channel);
stub.Echo(&cntl, &request, &response, NULL);
ASSERT_FALSE(cntl.Failed()) << cntl.ErrorText() << " request_stream=" << request_stream;
int send = 0;
butil::IOBuf out;
out.append(&send, sizeof(send));
ASSERT_EQ(0, brpc::StreamWrite(request_stream, out));
usleep(10 * 1000);
ASSERT_EQ(0, brpc::StreamClose(request_stream));
while (!resh.stopped() || !reqh.stopped()) {
usleep(100);
}
ASSERT_FALSE(resh.error());
ASSERT_FALSE(reqh.error());
ASSERT_EQ(0, resh.idle_times());
ASSERT_EQ(0, reqh.idle_times());
}
class SendNAfterAcceptStream : public AfterAcceptStream {
public:
explicit SendNAfterAcceptStream(int n)
: _n(n) {}
void action(brpc::StreamId s) {
for (int i = 0; i < _n; ++i) {
int network = htonl(i);
butil::IOBuf out;
out.append(&network, sizeof(network));
ASSERT_EQ(0, brpc::StreamWrite(s, out)) << "i=" << i;
}
}
private:
int _n;
};
TEST_F(StreamingRpcTest, server_send_data_before_run_done) {
const int N = 10000;
SendNAfterAcceptStream after_accept(N);
brpc::StreamOptions opt;
opt.max_buf_size = -1;
brpc::Server server;
MyServiceWithStream service(opt, &after_accept);
ASSERT_EQ(0, server.AddService(&service, brpc::SERVER_DOESNT_OWN_SERVICE));
ASSERT_EQ(0, server.Start(9007, NULL));
brpc::Channel channel;
ASSERT_EQ(0, channel.Init("127.0.0.1:9007", NULL));
OrderedInputHandler handler;
brpc::StreamOptions request_stream_options;
request_stream_options.handler = &handler;
brpc::StreamId request_stream;
brpc::Controller cntl;
ASSERT_EQ(0, StreamCreate(&request_stream, cntl, &request_stream_options));
brpc::ScopedStream stream_guard(request_stream);
test::EchoService_Stub stub(&channel);
stub.Echo(&cntl, &request, &response, NULL);
ASSERT_FALSE(cntl.Failed()) << cntl.ErrorText() << " request_stream=" << request_stream;
// wait flushing all the pending messages
while (handler._expected_next_value != N) {
usleep(100);
}
ASSERT_EQ(0, brpc::StreamClose(request_stream));
while (!handler.stopped()) {
usleep(100);
}
ASSERT_FALSE(handler.failed());
ASSERT_EQ(0, handler.idle_times());
}
TEST_F(StreamingRpcTest, segment_stream_data_automatically) {
GFLAGS_NAMESPACE::SetCommandLineOption("stream_write_max_segment_size", "1");
OrderedInputHandler handler;
brpc::StreamOptions opt;
opt.handler = &handler;
opt.messages_in_batch = 100;
brpc::Server server;
MyServiceWithStream service(opt);
ASSERT_EQ(0, server.AddService(&service, brpc::SERVER_DOESNT_OWN_SERVICE));
ASSERT_EQ(0, server.Start(9007, NULL));
brpc::Channel channel;
ASSERT_EQ(0, channel.Init("127.0.0.1:9007", NULL));
brpc::Controller cntl;
brpc::StreamId request_stream;
brpc::StreamOptions request_stream_options;
ASSERT_EQ(0, StreamCreate(&request_stream, cntl, &request_stream_options));
brpc::ScopedStream stream_guard(request_stream);
test::EchoService_Stub stub(&channel);
stub.Echo(&cntl, &request, &response, NULL);
ASSERT_FALSE(cntl.Failed()) << cntl.ErrorText() << " request_stream=" << request_stream;
const int N = 1000;
for (int i = 0; i < N; ++i) {
int network = htonl(i);
butil::IOBuf out;
out.append(&network, sizeof(network));
ASSERT_EQ(0, brpc::StreamWrite(request_stream, out)) << "i=" << i;
}
brpc::SocketUniquePtr host_socket_ptr;
{
brpc::SocketUniquePtr ptr;
ASSERT_EQ(0, brpc::Socket::Address(request_stream, &ptr));
brpc::Stream *s = (brpc::Stream *)ptr->conn();
ASSERT_TRUE(s->_host_socket != NULL);
s->_host_socket->ReAddress(&host_socket_ptr);
}
ASSERT_EQ(0, brpc::StreamClose(request_stream));
server.Stop(0);
server.Join();
while (!handler.stopped()) {
usleep(100);
}
const int64_t now_ms = butil::cpuwide_time_ms();
host_socket_ptr->UpdateStatsEverySecond(now_ms);
brpc::SocketStat stat;
host_socket_ptr->GetStat(&stat);
ASSERT_LT(N * sizeof(N), stat.out_num_messages_m);
ASSERT_FALSE(handler.failed());
ASSERT_EQ(0, handler.idle_times());
ASSERT_EQ(N, handler._expected_next_value);
GFLAGS_NAMESPACE::SetCommandLineOption("stream_write_max_segment_size", "536870912");
}