chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,123 @@
|
||||
# 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.
|
||||
|
||||
cmake_minimum_required(VERSION 3.16...3.28)
|
||||
project(http_c++ C CXX)
|
||||
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/../cmake/BrpcExample.cmake)
|
||||
|
||||
option(LINK_SO "Whether examples are linked dynamically" OFF)
|
||||
|
||||
execute_process(
|
||||
COMMAND bash -c "find ${PROJECT_SOURCE_DIR}/../.. -type d -regex \".*output/include$\" | head -n1 | xargs dirname | tr -d '\n'"
|
||||
OUTPUT_VARIABLE OUTPUT_PATH
|
||||
)
|
||||
|
||||
if(OUTPUT_PATH)
|
||||
list(PREPEND CMAKE_PREFIX_PATH ${OUTPUT_PATH})
|
||||
endif()
|
||||
|
||||
find_package(Threads REQUIRED)
|
||||
find_package(Protobuf REQUIRED)
|
||||
protobuf_generate_cpp(PROTO_SRC PROTO_HEADER http.proto)
|
||||
|
||||
# Search for libthrift* by best effort. If it is not found and brpc is
|
||||
# compiled with thrift protocol enabled, a link error would be reported.
|
||||
find_library(THRIFT_LIB NAMES thrift)
|
||||
if (NOT THRIFT_LIB)
|
||||
set(THRIFT_LIB "")
|
||||
endif()
|
||||
|
||||
find_path(GPERFTOOLS_INCLUDE_DIR NAMES gperftools/heap-profiler.h)
|
||||
find_library(GPERFTOOLS_LIBRARIES NAMES tcmalloc_and_profiler)
|
||||
|
||||
find_path(BRPC_INCLUDE_PATH NAMES brpc/server.h)
|
||||
if(LINK_SO)
|
||||
find_library(BRPC_LIB NAMES brpc)
|
||||
else()
|
||||
find_library(BRPC_LIB NAMES libbrpc.a brpc)
|
||||
endif()
|
||||
if((NOT BRPC_INCLUDE_PATH) OR (NOT BRPC_LIB))
|
||||
message(FATAL_ERROR "Fail to find brpc")
|
||||
endif()
|
||||
|
||||
find_path(GFLAGS_INCLUDE_PATH gflags/gflags.h)
|
||||
find_library(GFLAGS_LIBRARY NAMES gflags libgflags)
|
||||
if((NOT GFLAGS_INCLUDE_PATH) OR (NOT GFLAGS_LIBRARY))
|
||||
message(FATAL_ERROR "Fail to find gflags")
|
||||
endif()
|
||||
|
||||
set(BRPC_EXAMPLE_ENABLE_CPU_PROFILER ON)
|
||||
|
||||
find_path(LEVELDB_INCLUDE_PATH NAMES leveldb/db.h)
|
||||
find_library(LEVELDB_LIB NAMES leveldb)
|
||||
if ((NOT LEVELDB_INCLUDE_PATH) OR (NOT LEVELDB_LIB))
|
||||
message(FATAL_ERROR "Fail to find leveldb")
|
||||
endif()
|
||||
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "Darwin" AND NOT OPENSSL_ROOT_DIR)
|
||||
set(OPENSSL_ROOT_DIR
|
||||
"/usr/local/opt/openssl" # Homebrew installed OpenSSL
|
||||
)
|
||||
endif()
|
||||
|
||||
find_package(OpenSSL REQUIRED)
|
||||
|
||||
set(DYNAMIC_LIB
|
||||
Threads::Threads
|
||||
${GFLAGS_LIBRARY}
|
||||
${PROTOBUF_LIBRARIES}
|
||||
${LEVELDB_LIB}
|
||||
${OPENSSL_CRYPTO_LIBRARY}
|
||||
${OPENSSL_SSL_LIBRARY}
|
||||
${THRIFT_LIB}
|
||||
dl
|
||||
)
|
||||
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
|
||||
set(DYNAMIC_LIB ${DYNAMIC_LIB}
|
||||
pthread
|
||||
"-framework CoreFoundation"
|
||||
"-framework CoreGraphics"
|
||||
"-framework CoreData"
|
||||
"-framework CoreText"
|
||||
"-framework Security"
|
||||
"-framework Foundation"
|
||||
"-Wl,-U,_MallocExtension_ReleaseFreeMemory"
|
||||
"-Wl,-U,_ProfilerStart"
|
||||
"-Wl,-U,_ProfilerStop"
|
||||
"-Wl,-U,__Z13GetStackTracePPvii"
|
||||
"-Wl,-U,_mallctl"
|
||||
"-Wl,-U,_malloc_stats_print"
|
||||
)
|
||||
endif()
|
||||
|
||||
add_executable(http_client http_client.cpp)
|
||||
brpc_example_configure_target(http_client)
|
||||
add_executable(http_server http_server.cpp ${PROTO_SRC} ${PROTO_HEADER})
|
||||
brpc_example_configure_target(http_server)
|
||||
add_executable(benchmark_http benchmark_http.cpp)
|
||||
brpc_example_configure_target(benchmark_http)
|
||||
|
||||
target_link_libraries(http_server PRIVATE ${BRPC_LIB} ${DYNAMIC_LIB} ${GPERFTOOLS_LIBRARIES})
|
||||
target_link_libraries(http_client PRIVATE ${BRPC_LIB} ${DYNAMIC_LIB} ${GPERFTOOLS_LIBRARIES})
|
||||
target_link_libraries(benchmark_http PRIVATE ${BRPC_LIB} ${DYNAMIC_LIB} ${GPERFTOOLS_LIBRARIES})
|
||||
|
||||
file(COPY ${PROJECT_SOURCE_DIR}/key.pem
|
||||
DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
|
||||
file(COPY ${PROJECT_SOURCE_DIR}/cert.pem
|
||||
DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
|
||||
@@ -0,0 +1,135 @@
|
||||
// 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.
|
||||
|
||||
// Benchmark http-server by multiple threads.
|
||||
|
||||
#include <gflags/gflags.h>
|
||||
#include <bthread/bthread.h>
|
||||
#include <butil/logging.h>
|
||||
#include <brpc/channel.h>
|
||||
#include <brpc/server.h>
|
||||
#include <bvar/bvar.h>
|
||||
|
||||
DEFINE_string(data, "", "POST this data to the http server");
|
||||
DEFINE_int32(thread_num, 50, "Number of threads to send requests");
|
||||
DEFINE_bool(use_bthread, false, "Use bthread to send requests");
|
||||
DEFINE_string(connection_type, "", "Connection type. Available values: single, pooled, short");
|
||||
DEFINE_string(url, "0.0.0.0:8010/HttpService/Echo", "url of server");
|
||||
DEFINE_string(load_balancer, "", "The algorithm for load balancing");
|
||||
DEFINE_int32(timeout_ms, 100, "RPC timeout in milliseconds");
|
||||
DEFINE_int32(max_retry, 3, "Max retries(not including the first RPC)");
|
||||
DEFINE_bool(dont_fail, false, "Print fatal when some call failed");
|
||||
DEFINE_int32(dummy_port, -1, "Launch dummy server at this port");
|
||||
DEFINE_string(protocol, "http", "Client-side protocol");
|
||||
|
||||
bvar::LatencyRecorder g_latency_recorder("client");
|
||||
|
||||
static void* sender(void* arg) {
|
||||
brpc::Channel* channel = static_cast<brpc::Channel*>(arg);
|
||||
|
||||
while (!brpc::IsAskedToQuit()) {
|
||||
// We will receive response synchronously, safe to put variables
|
||||
// on stack.
|
||||
brpc::Controller cntl;
|
||||
|
||||
cntl.set_timeout_ms(FLAGS_timeout_ms/*milliseconds*/);
|
||||
cntl.set_max_retry(FLAGS_max_retry);
|
||||
cntl.http_request().uri() = FLAGS_url;
|
||||
if (!FLAGS_data.empty()) {
|
||||
cntl.http_request().set_method(brpc::HTTP_METHOD_POST);
|
||||
cntl.request_attachment().append(FLAGS_data);
|
||||
}
|
||||
|
||||
// Because `done'(last parameter) is NULL, this function waits until
|
||||
// the response comes back or error occurs(including timedout).
|
||||
channel->CallMethod(NULL, &cntl, NULL, NULL, NULL);
|
||||
if (!cntl.Failed()) {
|
||||
g_latency_recorder << cntl.latency_us();
|
||||
} else {
|
||||
CHECK(brpc::IsAskedToQuit() || !FLAGS_dont_fail)
|
||||
<< "error=" << cntl.ErrorText() << " latency=" << cntl.latency_us();
|
||||
// We can't connect to the server, sleep a while. Notice that this
|
||||
// is a specific sleeping to prevent this thread from spinning too
|
||||
// fast. You should continue the business logic in a production
|
||||
// server rather than sleeping.
|
||||
bthread_usleep(100000);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
// Parse gflags. We recommend you to use gflags as well.
|
||||
GFLAGS_NAMESPACE::ParseCommandLineFlags(&argc, &argv, true);
|
||||
|
||||
// A Channel represents a communication line to a Server. Notice that
|
||||
// Channel is thread-safe and can be shared by all threads in your program.
|
||||
brpc::Channel channel;
|
||||
brpc::ChannelOptions options;
|
||||
options.protocol = FLAGS_protocol;
|
||||
options.connection_type = FLAGS_connection_type;
|
||||
|
||||
// Initialize the channel, NULL means using default options.
|
||||
// options, see `brpc/channel.h'.
|
||||
if (channel.Init(FLAGS_url.c_str(), FLAGS_load_balancer.c_str(), &options) != 0) {
|
||||
LOG(ERROR) << "Fail to initialize channel";
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::vector<bthread_t> bids;
|
||||
std::vector<pthread_t> pids;
|
||||
if (!FLAGS_use_bthread) {
|
||||
pids.resize(FLAGS_thread_num);
|
||||
for (int i = 0; i < FLAGS_thread_num; ++i) {
|
||||
if (pthread_create(&pids[i], NULL, sender, &channel) != 0) {
|
||||
LOG(ERROR) << "Fail to create pthread";
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
bids.resize(FLAGS_thread_num);
|
||||
for (int i = 0; i < FLAGS_thread_num; ++i) {
|
||||
if (bthread_start_background(
|
||||
&bids[i], NULL, sender, &channel) != 0) {
|
||||
LOG(ERROR) << "Fail to create bthread";
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (FLAGS_dummy_port >= 0) {
|
||||
brpc::StartDummyServerAt(FLAGS_dummy_port);
|
||||
}
|
||||
|
||||
while (!brpc::IsAskedToQuit()) {
|
||||
sleep(1);
|
||||
LOG(INFO) << "Sending " << FLAGS_protocol << " requests at qps="
|
||||
<< g_latency_recorder.qps(1)
|
||||
<< " latency=" << g_latency_recorder.latency(1);
|
||||
}
|
||||
|
||||
LOG(INFO) << "benchmark_http is going to quit";
|
||||
for (int i = 0; i < FLAGS_thread_num; ++i) {
|
||||
if (!FLAGS_use_bthread) {
|
||||
pthread_join(pids[i], NULL);
|
||||
} else {
|
||||
bthread_join(bids[i], NULL);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIEUTCCAzmgAwIBAgIBADANBgkqhkiG9w0BAQQFADB9MQswCQYDVQQGEwJDTjER
|
||||
MA8GA1UECBMIU2hhbmdoYWkxETAPBgNVBAcTCFNoYW5naGFpMQ4wDAYDVQQKEwVC
|
||||
YWlkdTEMMAoGA1UECxMDSU5GMQwwCgYDVQQDEwNTQVQxHDAaBgkqhkiG9w0BCQEW
|
||||
DXNhdEBiYWlkdS5jb20wHhcNMTUwNzE2MDMxOTUxWhcNMTgwNTA1MDMxOTUxWjB9
|
||||
MQswCQYDVQQGEwJDTjERMA8GA1UECBMIU2hhbmdoYWkxETAPBgNVBAcTCFNoYW5n
|
||||
aGFpMQ4wDAYDVQQKEwVCYWlkdTEMMAoGA1UECxMDSU5GMQwwCgYDVQQDEwNTQVQx
|
||||
HDAaBgkqhkiG9w0BCQEWDXNhdEBiYWlkdS5jb20wggEiMA0GCSqGSIb3DQEBAQUA
|
||||
A4IBDwAwggEKAoIBAQCqdyAeHY39tqY1RYVbfpqZjZlJDtZb04znxjgQrX+mKmLb
|
||||
mwvXgJojlfn2Qcgp4NKYFqDFb9tU/Gbb436dRvkHyWOz0RPMspR0TTRU1NIY8wRy
|
||||
0A1LOCgLHsbRJHqktGjylejALdgsspFWyDY9bEfb4oWsnKGzJqcvIDXrPmMOOY4o
|
||||
pbA9SufSzwRZN7Yzc5jAedpaF9SK78RQXtvV0+JfCUwBsBWPKevRFFUrN7rQBYjP
|
||||
cgV/HgDuquPrqnESVSYyfEBKZba6cmNb+xzO3cB1brPTtobSXh+0o/0CtRA+2m63
|
||||
ODexxCLntgkPm42IYCJLM15xTatcfVX/3LHQ31DrAgMBAAGjgdswgdgwHQYDVR0O
|
||||
BBYEFGcd7lA//bSAoSC/NbWRx/H+O1zpMIGoBgNVHSMEgaAwgZ2AFGcd7lA//bSA
|
||||
oSC/NbWRx/H+O1zpoYGBpH8wfTELMAkGA1UEBhMCQ04xETAPBgNVBAgTCFNoYW5n
|
||||
aGFpMREwDwYDVQQHEwhTaGFuZ2hhaTEOMAwGA1UEChMFQmFpZHUxDDAKBgNVBAsT
|
||||
A0lORjEMMAoGA1UEAxMDU0FUMRwwGgYJKoZIhvcNAQkBFg1zYXRAYmFpZHUuY29t
|
||||
ggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADggEBAKfoCn8SpLk3uQyT
|
||||
X+oygcRWfTeJtN3D5J69NCMJ7wB+QPfpEBPwiqMgdbp4bRJ98H7x5UQsHT+EDOT/
|
||||
9OmipomHInFY4W1ew11zNKwuENeRrnZwTcCiVLZsxZsAU41ZeI5Yq+2WdtxnePCR
|
||||
VL1/NjKOq+WoRdb2nLSNDWgYMkLRVlt32hyzryyrBbmaxUl8BxnPqUiWduMwsZUz
|
||||
HNpXkoa1xTSd+En1SHYWfMg8BOVuV0I0/fjUUG9AXVqYpuogfbjAvibVNWAmxOfo
|
||||
fOjCPCGoJC1ET3AxYkgXGwioobz0pK/13k2pV+wu7W4g+6iTfz+hwZbPsUk2a/5I
|
||||
f6vXFB0=
|
||||
-----END CERTIFICATE-----
|
||||
@@ -0,0 +1,43 @@
|
||||
// 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.
|
||||
|
||||
syntax="proto2";
|
||||
package example;
|
||||
|
||||
option cc_generic_services = true;
|
||||
|
||||
message HttpRequest {};
|
||||
message HttpResponse {};
|
||||
|
||||
service HttpService {
|
||||
rpc Echo(HttpRequest) returns (HttpResponse);
|
||||
rpc EchoProtobuf(HttpRequest) returns (HttpResponse);
|
||||
};
|
||||
|
||||
service FileService {
|
||||
rpc default_method(HttpRequest) returns (HttpResponse);
|
||||
};
|
||||
|
||||
service QueueService {
|
||||
rpc start(HttpRequest) returns (HttpResponse);
|
||||
rpc stop(HttpRequest) returns (HttpResponse);
|
||||
rpc getstats(HttpRequest) returns (HttpResponse);
|
||||
};
|
||||
|
||||
service HttpSSEService {
|
||||
rpc stream(HttpRequest) returns (HttpResponse);
|
||||
};
|
||||
@@ -0,0 +1,86 @@
|
||||
// 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.
|
||||
|
||||
// - Access pb services via HTTP
|
||||
// ./http_client http://www.foo.com:8765/EchoService/Echo -d '{"message":"hello"}'
|
||||
// - Access builtin services
|
||||
// ./http_client http://www.foo.com:8765/vars/rpc_server*
|
||||
// - Access www.foo.com
|
||||
// ./http_client www.foo.com
|
||||
|
||||
#include <gflags/gflags.h>
|
||||
#include <butil/logging.h>
|
||||
#include <brpc/channel.h>
|
||||
|
||||
DEFINE_string(d, "", "POST this data to the http server");
|
||||
DEFINE_string(load_balancer, "", "The algorithm for load balancing");
|
||||
DEFINE_int32(timeout_ms, 2000, "RPC timeout in milliseconds");
|
||||
DEFINE_int32(max_retry, 3, "Max retries(not including the first RPC)");
|
||||
DEFINE_string(protocol, "http", "Client-side protocol");
|
||||
|
||||
namespace brpc {
|
||||
DECLARE_bool(http_verbose);
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
// Parse gflags. We recommend you to use gflags as well.
|
||||
GFLAGS_NAMESPACE::ParseCommandLineFlags(&argc, &argv, true);
|
||||
|
||||
if (argc != 2) {
|
||||
LOG(ERROR) << "Usage: ./http_client \"http(s)://www.foo.com\"";
|
||||
return -1;
|
||||
}
|
||||
char* url = argv[1];
|
||||
|
||||
// A Channel represents a communication line to a Server. Notice that
|
||||
// Channel is thread-safe and can be shared by all threads in your program.
|
||||
brpc::Channel channel;
|
||||
brpc::ChannelOptions options;
|
||||
options.protocol = FLAGS_protocol;
|
||||
options.timeout_ms = FLAGS_timeout_ms/*milliseconds*/;
|
||||
options.max_retry = FLAGS_max_retry;
|
||||
|
||||
// Initialize the channel, NULL means using default options.
|
||||
// options, see `brpc/channel.h'.
|
||||
if (channel.Init(url, FLAGS_load_balancer.c_str(), &options) != 0) {
|
||||
LOG(ERROR) << "Fail to initialize channel";
|
||||
return -1;
|
||||
}
|
||||
|
||||
// We will receive response synchronously, safe to put variables
|
||||
// on stack.
|
||||
brpc::Controller cntl;
|
||||
|
||||
cntl.http_request().uri() = url;
|
||||
if (!FLAGS_d.empty()) {
|
||||
cntl.http_request().set_method(brpc::HTTP_METHOD_POST);
|
||||
cntl.request_attachment().append(FLAGS_d);
|
||||
}
|
||||
|
||||
// Because `done'(last parameter) is NULL, this function waits until
|
||||
// the response comes back or error occurs(including timedout).
|
||||
channel.CallMethod(NULL, &cntl, NULL, NULL, NULL);
|
||||
if (cntl.Failed()) {
|
||||
std::cerr << cntl.ErrorText() << std::endl;
|
||||
return -1;
|
||||
}
|
||||
// If -http_verbose is on, brpc already prints the response to stderr.
|
||||
if (!brpc::FLAGS_http_verbose) {
|
||||
std::cout << cntl.response_attachment() << std::endl;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,279 @@
|
||||
// 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.
|
||||
|
||||
// A server to receive HttpRequest and send back HttpResponse.
|
||||
|
||||
#include <gflags/gflags.h>
|
||||
#include <butil/logging.h>
|
||||
#include <brpc/server.h>
|
||||
#include <brpc/restful.h>
|
||||
#include <json2pb/pb_to_json.h>
|
||||
#include "http.pb.h"
|
||||
|
||||
DEFINE_int32(port, 8010, "TCP Port of this server");
|
||||
DEFINE_int32(idle_timeout_s, -1, "Connection will be closed if there is no "
|
||||
"read/write operations during the last `idle_timeout_s'");
|
||||
|
||||
DEFINE_string(certificate, "cert.pem", "Certificate file path to enable SSL");
|
||||
DEFINE_string(private_key, "key.pem", "Private key file path to enable SSL");
|
||||
DEFINE_string(ciphers, "", "Cipher suite used for SSL connections");
|
||||
|
||||
namespace example {
|
||||
|
||||
// Service with static path.
|
||||
class HttpServiceImpl : public HttpService {
|
||||
public:
|
||||
HttpServiceImpl() {}
|
||||
virtual ~HttpServiceImpl() {}
|
||||
void Echo(google::protobuf::RpcController* cntl_base,
|
||||
const HttpRequest*,
|
||||
HttpResponse*,
|
||||
google::protobuf::Closure* done) {
|
||||
// This object helps you to call done->Run() in RAII style. If you need
|
||||
// to process the request asynchronously, pass done_guard.release().
|
||||
brpc::ClosureGuard done_guard(done);
|
||||
|
||||
brpc::Controller* cntl =
|
||||
static_cast<brpc::Controller*>(cntl_base);
|
||||
|
||||
// optional: set a callback function which is called after response is sent
|
||||
// and before cntl/req/res is destructed.
|
||||
cntl->set_after_rpc_resp_fn(std::bind(&HttpServiceImpl::CallAfterRpc,
|
||||
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
|
||||
|
||||
// Fill response.
|
||||
cntl->http_response().set_content_type("text/plain");
|
||||
butil::IOBufBuilder os;
|
||||
os << "queries:";
|
||||
for (brpc::URI::QueryIterator it = cntl->http_request().uri().QueryBegin();
|
||||
it != cntl->http_request().uri().QueryEnd(); ++it) {
|
||||
os << ' ' << it->first << '=' << it->second;
|
||||
}
|
||||
os << "\nbody: " << cntl->request_attachment() << '\n';
|
||||
os.move_to(cntl->response_attachment());
|
||||
}
|
||||
|
||||
// optional
|
||||
static void CallAfterRpc(brpc::Controller* cntl,
|
||||
const google::protobuf::Message* req,
|
||||
const google::protobuf::Message* res) {
|
||||
// at this time res is already sent to client, but cntl/req/res is not destructed
|
||||
std::string req_str;
|
||||
std::string res_str;
|
||||
json2pb::ProtoMessageToJson(*req, &req_str, NULL);
|
||||
json2pb::ProtoMessageToJson(*res, &res_str, NULL);
|
||||
LOG(INFO) << "req:" << req_str
|
||||
<< " res:" << res_str;
|
||||
}
|
||||
};
|
||||
|
||||
// Service with dynamic path.
|
||||
class FileServiceImpl : public FileService {
|
||||
public:
|
||||
FileServiceImpl() {}
|
||||
virtual ~FileServiceImpl() {}
|
||||
|
||||
struct Args {
|
||||
butil::intrusive_ptr<brpc::ProgressiveAttachment> pa;
|
||||
};
|
||||
|
||||
static void* SendLargeFile(void* raw_args) {
|
||||
std::unique_ptr<Args> args(static_cast<Args*>(raw_args));
|
||||
if (args->pa == NULL) {
|
||||
LOG(ERROR) << "ProgressiveAttachment is NULL";
|
||||
return NULL;
|
||||
}
|
||||
for (int i = 0; i < 100; ++i) {
|
||||
char buf[16];
|
||||
int len = snprintf(buf, sizeof(buf), "part_%d ", i);
|
||||
args->pa->Write(buf, len);
|
||||
|
||||
// sleep a while to send another part.
|
||||
bthread_usleep(10000);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void default_method(google::protobuf::RpcController* cntl_base,
|
||||
const HttpRequest*,
|
||||
HttpResponse*,
|
||||
google::protobuf::Closure* done) {
|
||||
brpc::ClosureGuard done_guard(done);
|
||||
brpc::Controller* cntl =
|
||||
static_cast<brpc::Controller*>(cntl_base);
|
||||
const std::string& filename = cntl->http_request().unresolved_path();
|
||||
if (filename == "largefile") {
|
||||
// Send the "largefile" with ProgressiveAttachment.
|
||||
std::unique_ptr<Args> args(new Args);
|
||||
args->pa = cntl->CreateProgressiveAttachment();
|
||||
bthread_t th;
|
||||
bthread_start_background(&th, NULL, SendLargeFile, args.release());
|
||||
} else {
|
||||
cntl->response_attachment().append("Getting file: ");
|
||||
cntl->response_attachment().append(filename);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Restful service. (The service implementation is exactly same with regular
|
||||
// services, the difference is that you need to pass a `restful_mappings'
|
||||
// when adding the service into server).
|
||||
class QueueServiceImpl : public example::QueueService {
|
||||
public:
|
||||
QueueServiceImpl() {}
|
||||
virtual ~QueueServiceImpl() {}
|
||||
void start(google::protobuf::RpcController* cntl_base,
|
||||
const HttpRequest*,
|
||||
HttpResponse*,
|
||||
google::protobuf::Closure* done) {
|
||||
brpc::ClosureGuard done_guard(done);
|
||||
brpc::Controller* cntl =
|
||||
static_cast<brpc::Controller*>(cntl_base);
|
||||
cntl->response_attachment().append("queue started");
|
||||
}
|
||||
void stop(google::protobuf::RpcController* cntl_base,
|
||||
const HttpRequest*,
|
||||
HttpResponse*,
|
||||
google::protobuf::Closure* done) {
|
||||
brpc::ClosureGuard done_guard(done);
|
||||
brpc::Controller* cntl =
|
||||
static_cast<brpc::Controller*>(cntl_base);
|
||||
cntl->response_attachment().append("queue stopped");
|
||||
}
|
||||
void getstats(google::protobuf::RpcController* cntl_base,
|
||||
const HttpRequest*,
|
||||
HttpResponse*,
|
||||
google::protobuf::Closure* done) {
|
||||
brpc::ClosureGuard done_guard(done);
|
||||
brpc::Controller* cntl =
|
||||
static_cast<brpc::Controller*>(cntl_base);
|
||||
const std::string& unresolved_path = cntl->http_request().unresolved_path();
|
||||
if (unresolved_path.empty()) {
|
||||
cntl->response_attachment().append("Require a name after /stats");
|
||||
} else {
|
||||
cntl->response_attachment().append("Get stats: ");
|
||||
cntl->response_attachment().append(unresolved_path);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class HttpSSEServiceImpl : public HttpSSEService {
|
||||
public:
|
||||
HttpSSEServiceImpl() {}
|
||||
virtual ~HttpSSEServiceImpl() {}
|
||||
|
||||
struct PredictJobArgs {
|
||||
std::vector<uint32_t> input_ids;
|
||||
butil::intrusive_ptr<brpc::ProgressiveAttachment> pa;
|
||||
};
|
||||
|
||||
static void* Predict(void* raw_args) {
|
||||
std::unique_ptr<PredictJobArgs> args(static_cast<PredictJobArgs*>(raw_args));
|
||||
if (args->pa == NULL) {
|
||||
LOG(ERROR) << "ProgressiveAttachment is NULL";
|
||||
return NULL;
|
||||
}
|
||||
for (int i = 0; i < 100; ++i) {
|
||||
char buf[48];
|
||||
int len = snprintf(buf, sizeof(buf), "event: foo\ndata: Hello, world! (%d)\n\n", i);
|
||||
args->pa->Write(buf, len);
|
||||
|
||||
// sleep a while to send another part.
|
||||
bthread_usleep(10000 * 10);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void stream(google::protobuf::RpcController* cntl_base,
|
||||
const HttpRequest*,
|
||||
HttpResponse*,
|
||||
google::protobuf::Closure* done) {
|
||||
brpc::ClosureGuard done_guard(done);
|
||||
brpc::Controller* cntl =
|
||||
static_cast<brpc::Controller*>(cntl_base);
|
||||
|
||||
// Send the first SSE response
|
||||
cntl->http_response().set_content_type("text/event-stream");
|
||||
cntl->http_response().set_status_code(200);
|
||||
cntl->http_response().SetHeader("Connection", "keep-alive");
|
||||
cntl->http_response().SetHeader("Cache-Control", "no-cache");
|
||||
|
||||
// Send the generated words with progressiveAttachment
|
||||
std::unique_ptr<PredictJobArgs> args(new PredictJobArgs);
|
||||
args->pa = cntl->CreateProgressiveAttachment();
|
||||
args->input_ids = {101, 102};
|
||||
bthread_t th;
|
||||
bthread_start_background(&th, NULL, Predict, args.release());
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace example
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
// Parse gflags. We recommend you to use gflags as well.
|
||||
GFLAGS_NAMESPACE::ParseCommandLineFlags(&argc, &argv, true);
|
||||
|
||||
// Generally you only need one Server.
|
||||
brpc::Server server;
|
||||
|
||||
example::HttpServiceImpl http_svc;
|
||||
example::FileServiceImpl file_svc;
|
||||
example::QueueServiceImpl queue_svc;
|
||||
example::HttpSSEServiceImpl sse_svc;
|
||||
|
||||
// Add services into server. Notice the second parameter, because the
|
||||
// service is put on stack, we don't want server to delete it, otherwise
|
||||
// use brpc::SERVER_OWNS_SERVICE.
|
||||
if (server.AddService(&http_svc,
|
||||
brpc::SERVER_DOESNT_OWN_SERVICE) != 0) {
|
||||
LOG(ERROR) << "Fail to add http_svc";
|
||||
return -1;
|
||||
}
|
||||
if (server.AddService(&file_svc,
|
||||
brpc::SERVER_DOESNT_OWN_SERVICE) != 0) {
|
||||
LOG(ERROR) << "Fail to add file_svc";
|
||||
return -1;
|
||||
}
|
||||
if (server.AddService(&queue_svc,
|
||||
brpc::SERVER_DOESNT_OWN_SERVICE,
|
||||
"/v1/queue/start => start,"
|
||||
"/v1/queue/stop => stop,"
|
||||
"/v1/queue/stats/* => getstats") != 0) {
|
||||
LOG(ERROR) << "Fail to add queue_svc";
|
||||
return -1;
|
||||
}
|
||||
if (server.AddService(&sse_svc,
|
||||
brpc::SERVER_DOESNT_OWN_SERVICE) != 0) {
|
||||
LOG(ERROR) << "Fail to add sse_svc";
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Start the server.
|
||||
brpc::ServerOptions options;
|
||||
options.idle_timeout_sec = FLAGS_idle_timeout_s;
|
||||
options.mutable_ssl_options()->default_cert.certificate = FLAGS_certificate;
|
||||
options.mutable_ssl_options()->default_cert.private_key = FLAGS_private_key;
|
||||
options.mutable_ssl_options()->ciphers = FLAGS_ciphers;
|
||||
if (server.Start(FLAGS_port, &options) != 0) {
|
||||
LOG(ERROR) << "Fail to start HttpServer";
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Wait until Ctrl-C is pressed, then Stop() and Join() the server.
|
||||
server.RunUntilAskedToQuit();
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEogIBAAKCAQEAqncgHh2N/bamNUWFW36amY2ZSQ7WW9OM58Y4EK1/pipi25sL
|
||||
14CaI5X59kHIKeDSmBagxW/bVPxm2+N+nUb5B8ljs9ETzLKUdE00VNTSGPMEctAN
|
||||
SzgoCx7G0SR6pLRo8pXowC3YLLKRVsg2PWxH2+KFrJyhsyanLyA16z5jDjmOKKWw
|
||||
PUrn0s8EWTe2M3OYwHnaWhfUiu/EUF7b1dPiXwlMAbAVjynr0RRVKze60AWIz3IF
|
||||
fx4A7qrj66pxElUmMnxASmW2unJjW/sczt3AdW6z07aG0l4ftKP9ArUQPtputzg3
|
||||
scQi57YJD5uNiGAiSzNecU2rXH1V/9yx0N9Q6wIDAQABAoIBADN3khflnnhKzDXr
|
||||
To9IU08nRG+dbjT9U16rJ0RJze+SfpSFZHblWiSCZJzoUZHrUkofEt1pn1QyfK/J
|
||||
KPI9enTSZirlZk/4XwAaS0GNm/1yahZsIIdkZhqtaSO+GtVdrw4HGuXjMZCVPXJx
|
||||
MocrCSsnYmqyQ9P+SJ3e4Mis5mVllwDiUVlnTIamSSt16qkPdamLSJrxvI4LirQK
|
||||
9MZWNLoDFpRU1MJxQ/QzrEC3ONTq4j++AfbGzYTmDDtLeM8OSH5o72YXZ2JkaA4c
|
||||
xCzHFT+NaJYxF7esn/ctzGg50LYl8IF2UQtzOkX2l3l/OktIB1w+jGV6ONb1EWx5
|
||||
4zkkzNkCgYEA2EXj7GMsyNE3OYdMw8zrqQKUMON2CNnD+mBseGlr22/bhXtzpqK8
|
||||
uNel8WF1ezOnVvNsU8pml/W/mKUu6KQt5JfaDzen3OKjzTABVlbJxwFhPvwAeaIA
|
||||
q/tmSKyqiCgOMbR7Cq4UEwGf2A9/RII4JEC0/aipRU5srF65OYPUOJcCgYEAycco
|
||||
DFVG6jUw9w68t/X4f7NT4IYP96hSAqLUPuVz2fWwXKLWEX8JiMI+Ue3PbMz6mPcs
|
||||
4vMu364u4R3IuzrrI+PRK9iTa/pahBP6eF6ZpbY1ObI8CVLTrqUS9p22rr9lBm8V
|
||||
EZA9hwcHLYt+PWzaKcsFpbP4+AeY7nBBbL9CAM0CgYAzuJsmeB1ItUgIuQOxu7sM
|
||||
AzLfcjZTLYkBwreOIGAL7XdJN9nTmw2ZAvGLhWwsF5FIaRSaAUiBxOKaJb7PIhxb
|
||||
k7kxdHTvjT/xHS7ksAK3VewkvO18KTMR7iBq9ugdgb7LQkc+qZzhYr0QVbxw7Ndy
|
||||
TAs8sm4wxe2VV13ilFVXZwKBgDfU6ZnwBr1Llo7l/wYQA4CiSDU6IzTt2DNuhrgY
|
||||
mWPX/cLEM+OHeUXkKYZV/S0n0rd8vWjWzUOLWOFlcmOMPAAkS36MYM5h6aXeOVIR
|
||||
KwaVUkjyrnYN+xC6EHM41JGp1/RdzECd3sh8A1pw3K92bS9fQ+LD18IZqBFh8lh6
|
||||
23KJAoGAe48SwAsaGvqRO61Taww/Wf+YpGc9lnVbCvNFGScYaycPMqaRBUBmz/U3
|
||||
QQgpQY8T7JIECbA8sf78SlAZ9x93r0UQ70RekV3WzKAQHfHK8nqTjd3T0+i4aySO
|
||||
yQpYYCgE24zYO6rQgwrhzI0S4rWe7izDDlg0RmLtQh7Xw+rlkAQ=
|
||||
-----END RSA PRIVATE KEY-----
|
||||
Reference in New Issue
Block a user