Migrate to protorpc (#598)

* refactor: migrate from grpc to protorpc

* fix

* fix

* fix

* cleanup

* Update mainwindow_grpc.cpp

* Update RPC.cpp

* fix

---------

Co-authored-by: parhelia512 <0011d3@gmail.com>
This commit is contained in:
Mahdi 2025-08-02 13:49:32 +03:30 committed by GitHub
parent 792dad2bc1
commit 2dde7dbb2e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
91 changed files with 7947 additions and 3085 deletions

View File

@ -72,7 +72,7 @@ jobs:
if: matrix.cross_os != 'public_res' if: matrix.cross_os != 'public_res'
run: | run: |
go install github.com/golang/protobuf/protoc-gen-go@latest go install github.com/golang/protobuf/protoc-gen-go@latest
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest go install github.com/chai2010/protorpc/protoc-gen-protorpc@latest
- name: Build golang parts - name: Build golang parts
if: steps.cache-common.outputs.cache-hit != 'true' if: steps.cache-common.outputs.cache-hit != 'true'
shell: bash shell: bash

97
3rdparty/protorpc/rpc_client.cc vendored Normal file
View File

@ -0,0 +1,97 @@
// Copyright 2013 <chaishushan{AT}gmail.com>. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#include "3rdparty/protorpc/rpc_client.h"
#include "3rdparty/protorpc/rpc_wire.h"
namespace protorpc {
Client::Client(const char* host, int port):
conn_(0), host_(host), port_(port), seq_(0) {
//
}
Client::~Client() {
Close();
}
const ::protorpc::Error Client::CallMethod(
const std::string& method,
const ::std::string* request,
::std::string* response
) {
if(!checkMothdValid(method, request, response)) {
return ::protorpc::Error::New(
std::string("protorpc.Client.CallMethod: Invalid method, method: ") + method
);
}
return callMethod(method, request, response);
}
// Close the connection
void Client::Close() {
conn_.Close();
}
// --------------------------------------------------------
const ::protorpc::Error Client::callMethod(
const std::string& method,
const ::std::string* request,
::std::string* response
) {
if(!conn_.IsValid()) {
if(!conn_.DialTCP(host_.c_str(), port_)) {
return ::protorpc::Error::New(
std::string("protorpc.Client.callMethod: DialTCP fail, ") +
std::string("host: ") + host_ + std::string(":") + std::to_string(static_cast<long long>(port_))
);
}
}
Error err;
uint64_t id = seq_++;
wire::ResponseHeader respHeader;
// send request
err = wire::SendRequest(&conn_, id, method, request);
if(!err.IsNil()) {
return err;
}
// recv response hdr
err = wire::RecvResponseHeader(&conn_, &respHeader);
if(!err.IsNil()) {
return err;
}
// recv response body
err = wire::RecvResponseBody(&conn_, &respHeader, response);
if(!err.IsNil()) {
return err;
}
if(respHeader.id != id) {
return Error::New("protorpc.Client.callMethod: unexpected call id.");
}
if(!respHeader.error.empty()) {
return Error::New(respHeader.error);
}
return Error::Nil();
}
// --------------------------------------------------------
bool Client::checkMothdValid(
const std::string& method,
const ::std::string* request,
::std::string* response
) const {
if(method.empty()) return false;
if(!request) return false;
if(!response) return false;
return true;
}
} // namespace protorpc

53
3rdparty/protorpc/rpc_client.h vendored Normal file
View File

@ -0,0 +1,53 @@
// Copyright 2013 <chaishushan{AT}gmail.com>. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#pragma once
#ifndef PROTORPC_CLIENT_H__
#define PROTORPC_CLIENT_H__
#include "3rdparty/protorpc/rpc_conn.h"
#include "3rdparty/protorpc/rpc_error.h"
#include <stdint.h>
#include <string>
namespace protorpc {
class Client {
public:
Client(const char* host, int port);
~Client();
const ::protorpc::Error CallMethod(
const std::string& method,
const ::std::string* request,
::std::string* response
);
// Close the connection
void Close();
private:
const ::protorpc::Error callMethod(
const std::string& method,
const ::std::string* request,
::std::string* response
);
bool checkMothdValid(
const std::string& method,
const ::std::string* request,
::std::string* response
) const;
std::string host_;
int port_;
Conn conn_;
uint64_t seq_;
};
} // namespace protorpc
#endif // PROTORPC_CLIENT_H__

98
3rdparty/protorpc/rpc_conn.cc vendored Normal file
View File

@ -0,0 +1,98 @@
// Copyright 2013 <chaishushan{AT}gmail.com>. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#include "3rdparty/protorpc/rpc_conn.h"
#if (defined(_WIN32) || defined(_WIN64))
# include "./rpc_conn_windows.cc"
#else
# include "./rpc_conn_posix.cc"
#endif
namespace protorpc {
// MaxVarintLenN is the maximum length of a varint-encoded N-bit integer.
static const int maxVarintLen16 = 3;
static const int maxVarintLen32 = 5;
static const int maxVarintLen64 = 10;
// PutUvarint encodes a uint64 into buf and returns the number of bytes written.
// If the buffer is too small, PutUvarint will panic.
static int putUvarint(uint8_t buf[], uint64_t x) {
auto i = 0;
while(x >= 0x80) {
buf[i] = uint8_t(x) | 0x80;
x >>= 7;
i++;
}
buf[i] = uint8_t(x);
return i + 1;
}
// ReadUvarint reads an encoded unsigned integer from r and returns it as a uint64.
bool Conn::ReadUvarint(uint64_t* rx) {
uint64_t x;
uint8_t s, b;
*rx = 0;
x = 0;
s = 0;
for(int i = 0; ; i++) {
if(!Read(&b, 1)) {
return false;
}
if(b < 0x80) {
if(i > 9 || i == 9 && b > 1){
printf("protorpc.Conn.ReadUvarint: varint overflows a 64-bit integer\n");
return false;
}
*rx = (x | uint64_t(b)<<s);
return true;
}
x |= (uint64_t(b&0x7f) << s);
s += 7;
}
printf("not reachable!\n");
return false;
}
// PutUvarint encodes a uint64 into buf and returns the number of bytes written.
// If the buffer is too small, PutUvarint will panic.
bool Conn::WriteUvarint(uint64_t x) {
uint8_t buf[maxVarintLen64];
int n = putUvarint(buf, x);
return Write(buf, n);
}
bool Conn::RecvFrame(::std::string* data) {
uint64_t size;
if(!ReadUvarint(&size)) {
return false;
}
if(size != 0) {
data->resize(size_t(size));
if(!Read((void*)data->data(), int(size))) {
data->clear();
return false;
}
}
return true;
}
bool Conn::SendFrame(const ::std::string* data) {
if(data == NULL) {
return WriteUvarint(uint64_t(0));
}
if(!WriteUvarint(uint64_t(data->size()))) {
return false;
}
if(!Write((void*)data->data(), data->size())) {
return false;
}
return true;
}
} // namespace protorpc

49
3rdparty/protorpc/rpc_conn.h vendored Normal file
View File

@ -0,0 +1,49 @@
// Copyright 2013 <chaishushan{AT}gmail.com>. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#pragma once
#ifndef PROTORPC_CONN_H__
#define PROTORPC_CONN_H__
#include <stdarg.h>
#include <stdint.h>
#include <string>
namespace protorpc {
// Initialize socket services
bool InitSocket();
// Stream-oriented network connection.
class Conn {
public:
Conn(int fd=0): sock_(fd) { InitSocket(); }
~Conn() {}
bool IsValid() const;
bool DialTCP(const char* host, int port);
bool ListenTCP(int port, int backlog=5);
void Close();
Conn* Accept();
bool Read(void* buf, int len);
bool Write(void* buf, int len);
bool ReadUvarint(uint64_t* x);
bool WriteUvarint(uint64_t x);
bool RecvFrame(::std::string* data);
bool SendFrame(const ::std::string* data);
private:
int sock_;
};
} // namespace protorpc
#endif // PROTORPC_CONN_H__

145
3rdparty/protorpc/rpc_conn_posix.cc vendored Normal file
View File

@ -0,0 +1,145 @@
// Copyright 2013 <chaishushan{AT}gmail.com>. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#include "3rdparty/protorpc/rpc_conn.h"
#include <string.h>
#include <errno.h>
#include <sys/time.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <fcntl.h>
#include <unistd.h>
#if defined(__APPLE__) && !defined(MSG_NOSIGNAL)
# define MSG_NOSIGNAL 0x4000
#endif
#ifndef NI_MAXSERV
# define NI_MAXSERV 32
#endif
namespace protorpc {
// [static]
// Initialize socket services
bool InitSocket() {
return true;
}
bool Conn::IsValid() const {
return sock_ != 0;
}
bool Conn::DialTCP(const char* host, int port) {
struct sockaddr_in sa;
int status, len;
if(IsValid()) Close();
if((sock_ = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
printf("protorpc.Conn.DialTCP: socket failed.\n");
sock_ = 0;
return false;
}
memset(sa.sin_zero, 0 , sizeof(sa.sin_zero));
sa.sin_family = AF_INET;
sa.sin_port = htons(port);
sa.sin_addr.s_addr = inet_addr(host);
size_t addressSize = sizeof(sa);
if(connect(sock_, (struct sockaddr*)&sa, addressSize) == -1) {
printf("protorpc.Conn.DialTCP: connect failed.\n");
Close();
return false;
}
int flag = 1;
setsockopt(sock_, IPPROTO_TCP, TCP_NODELAY, (char*)&flag, sizeof(flag));
return true;
}
bool Conn::ListenTCP(int port, int backlog) {
if(IsValid()) Close();
if((sock_ = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
printf("protorpc.Conn.ListenTCP: socket failed.\n");
sock_ = 0;
return false;
}
struct sockaddr_in saddr;
memset(&saddr, 0, sizeof(saddr));
saddr.sin_family = AF_INET;
saddr.sin_addr.s_addr = htonl(INADDR_ANY);
saddr.sin_port = htons((u_short) port);
if(bind(sock_, (struct sockaddr*)&saddr, sizeof(saddr)) == -1) {
printf("protorpc.Conn.ListenTCP: bind failed.\n");
Close();
return false;
}
if(::listen(sock_, backlog) != 0) {
printf("protorpc.Conn.ListenTCP: listen failed.\n");
Close();
return false;
}
int flag = 1;
setsockopt(sock_, IPPROTO_TCP, TCP_NODELAY, (char*)&flag, sizeof(flag));
return true;
}
void Conn::Close() {
if(IsValid()) {
::close(sock_);
sock_ = 0;
}
}
Conn* Conn::Accept() {
struct sockaddr_in addr;
socklen_t addrlen = sizeof(addr);
int sock = ::accept(sock_, (struct sockaddr*)&addr, &addrlen);
if(sock == 0) {
printf("protorpc.Conn.Accept: failed.\n");
return NULL;
}
return new Conn(sock);
}
bool Conn::Read (void* buf, int len) {
char *cbuf = (char*)buf;
while(len > 0) {
int sent = recv(sock_, cbuf, len, 0);
if(sent == 0 || sent == -1) {
printf("protorpc.Conn.Read: IO error, err = %d.\n", errno);
return false;
}
cbuf += sent;
len -= sent;
}
return true;
}
bool Conn::Write(void* buf, int len) {
const char *cbuf = (char*)buf;
int flags = MSG_NOSIGNAL;
while(len > 0) {
int sent = send(sock_, cbuf, len, flags);
if(sent == -1) {
printf("protorpc.Conn.Write: IO error, err = %d.\n", errno);
return false;
}
cbuf += sent;
len -= sent;
}
return true;
}
} // namespace protorpc

150
3rdparty/protorpc/rpc_conn_windows.cc vendored Normal file
View File

@ -0,0 +1,150 @@
// Copyright 2013 <chaishushan{AT}gmail.com>. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#include "3rdparty/protorpc/rpc_conn.h"
#include <string.h>
#ifdef _MSC_VER
# include <ws2tcpip.h> /* send,recv,socklen_t etc */
# include <wspiapi.h> /* addrinfo */
# pragma comment(lib, "ws2_32.lib")
#else
# include <ws2tcpip.h> /* send,recv,socklen_t etc */
# include <winsock2.h>
typedef int socklen_t;
#endif
#ifndef NI_MAXSERV
# define NI_MAXSERV 32
#endif
namespace protorpc {
// [static]
// Initialize socket services
bool InitSocket() {
WSADATA wsaData;
WORD wVers;
static bool called_once = false;
static bool retval = false;
if(called_once) return retval;
called_once = true;
wVers = MAKEWORD(1, 1);
retval = (WSAStartup(wVers, &wsaData) == 0);
return retval;
}
bool Conn::IsValid() const {
return sock_ != 0;
}
bool Conn::DialTCP(const char* host, int port) {
if(IsValid()) Close();
if((sock_ = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
printf("protorpc.Conn.DialTCP: socket failed.\n");
sock_ = 0;
return false;
}
struct sockaddr_in sa;
socklen_t addressSize;
memset(sa.sin_zero, 0 , sizeof(sa.sin_zero));
sa.sin_family = AF_INET;
sa.sin_port = htons(port);
sa.sin_addr.s_addr = inet_addr(host);
addressSize = sizeof(sa);
if(connect(sock_, ( struct sockaddr*)&sa, addressSize) == -1 ) {
printf("protorpc.Conn.DialTCP: connect failed.\n");
Close();
return false;
}
int flag = 1;
setsockopt(sock_, IPPROTO_TCP, TCP_NODELAY, (char*)&flag, sizeof(flag));
return true;
}
bool Conn::ListenTCP(int port, int backlog) {
if(IsValid()) Close();
if((sock_ = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
printf("protorpc.Conn.ListenTCP: socket failed.\n");
sock_ = 0;
return false;
}
struct sockaddr_in saddr;
memset(&saddr, 0, sizeof(saddr));
saddr.sin_family = AF_INET;
saddr.sin_addr.s_addr = htonl(INADDR_ANY);
saddr.sin_port = htons((u_short) port);
if(bind(sock_, (struct sockaddr*)&saddr, sizeof(saddr)) == -1) {
printf("protorpc.Conn.ListenTCP: bind failed.\n");
Close();
return false;
}
if(::listen(sock_, backlog) != 0) {
printf("protorpc.Conn.ListenTCP: listen failed.\n");
Close();
return false;
}
int flag = 1;
setsockopt(sock_, IPPROTO_TCP, TCP_NODELAY, (char*)&flag, sizeof(flag));
return true;
}
void Conn::Close() {
if(IsValid()) {
::closesocket(sock_);
sock_ = 0;
}
}
Conn* Conn::Accept() {
struct sockaddr_in addr;
int addrlen = sizeof(addr);
int sock = ::accept(sock_, (struct sockaddr*)&addr, &addrlen);
if(sock == 0) {
printf("protorpc.Conn.Accept: failed.\n");
return NULL;
}
return new Conn(sock);
}
bool Conn::Read (void* buf, int len) {
char *cbuf = (char*)buf;
while(len > 0) {
int sent = recv(sock_, cbuf, len, 0);
if(sent == 0 || sent == -1) {
printf("protorpc.Conn.Read: IO error, err = %d.\n", WSAGetLastError());
return false;
}
cbuf += sent;
len -= sent;
}
return true;
}
bool Conn::Write(void* buf, int len) {
const char *cbuf = (char*)buf;
int flags = 0;
while(len > 0) {
int sent = send(sock_, cbuf, len, flags );
if(sent == -1) {
printf("protorpc.Conn.Write: IO error, err = %d.\n", WSAGetLastError());
return false;
}
cbuf += sent;
len -= sent;
}
return true;
}
} // namespace protorpc

32
3rdparty/protorpc/rpc_error.h vendored Normal file
View File

@ -0,0 +1,32 @@
// Copyright 2013 <chaishushan{AT}gmail.com>. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#pragma once
#ifndef PROTORPC_ERROR_H__
#define PROTORPC_ERROR_H__
namespace protorpc {
// Error
class Error {
public:
Error(){}
Error(const std::string& err): err_text_(err) {}
Error(const Error& err): err_text_(err.err_text_) {}
~Error(){}
static Error Nil() { return Error(); }
static Error New(const std::string& err) { return Error(err); }
bool IsNil()const { return err_text_.empty(); }
const std::string& String()const { return err_text_; }
private:
std::string err_text_;
};
} // namespace protorpc
#endif // PROTORPC_ERROR_H__

158
3rdparty/protorpc/rpc_wire.cc vendored Normal file
View File

@ -0,0 +1,158 @@
// Copyright 2013 <chaishushan{AT}gmail.com>. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#include "3rdparty/protorpc/rpc_wire.h"
namespace protorpc {
namespace wire {
Error SendRequest(Conn* conn,
uint64_t id, const std::string& serviceMethod,
const ::std::string* request
) {
// marshal request
std::string pbRequest;
if(request != NULL) {
pbRequest = *request;
}
// generate header
RequestHeader header;
header.id = id;
header.method = serviceMethod;
header.raw_request_len = pbRequest.size();
// check header size
std::string pbHeader = spb::pb::serialize<std::string>(header);
if(pbHeader.size() > (unsigned int)protorpc::wire::Const::MAX_REQUEST_HEADER_LEN) {
return Error::New("protorpc.SendRequest: header larger than max_header_len.");
}
// send header
if(!conn->SendFrame(&pbHeader)) {
return Error::New("protorpc.SendRequest: SendFrame header failed.");
}
// send body
if(!conn->SendFrame(&pbRequest)) {
return Error::New("protorpc.SendRequest: SendFrame body failed.");
}
return Error::Nil();
}
Error RecvRequestHeader(Conn* conn,
RequestHeader* header
) {
// recv header
std::string pbHeader;
if(!conn->RecvFrame(&pbHeader)) {
return Error::New("protorpc.RecvRequestHeader: RecvFrame failed.");
}
if(pbHeader.size() > (unsigned int)protorpc::wire::Const::MAX_REQUEST_HEADER_LEN) {
return Error::New("protorpc.RecvRequestHeader: RecvFrame larger than max_header_len.");
}
// Marshal Header
*header = spb::pb::deserialize<RequestHeader>(pbHeader);
return Error::Nil();
}
Error RecvRequestBody(Conn* conn,
const RequestHeader* header,
::std::string* request
) {
// recv body
std::string pbRequest;
if(!conn->RecvFrame(&pbRequest)) {
return Error::New("protorpc.RecvRequestBody: RecvFrame failed.");
}
// check wire header: rawMsgLen
if(pbRequest.size() != header->raw_request_len) {
return Error::New("protorpc.RecvRequestBody: Unexcpeted raw msg len.");
}
// marshal request
*request = pbRequest;
return Error::Nil();
}
Error SendResponse(Conn* conn,
uint64_t id, const std::string& error,
const ::std::string* response
) {
// marshal response
std::string pbResponse;
if(response != NULL) {
pbResponse = *response;
}
// generate header
ResponseHeader header;
header.id = id;
header.error = error;
header.raw_response_len = pbResponse.size();
// check header size
std::string pbHeader = spb::pb::serialize<std::string>(header);
// send header
if(!conn->SendFrame(&pbHeader)) {
return Error::New("protorpc.SendResponse: SendFrame header failed.");
}
// send body
if(!conn->SendFrame(&pbResponse)) {
return Error::New("protorpc.SendResponse: SendFrame body failed.");
}
return Error::Nil();
}
Error RecvResponseHeader(Conn* conn,
ResponseHeader* header
) {
// recv header
std::string pbHeader;
if(!conn->RecvFrame(&pbHeader)) {
return Error::New("protorpc.RecvResponseHeader: RecvFrame failed.");
}
// Marshal Header
*header = spb::pb::deserialize<ResponseHeader>(pbHeader);
return Error::Nil();
}
Error RecvResponseBody(Conn* conn,
const ResponseHeader* header,
::std::string* response
) {
// recv body
std::string pbResponse;
if(!conn->RecvFrame(&pbResponse)) {
return Error::New("protorpc.RecvResponseBody: RecvFrame failed.");
}
// check wire header: rawMsgLen
if(pbResponse.size() != header->raw_response_len) {
return Error::New("protorpc.RecvResponseBody: Unexcpeted raw msg len.");
}
// marshal response
*response = pbResponse;
return Error::Nil();
}
} // namespace wire
} // namespace protorpc

48
3rdparty/protorpc/rpc_wire.h vendored Normal file
View File

@ -0,0 +1,48 @@
// Copyright 2013 <chaishushan{AT}gmail.com>. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#pragma once
#ifndef PROTORPC_WIRE_H__
#define PROTORPC_WIRE_H__
#include <stddef.h>
#include <stdint.h>
#include "3rdparty/protorpc/wire.pb/wire.pb.h"
#include "3rdparty/protorpc/rpc_error.h"
#include "3rdparty/protorpc/rpc_conn.h"
namespace protorpc {
namespace wire {
Error SendRequest(Conn* conn,
uint64_t id, const std::string& serviceMethod,
const ::std::string* request
);
Error RecvRequestHeader(Conn* conn,
RequestHeader* header
);
Error RecvRequestBody(Conn* conn,
const RequestHeader* header,
::std::string* request
);
Error SendResponse(Conn* conn,
uint64_t id, const std::string& error,
const ::std::string* response
);
Error RecvResponseHeader(Conn* conn,
ResponseHeader* header
);
Error RecvResponseBody(Conn* conn,
const ResponseHeader* header,
::std::string* request
);
} // namespace wire
} // namespace protorpc
#endif // PROTORPC_WIRE_H__

197
3rdparty/protorpc/wire.pb/wire.pb.cc vendored Normal file
View File

@ -0,0 +1,197 @@
#include "wire.pb.h"
#include <spb/json.hpp>
#include <system_error>
#include <type_traits>
namespace spb::json
{
namespace detail
{
void serialize_value( detail::ostream & stream, const ::protorpc::wire::Const & value )
{
switch( value )
{
case ::protorpc::wire::Const::ZERO:
return stream.serialize( "ZERO"sv);
case ::protorpc::wire::Const::MAX_REQUEST_HEADER_LEN:
return stream.serialize( "MAX_REQUEST_HEADER_LEN"sv);
default:
throw std::system_error( std::make_error_code( std::errc::invalid_argument ) );
}
}
void deserialize_value( detail::istream & stream, ::protorpc::wire::Const & value )
{
auto enum_value = stream.deserialize_string_or_int( 4, 22 );
std::visit( detail::overloaded{
[&]( std::string_view enum_str )
{
const auto enum_hash = djb2_hash( enum_str );
switch( enum_hash )
{
case detail::djb2_hash( "ZERO"sv ):
if( enum_str == "ZERO"sv ){
value = ::protorpc::wire::Const::ZERO;
return ; }
break ;
case detail::djb2_hash( "MAX_REQUEST_HEADER_LEN"sv ):
if( enum_str == "MAX_REQUEST_HEADER_LEN"sv ){
value = ::protorpc::wire::Const::MAX_REQUEST_HEADER_LEN;
return ; }
break ;
}
throw std::system_error( std::make_error_code( std::errc::invalid_argument ) );
},
[&]( int32_t enum_int )
{
switch( ::protorpc::wire::Const( enum_int ) )
{
case ::protorpc::wire::Const::ZERO:
case ::protorpc::wire::Const::MAX_REQUEST_HEADER_LEN:
value = ::protorpc::wire::Const( enum_int );
return ;
}
throw std::system_error( std::make_error_code( std::errc::invalid_argument ) );
}
}, enum_value );
}
} // namespace detail
namespace detail
{
void serialize_value( detail::ostream & stream, const ::protorpc::wire::RequestHeader & value )
{
stream.serialize( "id"sv, value.id );
stream.serialize( "method"sv, value.method );
stream.serialize( "raw_request_len"sv, value.raw_request_len );
}
void deserialize_value( detail::istream & stream, ::protorpc::wire::RequestHeader & value )
{
auto key = stream.deserialize_key( 2, 15 );
switch( djb2_hash( key ) )
{
case detail::djb2_hash( "id"sv ):
if( key == "id"sv )
{
return stream.deserialize( value.id );
}
break;
case detail::djb2_hash( "method"sv ):
if( key == "method"sv )
{
return stream.deserialize( value.method );
}
break;
case detail::djb2_hash( "raw_request_len"sv ):
if( key == "raw_request_len"sv )
{
return stream.deserialize( value.raw_request_len );
}
break;
case detail::djb2_hash( "rawRequestLen"sv ):
if( key == "rawRequestLen"sv )
{
return stream.deserialize( value.raw_request_len );
}
break;
}
return stream.skip_value( );
}
} // namespace detail
namespace detail
{
void serialize_value( detail::ostream & stream, const ::protorpc::wire::ResponseHeader & value )
{
stream.serialize( "id"sv, value.id );
stream.serialize( "error"sv, value.error );
stream.serialize( "raw_response_len"sv, value.raw_response_len );
}
void deserialize_value( detail::istream & stream, ::protorpc::wire::ResponseHeader & value )
{
auto key = stream.deserialize_key( 2, 16 );
switch( djb2_hash( key ) )
{
case detail::djb2_hash( "id"sv ):
if( key == "id"sv )
{
return stream.deserialize( value.id );
}
break;
case detail::djb2_hash( "error"sv ):
if( key == "error"sv )
{
return stream.deserialize( value.error );
}
break;
case detail::djb2_hash( "rawResponseLen"sv ):
if( key == "rawResponseLen"sv )
{
return stream.deserialize( value.raw_response_len );
}
break;
case detail::djb2_hash( "raw_response_len"sv ):
if( key == "raw_response_len"sv )
{
return stream.deserialize( value.raw_response_len );
}
break;
}
return stream.skip_value( );
}
} // namespace detail
} // namespace spb::json
#include "wire.pb.h"
#include <spb/pb.hpp>
#include <type_traits>
namespace spb::pb
{
namespace detail
{
void serialize( detail::ostream & stream, const ::protorpc::wire::RequestHeader & value )
{
stream.serialize_as<scalar_encoder::varint>( 1, value.id );
stream.serialize( 2, value.method );
stream.serialize_as<scalar_encoder::varint>( 3, value.raw_request_len );
}
void deserialize_value( detail::istream & stream, ::protorpc::wire::RequestHeader & value, uint32_t tag )
{
switch( field_from_tag( tag ) )
{
case 1:
return stream.deserialize_as<scalar_encoder::varint>( value.id, tag );
case 2:
return stream.deserialize( value.method, tag );
case 3:
return stream.deserialize_as<scalar_encoder::varint>( value.raw_request_len, tag );
default:
return stream.skip( tag );
}
}
} // namespace detail
namespace detail
{
void serialize( detail::ostream & stream, const ::protorpc::wire::ResponseHeader & value )
{
stream.serialize_as<scalar_encoder::varint>( 1, value.id );
stream.serialize( 2, value.error );
stream.serialize_as<scalar_encoder::varint>( 3, value.raw_response_len );
}
void deserialize_value( detail::istream & stream, ::protorpc::wire::ResponseHeader & value, uint32_t tag )
{
switch( field_from_tag( tag ) )
{
case 1:
return stream.deserialize_as<scalar_encoder::varint>( value.id, tag );
case 2:
return stream.deserialize( value.error, tag );
case 3:
return stream.deserialize_as<scalar_encoder::varint>( value.raw_response_len, tag );
default:
return stream.skip( tag );
}
}
} // namespace detail
} // namespace spb::pb

85
3rdparty/protorpc/wire.pb/wire.pb.h vendored Normal file
View File

@ -0,0 +1,85 @@
#pragma once
#include <cstddef>
#include <cstdint>
#include <spb/json.hpp>
#include <spb/pb.hpp>
#include <string>
// Copyright 2013 <chaishushan{AT}gmail.com>. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//
// protorpc wire format wrapper
//
// 0. Frame Format
// len : uvarint64
// data: byte[len]
//
// 1. Client Send Request
// Send RequestHeader: sendFrame(zsock, hdr, len(hdr))
// Send Request: sendFrame(zsock, body, hdr.snappy_compressed_request_len)
//
// 2. Server Recv Request
// Recv RequestHeader: recvFrame(zsock, hdr, max_hdr_len, 0)
// Recv Request: recvFrame(zsock, body, hdr.snappy_compressed_request_len, 0)
//
// 3. Server Send Response
// Send ResponseHeader: sendFrame(zsock, hdr, len(hdr))
// Send Response: sendFrame(zsock, body, hdr.snappy_compressed_response_len)
//
// 4. Client Recv Response
// Recv ResponseHeader: recvFrame(zsock, hdr, max_hdr_len, 0)
// Recv Response: recvFrame(zsock, body, hdr.snappy_compressed_response_len, 0)
//
namespace protorpc::wire
{
enum class Const : int32_t
{
ZERO = 0,
MAX_REQUEST_HEADER_LEN = 1024,
};
struct RequestHeader
{
uint64_t id;
std::string method;
uint32_t raw_request_len;
};
struct ResponseHeader
{
uint64_t id;
std::string error;
uint32_t raw_response_len;
};
}// namespace protorpc::wire
namespace spb::json::detail
{
struct ostream;
struct istream;
void serialize_value( ostream & stream, const protorpc::wire::RequestHeader & value );
void deserialize_value( istream & stream, protorpc::wire::RequestHeader & value );
void serialize_value( ostream & stream, const protorpc::wire::ResponseHeader & value );
void deserialize_value( istream & stream, protorpc::wire::ResponseHeader & value );
void serialize_value( ostream & stream, const protorpc::wire::Const & value );
void deserialize_value( istream & stream, protorpc::wire::Const & value );
} // namespace spb::json::detail
namespace spb::pb::detail
{
struct ostream;
struct istream;
void serialize( ostream & stream, const protorpc::wire::RequestHeader & value );
void deserialize_value( istream & stream, protorpc::wire::RequestHeader & value, uint32_t tag );
void serialize( ostream & stream, const protorpc::wire::ResponseHeader & value );
void deserialize_value( istream & stream, protorpc::wire::ResponseHeader & value, uint32_t tag );
} // namespace spb::pb::detail

50
3rdparty/protorpc/wire.pb/wire.proto vendored Normal file
View File

@ -0,0 +1,50 @@
// Copyright 2013 <chaishushan{AT}gmail.com>. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
syntax = "proto2";
//
// protorpc wire format wrapper
//
// 0. Frame Format
// len : uvarint64
// data: byte[len]
//
// 1. Client Send Request
// Send RequestHeader: sendFrame(zsock, hdr, len(hdr))
// Send Request: sendFrame(zsock, body, hdr.snappy_compressed_request_len)
//
// 2. Server Recv Request
// Recv RequestHeader: recvFrame(zsock, hdr, max_hdr_len, 0)
// Recv Request: recvFrame(zsock, body, hdr.snappy_compressed_request_len, 0)
//
// 3. Server Send Response
// Send ResponseHeader: sendFrame(zsock, hdr, len(hdr))
// Send Response: sendFrame(zsock, body, hdr.snappy_compressed_response_len)
//
// 4. Client Recv Response
// Recv ResponseHeader: recvFrame(zsock, hdr, max_hdr_len, 0)
// Recv Response: recvFrame(zsock, body, hdr.snappy_compressed_response_len, 0)
//
package protorpc.wire;
option go_package = "protorpc.wire";
enum Const {
ZERO = 0;
MAX_REQUEST_HEADER_LEN = 1024;
}
message RequestHeader {
required uint64 id = 1;
required string method = 2;
required uint32 raw_request_len = 3;
}
message ResponseHeader {
required uint64 id = 1;
required string error = 2;
required uint32 raw_response_len = 3;
}

View File

@ -85,7 +85,12 @@ set(PROJECT_SOURCES
3rdparty/quirc/quirc.c 3rdparty/quirc/quirc.c
3rdparty/quirc/version_db.c 3rdparty/quirc/version_db.c
src/api/gRPC.cpp 3rdparty/protorpc/rpc_client.cc
3rdparty/protorpc/rpc_conn.cc
3rdparty/protorpc/rpc_wire.cc
3rdparty/protorpc/wire.pb/wire.pb.cc
src/api/RPC.cpp
src/dataStore/Database.cpp src/dataStore/Database.cpp
src/stats/traffic/TrafficLooper.cpp src/stats/traffic/TrafficLooper.cpp

View File

@ -0,0 +1,29 @@
# Copyright 2018 <chaishushan{AT}gmail.com>. All rights reserved.
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file.
# http://editorconfig.org/
root = true
# Unix-style newlines with a newline ending every file
[*]
charset = utf-8
end_of_line = lf
trim_trailing_whitespace = true
insert_final_newline = true
[*]
indent_style = tab
indent_size = 4
tab_width = 4
[*.{go,proto}]
charset = utf-8
indent_style = tab
tab_width = 4
# Matches the exact files either package.json or .travis.yml
[{package.json,.travis.yml}]
indent_style = space
indent_size = 2

12
core/protorpc/.travis.yml Normal file
View File

@ -0,0 +1,12 @@
language: go
go:
- 1.13
- 1.14
- 1.15
- 1.16
- tip
before_script:
- go get github.com/golang/snappy
- go get github.com/golang/protobuf/proto

24
core/protorpc/Dockerfile Normal file
View File

@ -0,0 +1,24 @@
# Copyright 2018 <chaishushan{AT}gmail.com>. All rights reserved.
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file.
FROM golang:1.9.2-alpine3.6 as builder
RUN apk add --no-cache git curl openssl
RUN go get github.com/golang/protobuf/protoc-gen-go
RUN go get github.com/chai2010/protorpc/protoc-gen-protorpc
RUN go get github.com/chai2010/protorpc/protoc-gen-stdrpc
# the protoc can't run on alpine,
# we only need the protobuf's stdarnd library in the `/protoc/include`.
RUN mkdir -p /protoc && cd /protoc \
&& wget https://github.com/google/protobuf/releases/download/v3.5.0/protoc-3.5.0-linux-x86_64.zip \
&& unzip protoc-3.5.0-linux-x86_64.zip
FROM golang:1.9.2-alpine3.6
RUN apk add --no-cache git protobuf make curl openssl
COPY --from=builder /protoc/include /usr/local/include
COPY --from=builder /go/bin /go/bin

27
core/protorpc/LICENSE Normal file
View File

@ -0,0 +1,27 @@
Copyright (c) 2014, chai2010
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 the {organization} 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 HOLDER 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.

173
core/protorpc/README.md Normal file
View File

@ -0,0 +1,173 @@
- *Go语言QQ群: 102319854, 1055927514*
- *凹语言(凹读音“Wa”)(The Wa Programming Language): https://github.com/wa-lang/wa*
----
# protorpc
```
██████╗ ██████╗ ██████╗ ████████╗ ██████╗ ██████╗ ██████╗ ██████╗
██╔══██╗██╔══██╗██╔═══██╗╚══██╔══╝██╔═══██╗ ██╔══██╗██╔══██╗██╔════╝
██████╔╝██████╔╝██║ ██║ ██║ ██║ ██║█████╗██████╔╝██████╔╝██║
██╔═══╝ ██╔══██╗██║ ██║ ██║ ██║ ██║╚════╝██╔══██╗██╔═══╝ ██║
██║ ██║ ██║╚██████╔╝ ██║ ╚██████╔╝ ██║ ██║██║ ╚██████╗
╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═════╝
```
[![Build Status](https://travis-ci.org/chai2010/protorpc.svg)](https://travis-ci.org/chai2010/protorpc)
[![GoDoc](https://godoc.org/github.com/chai2010/protorpc?status.svg)](https://godoc.org/github.com/chai2010/protorpc)
- C++ Version(Proto2): [https://github.com/chai2010/protorpc.cxx](https://github.com/chai2010/protorpc.cxx)
- C++ Version(Proto3): [https://github.com/chai2010/protorpc3-cxx](https://github.com/chai2010/protorpc3-cxx)
- Talks: [Go/C++语言Protobuf-RPC简介](http://go-talks.appspot.com/github.com/chai2010/talks/chai2010-protorpc-intro.slide)
# Install
Install `protorpc` package:
1. `go install github.com/golang/protobuf/protoc-gen-go`
1. `go get github.com/chai2010/protorpc`
1. `go run hello.go`
Install `protoc-gen-go` plugin:
1. install `protoc` at first: http://github.com/google/protobuf/releases
1. `go get github.com/golang/protobuf/protoc-gen-go`
1. `go get github.com/chai2010/protorpc/protoc-gen-protorpc`
1. `go generate github.com/chai2010/protorpc/examples/service.pb`
1. `go test github.com/chai2010/protorpc/examples/service.pb`
# Examples
First, create [echo.proto](examples/service.pb/echo.proto):
```Proto
syntax = "proto3";
package service;
message EchoRequest {
string msg = 1;
}
message EchoResponse {
string msg = 1;
}
service EchoService {
rpc Echo (EchoRequest) returns (EchoResponse);
rpc EchoTwice (EchoRequest) returns (EchoResponse);
}
```
Second, generate [echo.pb.go](examples/service.pb/echo.pb.go) and [echo.pb.protorpc.go](examples/service.pb/echo.pb.protorpc.go)
from [echo.proto](examples/service.pb/echo.proto) (we can use `go generate` to invoke this command, see [proto.go](examples/service.pb/proto.go)).
protoc --go_out=. echo.proto
protoc --protorpc_out=. echo.proto
Now, we can use the stub code like this:
```Go
package main
import (
"fmt"
"log"
"github.com/chai2010/protorpc"
service "github.com/chai2010/protorpc/examples/service.pb"
)
type Echo int
func (t *Echo) Echo(args *service.EchoRequest, reply *service.EchoResponse) error {
reply.Msg = args.Msg
return nil
}
func (t *Echo) EchoTwice(args *service.EchoRequest, reply *service.EchoResponse) error {
reply.Msg = args.Msg + args.Msg
return nil
}
func init() {
go service.ListenAndServeEchoService("tcp", `127.0.0.1:9527`, new(Echo))
}
func main() {
echoClient, err := service.DialEchoService("tcp", `127.0.0.1:9527`)
if err != nil {
log.Fatalf("service.DialEchoService: %v", err)
}
defer echoClient.Close()
args := &service.EchoRequest{Msg: "你好, 世界!"}
reply, err := echoClient.EchoTwice(args)
if err != nil {
log.Fatalf("echoClient.EchoTwice: %v", err)
}
fmt.Println(reply.Msg)
// or use normal client
client, err := protorpc.Dial("tcp", `127.0.0.1:9527`)
if err != nil {
log.Fatalf("protorpc.Dial: %v", err)
}
defer client.Close()
echoClient1 := &service.EchoServiceClient{client}
echoClient2 := &service.EchoServiceClient{client}
reply, err = echoClient1.EchoTwice(args)
reply, err = echoClient2.EchoTwice(args)
_, _ = reply, err
// Output:
// 你好, 世界!你好, 世界!
}
```
[More examples](examples).
# standard net/rpc
First, create [echo.proto](examples/stdrpc.pb/echo.proto):
```Proto
syntax = "proto3";
package service;
message EchoRequest {
string msg = 1;
}
message EchoResponse {
string msg = 1;
}
service EchoService {
rpc Echo (EchoRequest) returns (EchoResponse);
rpc EchoTwice (EchoRequest) returns (EchoResponse);
}
```
Second, generate [echo.pb.go](examples/stdrpc.pb/echo.pb.go) from [echo.proto](examples/stdrpc.pb/echo.proto) with `protoc-gen-stdrpc` plugin.
protoc --stdrpc_out=. echo.proto
The stdrpc plugin generated code do not depends **protorpc** package, it use gob as the default rpc encoding.
# Add prefix
```
$ ENV_PROTOC_GEN_PROTORPC_FLAG_PREFIX=abc protoc --protorpc_out=. x.proto
```
# BUGS
Report bugs to <chaishushan@gmail.com>.
Thanks!

View File

@ -0,0 +1,23 @@
# Changelogs
## 1.1.3 - 2021.7.12
- fix `readRequestHeader` maxSize, response error maybe very long
## 1.1.2 - 2021.7.6
- fix `UseSnappy` typo
## 1.1.1 - 2021.7.4
- protoc-gen-plugin: ignore format error
## 1.1.0 - 2021.7.4
- add `UseSappy` and `UseCrc32ChecksumIEEE`
- add `ENV_PROTOC_GEN_PROTORPC_FLAG_PREFIX` env flag
- fix frame size overflow panic (issue12)
## 1.0.0 - go mod version (2018)
- init version

135
core/protorpc/client.go Normal file
View File

@ -0,0 +1,135 @@
// Copyright 2013 <chaishushan{AT}gmail.com>. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package protorpc
import (
"fmt"
"io"
"net"
"net/rpc"
"sync"
"time"
wire "github.com/chai2010/protorpc/wire.pb"
"github.com/golang/protobuf/proto"
)
type clientCodec struct {
r io.Reader
w io.Writer
c io.Closer
// temporary work space
respHeader wire.ResponseHeader
// Protobuf-RPC responses include the request id but not the request method.
// Package rpc expects both.
// We save the request method in pending when sending a request
// and then look it up by request ID when filling out the rpc Response.
mutex sync.Mutex // protects pending
pending map[uint64]string // map request id to method name
}
// NewClientCodec returns a new rpc.ClientCodec using Protobuf-RPC on conn.
func NewClientCodec(conn io.ReadWriteCloser) rpc.ClientCodec {
return &clientCodec{
r: conn,
w: conn,
c: conn,
pending: make(map[uint64]string),
}
}
func (c *clientCodec) WriteRequest(r *rpc.Request, param interface{}) error {
c.mutex.Lock()
c.pending[r.Seq] = r.ServiceMethod
c.mutex.Unlock()
var request proto.Message
if param != nil {
var ok bool
if request, ok = param.(proto.Message); !ok {
return fmt.Errorf(
"protorpc.ClientCodec.WriteRequest: %T does not implement proto.Message",
param,
)
}
}
err := writeRequest(c.w, r.Seq, r.ServiceMethod, request)
if err != nil {
return err
}
return nil
}
func (c *clientCodec) ReadResponseHeader(r *rpc.Response) error {
header := wire.ResponseHeader{}
err := readResponseHeader(c.r, &header)
if err != nil {
return err
}
c.mutex.Lock()
r.Seq = *header.Id
r.Error = *header.Error
r.ServiceMethod = c.pending[r.Seq]
delete(c.pending, r.Seq)
c.mutex.Unlock()
c.respHeader = header
return nil
}
func (c *clientCodec) ReadResponseBody(x interface{}) error {
var response proto.Message
if x != nil {
var ok bool
response, ok = x.(proto.Message)
if !ok {
return fmt.Errorf(
"protorpc.ClientCodec.ReadResponseBody: %T does not implement proto.Message",
x,
)
}
}
err := readResponseBody(c.r, &c.respHeader, response)
if err != nil {
return nil
}
c.respHeader = wire.ResponseHeader{}
return nil
}
// Close closes the underlying connection.
func (c *clientCodec) Close() error {
return c.c.Close()
}
// NewClient returns a new rpc.Client to handle requests to the
// set of services at the other end of the connection.
func NewClient(conn io.ReadWriteCloser) *rpc.Client {
return rpc.NewClientWithCodec(NewClientCodec(conn))
}
// Dial connects to a Protobuf-RPC server at the specified network address.
func Dial(network, address string) (*rpc.Client, error) {
conn, err := net.Dial(network, address)
if err != nil {
return nil, err
}
return NewClient(conn), err
}
// DialTimeout connects to a Protobuf-RPC server at the specified network address.
func DialTimeout(network, address string, timeout time.Duration) (*rpc.Client, error) {
conn, err := net.DialTimeout(network, address, timeout)
if err != nil {
return nil, err
}
return NewClient(conn), err
}

117
core/protorpc/conn.go Normal file
View File

@ -0,0 +1,117 @@
// Copyright 2013 <chaishushan{AT}gmail.com>. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package protorpc
import (
"encoding/binary"
"errors"
"fmt"
"io"
"net"
)
func sendFrame(w io.Writer, data []byte) (err error) {
// Allocate enough space for the biggest uvarint
var size [binary.MaxVarintLen64]byte
if data == nil || len(data) == 0 {
n := binary.PutUvarint(size[:], uint64(0))
if err = write(w, size[:n], false); err != nil {
return
}
return
}
// Write the size and data
n := binary.PutUvarint(size[:], uint64(len(data)))
if err = write(w, size[:n], false); err != nil {
return
}
if err = write(w, data, false); err != nil {
return
}
return
}
func recvFrame(r io.Reader, maxSize int) (data []byte, err error) {
size, err := readUvarint(r)
if err != nil {
return nil, err
}
if maxSize > 0 {
if int(size) > maxSize {
return nil, fmt.Errorf("protorpc: varint overflows maxSize(%d)", maxSize)
}
}
if size != 0 {
data = make([]byte, size)
if err = read(r, data); err != nil {
return nil, err
}
}
return data, nil
}
// ReadUvarint reads an encoded unsigned integer from r and returns it as a uint64.
func readUvarint(r io.Reader) (uint64, error) {
var x uint64
var s uint
for i := 0; ; i++ {
var b byte
b, err := readByte(r)
if err != nil {
return 0, err
}
if b < 0x80 {
if i > 9 || i == 9 && b > 1 {
return x, errors.New("protorpc: varint overflows a 64-bit integer")
}
return x | uint64(b)<<s, nil
}
x |= uint64(b&0x7f) << s
s += 7
}
}
func write(w io.Writer, data []byte, onePacket bool) error {
if onePacket {
if _, err := w.Write(data); err != nil {
return err
}
return nil
}
for index := 0; index < len(data); {
n, err := w.Write(data[index:])
if err != nil {
if nerr, ok := err.(net.Error); !ok || !nerr.Temporary() {
return err
}
}
index += n
}
return nil
}
func read(r io.Reader, data []byte) error {
for index := 0; index < len(data); {
n, err := r.Read(data[index:])
if err != nil {
if nerr, ok := err.(net.Error); !ok || !nerr.Temporary() {
return err
}
}
index += n
}
return nil
}
func readByte(r io.Reader) (c byte, err error) {
data := make([]byte, 1)
if err = read(r, data); err != nil {
return 0, err
}
c = data[0]
return
}

111
core/protorpc/doc.go Normal file
View File

@ -0,0 +1,111 @@
// Copyright 2013 <chaishushan{AT}gmail.com>. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
/*
Package protorpc implements a Protobuf-RPC ClientCodec and ServerCodec
for the rpc package.
To install it, you must first have Go (version 1) installed
(see http://golang.org/doc/install). Next, install the standard
protocol buffer implementation from http://github.com/google/protobuf/;
you must be running version 2.3 or higher.
Finally run
go get github.com/chai2010/protorpc
go get github.com/chai2010/protorpc/protoc-gen-protorpc
to install the support library and protocol compiler.
Here is a simple proto file("arith.pb/arith.proto"):
package arith;
message ArithRequest {
optional int32 a = 1;
optional int32 b = 2;
}
message ArithResponse {
optional int32 val = 1;
optional int32 quo = 2;
optional int32 rem = 3;
}
service ArithService {
rpc multiply (ArithRequest) returns (ArithResponse);
rpc divide (ArithRequest) returns (ArithResponse);
}
Then use "protoc-gen-go" to generate "arith.pb.go" file:
cd arith.pb && protoc --go_out=. arith.proto
Use "protoc-gen-protorpc" to generate "arith.pb.protorpc.go" file (include stub code):
cd arith.pb && protoc --protorpc_out=. arith.proto
The server calls (for TCP service):
package server
import (
"errors"
"github.com/golang/protobuf/proto"
"./arith.pb"
)
type Arith int
func (t *Arith) Multiply(args *arith.ArithRequest, reply *arith.ArithResponse) error {
reply.Val = proto.Int32(args.GetA() * args.GetB())
return nil
}
func (t *Arith) Divide(args *arith.ArithRequest, reply *arith.ArithResponse) error {
if args.GetB() == 0 {
return errors.New("divide by zero")
}
reply.Quo = proto.Int32(args.GetA() / args.GetB())
reply.Rem = proto.Int32(args.GetA() % args.GetB())
return nil
}
func main() {
arith.ListenAndServeArithService("tcp", ":1984", new(Arith))
}
At this point, clients can see a service "Arith" with methods "ArithService.Multiply" and
"ArithService.Divide". To invoke one, a client first dials the server:
stub, err := arith.DialArithService("tcp", "127.0.0.1:1984")
if err != nil {
log.Fatal(`arith.DialArithService("tcp", "127.0.0.1:1984"):`, err)
}
defer stub.Close()
Then it can make a remote call with stub:
var args ArithRequest
args.A = proto.Int32(7)
args.B = proto.Int32(8)
reply, err := stub.Multiply(&args, &reply)
if err != nil {
log.Fatal("arith error:", err)
}
fmt.Printf("Arith: %d*%d=%d", args.GetA(), args.GetB(), reply.GetVal())
More example:
go test github.com/chai2010/protorpc/internal/service.pb
Report bugs to <chaishushan@gmail.com>.
Thanks!
*/
package protorpc // import "github.com/chai2010/protorpc"

View File

@ -0,0 +1,90 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: arith.proto
/*
Package message is a generated protocol buffer package.
It is generated from these files:
arith.proto
echo.proto
It has these top-level messages:
ArithRequest
ArithResponse
EchoRequest
EchoResponse
*/
package message
import proto "github.com/golang/protobuf/proto"
import fmt "fmt"
import math "math"
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
type ArithRequest struct {
A int32 `protobuf:"varint,1,opt,name=a" json:"a,omitempty"`
B int32 `protobuf:"varint,2,opt,name=b" json:"b,omitempty"`
}
func (m *ArithRequest) Reset() { *m = ArithRequest{} }
func (m *ArithRequest) String() string { return proto.CompactTextString(m) }
func (*ArithRequest) ProtoMessage() {}
func (*ArithRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
func (m *ArithRequest) GetA() int32 {
if m != nil {
return m.A
}
return 0
}
func (m *ArithRequest) GetB() int32 {
if m != nil {
return m.B
}
return 0
}
type ArithResponse struct {
C int32 `protobuf:"varint,1,opt,name=c" json:"c,omitempty"`
}
func (m *ArithResponse) Reset() { *m = ArithResponse{} }
func (m *ArithResponse) String() string { return proto.CompactTextString(m) }
func (*ArithResponse) ProtoMessage() {}
func (*ArithResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
func (m *ArithResponse) GetC() int32 {
if m != nil {
return m.C
}
return 0
}
func init() {
proto.RegisterType((*ArithRequest)(nil), "message.ArithRequest")
proto.RegisterType((*ArithResponse)(nil), "message.ArithResponse")
}
func init() { proto.RegisterFile("arith.proto", fileDescriptor0) }
var fileDescriptor0 = []byte{
// 104 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x4e, 0x2c, 0xca, 0x2c,
0xc9, 0xd0, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0xcf, 0x4d, 0x2d, 0x2e, 0x4e, 0x4c, 0x4f,
0x55, 0xd2, 0xe2, 0xe2, 0x71, 0x04, 0x89, 0x07, 0xa5, 0x16, 0x96, 0xa6, 0x16, 0x97, 0x08, 0xf1,
0x70, 0x31, 0x26, 0x4a, 0x30, 0x2a, 0x30, 0x6a, 0xb0, 0x06, 0x31, 0x26, 0x82, 0x78, 0x49, 0x12,
0x4c, 0x10, 0x5e, 0x92, 0x92, 0x2c, 0x17, 0x2f, 0x54, 0x6d, 0x71, 0x41, 0x7e, 0x5e, 0x71, 0x2a,
0x48, 0x3a, 0x19, 0xa6, 0x38, 0x39, 0x89, 0x0d, 0x6c, 0xb4, 0x31, 0x20, 0x00, 0x00, 0xff, 0xff,
0x61, 0x27, 0x57, 0x32, 0x69, 0x00, 0x00, 0x00,
}

View File

@ -0,0 +1,16 @@
// Copyright 2013 <chaishushan{AT}gmail.com>. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
syntax = "proto3";
package message;
message ArithRequest {
int32 a = 1;
int32 b = 2;
}
message ArithResponse {
int32 c = 1;
}

View File

@ -0,0 +1,62 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: echo.proto
package message
import proto "github.com/golang/protobuf/proto"
import fmt "fmt"
import math "math"
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
type EchoRequest struct {
Msg string `protobuf:"bytes,1,opt,name=msg" json:"msg,omitempty"`
}
func (m *EchoRequest) Reset() { *m = EchoRequest{} }
func (m *EchoRequest) String() string { return proto.CompactTextString(m) }
func (*EchoRequest) ProtoMessage() {}
func (*EchoRequest) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{0} }
func (m *EchoRequest) GetMsg() string {
if m != nil {
return m.Msg
}
return ""
}
type EchoResponse struct {
Msg string `protobuf:"bytes,1,opt,name=msg" json:"msg,omitempty"`
}
func (m *EchoResponse) Reset() { *m = EchoResponse{} }
func (m *EchoResponse) String() string { return proto.CompactTextString(m) }
func (*EchoResponse) ProtoMessage() {}
func (*EchoResponse) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{1} }
func (m *EchoResponse) GetMsg() string {
if m != nil {
return m.Msg
}
return ""
}
func init() {
proto.RegisterType((*EchoRequest)(nil), "message.EchoRequest")
proto.RegisterType((*EchoResponse)(nil), "message.EchoResponse")
}
func init() { proto.RegisterFile("echo.proto", fileDescriptor1) }
var fileDescriptor1 = []byte{
// 95 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x4a, 0x4d, 0xce, 0xc8,
0xd7, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0xcf, 0x4d, 0x2d, 0x2e, 0x4e, 0x4c, 0x4f, 0x55,
0x92, 0xe7, 0xe2, 0x76, 0x4d, 0xce, 0xc8, 0x0f, 0x4a, 0x2d, 0x2c, 0x4d, 0x2d, 0x2e, 0x11, 0x12,
0xe0, 0x62, 0xce, 0x2d, 0x4e, 0x97, 0x60, 0x54, 0x60, 0xd4, 0xe0, 0x0c, 0x02, 0x31, 0x95, 0x14,
0xb8, 0x78, 0x20, 0x0a, 0x8a, 0x0b, 0xf2, 0xf3, 0x8a, 0x53, 0x31, 0x55, 0x24, 0xb1, 0x81, 0x8d,
0x34, 0x06, 0x04, 0x00, 0x00, 0xff, 0xff, 0xcb, 0xea, 0xe4, 0xa6, 0x60, 0x00, 0x00, 0x00,
}

View File

@ -0,0 +1,15 @@
// Copyright 2013 <chaishushan{AT}gmail.com>. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
syntax = "proto3";
package message;
message EchoRequest {
string msg = 1;
}
message EchoResponse {
string msg = 1;
}

View File

@ -0,0 +1,7 @@
// Copyright 2014 <chaishushan{AT}gmail.com>. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:generate protoc --go_out=. arith.proto echo.proto
package message

View File

@ -0,0 +1,14 @@
# Copyright 2013 <chaishushan{AT}gmail.com>. All rights reserved.
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file.
PROTO_FILES=$(sort $(wildcard ./*.proto))
default: $(PROTO_FILES) Makefile
go install github.com/golang/protobuf/protoc-gen-go
go install github.com/chai2010/protorpc/protoc-gen-protorpc
protoc --go_out=. ${PROTO_FILES}
protoc --protorpc_out=. ${PROTO_FILES}
go test
clean:

View File

@ -0,0 +1,8 @@
// Copyright 2014 <chaishushan{AT}gmail.com>. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:generate protoc --go_out=. proto3.proto
//go:generate protoc --protorpc_out=. proto3.proto
package proto3_proto

View File

@ -0,0 +1,220 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: proto3.proto
/*
Package proto3_proto is a generated protocol buffer package.
It is generated from these files:
proto3.proto
It has these top-level messages:
Message
Nested
MessageWithMap
*/
package proto3_proto
import proto "github.com/golang/protobuf/proto"
import fmt "fmt"
import math "math"
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
type Message_Humour int32
const (
Message_UNKNOWN Message_Humour = 0
Message_PUNS Message_Humour = 1
Message_SLAPSTICK Message_Humour = 2
Message_BILL_BAILEY Message_Humour = 3
)
var Message_Humour_name = map[int32]string{
0: "UNKNOWN",
1: "PUNS",
2: "SLAPSTICK",
3: "BILL_BAILEY",
}
var Message_Humour_value = map[string]int32{
"UNKNOWN": 0,
"PUNS": 1,
"SLAPSTICK": 2,
"BILL_BAILEY": 3,
}
func (x Message_Humour) String() string {
return proto.EnumName(Message_Humour_name, int32(x))
}
func (Message_Humour) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{0, 0} }
type Message struct {
Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
Hilarity Message_Humour `protobuf:"varint,2,opt,name=hilarity,enum=proto3_proto.Message_Humour" json:"hilarity,omitempty"`
HeightInCm uint32 `protobuf:"varint,3,opt,name=height_in_cm,json=heightInCm" json:"height_in_cm,omitempty"`
Data []byte `protobuf:"bytes,4,opt,name=data,proto3" json:"data,omitempty"`
ResultCount int64 `protobuf:"varint,7,opt,name=result_count,json=resultCount" json:"result_count,omitempty"`
TrueScotsman bool `protobuf:"varint,8,opt,name=true_scotsman,json=trueScotsman" json:"true_scotsman,omitempty"`
Score float32 `protobuf:"fixed32,9,opt,name=score" json:"score,omitempty"`
Key []uint64 `protobuf:"varint,5,rep,packed,name=key" json:"key,omitempty"`
Nested *Nested `protobuf:"bytes,6,opt,name=nested" json:"nested,omitempty"`
Terrain map[string]*Nested `protobuf:"bytes,10,rep,name=terrain" json:"terrain,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
}
func (m *Message) Reset() { *m = Message{} }
func (m *Message) String() string { return proto.CompactTextString(m) }
func (*Message) ProtoMessage() {}
func (*Message) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
func (m *Message) GetName() string {
if m != nil {
return m.Name
}
return ""
}
func (m *Message) GetHilarity() Message_Humour {
if m != nil {
return m.Hilarity
}
return Message_UNKNOWN
}
func (m *Message) GetHeightInCm() uint32 {
if m != nil {
return m.HeightInCm
}
return 0
}
func (m *Message) GetData() []byte {
if m != nil {
return m.Data
}
return nil
}
func (m *Message) GetResultCount() int64 {
if m != nil {
return m.ResultCount
}
return 0
}
func (m *Message) GetTrueScotsman() bool {
if m != nil {
return m.TrueScotsman
}
return false
}
func (m *Message) GetScore() float32 {
if m != nil {
return m.Score
}
return 0
}
func (m *Message) GetKey() []uint64 {
if m != nil {
return m.Key
}
return nil
}
func (m *Message) GetNested() *Nested {
if m != nil {
return m.Nested
}
return nil
}
func (m *Message) GetTerrain() map[string]*Nested {
if m != nil {
return m.Terrain
}
return nil
}
type Nested struct {
Bunny string `protobuf:"bytes,1,opt,name=bunny" json:"bunny,omitempty"`
}
func (m *Nested) Reset() { *m = Nested{} }
func (m *Nested) String() string { return proto.CompactTextString(m) }
func (*Nested) ProtoMessage() {}
func (*Nested) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
func (m *Nested) GetBunny() string {
if m != nil {
return m.Bunny
}
return ""
}
type MessageWithMap struct {
ByteMapping map[bool][]byte `protobuf:"bytes,1,rep,name=byte_mapping,json=byteMapping" json:"byte_mapping,omitempty" protobuf_key:"varint,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value,proto3"`
}
func (m *MessageWithMap) Reset() { *m = MessageWithMap{} }
func (m *MessageWithMap) String() string { return proto.CompactTextString(m) }
func (*MessageWithMap) ProtoMessage() {}
func (*MessageWithMap) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} }
func (m *MessageWithMap) GetByteMapping() map[bool][]byte {
if m != nil {
return m.ByteMapping
}
return nil
}
func init() {
proto.RegisterType((*Message)(nil), "proto3_proto.Message")
proto.RegisterType((*Nested)(nil), "proto3_proto.Nested")
proto.RegisterType((*MessageWithMap)(nil), "proto3_proto.MessageWithMap")
proto.RegisterEnum("proto3_proto.Message_Humour", Message_Humour_name, Message_Humour_value)
}
func init() { proto.RegisterFile("proto3.proto", fileDescriptor0) }
var fileDescriptor0 = []byte{
// 473 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x90, 0x5f, 0x8b, 0xd3, 0x40,
0x14, 0xc5, 0x9d, 0xa6, 0xdb, 0x3f, 0x37, 0xe9, 0x1a, 0x86, 0x0a, 0x43, 0x11, 0x19, 0xeb, 0xcb,
0x20, 0xda, 0x87, 0xea, 0xc3, 0x22, 0xa2, 0x6c, 0x4b, 0xc1, 0xb2, 0x6d, 0x2d, 0xd3, 0x5d, 0x16,
0x9f, 0xc2, 0x34, 0x3b, 0xb4, 0xc1, 0x66, 0x52, 0x26, 0x93, 0x85, 0x7c, 0x1d, 0x3f, 0xa7, 0x0f,
0x92, 0x4c, 0xaa, 0x51, 0xea, 0xd3, 0xdc, 0x7b, 0xe6, 0xdc, 0x99, 0x73, 0x7f, 0xe0, 0x1d, 0x75,
0x62, 0x92, 0x77, 0xa3, 0xf2, 0xc0, 0x55, 0x17, 0x94, 0xc7, 0xf0, 0xa7, 0x03, 0xed, 0xa5, 0x4c,
0x53, 0xb1, 0x93, 0x18, 0x43, 0x53, 0x89, 0x58, 0x12, 0x44, 0x11, 0xeb, 0xf2, 0xb2, 0xc6, 0x57,
0xd0, 0xd9, 0x47, 0x07, 0xa1, 0x23, 0x93, 0x93, 0x06, 0x45, 0xec, 0x72, 0xfc, 0x7c, 0x54, 0x7f,
0x60, 0x54, 0x0d, 0x8f, 0xbe, 0x64, 0x71, 0x92, 0x69, 0xfe, 0xdb, 0x8d, 0x29, 0x78, 0x7b, 0x19,
0xed, 0xf6, 0x26, 0x88, 0x54, 0x10, 0xc6, 0xc4, 0xa1, 0x88, 0xf5, 0x38, 0x58, 0x6d, 0xae, 0xa6,
0x71, 0xf1, 0xdf, 0x83, 0x30, 0x82, 0x34, 0x29, 0x62, 0x1e, 0x2f, 0x6b, 0xfc, 0x12, 0x3c, 0x2d,
0xd3, 0xec, 0x60, 0x82, 0x30, 0xc9, 0x94, 0x21, 0x6d, 0x8a, 0x98, 0xc3, 0x5d, 0xab, 0x4d, 0x0b,
0x09, 0xbf, 0x82, 0x9e, 0xd1, 0x99, 0x0c, 0xd2, 0x30, 0x31, 0x69, 0x2c, 0x14, 0xe9, 0x50, 0xc4,
0x3a, 0xdc, 0x2b, 0xc4, 0x4d, 0xa5, 0xe1, 0x3e, 0x5c, 0xa4, 0x61, 0xa2, 0x25, 0xe9, 0x52, 0xc4,
0x1a, 0xdc, 0x36, 0xd8, 0x07, 0xe7, 0xbb, 0xcc, 0xc9, 0x05, 0x75, 0x58, 0x93, 0x17, 0x25, 0x7e,
0x03, 0x2d, 0x25, 0x53, 0x23, 0x1f, 0x48, 0x8b, 0x22, 0xe6, 0x8e, 0xfb, 0x7f, 0x6f, 0xb7, 0x2a,
0xef, 0x78, 0xe5, 0xc1, 0x1f, 0xa1, 0x6d, 0xa4, 0xd6, 0x22, 0x52, 0x04, 0xa8, 0xc3, 0xdc, 0xf1,
0xf0, 0x3c, 0x8c, 0x5b, 0x6b, 0x9a, 0x29, 0xa3, 0x73, 0x7e, 0x1a, 0x19, 0xac, 0xc1, 0xab, 0x5f,
0x9c, 0xd2, 0x58, 0xdc, 0x65, 0x9a, 0xd7, 0x70, 0xf1, 0x28, 0x0e, 0x99, 0x2c, 0x51, 0xff, 0x2f,
0x8c, 0xb5, 0x7c, 0x68, 0x5c, 0xa1, 0xe1, 0x67, 0x68, 0x59, 0xee, 0xd8, 0x85, 0xf6, 0xdd, 0xea,
0x66, 0xf5, 0xf5, 0x7e, 0xe5, 0x3f, 0xc1, 0x1d, 0x68, 0xae, 0xef, 0x56, 0x1b, 0x1f, 0xe1, 0x1e,
0x74, 0x37, 0x8b, 0xeb, 0xf5, 0xe6, 0x76, 0x3e, 0xbd, 0xf1, 0x1b, 0xf8, 0x29, 0xb8, 0x93, 0xf9,
0x62, 0x11, 0x4c, 0xae, 0xe7, 0x8b, 0xd9, 0x37, 0xdf, 0x19, 0xbe, 0x80, 0x96, 0x7d, 0xb5, 0x00,
0xb6, 0xcd, 0x94, 0x3a, 0xc5, 0xb1, 0xcd, 0xf0, 0x07, 0x82, 0xcb, 0x6a, 0xa9, 0xfb, 0xc8, 0xec,
0x97, 0xe2, 0x88, 0xd7, 0xe0, 0x6d, 0x73, 0x23, 0x83, 0x58, 0x1c, 0x8f, 0x91, 0xda, 0x11, 0x54,
0x82, 0x78, 0x7b, 0x16, 0x44, 0x35, 0x33, 0x9a, 0xe4, 0x46, 0x2e, 0xad, 0xdf, 0x32, 0x71, 0xb7,
0x7f, 0x94, 0xc1, 0x27, 0xf0, 0xff, 0x35, 0xd4, 0xd9, 0x74, 0x2c, 0x9b, 0x7e, 0x9d, 0x8d, 0x57,
0xa3, 0x30, 0x9e, 0x82, 0x3b, 0x0b, 0xf7, 0xc9, 0x46, 0xea, 0xc7, 0x28, 0x94, 0xf8, 0x3d, 0x34,
0x8b, 0x16, 0x3f, 0x3b, 0x1b, 0x69, 0x70, 0x5e, 0xde, 0xb6, 0xac, 0xfa, 0x2b, 0x00, 0x00, 0xff,
0xff, 0xff, 0xc1, 0x87, 0xd6, 0x2d, 0x03, 0x00, 0x00,
}

View File

@ -0,0 +1,171 @@
// Code generated by protoc-gen-protorpc. DO NOT EDIT.
//
// plugin: https://github.com/chai2010/protorpc/tree/master/protoc-gen-protorpc
// plugin: https://github.com/chai2010/protorpc/tree/master/protoc-plugin-common
//
// source: proto3.proto
package proto3_proto
import (
"fmt"
"io"
"log"
"net"
"net/rpc"
"time"
"github.com/chai2010/protorpc"
"github.com/golang/protobuf/proto"
)
var (
_ = fmt.Sprint
_ = io.Reader(nil)
_ = log.Print
_ = net.Addr(nil)
_ = rpc.Call{}
_ = time.Second
_ = proto.String
_ = protorpc.Dial
)
type EchoService interface {
Echo(in *Message, out *Message) error
}
// AcceptEchoServiceClient accepts connections on the listener and serves requests
// for each incoming connection. Accept blocks; the caller typically
// invokes it in a go statement.
func AcceptEchoServiceClient(lis net.Listener, x EchoService) {
srv := rpc.NewServer()
if err := srv.RegisterName("EchoService", x); err != nil {
log.Fatal(err)
}
for {
conn, err := lis.Accept()
if err != nil {
log.Fatalf("lis.Accept(): %v\n", err)
}
go srv.ServeCodec(protorpc.NewServerCodec(conn))
}
}
// RegisterEchoService publish the given EchoService implementation on the server.
func RegisterEchoService(srv *rpc.Server, x EchoService) error {
if err := srv.RegisterName("EchoService", x); err != nil {
return err
}
return nil
}
// NewEchoServiceServer returns a new EchoService Server.
func NewEchoServiceServer(x EchoService) *rpc.Server {
srv := rpc.NewServer()
if err := srv.RegisterName("EchoService", x); err != nil {
log.Fatal(err)
}
return srv
}
// ListenAndServeEchoService listen announces on the local network address laddr
// and serves the given EchoService implementation.
func ListenAndServeEchoService(network, addr string, x EchoService) error {
lis, err := net.Listen(network, addr)
if err != nil {
return err
}
defer lis.Close()
srv := rpc.NewServer()
if err := srv.RegisterName("EchoService", x); err != nil {
return err
}
for {
conn, err := lis.Accept()
if err != nil {
log.Fatalf("lis.Accept(): %v\n", err)
}
go srv.ServeCodec(protorpc.NewServerCodec(conn))
}
}
// ServeEchoService serves the given EchoService implementation.
func ServeEchoService(conn io.ReadWriteCloser, x EchoService) {
srv := rpc.NewServer()
if err := srv.RegisterName("EchoService", x); err != nil {
log.Fatal(err)
}
srv.ServeCodec(protorpc.NewServerCodec(conn))
}
type EchoServiceClient struct {
*rpc.Client
}
// NewEchoServiceClient returns a EchoService stub to handle
// requests to the set of EchoService at the other end of the connection.
func NewEchoServiceClient(conn io.ReadWriteCloser) *EchoServiceClient {
c := rpc.NewClientWithCodec(protorpc.NewClientCodec(conn))
return &EchoServiceClient{c}
}
func (c *EchoServiceClient) Echo(in *Message) (out *Message, err error) {
if in == nil {
in = new(Message)
}
type Validator interface {
Validate() error
}
if x, ok := proto.Message(in).(Validator); ok {
if err := x.Validate(); err != nil {
return nil, err
}
}
out = new(Message)
if err = c.Call("EchoService.Echo", in, out); err != nil {
return nil, err
}
if x, ok := proto.Message(out).(Validator); ok {
if err := x.Validate(); err != nil {
return out, err
}
}
return out, nil
}
func (c *EchoServiceClient) AsyncEcho(in *Message, out *Message, done chan *rpc.Call) *rpc.Call {
if in == nil {
in = new(Message)
}
return c.Go(
"EchoService.Echo",
in, out,
done,
)
}
// DialEchoService connects to an EchoService at the specified network address.
func DialEchoService(network, addr string) (*EchoServiceClient, error) {
c, err := protorpc.Dial(network, addr)
if err != nil {
return nil, err
}
return &EchoServiceClient{c}, nil
}
// DialEchoServiceTimeout connects to an EchoService at the specified network address.
func DialEchoServiceTimeout(network, addr string, timeout time.Duration) (*EchoServiceClient, error) {
c, err := protorpc.DialTimeout(network, addr, timeout)
if err != nil {
return nil, err
}
return &EchoServiceClient{c}, nil
}

View File

@ -0,0 +1,41 @@
// Copyright 2015 <chaishushan{AT}gmail.com>. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
syntax = "proto3";
package proto3_proto;
message Message {
enum Humour {
UNKNOWN = 0;
PUNS = 1;
SLAPSTICK = 2;
BILL_BAILEY = 3;
}
string name = 1;
Humour hilarity = 2;
uint32 height_in_cm = 3;
bytes data = 4;
int64 result_count = 7;
bool true_scotsman = 8;
float score = 9;
repeated uint64 key = 5;
Nested nested = 6;
map<string, Nested> terrain = 10;
}
message Nested {
string bunny = 1;
}
message MessageWithMap {
map<bool, bytes> byte_mapping = 1;
}
service EchoService {
rpc Echo(Message) returns (Message);
}

View File

@ -0,0 +1,72 @@
// Copyright 2015 <chaishushan{AT}gmail.com>. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package proto3_proto
import (
"bytes"
"encoding/gob"
"log"
"os"
"reflect"
"testing"
"time"
)
type tEchoService struct {
private int
}
func (p *tEchoService) Echo(in *Message, out *Message) error {
var buf bytes.Buffer
if err := gob.NewEncoder(&buf).Encode(in); err != nil {
return err
}
if err := gob.NewDecoder(bytes.NewBuffer(buf.Bytes())).Decode(out); err != nil {
return err
}
return nil
}
func TestMain(m *testing.M) {
go func() {
if err := ListenAndServeEchoService("tcp", "127.0.0.1:3000", new(tEchoService)); err != nil {
log.Fatal(err)
}
}()
time.Sleep(time.Second * 3) // wait for start the server
os.Exit(m.Run())
}
func TestEchoService(t *testing.T) {
c, err := DialEchoService("tcp", "127.0.0.1:3000")
if err != nil {
t.Fatal(err)
}
defer c.Close()
in := Message{
Name: "github.com/chai2010/protorpc",
Hilarity: Message_PUNS,
HeightInCm: 13,
Data: []byte("bin data"),
ResultCount: 2<<35 + 1,
TrueScotsman: true,
Score: 3.14,
Key: []uint64{1, 1001},
Nested: &Nested{Bunny: "{{Bunny}}"},
Terrain: map[string]*Nested{
"A": &Nested{Bunny: "{{A}}"},
"B": &Nested{Bunny: "{{B}}"},
},
}
out, err := c.Echo(&in)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(&in, out) {
t.Fatalf("not euqal, got = %v\n", &out)
}
}

View File

@ -0,0 +1,14 @@
# Copyright 2013 <chaishushan{AT}gmail.com>. All rights reserved.
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file.
PROTO_FILES=$(sort $(wildcard ./*.proto))
default: $(PROTO_FILES) Makefile
go install github.com/golang/protobuf/protoc-gen-go
go install github.com/chai2010/protorpc/protoc-gen-protorpc
protoc --go_out=. ${PROTO_FILES}
ENV_PROTOC_GEN_PROTORPC_FLAG_PREFIX= protoc --protorpc_out=. ${PROTO_FILES}
go test
clean:

View File

@ -0,0 +1,193 @@
// Copyright 2013 <chaishushan{AT}gmail.com>. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package service
import (
"log"
"net"
"net/rpc"
"testing"
"github.com/chai2010/protorpc"
)
func init() {
err := listenAndServeArithAndEchoService("tcp", "127.0.0.1:1984")
if err != nil {
log.Fatalf("listenAndServeArithAndEchoService: %v", err)
}
}
func TestAll(t *testing.T) {
conn, err := net.Dial("tcp", "127.0.0.1:1984")
if err != nil {
t.Fatalf(`net.Dial("tcp", "127.0.0.1:1984"): %v`, err)
}
client := rpc.NewClientWithCodec(protorpc.NewClientCodec(conn))
defer client.Close()
testArithClient(t, client)
testEchoClient(t, client)
arithStub := &ArithServiceClient{client}
echoStub := &EchoServiceClient{client}
testArithStub(t, arithStub)
testEchoStub(t, echoStub)
}
func listenAndServeArithAndEchoService(network, addr string) error {
clients, err := net.Listen(network, addr)
if err != nil {
return err
}
srv := rpc.NewServer()
if err := RegisterArithService(srv, new(Arith)); err != nil {
return err
}
if err := RegisterEchoService(srv, new(Echo)); err != nil {
return err
}
go func() {
for {
conn, err := clients.Accept()
if err != nil {
log.Printf("clients.Accept(): %v\n", err)
continue
}
go srv.ServeCodec(protorpc.NewServerCodec(conn))
}
}()
return nil
}
func testArithClient(t *testing.T, client *rpc.Client) {
var args ArithRequest
var reply ArithResponse
var err error
// Add
args.A = 1
args.B = 2
if err = client.Call("ArithService.Add", &args, &reply); err != nil {
t.Fatalf(`arith.Add: %v`, err)
}
if reply.C != 3 {
t.Fatalf(`arith.Add: expected = %d, got = %d`, 3, reply.C)
}
// Mul
args.A = 2
args.B = 3
if err = client.Call("ArithService.Mul", &args, &reply); err != nil {
t.Fatalf(`arith.Mul: %v`, err)
}
if reply.C != 6 {
t.Fatalf(`arith.Mul: expected = %d, got = %d`, 6, reply.C)
}
// Div
args.A = 13
args.B = 5
if err = client.Call("ArithService.Div", &args, &reply); err != nil {
t.Fatalf(`arith.Div: %v`, err)
}
if reply.C != 2 {
t.Fatalf(`arith.Div: expected = %d, got = %d`, 2, reply.C)
}
// Div zero
args.A = 1
args.B = 0
if err = client.Call("ArithService.Div", &args, &reply); err.Error() != "divide by zero" {
t.Fatalf(`arith.Div: expected = "%s", got = "%s"`, "divide by zero", err.Error())
}
// Error
args.A = 1
args.B = 2
if err = client.Call("ArithService.Error", &args, &reply); err.Error() != "ArithError" {
t.Fatalf(`arith.Error: expected = "%s", got = "%s"`, "ArithError", err.Error())
}
}
func testEchoClient(t *testing.T, client *rpc.Client) {
var args EchoRequest
var reply EchoResponse
var err error
// EchoService.Echo
args.Msg = "Hello, Protobuf-RPC"
if err = client.Call("EchoService.Echo", &args, &reply); err != nil {
t.Fatalf(`echo.Echo: %v`, err)
}
if reply.Msg != args.Msg {
t.Fatalf(`echo.Echo: expected = "%s", got = "%s"`, args.Msg, reply.Msg)
}
}
func testArithStub(t *testing.T, stub *ArithServiceClient) {
var args ArithRequest
var reply *ArithResponse
var err error
// Add
args.A = 1
args.B = 2
if reply, err = stub.Add(&args); err != nil {
t.Fatalf(`stub.Add: %v`, err)
}
if reply.C != 3 {
t.Fatalf(`stub.Add: expected = %d, got = %d`, 3, reply.C)
}
// Mul
args.A = 2
args.B = 3
if reply, err = stub.Mul(&args); err != nil {
t.Fatalf(`stub.Mul: %v`, err)
}
if reply.C != 6 {
t.Fatalf(`stub.Mul: expected = %d, got = %d`, 6, reply.C)
}
// Div
args.A = 13
args.B = 5
if reply, err = stub.Div(&args); err != nil {
t.Fatalf(`stub.Div: %v`, err)
}
if reply.C != 2 {
t.Fatalf(`stub.Div: expected = %d, got = %d`, 2, reply.C)
}
// Div zero
args.A = 1
args.B = 0
if reply, err = stub.Div(&args); err.Error() != "divide by zero" {
t.Fatalf(`stub.Div: expected = "%s", got = "%s"`, "divide by zero", err.Error())
}
// Error
args.A = 1
args.B = 2
if reply, err = stub.Error(&args); err.Error() != "ArithError" {
t.Fatalf(`stub.Error: expected = "%s", got = "%s"`, "ArithError", err.Error())
}
}
func testEchoStub(t *testing.T, stub *EchoServiceClient) {
var args EchoRequest
var reply *EchoResponse
var err error
// EchoService.Echo
args.Msg = "Hello, Protobuf-RPC"
if reply, err = stub.Echo(&args); err != nil {
t.Fatalf(`stub.Echo: %v`, err)
}
if reply.Msg != args.Msg {
t.Fatalf(`stub.Echo: expected = "%s", got = "%s"`, args.Msg, reply.Msg)
}
}

View File

@ -0,0 +1,33 @@
// Copyright 2013 <chaishushan{AT}gmail.com>. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package service
import (
"errors"
)
type Arith int
func (t *Arith) Add(args *ArithRequest, reply *ArithResponse) error {
reply.C = args.A + args.B
return nil
}
func (t *Arith) Mul(args *ArithRequest, reply *ArithResponse) error {
reply.C = args.A * args.B
return nil
}
func (t *Arith) Div(args *ArithRequest, reply *ArithResponse) error {
if args.B == 0 {
return errors.New("divide by zero")
}
reply.C = args.A / args.B
return nil
}
func (t *Arith) Error(args *ArithRequest, reply *ArithResponse) error {
return errors.New("ArithError")
}

View File

@ -0,0 +1,93 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: arith.proto
/*
Package service is a generated protocol buffer package.
It is generated from these files:
arith.proto
echo.proto
It has these top-level messages:
ArithRequest
ArithResponse
EchoRequest
EchoResponse
*/
package service
import proto "github.com/golang/protobuf/proto"
import fmt "fmt"
import math "math"
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
type ArithRequest struct {
A int32 `protobuf:"varint,1,opt,name=a" json:"a,omitempty"`
B int32 `protobuf:"varint,2,opt,name=b" json:"b,omitempty"`
}
func (m *ArithRequest) Reset() { *m = ArithRequest{} }
func (m *ArithRequest) String() string { return proto.CompactTextString(m) }
func (*ArithRequest) ProtoMessage() {}
func (*ArithRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
func (m *ArithRequest) GetA() int32 {
if m != nil {
return m.A
}
return 0
}
func (m *ArithRequest) GetB() int32 {
if m != nil {
return m.B
}
return 0
}
type ArithResponse struct {
C int32 `protobuf:"varint,1,opt,name=c" json:"c,omitempty"`
}
func (m *ArithResponse) Reset() { *m = ArithResponse{} }
func (m *ArithResponse) String() string { return proto.CompactTextString(m) }
func (*ArithResponse) ProtoMessage() {}
func (*ArithResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
func (m *ArithResponse) GetC() int32 {
if m != nil {
return m.C
}
return 0
}
func init() {
proto.RegisterType((*ArithRequest)(nil), "service.ArithRequest")
proto.RegisterType((*ArithResponse)(nil), "service.ArithResponse")
}
func init() { proto.RegisterFile("arith.proto", fileDescriptor0) }
var fileDescriptor0 = []byte{
// 159 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x4e, 0x2c, 0xca, 0x2c,
0xc9, 0xd0, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x2f, 0x4e, 0x2d, 0x2a, 0xcb, 0x4c, 0x4e,
0x55, 0xd2, 0xe2, 0xe2, 0x71, 0x04, 0x89, 0x07, 0xa5, 0x16, 0x96, 0xa6, 0x16, 0x97, 0x08, 0xf1,
0x70, 0x31, 0x26, 0x4a, 0x30, 0x2a, 0x30, 0x6a, 0xb0, 0x06, 0x31, 0x26, 0x82, 0x78, 0x49, 0x12,
0x4c, 0x10, 0x5e, 0x92, 0x92, 0x2c, 0x17, 0x2f, 0x54, 0x6d, 0x71, 0x41, 0x7e, 0x5e, 0x71, 0x2a,
0x48, 0x3a, 0x19, 0xa6, 0x38, 0xd9, 0xe8, 0x05, 0x23, 0xd4, 0xac, 0x60, 0x88, 0xd9, 0x42, 0x26,
0x5c, 0xcc, 0x89, 0x29, 0x29, 0x42, 0xa2, 0x7a, 0x50, 0xcb, 0xf4, 0x90, 0x6d, 0x92, 0x12, 0x43,
0x17, 0x86, 0x1a, 0x6a, 0xc2, 0xc5, 0x9c, 0x5b, 0x9a, 0x43, 0x86, 0xae, 0x94, 0xcc, 0x32, 0x52,
0x75, 0x99, 0x71, 0xb1, 0xa6, 0x16, 0x15, 0xe5, 0x17, 0x91, 0xa8, 0x2f, 0x89, 0x0d, 0x1c, 0x8a,
0xc6, 0x80, 0x00, 0x00, 0x00, 0xff, 0xff, 0xa2, 0x62, 0xfd, 0xbd, 0x54, 0x01, 0x00, 0x00,
}

View File

@ -0,0 +1,291 @@
// Code generated by protoc-gen-protorpc. DO NOT EDIT.
//
// plugin: https://github.com/chai2010/protorpc/tree/master/protoc-gen-plugin
// plugin: https://github.com/chai2010/protorpc/tree/master/protoc-gen-protorpc
//
// source: arith.proto
package service
import (
"fmt"
"io"
"log"
"net"
"net/rpc"
"time"
"github.com/chai2010/protorpc"
"github.com/golang/protobuf/proto"
)
var (
_ = fmt.Sprint
_ = io.Reader(nil)
_ = log.Print
_ = net.Addr(nil)
_ = rpc.Call{}
_ = time.Second
_ = proto.String
_ = protorpc.Dial
)
type ArithService interface {
Add(in *ArithRequest, out *ArithResponse) error
Mul(in *ArithRequest, out *ArithResponse) error
Div(in *ArithRequest, out *ArithResponse) error
Error(in *ArithRequest, out *ArithResponse) error
}
// AcceptArithServiceClient accepts connections on the listener and serves requests
// for each incoming connection. Accept blocks; the caller typically
// invokes it in a go statement.
func AcceptArithServiceClient(lis net.Listener, x ArithService) {
srv := rpc.NewServer()
if err := srv.RegisterName("ArithService", x); err != nil {
log.Fatal(err)
}
for {
conn, err := lis.Accept()
if err != nil {
log.Fatalf("lis.Accept(): %v\n", err)
}
go srv.ServeCodec(protorpc.NewServerCodec(conn))
}
}
// RegisterArithService publish the given ArithService implementation on the server.
func RegisterArithService(srv *rpc.Server, x ArithService) error {
if err := srv.RegisterName("ArithService", x); err != nil {
return err
}
return nil
}
// NewArithServiceServer returns a new ArithService Server.
func NewArithServiceServer(x ArithService) *rpc.Server {
srv := rpc.NewServer()
if err := srv.RegisterName("ArithService", x); err != nil {
log.Fatal(err)
}
return srv
}
// ListenAndServeArithService listen announces on the local network address laddr
// and serves the given ArithService implementation.
func ListenAndServeArithService(network, addr string, x ArithService) error {
lis, err := net.Listen(network, addr)
if err != nil {
return err
}
defer lis.Close()
srv := rpc.NewServer()
if err := srv.RegisterName("ArithService", x); err != nil {
return err
}
for {
conn, err := lis.Accept()
if err != nil {
log.Fatalf("lis.Accept(): %v\n", err)
}
go srv.ServeCodec(protorpc.NewServerCodec(conn))
}
}
// ServeArithService serves the given ArithService implementation.
func ServeArithService(conn io.ReadWriteCloser, x ArithService) {
srv := rpc.NewServer()
if err := srv.RegisterName("ArithService", x); err != nil {
log.Fatal(err)
}
srv.ServeCodec(protorpc.NewServerCodec(conn))
}
type ArithServiceClient struct {
*rpc.Client
}
// NewArithServiceClient returns a ArithService stub to handle
// requests to the set of ArithService at the other end of the connection.
func NewArithServiceClient(conn io.ReadWriteCloser) *ArithServiceClient {
c := rpc.NewClientWithCodec(protorpc.NewClientCodec(conn))
return &ArithServiceClient{c}
}
func (c *ArithServiceClient) Add(in *ArithRequest) (out *ArithResponse, err error) {
if in == nil {
in = new(ArithRequest)
}
type Validator interface {
Validate() error
}
if x, ok := proto.Message(in).(Validator); ok {
if err := x.Validate(); err != nil {
return nil, err
}
}
out = new(ArithResponse)
if err = c.Call("ArithService.Add", in, out); err != nil {
return nil, err
}
if x, ok := proto.Message(out).(Validator); ok {
if err := x.Validate(); err != nil {
return out, err
}
}
return out, nil
}
func (c *ArithServiceClient) AsyncAdd(in *ArithRequest, out *ArithResponse, done chan *rpc.Call) *rpc.Call {
if in == nil {
in = new(ArithRequest)
}
return c.Go(
"ArithService.Add",
in, out,
done,
)
}
func (c *ArithServiceClient) Mul(in *ArithRequest) (out *ArithResponse, err error) {
if in == nil {
in = new(ArithRequest)
}
type Validator interface {
Validate() error
}
if x, ok := proto.Message(in).(Validator); ok {
if err := x.Validate(); err != nil {
return nil, err
}
}
out = new(ArithResponse)
if err = c.Call("ArithService.Mul", in, out); err != nil {
return nil, err
}
if x, ok := proto.Message(out).(Validator); ok {
if err := x.Validate(); err != nil {
return out, err
}
}
return out, nil
}
func (c *ArithServiceClient) AsyncMul(in *ArithRequest, out *ArithResponse, done chan *rpc.Call) *rpc.Call {
if in == nil {
in = new(ArithRequest)
}
return c.Go(
"ArithService.Mul",
in, out,
done,
)
}
func (c *ArithServiceClient) Div(in *ArithRequest) (out *ArithResponse, err error) {
if in == nil {
in = new(ArithRequest)
}
type Validator interface {
Validate() error
}
if x, ok := proto.Message(in).(Validator); ok {
if err := x.Validate(); err != nil {
return nil, err
}
}
out = new(ArithResponse)
if err = c.Call("ArithService.Div", in, out); err != nil {
return nil, err
}
if x, ok := proto.Message(out).(Validator); ok {
if err := x.Validate(); err != nil {
return out, err
}
}
return out, nil
}
func (c *ArithServiceClient) AsyncDiv(in *ArithRequest, out *ArithResponse, done chan *rpc.Call) *rpc.Call {
if in == nil {
in = new(ArithRequest)
}
return c.Go(
"ArithService.Div",
in, out,
done,
)
}
func (c *ArithServiceClient) Error(in *ArithRequest) (out *ArithResponse, err error) {
if in == nil {
in = new(ArithRequest)
}
type Validator interface {
Validate() error
}
if x, ok := proto.Message(in).(Validator); ok {
if err := x.Validate(); err != nil {
return nil, err
}
}
out = new(ArithResponse)
if err = c.Call("ArithService.Error", in, out); err != nil {
return nil, err
}
if x, ok := proto.Message(out).(Validator); ok {
if err := x.Validate(); err != nil {
return out, err
}
}
return out, nil
}
func (c *ArithServiceClient) AsyncError(in *ArithRequest, out *ArithResponse, done chan *rpc.Call) *rpc.Call {
if in == nil {
in = new(ArithRequest)
}
return c.Go(
"ArithService.Error",
in, out,
done,
)
}
// DialArithService connects to an ArithService at the specified network address.
func DialArithService(network, addr string) (*ArithServiceClient, error) {
c, err := protorpc.Dial(network, addr)
if err != nil {
return nil, err
}
return &ArithServiceClient{c}, nil
}
// DialArithServiceTimeout connects to an ArithService at the specified network address.
func DialArithServiceTimeout(network, addr string, timeout time.Duration) (*ArithServiceClient, error) {
c, err := protorpc.DialTimeout(network, addr, timeout)
if err != nil {
return nil, err
}
return &ArithServiceClient{c}, nil
}

View File

@ -0,0 +1,23 @@
// Copyright 2013 <chaishushan{AT}gmail.com>. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
syntax = "proto3";
package service;
message ArithRequest {
int32 a = 1;
int32 b = 2;
}
message ArithResponse {
int32 c = 1;
}
service ArithService {
rpc add (ArithRequest) returns (ArithResponse);
rpc mul (ArithRequest) returns (ArithResponse);
rpc div (ArithRequest) returns (ArithResponse);
rpc error (ArithRequest) returns (ArithResponse);
}

View File

@ -0,0 +1,34 @@
// Copyright 2013 <chaishushan{AT}gmail.com>. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package service
import (
"fmt"
"log"
"sync"
)
var (
arithHost = "127.0.0.1"
arithPort = 2010
onceArith sync.Once
)
func setupArithServer() {
var wg sync.WaitGroup
wg.Add(1)
defer wg.Wait()
go func() {
wg.Done()
addr := fmt.Sprintf("127.0.0.1:%d", arithPort)
err := ListenAndServeArithService("tcp", addr, new(Arith))
if err != nil {
log.Fatalf("ListenAndServeArithService: %v", err)
}
}()
}

View File

@ -0,0 +1,17 @@
// Copyright 2013 <chaishushan{AT}gmail.com>. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package service
type Echo int
func (t *Echo) Echo(args *EchoRequest, reply *EchoResponse) error {
reply.Msg = args.Msg
return nil
}
func (t *Echo) EchoTwice(args *EchoRequest, reply *EchoResponse) error {
reply.Msg = args.Msg + args.Msg
return nil
}

View File

@ -0,0 +1,65 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: echo.proto
package service
import proto "github.com/golang/protobuf/proto"
import fmt "fmt"
import math "math"
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
type EchoRequest struct {
Msg string `protobuf:"bytes,1,opt,name=msg" json:"msg,omitempty"`
}
func (m *EchoRequest) Reset() { *m = EchoRequest{} }
func (m *EchoRequest) String() string { return proto.CompactTextString(m) }
func (*EchoRequest) ProtoMessage() {}
func (*EchoRequest) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{0} }
func (m *EchoRequest) GetMsg() string {
if m != nil {
return m.Msg
}
return ""
}
type EchoResponse struct {
Msg string `protobuf:"bytes,1,opt,name=msg" json:"msg,omitempty"`
}
func (m *EchoResponse) Reset() { *m = EchoResponse{} }
func (m *EchoResponse) String() string { return proto.CompactTextString(m) }
func (*EchoResponse) ProtoMessage() {}
func (*EchoResponse) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{1} }
func (m *EchoResponse) GetMsg() string {
if m != nil {
return m.Msg
}
return ""
}
func init() {
proto.RegisterType((*EchoRequest)(nil), "service.EchoRequest")
proto.RegisterType((*EchoResponse)(nil), "service.EchoResponse")
}
func init() { proto.RegisterFile("echo.proto", fileDescriptor1) }
var fileDescriptor1 = []byte{
// 134 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x4a, 0x4d, 0xce, 0xc8,
0xd7, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x2f, 0x4e, 0x2d, 0x2a, 0xcb, 0x4c, 0x4e, 0x55,
0x92, 0xe7, 0xe2, 0x76, 0x4d, 0xce, 0xc8, 0x0f, 0x4a, 0x2d, 0x2c, 0x4d, 0x2d, 0x2e, 0x11, 0x12,
0xe0, 0x62, 0xce, 0x2d, 0x4e, 0x97, 0x60, 0x54, 0x60, 0xd4, 0xe0, 0x0c, 0x02, 0x31, 0x95, 0x14,
0xb8, 0x78, 0x20, 0x0a, 0x8a, 0x0b, 0xf2, 0xf3, 0x8a, 0x53, 0x31, 0x55, 0x18, 0xd5, 0x40, 0x8c,
0x08, 0x86, 0x98, 0x28, 0x64, 0xcc, 0xc5, 0x02, 0xe2, 0x0a, 0x89, 0xe8, 0x41, 0xed, 0xd0, 0x43,
0xb2, 0x40, 0x4a, 0x14, 0x4d, 0x14, 0x6a, 0xaa, 0x05, 0x17, 0x27, 0x88, 0x1f, 0x52, 0x0e, 0x32,
0x81, 0x14, 0x9d, 0x49, 0x6c, 0x60, 0x0f, 0x19, 0x03, 0x02, 0x00, 0x00, 0xff, 0xff, 0x00, 0xc1,
0xd4, 0xbd, 0xde, 0x00, 0x00, 0x00,
}

View File

@ -0,0 +1,211 @@
// Code generated by protoc-gen-protorpc. DO NOT EDIT.
//
// plugin: https://github.com/chai2010/protorpc/tree/master/protoc-gen-plugin
// plugin: https://github.com/chai2010/protorpc/tree/master/protoc-gen-protorpc
//
// source: echo.proto
package service
import (
"fmt"
"io"
"log"
"net"
"net/rpc"
"time"
"github.com/chai2010/protorpc"
"github.com/golang/protobuf/proto"
)
var (
_ = fmt.Sprint
_ = io.Reader(nil)
_ = log.Print
_ = net.Addr(nil)
_ = rpc.Call{}
_ = time.Second
_ = proto.String
_ = protorpc.Dial
)
type EchoService interface {
Echo(in *EchoRequest, out *EchoResponse) error
EchoTwice(in *EchoRequest, out *EchoResponse) error
}
// AcceptEchoServiceClient accepts connections on the listener and serves requests
// for each incoming connection. Accept blocks; the caller typically
// invokes it in a go statement.
func AcceptEchoServiceClient(lis net.Listener, x EchoService) {
srv := rpc.NewServer()
if err := srv.RegisterName("EchoService", x); err != nil {
log.Fatal(err)
}
for {
conn, err := lis.Accept()
if err != nil {
log.Fatalf("lis.Accept(): %v\n", err)
}
go srv.ServeCodec(protorpc.NewServerCodec(conn))
}
}
// RegisterEchoService publish the given EchoService implementation on the server.
func RegisterEchoService(srv *rpc.Server, x EchoService) error {
if err := srv.RegisterName("EchoService", x); err != nil {
return err
}
return nil
}
// NewEchoServiceServer returns a new EchoService Server.
func NewEchoServiceServer(x EchoService) *rpc.Server {
srv := rpc.NewServer()
if err := srv.RegisterName("EchoService", x); err != nil {
log.Fatal(err)
}
return srv
}
// ListenAndServeEchoService listen announces on the local network address laddr
// and serves the given EchoService implementation.
func ListenAndServeEchoService(network, addr string, x EchoService) error {
lis, err := net.Listen(network, addr)
if err != nil {
return err
}
defer lis.Close()
srv := rpc.NewServer()
if err := srv.RegisterName("EchoService", x); err != nil {
return err
}
for {
conn, err := lis.Accept()
if err != nil {
log.Fatalf("lis.Accept(): %v\n", err)
}
go srv.ServeCodec(protorpc.NewServerCodec(conn))
}
}
// ServeEchoService serves the given EchoService implementation.
func ServeEchoService(conn io.ReadWriteCloser, x EchoService) {
srv := rpc.NewServer()
if err := srv.RegisterName("EchoService", x); err != nil {
log.Fatal(err)
}
srv.ServeCodec(protorpc.NewServerCodec(conn))
}
type EchoServiceClient struct {
*rpc.Client
}
// NewEchoServiceClient returns a EchoService stub to handle
// requests to the set of EchoService at the other end of the connection.
func NewEchoServiceClient(conn io.ReadWriteCloser) *EchoServiceClient {
c := rpc.NewClientWithCodec(protorpc.NewClientCodec(conn))
return &EchoServiceClient{c}
}
func (c *EchoServiceClient) Echo(in *EchoRequest) (out *EchoResponse, err error) {
if in == nil {
in = new(EchoRequest)
}
type Validator interface {
Validate() error
}
if x, ok := proto.Message(in).(Validator); ok {
if err := x.Validate(); err != nil {
return nil, err
}
}
out = new(EchoResponse)
if err = c.Call("EchoService.Echo", in, out); err != nil {
return nil, err
}
if x, ok := proto.Message(out).(Validator); ok {
if err := x.Validate(); err != nil {
return out, err
}
}
return out, nil
}
func (c *EchoServiceClient) AsyncEcho(in *EchoRequest, out *EchoResponse, done chan *rpc.Call) *rpc.Call {
if in == nil {
in = new(EchoRequest)
}
return c.Go(
"EchoService.Echo",
in, out,
done,
)
}
func (c *EchoServiceClient) EchoTwice(in *EchoRequest) (out *EchoResponse, err error) {
if in == nil {
in = new(EchoRequest)
}
type Validator interface {
Validate() error
}
if x, ok := proto.Message(in).(Validator); ok {
if err := x.Validate(); err != nil {
return nil, err
}
}
out = new(EchoResponse)
if err = c.Call("EchoService.EchoTwice", in, out); err != nil {
return nil, err
}
if x, ok := proto.Message(out).(Validator); ok {
if err := x.Validate(); err != nil {
return out, err
}
}
return out, nil
}
func (c *EchoServiceClient) AsyncEchoTwice(in *EchoRequest, out *EchoResponse, done chan *rpc.Call) *rpc.Call {
if in == nil {
in = new(EchoRequest)
}
return c.Go(
"EchoService.EchoTwice",
in, out,
done,
)
}
// DialEchoService connects to an EchoService at the specified network address.
func DialEchoService(network, addr string) (*EchoServiceClient, error) {
c, err := protorpc.Dial(network, addr)
if err != nil {
return nil, err
}
return &EchoServiceClient{c}, nil
}
// DialEchoServiceTimeout connects to an EchoService at the specified network address.
func DialEchoServiceTimeout(network, addr string, timeout time.Duration) (*EchoServiceClient, error) {
c, err := protorpc.DialTimeout(network, addr, timeout)
if err != nil {
return nil, err
}
return &EchoServiceClient{c}, nil
}

View File

@ -0,0 +1,20 @@
// Copyright 2013 <chaishushan{AT}gmail.com>. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
syntax = "proto3";
package service;
message EchoRequest {
string msg = 1;
}
message EchoResponse {
string msg = 1;
}
service EchoService {
rpc Echo (EchoRequest) returns (EchoResponse);
rpc EchoTwice (EchoRequest) returns (EchoResponse);
}

View File

@ -0,0 +1,698 @@
// Copyright 2013 <chaishushan{AT}gmail.com>. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package service
import (
"fmt"
"log"
"net/rpc"
"sync"
"testing"
"unicode/utf8"
)
var (
echoHost = "127.0.0.1"
echoPort = 2015
echoRequest = "Hello, new gopher!"
echoResponse = echoRequest + echoRequest
echoMassiveRequest = makeMassive("Hello, 世界.")
echoMassiveResponse = echoMassiveRequest + echoMassiveRequest
onceEcho sync.Once
)
func makeMassive(args string) string {
runeLen := utf8.RuneCountInString(args)
runeBuf := make([]rune, runeLen*1024*100)
for i := 0; i < 1024*100; i++ {
offset := i * runeLen
j := 0
for _, r := range args {
runeBuf[offset+j] = r
j++
}
}
return string(runeBuf)
}
func setupEchoServer() {
var wg sync.WaitGroup
wg.Add(1)
defer wg.Wait()
go func() {
wg.Done()
addr := fmt.Sprintf("127.0.0.1:%d", echoPort)
err := ListenAndServeEchoService("tcp", addr, new(Echo))
if err != nil {
log.Fatalf("ListenAndServeEchoService: %v", err)
}
}()
}
func TestEchoService(t *testing.T) {
onceEcho.Do(setupEchoServer)
addr := fmt.Sprintf("%s:%d", echoHost, echoPort)
c, err := DialEchoService("tcp", addr)
if err != nil {
t.Fatalf(
`net.Dial("tcp", "%s:%d"): %v`,
echoHost, echoPort,
err,
)
}
defer c.Close()
testEchoService(t, c.Client)
}
func testEchoService(t *testing.T, client *rpc.Client) {
var args EchoRequest
var reply EchoResponse
var err error
// EchoService.EchoTwice
args.Msg = echoRequest
err = client.Call("EchoService.EchoTwice", &args, &reply)
if err != nil {
t.Fatalf(`EchoService.EchoTwice: %v`, err)
}
if reply.Msg != echoResponse {
t.Fatalf(
`EchoService.EchoTwice: expected = "%s", got = "%s"`,
echoResponse, reply.Msg,
)
}
// EchoService.EchoTwice (Massive)
args.Msg = echoMassiveRequest
err = client.Call("EchoService.EchoTwice", &args, &reply)
if err != nil {
t.Fatalf(`EchoService.EchoTwice: %v`, err)
}
if reply.Msg != echoMassiveResponse {
got := reply.Msg
if len(got) > 8 {
got = got[:8] + "..."
}
t.Fatalf(`EchoService.EchoTwice: len = %d, got = %v`,
len(reply.Msg), got,
)
}
}
func TestClientSyncEcho(t *testing.T) {
onceEcho.Do(setupEchoServer)
addr := fmt.Sprintf("%s:%d", echoHost, echoPort)
echoClient, err := DialEchoService("tcp", addr)
if err != nil {
t.Fatalf(
`net.Dial("tcp", "%s:%d"): %v`,
echoHost, echoPort,
err,
)
}
defer echoClient.Close()
var args EchoRequest
var reply *EchoResponse
// EchoService.EchoTwice
args.Msg = "abc"
reply, err = echoClient.EchoTwice(&args)
if err != nil {
t.Fatalf(`EchoService.EchoTwice: %v`, err)
}
if reply.Msg != args.Msg+args.Msg {
t.Fatalf(
`EchoService.EchoTwice: expected = "%s", got = "%s"`,
args.Msg+args.Msg, reply.Msg,
)
}
// EchoService.EchoTwice
args.Msg = "你好, 世界"
reply, err = echoClient.EchoTwice(&args)
if err != nil {
t.Fatalf(`EchoService.EchoTwice: %v`, err)
}
if reply.Msg != args.Msg+args.Msg {
t.Fatalf(
`EchoService.EchoTwice: expected = "%s", got = "%s"`,
args.Msg+args.Msg, reply.Msg,
)
}
}
func TestClientSyncMassive(t *testing.T) {
onceEcho.Do(setupEchoServer)
addr := fmt.Sprintf("%s:%d", echoHost, echoPort)
echoClient, err := DialEchoService("tcp", addr)
if err != nil {
t.Fatalf(
`net.Dial("tcp", "%s:%d"): %v`,
echoHost, echoPort,
err,
)
}
defer echoClient.Close()
var args EchoRequest
var reply *EchoResponse
// EchoService.EchoTwice
args.Msg = echoMassiveRequest + "abc"
reply, err = echoClient.EchoTwice(&args)
if err != nil {
t.Fatalf(`EchoService.EchoTwice: %v`, err)
}
if reply.Msg != args.Msg+args.Msg {
got := reply.Msg
if len(got) > 8 {
got = got[:8] + "..."
}
t.Fatalf(`EchoService.EchoTwice: len = %d, got = %v`,
len(reply.Msg), got,
)
}
// EchoService.EchoTwice
args.Msg = echoMassiveRequest + "你好, 世界"
reply, err = echoClient.EchoTwice(&args)
if err != nil {
t.Fatalf(`EchoService.EchoTwice: %v`, err)
}
if reply.Msg != args.Msg+args.Msg {
got := reply.Msg
if len(got) > 8 {
got = got[:8] + "..."
}
t.Fatalf(`EchoService.EchoTwice: len = %d, got = %v`,
len(reply.Msg), got,
)
}
}
func TestClientAsyncEcho(t *testing.T) {
onceEcho.Do(setupEchoServer)
addr := fmt.Sprintf("%s:%d", echoHost, echoPort)
client, err := DialEchoService("tcp", addr)
if err != nil {
t.Fatalf(
`net.Dial("tcp", "%s:%d"): %v`,
echoHost, echoPort,
err,
)
}
defer client.Close()
var args EchoRequest
var reply EchoResponse
// EchoService.EchoTwice
args.Msg = echoRequest
call := client.Go("EchoService.EchoTwice", &args, &reply, nil)
call = <-call.Done
if call.Error != nil {
t.Fatalf(`EchoService.EchoTwice: %v`, call.Error)
}
if call.Reply.(*EchoResponse).Msg != echoResponse {
t.Fatalf(
`EchoService.EchoTwice: expected = "%s", got = "%s"`,
echoResponse, call.Reply.(*EchoResponse).Msg,
)
}
}
func TestClientAsyncEchoBatches(t *testing.T) {
onceEcho.Do(setupEchoServer)
addr := fmt.Sprintf("%s:%d", echoHost, echoPort)
client, err := DialEchoService("tcp", addr)
if err != nil {
t.Fatalf(
`net.Dial("tcp", "%s:%d"): %v`,
echoHost, echoPort,
err,
)
}
defer client.Close()
var args1 EchoRequest
var reply1 EchoResponse
var args2 EchoRequest
var reply2 EchoResponse
var args3 EchoRequest
var reply3 EchoResponse
// EchoService.EchoTwice
args1.Msg = "abc"
call1 := client.Go("EchoService.EchoTwice", &args1, &reply1, nil)
args2.Msg = "你好, 世界"
call2 := client.Go("EchoService.EchoTwice", &args2, &reply2, nil)
args3.Msg = "Hello, 世界"
call3 := client.Go("EchoService.EchoTwice", &args3, &reply3, nil)
call1 = <-call1.Done
call2 = <-call2.Done
call3 = <-call3.Done
// call1
if call1.Error != nil {
t.Fatalf(`EchoService.EchoTwice: %v`, call1.Error)
}
if call1.Reply.(*EchoResponse).Msg != args1.Msg+args1.Msg {
t.Fatalf(
`EchoService.EchoTwice: expected = "%s", got = "%s"`,
args1.Msg+args1.Msg,
call1.Reply.(*EchoResponse).Msg,
)
}
// call2
if call2.Error != nil {
t.Fatalf(`EchoService.EchoTwice: %v`, call2.Error)
}
if call2.Reply.(*EchoResponse).Msg != args2.Msg+args2.Msg {
t.Fatalf(
`EchoService.EchoTwice: expected = "%s", got = "%s"`,
args2.Msg+args2.Msg,
call2.Reply.(*EchoResponse).Msg,
)
}
// call3
if call3.Error != nil {
t.Fatalf(`EchoService.EchoTwice: %v`, call3.Error)
}
if call3.Reply.(*EchoResponse).Msg != args3.Msg+args3.Msg {
t.Fatalf(
`EchoService.EchoTwice: expected = "%s", got = "%s"`,
args3.Msg+args3.Msg,
call3.Reply.(*EchoResponse).Msg,
)
}
}
func TestClientAsyncMassive(t *testing.T) {
onceEcho.Do(setupEchoServer)
addr := fmt.Sprintf("%s:%d", echoHost, echoPort)
client, err := DialEchoService("tcp", addr)
if err != nil {
t.Fatalf(
`net.Dial("tcp", "%s:%d"): %v`,
echoHost, echoPort,
err,
)
}
defer client.Close()
var args EchoRequest
var reply EchoResponse
// EchoService.EchoTwice
args.Msg = echoMassiveRequest
call := client.Go("EchoService.EchoTwice", &args, &reply, nil)
call = <-call.Done
if call.Error != nil {
t.Fatalf(`EchoService.EchoTwice: %v`, call.Error)
}
if call.Reply.(*EchoResponse).Msg != echoMassiveResponse {
got := call.Reply.(*EchoResponse).Msg
if len(got) > 8 {
got = got[:8] + "..."
}
t.Fatalf(`EchoService.EchoTwice: len = %d, got = %v`,
len(call.Reply.(*EchoResponse).Msg), got,
)
}
}
func TestClientAsyncMassiveBatches(t *testing.T) {
onceEcho.Do(setupEchoServer)
addr := fmt.Sprintf("%s:%d", echoHost, echoPort)
client, err := DialEchoService("tcp", addr)
if err != nil {
t.Fatalf(
`net.Dial("tcp", "%s:%d"): %v`,
echoHost, echoPort,
err,
)
}
defer client.Close()
var args1 EchoRequest
var reply1 EchoResponse
var args2 EchoRequest
var reply2 EchoResponse
var args3 EchoRequest
var reply3 EchoResponse
// EchoService.EchoTwice
args1.Msg = echoMassiveRequest + "abc"
call1 := client.Go("EchoService.EchoTwice", &args1, &reply1, nil)
args2.Msg = echoMassiveRequest + "你好, 世界"
call2 := client.Go("EchoService.EchoTwice", &args2, &reply2, nil)
args3.Msg = echoMassiveRequest + "Hello, 世界"
call3 := client.Go("EchoService.EchoTwice", &args3, &reply3, nil)
call1 = <-call1.Done
call2 = <-call2.Done
call3 = <-call3.Done
// call1
if call1.Error != nil {
t.Fatalf(`EchoService.EchoTwice: %v`, call1.Error)
}
if call1.Reply.(*EchoResponse).Msg != args1.Msg+args1.Msg {
got := call1.Reply.(*EchoResponse).Msg
if len(got) > 8 {
got = got[:8] + "..."
}
t.Fatalf(`EchoService.EchoTwice: len = %d, got = %v`,
len(call1.Reply.(*EchoResponse).Msg), got,
)
}
// call2
if call2.Error != nil {
t.Fatalf(`EchoService.EchoTwice: %v`, call2.Error)
}
if call2.Reply.(*EchoResponse).Msg != args2.Msg+args2.Msg {
got := call2.Reply.(*EchoResponse).Msg
if len(got) > 8 {
got = got[:8] + "..."
}
t.Fatalf(`EchoService.EchoTwice: len = %d, got = %v`,
len(call2.Reply.(*EchoResponse).Msg), got,
)
}
// call3
if call3.Error != nil {
t.Fatalf(`EchoService.EchoTwice: %v`, call3.Error)
}
if call3.Reply.(*EchoResponse).Msg != args3.Msg+args3.Msg {
got := call3.Reply.(*EchoResponse).Msg
if len(got) > 8 {
got = got[:8] + "..."
}
t.Fatalf(`EchoService.EchoTwice: len = %d, got = %v`,
len(call3.Reply.(*EchoResponse).Msg), got,
)
}
}
func BenchmarkSyncEcho(b *testing.B) {
onceEcho.Do(setupEchoServer)
addr := fmt.Sprintf("%s:%d", echoHost, echoPort)
echoClient, err := DialEchoService("tcp", addr)
if err != nil {
b.Fatalf(
`net.Dial("tcp", "%s:%d"): %v`,
echoHost, echoPort,
err,
)
}
defer echoClient.Close()
b.ResetTimer()
for i := 0; i < b.N; i++ {
var args EchoRequest
var reply *EchoResponse
// EchoService.EchoTwice
args.Msg = "abc"
reply, err = echoClient.EchoTwice(&args)
if err != nil {
b.Fatalf(`EchoService.EchoTwice: %v`, err)
}
if reply.Msg != args.Msg+args.Msg {
b.Fatalf(
`EchoService.EchoTwice: expected = "%s", got = "%s"`,
args.Msg+args.Msg, reply.Msg,
)
}
// EchoService.EchoTwice
args.Msg = "你好, 世界"
reply, err = echoClient.EchoTwice(&args)
if err != nil {
b.Fatalf(`EchoService.EchoTwice: %v`, err)
}
if reply.Msg != args.Msg+args.Msg {
b.Fatalf(
`EchoService.EchoTwice: expected = "%s", got = "%s"`,
args.Msg+args.Msg, reply.Msg,
)
}
// EchoService.EchoTwice
args.Msg = "Hello, 世界"
reply, err = echoClient.EchoTwice(&args)
if err != nil {
b.Fatalf(`EchoService.EchoTwice: %v`, err)
}
if reply.Msg != args.Msg+args.Msg {
b.Fatalf(
`EchoService.EchoTwice: expected = "%s", got = "%s"`,
args.Msg+args.Msg, reply.Msg,
)
}
}
}
func BenchmarkSyncMassive(b *testing.B) {
onceEcho.Do(setupEchoServer)
addr := fmt.Sprintf("%s:%d", echoHost, echoPort)
echoClient, err := DialEchoService("tcp", addr)
if err != nil {
b.Fatalf(
`net.Dial("tcp", "%s:%d"): %v`,
echoHost, echoPort,
err,
)
}
defer echoClient.Close()
b.ResetTimer()
for i := 0; i < b.N; i++ {
var args EchoRequest
var reply *EchoResponse
// EchoService.EchoTwice
args.Msg = echoMassiveRequest + "abc"
reply, err = echoClient.EchoTwice(&args)
if err != nil {
b.Fatalf(`EchoService.EchoTwice: %v`, err)
}
if reply.Msg != args.Msg+args.Msg {
got := reply.Msg
if len(got) > 8 {
got = got[:8] + "..."
}
b.Fatalf(`EchoService.EchoTwice: len = %d, got = %v`,
len(reply.Msg), got,
)
}
// EchoService.EchoTwice
args.Msg = echoMassiveRequest + "你好, 世界"
reply, err = echoClient.EchoTwice(&args)
if err != nil {
b.Fatalf(`EchoService.EchoTwice: %v`, err)
}
if reply.Msg != args.Msg+args.Msg {
got := reply.Msg
if len(got) > 8 {
got = got[:8] + "..."
}
b.Fatalf(`EchoService.EchoTwice: len = %d, got = %v`,
len(reply.Msg), got,
)
}
// EchoService.EchoTwice
args.Msg = echoMassiveRequest + "Hello, 世界"
reply, err = echoClient.EchoTwice(&args)
if err != nil {
b.Fatalf(`EchoService.EchoTwice: %v`, err)
}
if reply.Msg != args.Msg+args.Msg {
got := reply.Msg
if len(got) > 8 {
got = got[:8] + "..."
}
b.Fatalf(`EchoService.EchoTwice: len = %d, got = %v`,
len(reply.Msg), got,
)
}
}
}
func BenchmarkAsyncEcho(b *testing.B) {
onceEcho.Do(setupEchoServer)
addr := fmt.Sprintf("%s:%d", echoHost, echoPort)
client, err := DialEchoService("tcp", addr)
if err != nil {
b.Fatalf(
`net.Dial("tcp", "%s:%d"): %v`,
echoHost, echoPort,
err,
)
}
defer client.Close()
b.ResetTimer()
for i := 0; i < b.N; i++ {
var args1 EchoRequest
var reply1 EchoResponse
var args2 EchoRequest
var reply2 EchoResponse
var args3 EchoRequest
var reply3 EchoResponse
// EchoService.EchoTwice
args1.Msg = "abc"
call1 := client.Go("EchoService.EchoTwice", &args1, &reply1, nil)
args2.Msg = "你好, 世界"
call2 := client.Go("EchoService.EchoTwice", &args2, &reply2, nil)
args3.Msg = "Hello, 世界"
call3 := client.Go("EchoService.EchoTwice", &args3, &reply3, nil)
call1 = <-call1.Done
call2 = <-call2.Done
call3 = <-call3.Done
// call1
if call1.Error != nil {
b.Fatalf(`EchoService.EchoTwice: %v`, call1.Error)
}
if call1.Reply.(*EchoResponse).Msg != args1.Msg+args1.Msg {
b.Fatalf(
`EchoService.EchoTwice: expected = "%s", got = "%s"`,
args1.Msg+args1.Msg,
call1.Reply.(*EchoResponse).Msg,
)
}
// call2
if call2.Error != nil {
b.Fatalf(`EchoService.EchoTwice: %v`, call2.Error)
}
if call2.Reply.(*EchoResponse).Msg != args2.Msg+args2.Msg {
b.Fatalf(
`EchoService.EchoTwice: expected = "%s", got = "%s"`,
args2.Msg+args2.Msg,
call2.Reply.(*EchoResponse).Msg,
)
}
// call3
if call3.Error != nil {
b.Fatalf(`EchoService.EchoTwice: %v`, call3.Error)
}
if call3.Reply.(*EchoResponse).Msg != args3.Msg+args3.Msg {
b.Fatalf(
`EchoService.EchoTwice: expected = "%s", got = "%s"`,
args3.Msg+args3.Msg,
call3.Reply.(*EchoResponse).Msg,
)
}
}
}
func BenchmarkAsyncMassive(b *testing.B) {
onceEcho.Do(setupEchoServer)
addr := fmt.Sprintf("%s:%d", echoHost, echoPort)
client, err := DialEchoService("tcp", addr)
if err != nil {
b.Fatalf(
`net.Dial("tcp", "%s:%d"): %v`,
echoHost, echoPort,
err,
)
}
defer client.Close()
b.ResetTimer()
for i := 0; i < b.N; i++ {
var args1 EchoRequest
var reply1 EchoResponse
var args2 EchoRequest
var reply2 EchoResponse
var args3 EchoRequest
var reply3 EchoResponse
// EchoService.EchoTwice
args1.Msg = echoMassiveRequest + "abc"
call1 := client.Go("EchoService.EchoTwice", &args1, &reply1, nil)
args2.Msg = echoMassiveRequest + "你好, 世界"
call2 := client.Go("EchoService.EchoTwice", &args2, &reply2, nil)
args3.Msg = echoMassiveRequest + "Hello, 世界"
call3 := client.Go("EchoService.EchoTwice", &args3, &reply3, nil)
call1 = <-call1.Done
call2 = <-call2.Done
call3 = <-call3.Done
// call1
if call1.Error != nil {
b.Fatalf(`EchoService.EchoTwice: %v`, call1.Error)
}
if call1.Reply.(*EchoResponse).Msg != args1.Msg+args1.Msg {
got := call1.Reply.(*EchoResponse).Msg
if len(got) > 8 {
got = got[:8] + "..."
}
b.Fatalf(`EchoService.EchoTwice: len = %d, got = %v`,
len(call1.Reply.(*EchoResponse).Msg), got,
)
}
// call2
if call2.Error != nil {
b.Fatalf(`EchoService.EchoTwice: %v`, call2.Error)
}
if call2.Reply.(*EchoResponse).Msg != args2.Msg+args2.Msg {
got := call2.Reply.(*EchoResponse).Msg
if len(got) > 8 {
got = got[:8] + "..."
}
b.Fatalf(`EchoService.EchoTwice: len = %d, got = %v`,
len(call2.Reply.(*EchoResponse).Msg), got,
)
}
// call3
if call3.Error != nil {
b.Fatalf(`EchoService.EchoTwice: %v`, call3.Error)
}
if call3.Reply.(*EchoResponse).Msg != args3.Msg+args3.Msg {
got := call3.Reply.(*EchoResponse).Msg
if len(got) > 8 {
got = got[:8] + "..."
}
b.Fatalf(`EchoService.EchoTwice: len = %d, got = %v`,
len(call3.Reply.(*EchoResponse).Msg), got,
)
}
}
}

View File

@ -0,0 +1,8 @@
// Copyright 2014 <chaishushan{AT}gmail.com>. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:generate protoc --go_out=. arith.proto echo.proto
//go:generate protoc --protorpc_out=. arith.proto echo.proto
package service

View File

@ -0,0 +1,13 @@
# Copyright 2013 <chaishushan{AT}gmail.com>. All rights reserved.
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file.
PROTO_FILES=$(sort $(wildcard ./*.proto))
default: $(PROTO_FILES) Makefile
go install github.com/golang/protobuf/protoc-gen-go
go install github.com/chai2010/protorpc/protoc-gen-stdrpc
protoc --stdrpc_out=. ${PROTO_FILES}
go test
clean:

View File

@ -0,0 +1,401 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: arith.proto
/*
Package service is a generated protocol buffer package.
It is generated from these files:
arith.proto
echo.proto
It has these top-level messages:
ArithRequest
ArithResponse
EchoRequest
EchoResponse
*/
package service
import proto "github.com/golang/protobuf/proto"
import fmt "fmt"
import math "math"
import "bufio"
import "crypto/tls"
import "errors"
import "io"
import "log"
import "net"
import "net/http"
import "net/rpc"
import "time"
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
type ArithRequest struct {
A int32 `protobuf:"varint,1,opt,name=a" json:"a,omitempty"`
B int32 `protobuf:"varint,2,opt,name=b" json:"b,omitempty"`
}
func (m *ArithRequest) Reset() { *m = ArithRequest{} }
func (m *ArithRequest) String() string { return proto.CompactTextString(m) }
func (*ArithRequest) ProtoMessage() {}
func (*ArithRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
func (m *ArithRequest) GetA() int32 {
if m != nil {
return m.A
}
return 0
}
func (m *ArithRequest) GetB() int32 {
if m != nil {
return m.B
}
return 0
}
type ArithResponse struct {
C int32 `protobuf:"varint,1,opt,name=c" json:"c,omitempty"`
}
func (m *ArithResponse) Reset() { *m = ArithResponse{} }
func (m *ArithResponse) String() string { return proto.CompactTextString(m) }
func (*ArithResponse) ProtoMessage() {}
func (*ArithResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
func (m *ArithResponse) GetC() int32 {
if m != nil {
return m.C
}
return 0
}
func init() {
proto.RegisterType((*ArithRequest)(nil), "service.ArithRequest")
proto.RegisterType((*ArithResponse)(nil), "service.ArithResponse")
}
type ArithService interface {
Add(in *ArithRequest, out *ArithResponse) error
Mul(in *ArithRequest, out *ArithResponse) error
Div(in *ArithRequest, out *ArithResponse) error
Error(in *ArithRequest, out *ArithResponse) error
}
// AcceptArithServiceClient accepts connections on the listener and serves requests
// for each incoming connection. Accept blocks; the caller typically
// invokes it in a go statement.
func AcceptArithServiceClient(lis net.Listener, x ArithService) {
srv := rpc.NewServer()
if err := srv.RegisterName("service.ArithService", x); err != nil {
log.Fatal(err)
}
for {
conn, err := lis.Accept()
if err != nil {
log.Fatalf("lis.Accept(): %v\n", err)
}
go srv.ServeConn(conn)
}
}
// RegisterArithService publish the given ArithService implementation on the server.
func RegisterArithService(srv *rpc.Server, x ArithService) error {
if err := srv.RegisterName("service.ArithService", x); err != nil {
return err
}
return nil
}
// NewArithServiceServer returns a new ArithService Server.
func NewArithServiceServer(x ArithService) *rpc.Server {
srv := rpc.NewServer()
if err := srv.RegisterName("service.ArithService", x); err != nil {
log.Fatal(err)
}
return srv
}
// ListenAndServeArithService listen announces on the local network address laddr
// and serves the given ArithService implementation.
func ListenAndServeArithService(network, addr string, x ArithService) error {
lis, err := net.Listen(network, addr)
if err != nil {
return err
}
defer lis.Close()
srv := rpc.NewServer()
if err := srv.RegisterName("service.ArithService", x); err != nil {
return err
}
for {
conn, err := lis.Accept()
if err != nil {
log.Fatalf("lis.Accept(): %v\n", err)
}
go srv.ServeConn(conn)
}
}
// ServeArithService serves the given ArithService implementation.
func ServeArithService(conn io.ReadWriteCloser, x ArithService) {
srv := rpc.NewServer()
if err := srv.RegisterName("service.ArithService", x); err != nil {
log.Fatal(err)
}
srv.ServeConn(conn)
}
type ArithServiceClient struct {
*rpc.Client
}
// NewArithServiceClient returns a ArithService stub to handle
// requests to the set of ArithService at the other end of the connection.
func NewArithServiceClient(conn io.ReadWriteCloser) *ArithServiceClient {
c := rpc.NewClient(conn)
return &ArithServiceClient{c}
}
func (c *ArithServiceClient) Add(in *ArithRequest) (out *ArithResponse, err error) {
if in == nil {
in = new(ArithRequest)
}
type Validator interface {
Validate() error
}
if x, ok := proto.Message(in).(Validator); ok {
if err := x.Validate(); err != nil {
return nil, err
}
}
out = new(ArithResponse)
if err = c.Call("service.ArithService.Add", in, out); err != nil {
return nil, err
}
if x, ok := proto.Message(out).(Validator); ok {
if err := x.Validate(); err != nil {
return out, err
}
}
return out, nil
}
func (c *ArithServiceClient) AsyncAdd(in *ArithRequest, out *ArithResponse, done chan *rpc.Call) *rpc.Call {
if in == nil {
in = new(ArithRequest)
}
return c.Go(
"service.ArithService.Add",
in, out,
done,
)
}
func (c *ArithServiceClient) Mul(in *ArithRequest) (out *ArithResponse, err error) {
if in == nil {
in = new(ArithRequest)
}
type Validator interface {
Validate() error
}
if x, ok := proto.Message(in).(Validator); ok {
if err := x.Validate(); err != nil {
return nil, err
}
}
out = new(ArithResponse)
if err = c.Call("service.ArithService.Mul", in, out); err != nil {
return nil, err
}
if x, ok := proto.Message(out).(Validator); ok {
if err := x.Validate(); err != nil {
return out, err
}
}
return out, nil
}
func (c *ArithServiceClient) AsyncMul(in *ArithRequest, out *ArithResponse, done chan *rpc.Call) *rpc.Call {
if in == nil {
in = new(ArithRequest)
}
return c.Go(
"service.ArithService.Mul",
in, out,
done,
)
}
func (c *ArithServiceClient) Div(in *ArithRequest) (out *ArithResponse, err error) {
if in == nil {
in = new(ArithRequest)
}
type Validator interface {
Validate() error
}
if x, ok := proto.Message(in).(Validator); ok {
if err := x.Validate(); err != nil {
return nil, err
}
}
out = new(ArithResponse)
if err = c.Call("service.ArithService.Div", in, out); err != nil {
return nil, err
}
if x, ok := proto.Message(out).(Validator); ok {
if err := x.Validate(); err != nil {
return out, err
}
}
return out, nil
}
func (c *ArithServiceClient) AsyncDiv(in *ArithRequest, out *ArithResponse, done chan *rpc.Call) *rpc.Call {
if in == nil {
in = new(ArithRequest)
}
return c.Go(
"service.ArithService.Div",
in, out,
done,
)
}
func (c *ArithServiceClient) Error(in *ArithRequest) (out *ArithResponse, err error) {
if in == nil {
in = new(ArithRequest)
}
type Validator interface {
Validate() error
}
if x, ok := proto.Message(in).(Validator); ok {
if err := x.Validate(); err != nil {
return nil, err
}
}
out = new(ArithResponse)
if err = c.Call("service.ArithService.Error", in, out); err != nil {
return nil, err
}
if x, ok := proto.Message(out).(Validator); ok {
if err := x.Validate(); err != nil {
return out, err
}
}
return out, nil
}
func (c *ArithServiceClient) AsyncError(in *ArithRequest, out *ArithResponse, done chan *rpc.Call) *rpc.Call {
if in == nil {
in = new(ArithRequest)
}
return c.Go(
"service.ArithService.Error",
in, out,
done,
)
}
// DialArithService connects to an ArithService at the specified network address.
func DialArithService(network, addr string) (*ArithServiceClient, error) {
c, err := rpc.Dial(network, addr)
if err != nil {
return nil, err
}
return &ArithServiceClient{c}, nil
}
// DialArithServiceTimeout connects to an ArithService at the specified network address.
func DialArithServiceTimeout(network, addr string, timeout time.Duration) (*ArithServiceClient, error) {
conn, err := net.DialTimeout(network, addr, timeout)
if err != nil {
return nil, err
}
return &ArithServiceClient{rpc.NewClient(conn)}, nil
}
// DialArithServiceHTTP connects to an HTTP RPC server at the specified network address
// listening on the default HTTP RPC path.
func DialArithServiceHTTP(network, address string) (*ArithServiceClient, error) {
return DialArithServiceHTTPPath(network, address, rpc.DefaultRPCPath)
}
// DialArithServiceHTTPPath connects to an HTTP RPC server
// at the specified network address and path.
func DialArithServiceHTTPPath(network, address, path string) (*ArithServiceClient, error) {
conn, err := net.Dial(network, address)
if err != nil {
return nil, err
}
return dialArithServicePath(network, address, path, conn)
}
// DialArithServiceHTTPS connects to an HTTPS RPC server at the specified network address
// listening on the default HTTP RPC path.
func DialArithServiceHTTPS(network, address string, tlsConfig *tls.Config) (*ArithServiceClient, error) {
return DialArithServiceHTTPSPath(network, address, rpc.DefaultRPCPath, tlsConfig)
}
// DialArithServiceHTTPSPath connects to an HTTPS RPC server
// at the specified network address and path.
func DialArithServiceHTTPSPath(network, address, path string, tlsConfig *tls.Config) (*ArithServiceClient, error) {
conn, err := tls.Dial(network, address, tlsConfig)
if err != nil {
return nil, err
}
return dialArithServicePath(network, address, path, conn)
}
func dialArithServicePath(network, address, path string, conn net.Conn) (*ArithServiceClient, error) {
const net_rpc_connected = "200 Connected to Go RPC"
io.WriteString(conn, "CONNECT "+path+" HTTP/1.0\n\n")
// Require successful HTTP response
// before switching to RPC protocol.
resp, err := http.ReadResponse(bufio.NewReader(conn), &http.Request{Method: "CONNECT"})
if err == nil && resp.Status == net_rpc_connected {
return &ArithServiceClient{rpc.NewClient(conn)}, nil
}
if err == nil {
err = errors.New("unexpected HTTP response: " + resp.Status)
}
conn.Close()
return nil, &net.OpError{
Op: "dial-http",
Net: network + " " + address,
Addr: nil,
Err: err,
}
}
func init() { proto.RegisterFile("arith.proto", fileDescriptor0) }
var fileDescriptor0 = []byte{
// 159 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x4e, 0x2c, 0xca, 0x2c,
0xc9, 0xd0, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x2f, 0x4e, 0x2d, 0x2a, 0xcb, 0x4c, 0x4e,
0x55, 0xd2, 0xe2, 0xe2, 0x71, 0x04, 0x89, 0x07, 0xa5, 0x16, 0x96, 0xa6, 0x16, 0x97, 0x08, 0xf1,
0x70, 0x31, 0x26, 0x4a, 0x30, 0x2a, 0x30, 0x6a, 0xb0, 0x06, 0x31, 0x26, 0x82, 0x78, 0x49, 0x12,
0x4c, 0x10, 0x5e, 0x92, 0x92, 0x2c, 0x17, 0x2f, 0x54, 0x6d, 0x71, 0x41, 0x7e, 0x5e, 0x71, 0x2a,
0x48, 0x3a, 0x19, 0xa6, 0x38, 0xd9, 0xe8, 0x05, 0x23, 0xd4, 0xac, 0x60, 0x88, 0xd9, 0x42, 0x26,
0x5c, 0xcc, 0x89, 0x29, 0x29, 0x42, 0xa2, 0x7a, 0x50, 0xcb, 0xf4, 0x90, 0x6d, 0x92, 0x12, 0x43,
0x17, 0x86, 0x1a, 0x6a, 0xc2, 0xc5, 0x9c, 0x5b, 0x9a, 0x43, 0x86, 0xae, 0x94, 0xcc, 0x32, 0x52,
0x75, 0x99, 0x71, 0xb1, 0xa6, 0x16, 0x15, 0xe5, 0x17, 0x91, 0xa8, 0x2f, 0x89, 0x0d, 0x1c, 0x8a,
0xc6, 0x80, 0x00, 0x00, 0x00, 0xff, 0xff, 0xa2, 0x62, 0xfd, 0xbd, 0x54, 0x01, 0x00, 0x00,
}

View File

@ -0,0 +1,23 @@
// Copyright 2013 <chaishushan{AT}gmail.com>. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
syntax = "proto3";
package service;
message ArithRequest {
int32 a = 1;
int32 b = 2;
}
message ArithResponse {
int32 c = 1;
}
service ArithService {
rpc add (ArithRequest) returns (ArithResponse);
rpc mul (ArithRequest) returns (ArithResponse);
rpc div (ArithRequest) returns (ArithResponse);
rpc error (ArithRequest) returns (ArithResponse);
}

View File

@ -0,0 +1,301 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: echo.proto
package service
import proto "github.com/golang/protobuf/proto"
import fmt "fmt"
import math "math"
import "bufio"
import "crypto/tls"
import "errors"
import "io"
import "log"
import "net"
import "net/http"
import "net/rpc"
import "time"
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
type EchoRequest struct {
Msg string `protobuf:"bytes,1,opt,name=msg" json:"msg,omitempty"`
}
func (m *EchoRequest) Reset() { *m = EchoRequest{} }
func (m *EchoRequest) String() string { return proto.CompactTextString(m) }
func (*EchoRequest) ProtoMessage() {}
func (*EchoRequest) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{0} }
func (m *EchoRequest) GetMsg() string {
if m != nil {
return m.Msg
}
return ""
}
type EchoResponse struct {
Msg string `protobuf:"bytes,1,opt,name=msg" json:"msg,omitempty"`
}
func (m *EchoResponse) Reset() { *m = EchoResponse{} }
func (m *EchoResponse) String() string { return proto.CompactTextString(m) }
func (*EchoResponse) ProtoMessage() {}
func (*EchoResponse) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{1} }
func (m *EchoResponse) GetMsg() string {
if m != nil {
return m.Msg
}
return ""
}
func init() {
proto.RegisterType((*EchoRequest)(nil), "service.EchoRequest")
proto.RegisterType((*EchoResponse)(nil), "service.EchoResponse")
}
type EchoService interface {
Echo(in *EchoRequest, out *EchoResponse) error
EchoTwice(in *EchoRequest, out *EchoResponse) error
}
// AcceptEchoServiceClient accepts connections on the listener and serves requests
// for each incoming connection. Accept blocks; the caller typically
// invokes it in a go statement.
func AcceptEchoServiceClient(lis net.Listener, x EchoService) {
srv := rpc.NewServer()
if err := srv.RegisterName("service.EchoService", x); err != nil {
log.Fatal(err)
}
for {
conn, err := lis.Accept()
if err != nil {
log.Fatalf("lis.Accept(): %v\n", err)
}
go srv.ServeConn(conn)
}
}
// RegisterEchoService publish the given EchoService implementation on the server.
func RegisterEchoService(srv *rpc.Server, x EchoService) error {
if err := srv.RegisterName("service.EchoService", x); err != nil {
return err
}
return nil
}
// NewEchoServiceServer returns a new EchoService Server.
func NewEchoServiceServer(x EchoService) *rpc.Server {
srv := rpc.NewServer()
if err := srv.RegisterName("service.EchoService", x); err != nil {
log.Fatal(err)
}
return srv
}
// ListenAndServeEchoService listen announces on the local network address laddr
// and serves the given EchoService implementation.
func ListenAndServeEchoService(network, addr string, x EchoService) error {
lis, err := net.Listen(network, addr)
if err != nil {
return err
}
defer lis.Close()
srv := rpc.NewServer()
if err := srv.RegisterName("service.EchoService", x); err != nil {
return err
}
for {
conn, err := lis.Accept()
if err != nil {
log.Fatalf("lis.Accept(): %v\n", err)
}
go srv.ServeConn(conn)
}
}
// ServeEchoService serves the given EchoService implementation.
func ServeEchoService(conn io.ReadWriteCloser, x EchoService) {
srv := rpc.NewServer()
if err := srv.RegisterName("service.EchoService", x); err != nil {
log.Fatal(err)
}
srv.ServeConn(conn)
}
type EchoServiceClient struct {
*rpc.Client
}
// NewEchoServiceClient returns a EchoService stub to handle
// requests to the set of EchoService at the other end of the connection.
func NewEchoServiceClient(conn io.ReadWriteCloser) *EchoServiceClient {
c := rpc.NewClient(conn)
return &EchoServiceClient{c}
}
func (c *EchoServiceClient) Echo(in *EchoRequest) (out *EchoResponse, err error) {
if in == nil {
in = new(EchoRequest)
}
type Validator interface {
Validate() error
}
if x, ok := proto.Message(in).(Validator); ok {
if err := x.Validate(); err != nil {
return nil, err
}
}
out = new(EchoResponse)
if err = c.Call("service.EchoService.Echo", in, out); err != nil {
return nil, err
}
if x, ok := proto.Message(out).(Validator); ok {
if err := x.Validate(); err != nil {
return out, err
}
}
return out, nil
}
func (c *EchoServiceClient) AsyncEcho(in *EchoRequest, out *EchoResponse, done chan *rpc.Call) *rpc.Call {
if in == nil {
in = new(EchoRequest)
}
return c.Go(
"service.EchoService.Echo",
in, out,
done,
)
}
func (c *EchoServiceClient) EchoTwice(in *EchoRequest) (out *EchoResponse, err error) {
if in == nil {
in = new(EchoRequest)
}
type Validator interface {
Validate() error
}
if x, ok := proto.Message(in).(Validator); ok {
if err := x.Validate(); err != nil {
return nil, err
}
}
out = new(EchoResponse)
if err = c.Call("service.EchoService.EchoTwice", in, out); err != nil {
return nil, err
}
if x, ok := proto.Message(out).(Validator); ok {
if err := x.Validate(); err != nil {
return out, err
}
}
return out, nil
}
func (c *EchoServiceClient) AsyncEchoTwice(in *EchoRequest, out *EchoResponse, done chan *rpc.Call) *rpc.Call {
if in == nil {
in = new(EchoRequest)
}
return c.Go(
"service.EchoService.EchoTwice",
in, out,
done,
)
}
// DialEchoService connects to an EchoService at the specified network address.
func DialEchoService(network, addr string) (*EchoServiceClient, error) {
c, err := rpc.Dial(network, addr)
if err != nil {
return nil, err
}
return &EchoServiceClient{c}, nil
}
// DialEchoServiceTimeout connects to an EchoService at the specified network address.
func DialEchoServiceTimeout(network, addr string, timeout time.Duration) (*EchoServiceClient, error) {
conn, err := net.DialTimeout(network, addr, timeout)
if err != nil {
return nil, err
}
return &EchoServiceClient{rpc.NewClient(conn)}, nil
}
// DialEchoServiceHTTP connects to an HTTP RPC server at the specified network address
// listening on the default HTTP RPC path.
func DialEchoServiceHTTP(network, address string) (*EchoServiceClient, error) {
return DialEchoServiceHTTPPath(network, address, rpc.DefaultRPCPath)
}
// DialEchoServiceHTTPPath connects to an HTTP RPC server
// at the specified network address and path.
func DialEchoServiceHTTPPath(network, address, path string) (*EchoServiceClient, error) {
conn, err := net.Dial(network, address)
if err != nil {
return nil, err
}
return dialEchoServicePath(network, address, path, conn)
}
// DialEchoServiceHTTPS connects to an HTTPS RPC server at the specified network address
// listening on the default HTTP RPC path.
func DialEchoServiceHTTPS(network, address string, tlsConfig *tls.Config) (*EchoServiceClient, error) {
return DialEchoServiceHTTPSPath(network, address, rpc.DefaultRPCPath, tlsConfig)
}
// DialEchoServiceHTTPSPath connects to an HTTPS RPC server
// at the specified network address and path.
func DialEchoServiceHTTPSPath(network, address, path string, tlsConfig *tls.Config) (*EchoServiceClient, error) {
conn, err := tls.Dial(network, address, tlsConfig)
if err != nil {
return nil, err
}
return dialEchoServicePath(network, address, path, conn)
}
func dialEchoServicePath(network, address, path string, conn net.Conn) (*EchoServiceClient, error) {
const net_rpc_connected = "200 Connected to Go RPC"
io.WriteString(conn, "CONNECT "+path+" HTTP/1.0\n\n")
// Require successful HTTP response
// before switching to RPC protocol.
resp, err := http.ReadResponse(bufio.NewReader(conn), &http.Request{Method: "CONNECT"})
if err == nil && resp.Status == net_rpc_connected {
return &EchoServiceClient{rpc.NewClient(conn)}, nil
}
if err == nil {
err = errors.New("unexpected HTTP response: " + resp.Status)
}
conn.Close()
return nil, &net.OpError{
Op: "dial-http",
Net: network + " " + address,
Addr: nil,
Err: err,
}
}
func init() { proto.RegisterFile("echo.proto", fileDescriptor1) }
var fileDescriptor1 = []byte{
// 134 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x4a, 0x4d, 0xce, 0xc8,
0xd7, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x2f, 0x4e, 0x2d, 0x2a, 0xcb, 0x4c, 0x4e, 0x55,
0x92, 0xe7, 0xe2, 0x76, 0x4d, 0xce, 0xc8, 0x0f, 0x4a, 0x2d, 0x2c, 0x4d, 0x2d, 0x2e, 0x11, 0x12,
0xe0, 0x62, 0xce, 0x2d, 0x4e, 0x97, 0x60, 0x54, 0x60, 0xd4, 0xe0, 0x0c, 0x02, 0x31, 0x95, 0x14,
0xb8, 0x78, 0x20, 0x0a, 0x8a, 0x0b, 0xf2, 0xf3, 0x8a, 0x53, 0x31, 0x55, 0x18, 0xd5, 0x40, 0x8c,
0x08, 0x86, 0x98, 0x28, 0x64, 0xcc, 0xc5, 0x02, 0xe2, 0x0a, 0x89, 0xe8, 0x41, 0xed, 0xd0, 0x43,
0xb2, 0x40, 0x4a, 0x14, 0x4d, 0x14, 0x6a, 0xaa, 0x05, 0x17, 0x27, 0x88, 0x1f, 0x52, 0x0e, 0x32,
0x81, 0x14, 0x9d, 0x49, 0x6c, 0x60, 0x0f, 0x19, 0x03, 0x02, 0x00, 0x00, 0xff, 0xff, 0x00, 0xc1,
0xd4, 0xbd, 0xde, 0x00, 0x00, 0x00,
}

View File

@ -0,0 +1,20 @@
// Copyright 2013 <chaishushan{AT}gmail.com>. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
syntax = "proto3";
package service;
message EchoRequest {
string msg = 1;
}
message EchoResponse {
string msg = 1;
}
service EchoService {
rpc Echo (EchoRequest) returns (EchoResponse);
rpc EchoTwice (EchoRequest) returns (EchoResponse);
}

11
core/protorpc/go.mod Normal file
View File

@ -0,0 +1,11 @@
// Copyright 2021 <chaishushan{AT}gmail.com>. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
module github.com/chai2010/protorpc
go 1.23
require github.com/golang/protobuf v1.5.4
require google.golang.org/protobuf v1.36.6 // indirect

62
core/protorpc/hello.go Normal file
View File

@ -0,0 +1,62 @@
// Copyright 2014 <chaishushan{AT}gmail.com>. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build ingore
package main
import (
"fmt"
"log"
"github.com/chai2010/protorpc"
service "github.com/chai2010/protorpc/examples/service.pb"
)
type Echo int
func (t *Echo) Echo(args *service.EchoRequest, reply *service.EchoResponse) error {
reply.Msg = args.Msg
return nil
}
func (t *Echo) EchoTwice(args *service.EchoRequest, reply *service.EchoResponse) error {
reply.Msg = args.Msg + args.Msg
return nil
}
func init() {
go service.ListenAndServeEchoService("tcp", `127.0.0.1:9527`, new(Echo))
}
func main() {
echoClient, err := service.DialEchoService("tcp", `127.0.0.1:9527`)
if err != nil {
log.Fatalf("service.DialEchoService: %v", err)
}
defer echoClient.Close()
args := &service.EchoRequest{Msg: "你好, 世界!"}
reply, err := echoClient.EchoTwice(args)
if err != nil {
log.Fatalf("echoClient.EchoTwice: %v", err)
}
fmt.Println(reply.Msg)
// or use normal client
client, err := protorpc.Dial("tcp", `127.0.0.1:9527`)
if err != nil {
log.Fatalf("protorpc.Dial: %v", err)
}
defer client.Close()
echoClient1 := &service.EchoServiceClient{client}
echoClient2 := &service.EchoServiceClient{client}
reply, err = echoClient1.EchoTwice(args)
reply, err = echoClient2.EchoTwice(args)
_, _ = reply, err
// Output:
// 你好, 世界!你好, 世界!
}

65
core/protorpc/hello2.go Normal file
View File

@ -0,0 +1,65 @@
// Copyright 2014 <chaishushan{AT}gmail.com>. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build ingore
package main
import (
"fmt"
"log"
"net/rpc"
"time"
stdrpc "github.com/chai2010/protorpc/examples/stdrpc.pb"
)
type Echo int
func (t *Echo) Echo(args *stdrpc.EchoRequest, reply *stdrpc.EchoResponse) error {
reply.Msg = args.Msg
return nil
}
func (t *Echo) EchoTwice(args *stdrpc.EchoRequest, reply *stdrpc.EchoResponse) error {
reply.Msg = args.Msg + args.Msg
return nil
}
func init() {
go stdrpc.ListenAndServeEchoService("tcp", `127.0.0.1:9527`, new(Echo))
}
func main() {
time.Sleep(time.Second)
echoClient, err := stdrpc.DialEchoService("tcp", `127.0.0.1:9527`)
if err != nil {
log.Fatalf("stdrpc.DialEchoService: %v", err)
}
defer echoClient.Close()
args := &stdrpc.EchoRequest{Msg: "你好, 世界!"}
reply, err := echoClient.EchoTwice(args)
if err != nil {
log.Fatalf("echoClient.EchoTwice: %v", err)
}
fmt.Println(reply.Msg)
// or use normal client
client, err := rpc.Dial("tcp", `127.0.0.1:9527`)
if err != nil {
log.Fatalf("rpc.Dial: %v", err)
}
defer client.Close()
echoClient1 := &stdrpc.EchoServiceClient{client}
echoClient2 := &stdrpc.EchoServiceClient{client}
reply, err = echoClient1.EchoTwice(args)
reply, err = echoClient2.EchoTwice(args)
_, _ = reply, err
// Output:
// 你好, 世界!你好, 世界!
}

View File

@ -0,0 +1,52 @@
// Copyright 2017 <chaishushan{AT}gmail.com>. All rights reserved.
// Use of this source code is governed by a Apache
// license that can be found in the LICENSE file.
package plugin
import (
"github.com/golang/protobuf/protoc-gen-go/descriptor"
"github.com/golang/protobuf/protoc-gen-go/generator"
)
var pkgCodeGeneratorList []CodeGenerator
type CodeGenerator interface {
Name() string
FileNameExt() string
HeaderCode(g *generator.Generator, file *generator.FileDescriptor) string
ServiceCode(g *generator.Generator, file *generator.FileDescriptor, svc *descriptor.ServiceDescriptorProto) string
MessageCode(g *generator.Generator, file *generator.FileDescriptor, msg *descriptor.DescriptorProto) string
}
func RegisterCodeGenerator(g CodeGenerator) {
pkgCodeGeneratorList = append(pkgCodeGeneratorList, g)
}
func getAllCodeGenerator() []CodeGenerator {
return pkgCodeGeneratorList
}
func getAllServiceGeneratorNames() (names []string) {
for _, g := range pkgCodeGeneratorList {
names = append(names, g.Name())
}
return
}
func getFirstServiceGeneratorName() string {
if len(pkgCodeGeneratorList) > 0 {
return pkgCodeGeneratorList[0].Name()
}
return ""
}
func getCodeGenerator(name string) CodeGenerator {
for _, g := range pkgCodeGeneratorList {
if g.Name() == name {
return g
}
}
return nil
}

View File

@ -0,0 +1,115 @@
// Copyright 2017 <chaishushan{AT}gmail.com>. All rights reserved.
// Use of this source code is governed by a Apache
// license that can be found in the LICENSE file.
package plugin
import (
"io/ioutil"
"log"
"os"
"strings"
"github.com/golang/protobuf/proto"
"github.com/golang/protobuf/protoc-gen-go/generator"
)
func Main() {
mainPlugin := new(mainPlugin)
generator.RegisterPlugin(mainPlugin)
// Begin by allocating a generator. The request and response structures are stored there
// so we can do error handling easily - the response structure contains the field to
// report failure.
g := generator.New()
if len(getAllCodeGenerator()) == 0 {
g.Fail("no code generator plugin")
}
pkgReadRequetFromStdin(g)
pkgGenerateAllFiles(g, mainPlugin)
pkgWriteResponseToStdout(g)
}
func pkgGenerateAllFiles(g *generator.Generator, plugin *mainPlugin) {
// set default plugins
// protoc --xxx_out=. x.proto
plugin.InitService(pkgGetUserPlugin(g))
// parse command line parameters
g.CommandLineParameters("plugins=" + plugin.Name())
// Create a wrapped version of the Descriptors and EnumDescriptors that
// point to the file that defines them.
g.WrapTypes()
g.SetPackageNames()
g.BuildTypeNameMap()
g.GenerateAllFiles()
// skip non *.pb.xxx.go
respFileList := g.Response.File[:0]
for _, file := range g.Response.File {
fileName := file.GetName()
extName := plugin.FileNameExt()
if strings.HasSuffix(fileName, extName) {
respFileList = append(respFileList, file)
}
}
g.Response.File = respFileList
}
func pkgReadRequetFromStdin(g *generator.Generator) {
data, err := ioutil.ReadAll(os.Stdin)
if err != nil {
g.Error(err, "reading input")
}
if err := proto.Unmarshal(data, g.Request); err != nil {
g.Error(err, "parsing input proto")
}
if len(g.Request.FileToGenerate) == 0 {
g.Fail("no files to generate")
}
}
func pkgWriteResponseToStdout(g *generator.Generator) {
data, err := proto.Marshal(g.Response)
if err != nil {
g.Error(err, "failed to marshal output proto")
}
_, err = os.Stdout.Write(data)
if err != nil {
g.Error(err, "failed to write output proto")
}
}
func pkgGetUserPlugin(g *generator.Generator) CodeGenerator {
args := g.Request.GetParameter()
userPluginName := pkgGetParameterValue(args, "plugin")
if userPluginName == "" {
userPluginName = getFirstServiceGeneratorName()
}
userPlugin := getCodeGenerator(userPluginName)
if userPlugin == nil {
log.Print("protoc-gen-plugin: registor plugins:", getAllServiceGeneratorNames())
g.Fail("invalid plugin option:", userPluginName)
}
return userPlugin
}
func pkgGetParameterValue(parameter, key string) string {
for _, p := range strings.Split(parameter, ",") {
if i := strings.Index(p, "="); i > 0 {
if p[0:i] == key {
return p[i+1:]
}
}
}
return ""
}

View File

@ -0,0 +1,119 @@
// Copyright 2017 <chaishushan{AT}gmail.com>. All rights reserved.
// Use of this source code is governed by a Apache
// license that can be found in the LICENSE file.
package plugin
import (
"bytes"
"fmt"
"go/format"
"path"
"strings"
"github.com/golang/protobuf/proto"
"github.com/golang/protobuf/protoc-gen-go/generator"
plugin "github.com/golang/protobuf/protoc-gen-go/plugin"
)
// mainPlugin produce the Service interface.
type mainPlugin struct {
*generator.Generator
CodeGenerator
}
// Name returns the name of the plugin.
func (p *mainPlugin) Name() string { return "main-plugin" }
// Init is called once after data structures are built but before
// code generation begins.
func (p *mainPlugin) Init(g *generator.Generator) {
p.Generator = g
}
func (p *mainPlugin) InitService(g CodeGenerator) {
p.CodeGenerator = g
}
// Generate produces the code generated by the plugin for this file.
func (p *mainPlugin) GenerateImports(file *generator.FileDescriptor) {
// skip
}
// Generate generates the Service interface.
// rpc service can't handle other proto message!!!
func (p *mainPlugin) Generate(file *generator.FileDescriptor) {
if !p.isFileNeedGenerate(file) {
return
}
var buf bytes.Buffer
fmt.Fprintln(&buf, p.HeaderCode(p.Generator, file))
for _, msg := range file.MessageType {
fmt.Fprintln(&buf, p.MessageCode(p.Generator, file, msg))
}
for _, svc := range file.Service {
fmt.Fprintln(&buf, p.ServiceCode(p.Generator, file, svc))
}
fileContent := buf.String()
if code, err := format.Source(buf.Bytes()); err == nil {
fileContent = string(code)
}
p.Generator.Response.File = append(p.Generator.Response.File, &plugin.CodeGeneratorResponse_File{
Name: proto.String(p.goFileName(file)),
Content: proto.String(fileContent),
})
}
func (p *mainPlugin) isFileNeedGenerate(file *generator.FileDescriptor) bool {
for _, v := range p.Generator.Request.FileToGenerate {
if v == file.GetName() {
return true
}
}
return false
}
func (p *mainPlugin) goFileName(file *generator.FileDescriptor) string {
name := *file.Name
if ext := path.Ext(name); ext == ".proto" || ext == ".protodevel" {
name = name[:len(name)-len(ext)]
}
name += p.FileNameExt()
// Does the file have a "go_package" option?
// If it does, it may override the filename.
if impPath, _, ok := p.goPackageOption(file); ok && impPath != "" {
// Replace the existing dirname with the declared import path.
_, name = path.Split(name)
name = path.Join(impPath, name)
return name
}
return name
}
func (p *mainPlugin) goPackageOption(file *generator.FileDescriptor) (impPath, pkg string, ok bool) {
pkg = file.GetOptions().GetGoPackage()
if pkg == "" {
return
}
ok = true
// The presence of a slash implies there's an import path.
slash := strings.LastIndex(pkg, "/")
if slash < 0 {
return
}
impPath, pkg = pkg, pkg[slash+1:]
// A semicolon-delimited suffix overrides the package name.
sc := strings.IndexByte(impPath, ';')
if sc < 0 {
return
}
impPath, pkg = impPath[:sc], impPath[sc+1:]
return
}

View File

@ -0,0 +1,7 @@
// Copyright 2013 <chaishushan{AT}gmail.com>. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
const ENV_PROTOC_GEN_PROTORPC_FLAG_PREFIX = "ENV_PROTOC_GEN_PROTORPC_FLAG_PREFIX"

View File

@ -0,0 +1,381 @@
// Copyright 2018 <chaishushan{AT}gmail.com>. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"bytes"
"log"
"os"
"text/template"
plugin "github.com/chai2010/protorpc/protoc-gen-plugin"
"github.com/golang/protobuf/protoc-gen-go/descriptor"
"github.com/golang/protobuf/protoc-gen-go/generator"
)
var flagPrefix = os.Getenv(ENV_PROTOC_GEN_PROTORPC_FLAG_PREFIX)
func main() {
plugin.Main()
}
func init() {
plugin.RegisterCodeGenerator(new(protorpcPlugin))
}
type protorpcPlugin struct{}
func (p *protorpcPlugin) Name() string { return "protorpc-go" }
func (p *protorpcPlugin) FileNameExt() string { return ".pb.protorpc.go" }
func (p *protorpcPlugin) HeaderCode(g *generator.Generator, file *generator.FileDescriptor) string {
const tmpl = `
{{- $G := .G -}}
{{- $File := .File -}}
// Code generated by protoc-gen-protorpc. DO NOT EDIT.
//
// plugin: https://github.com/chai2010/protorpc/tree/master/protoc-gen-plugin
// plugin: https://github.com/chai2010/protorpc/tree/master/protoc-gen-protorpc
//
// source: {{$File.GetName}}
package {{$File.PackageName}}
import (
"fmt"
"io"
"log"
"net"
"net/rpc"
"time"
"github.com/chai2010/protorpc"
"github.com/golang/protobuf/proto"
)
var (
_ = fmt.Sprint
_ = io.Reader(nil)
_ = log.Print
_ = net.Addr(nil)
_ = rpc.Call{}
_ = time.Second
_ = proto.String
_ = protorpc.Dial
)
`
var buf bytes.Buffer
t := template.Must(template.New("").Parse(tmpl))
err := t.Execute(&buf,
struct {
G *generator.Generator
File *generator.FileDescriptor
Prefix string
}{
G: g,
File: file,
Prefix: flagPrefix,
},
)
if err != nil {
log.Fatal(err)
}
return buf.String()
}
func (p *protorpcPlugin) ServiceCode(g *generator.Generator, file *generator.FileDescriptor, svc *descriptor.ServiceDescriptorProto) string {
var code string
code += p.genServiceInterface(g, file, svc)
code += p.genServiceServer(g, file, svc)
code += p.genServiceClient(g, file, svc)
return code
}
func (p *protorpcPlugin) MessageCode(g *generator.Generator, file *generator.FileDescriptor, msg *descriptor.DescriptorProto) string {
return ""
}
func (p *protorpcPlugin) genServiceInterface(
g *generator.Generator,
file *generator.FileDescriptor,
svc *descriptor.ServiceDescriptorProto,
) string {
const serviceInterfaceTmpl = `
type {{.Prefix}}{{.ServiceName}} interface {
{{.CallMethodList}}
}
`
const callMethodTmpl = `
{{.MethodName}}(in *{{.ArgsType}}, out *{{.ReplyType}}) error`
// gen call method list
var callMethodList string
for _, m := range svc.Method {
out := bytes.NewBuffer([]byte{})
t := template.Must(template.New("").Parse(callMethodTmpl))
t.Execute(out, &struct {
Prefix string
ServiceName string
MethodName string
ArgsType string
ReplyType string
}{
Prefix: flagPrefix,
ServiceName: generator.CamelCase(svc.GetName()),
MethodName: generator.CamelCase(m.GetName()),
ArgsType: g.TypeName(g.ObjectNamed(m.GetInputType())),
ReplyType: g.TypeName(g.ObjectNamed(m.GetOutputType())),
})
callMethodList += out.String()
g.RecordTypeUse(m.GetInputType())
g.RecordTypeUse(m.GetOutputType())
}
// gen all interface code
{
out := bytes.NewBuffer([]byte{})
t := template.Must(template.New("").Parse(serviceInterfaceTmpl))
t.Execute(out, &struct {
Prefix string
ServiceName string
CallMethodList string
}{
Prefix: flagPrefix,
ServiceName: generator.CamelCase(svc.GetName()),
CallMethodList: callMethodList,
})
return out.String()
}
}
func (p *protorpcPlugin) genServiceServer(
g *generator.Generator,
file *generator.FileDescriptor,
svc *descriptor.ServiceDescriptorProto,
) string {
const serviceHelperFunTmpl = `
// {{.Prefix}}Accept{{.ServiceName}}Client accepts connections on the listener and serves requests
// for each incoming connection. Accept blocks; the caller typically
// invokes it in a go statement.
func {{.Prefix}}Accept{{.ServiceName}}Client(lis net.Listener, x {{.Prefix}}{{.ServiceName}}) {
srv := rpc.NewServer()
if err := srv.RegisterName("{{.ServiceRegisterName}}", x); err != nil {
log.Fatal(err)
}
for {
conn, err := lis.Accept()
if err != nil {
log.Fatalf("lis.Accept(): %v\n", err)
}
go srv.ServeCodec(protorpc.NewServerCodec(conn))
}
}
// {{.Prefix}}Register{{.ServiceName}} publish the given {{.Prefix}}{{.ServiceName}} implementation on the server.
func {{.Prefix}}Register{{.ServiceName}}(srv *rpc.Server, x {{.Prefix}}{{.ServiceName}}) error {
if err := srv.RegisterName("{{.ServiceRegisterName}}", x); err != nil {
return err
}
return nil
}
// {{.Prefix}}New{{.ServiceName}}Server returns a new {{.Prefix}}{{.ServiceName}} Server.
func {{.Prefix}}New{{.ServiceName}}Server(x {{.Prefix}}{{.ServiceName}}) *rpc.Server {
srv := rpc.NewServer()
if err := srv.RegisterName("{{.ServiceRegisterName}}", x); err != nil {
log.Fatal(err)
}
return srv
}
// {{.Prefix}}ListenAndServe{{.ServiceName}} listen announces on the local network address laddr
// and serves the given {{.ServiceName}} implementation.
func {{.Prefix}}ListenAndServe{{.ServiceName}}(network, addr string, x {{.Prefix}}{{.ServiceName}}) error {
lis, err := net.Listen(network, addr)
if err != nil {
return err
}
defer lis.Close()
srv := rpc.NewServer()
if err := srv.RegisterName("{{.ServiceRegisterName}}", x); err != nil {
return err
}
for {
conn, err := lis.Accept()
if err != nil {
log.Fatalf("lis.Accept(): %v\n", err)
}
go srv.ServeCodec(protorpc.NewServerCodec(conn))
}
}
// {{.Prefix}}Serve{{.ServiceName}} serves the given {{.Prefix}}{{.ServiceName}} implementation.
func {{.Prefix}}Serve{{.ServiceName}}(conn io.ReadWriteCloser, x {{.Prefix}}{{.ServiceName}}) {
srv := rpc.NewServer()
if err := srv.RegisterName("{{.ServiceRegisterName}}", x); err != nil {
log.Fatal(err)
}
srv.ServeCodec(protorpc.NewServerCodec(conn))
}
`
{
out := bytes.NewBuffer([]byte{})
t := template.Must(template.New("").Parse(serviceHelperFunTmpl))
t.Execute(out, &struct {
Prefix string
PackageName string
ServiceName string
ServiceRegisterName string
}{
Prefix: flagPrefix,
PackageName: file.GetPackage(),
ServiceName: generator.CamelCase(svc.GetName()),
ServiceRegisterName: p.makeServiceRegisterName(
file, file.GetPackage(), generator.CamelCase(svc.GetName()),
),
})
return out.String()
}
}
func (p *protorpcPlugin) genServiceClient(
g *generator.Generator,
file *generator.FileDescriptor,
svc *descriptor.ServiceDescriptorProto,
) string {
const clientHelperFuncTmpl = `
type {{.Prefix}}{{.ServiceName}}Client struct {
*rpc.Client
}
// {{.Prefix}}New{{.ServiceName}}Client returns a {{.Prefix}}{{.ServiceName}} stub to handle
// requests to the set of {{.Prefix}}{{.ServiceName}} at the other end of the connection.
func {{.Prefix}}New{{.ServiceName}}Client(conn io.ReadWriteCloser) (*{{.Prefix}}{{.ServiceName}}Client) {
c := rpc.NewClientWithCodec(protorpc.NewClientCodec(conn))
return &{{.Prefix}}{{.ServiceName}}Client{c}
}
{{.MethodList}}
// {{.Prefix}}Dial{{.ServiceName}} connects to an {{.Prefix}}{{.ServiceName}} at the specified network address.
func {{.Prefix}}Dial{{.ServiceName}}(network, addr string) (*{{.Prefix}}{{.ServiceName}}Client, error) {
c, err := protorpc.Dial(network, addr)
if err != nil {
return nil, err
}
return &{{.Prefix}}{{.ServiceName}}Client{c}, nil
}
// {{.Prefix}}Dial{{.ServiceName}}Timeout connects to an {{.Prefix}}{{.ServiceName}} at the specified network address.
func {{.Prefix}}Dial{{.ServiceName}}Timeout(network, addr string, timeout time.Duration) (*{{.Prefix}}{{.ServiceName}}Client, error) {
c, err := protorpc.DialTimeout(network, addr, timeout)
if err != nil {
return nil, err
}
return &{{.Prefix}}{{.ServiceName}}Client{c}, nil
}
`
const clientMethodTmpl = `
func (c *{{.Prefix}}{{.ServiceName}}Client) {{.MethodName}}(in *{{.ArgsType}}) (out *{{.ReplyType}}, err error) {
if in == nil {
in = new({{.ArgsType}})
}
type Validator interface {
Validate() error
}
if x, ok := proto.Message(in).(Validator); ok {
if err := x.Validate(); err != nil {
return nil, err
}
}
out = new({{.ReplyType}})
if err = c.Call("{{.ServiceRegisterName}}.{{.MethodName}}", in, out); err != nil {
return nil, err
}
if x, ok := proto.Message(out).(Validator); ok {
if err := x.Validate(); err != nil {
return out, err
}
}
return out, nil
}
func (c *{{.Prefix}}{{.ServiceName}}Client) Async{{.MethodName}}(in *{{.ArgsType}}, out *{{.ReplyType}}, done chan *rpc.Call) *rpc.Call {
if in == nil {
in = new({{.ArgsType}})
}
return c.Go(
"{{.ServiceRegisterName}}.{{.MethodName}}",
in, out,
done,
)
}
`
// gen client method list
var methodList string
for _, m := range svc.Method {
out := bytes.NewBuffer([]byte{})
t := template.Must(template.New("").Parse(clientMethodTmpl))
t.Execute(out, &struct {
Prefix string
ServiceName string
ServiceRegisterName string
MethodName string
ArgsType string
ReplyType string
}{
Prefix: flagPrefix,
ServiceName: generator.CamelCase(svc.GetName()),
ServiceRegisterName: p.makeServiceRegisterName(
file, file.GetPackage(), generator.CamelCase(svc.GetName()),
),
MethodName: generator.CamelCase(m.GetName()),
ArgsType: g.TypeName(g.ObjectNamed(m.GetInputType())),
ReplyType: g.TypeName(g.ObjectNamed(m.GetOutputType())),
})
methodList += out.String()
}
// gen all client code
{
out := bytes.NewBuffer([]byte{})
t := template.Must(template.New("").Parse(clientHelperFuncTmpl))
t.Execute(out, &struct {
Prefix string
PackageName string
ServiceName string
MethodList string
}{
Prefix: flagPrefix,
PackageName: file.GetPackage(),
ServiceName: generator.CamelCase(svc.GetName()),
MethodList: methodList,
})
return out.String()
}
}
func (p *protorpcPlugin) makeServiceRegisterName(
file *generator.FileDescriptor,
packageName, serviceName string,
) string {
// return packageName + "." + serviceName
return serviceName
}

View File

@ -0,0 +1,342 @@
// Copyright 2018 <chaishushan{AT}gmail.com>. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"bytes"
"log"
"text/template"
plugin "github.com/chai2010/protorpc/protoc-gen-plugin"
"github.com/golang/protobuf/protoc-gen-go/descriptor"
"github.com/golang/protobuf/protoc-gen-go/generator"
)
func main() {
plugin.Main()
}
func init() {
plugin.RegisterCodeGenerator(new(protorpcPlugin))
}
type protorpcPlugin struct{}
func (p *protorpcPlugin) Name() string { return "stdrpc-go" }
func (p *protorpcPlugin) FileNameExt() string { return ".pb.stdrpc.go" }
func (p *protorpcPlugin) HeaderCode(g *generator.Generator, file *generator.FileDescriptor) string {
const tmpl = `
{{- $G := .G -}}
{{- $File := .File -}}
// Code generated by protoc-gen-stdrpc. DO NOT EDIT.
//
// plugin: https://github.com/chai2010/protorpc/tree/master/protoc-gen-plugin
// plugin: https://github.com/chai2010/protorpc/tree/master/protoc-gen-stdrpc
//
// source: {{$File.GetName}}
package {{$File.PackageName}}
import (
"fmt"
"io"
"log"
"net"
"net/rpc"
"time"
"github.com/golang/protobuf/proto"
)
var (
_ = fmt.Sprint
_ = io.Reader(nil)
_ = log.Print
_ = net.Addr(nil)
_ = rpc.Call{}
_ = time.Second
_ = proto.String
)
`
var buf bytes.Buffer
t := template.Must(template.New("").Parse(tmpl))
err := t.Execute(&buf,
struct {
G *generator.Generator
File *generator.FileDescriptor
}{
G: g,
File: file,
},
)
if err != nil {
log.Fatal(err)
}
return buf.String()
}
func (p *protorpcPlugin) ServiceCode(g *generator.Generator, file *generator.FileDescriptor, svc *descriptor.ServiceDescriptorProto) string {
var code string
code += p.genServiceInterface(g, file, svc)
code += p.genServiceServer(g, file, svc)
code += p.genServiceClient(g, file, svc)
return code
}
func (p *protorpcPlugin) MessageCode(g *generator.Generator, file *generator.FileDescriptor, msg *descriptor.DescriptorProto) string {
return ""
}
func (p *protorpcPlugin) genServiceInterface(
g *generator.Generator,
file *generator.FileDescriptor,
svc *descriptor.ServiceDescriptorProto,
) string {
const serviceInterfaceTmpl = `
type {{.ServiceName}} interface {
{{.CallMethodList}}
}
`
const callMethodTmpl = `
{{.MethodName}}(in *{{.ArgsType}}, out *{{.ReplyType}}) error`
// gen call method list
var callMethodList string
for _, m := range svc.Method {
out := bytes.NewBuffer([]byte{})
t := template.Must(template.New("").Parse(callMethodTmpl))
t.Execute(out, &struct{ ServiceName, MethodName, ArgsType, ReplyType string }{
ServiceName: generator.CamelCase(svc.GetName()),
MethodName: generator.CamelCase(m.GetName()),
ArgsType: g.TypeName(g.ObjectNamed(m.GetInputType())),
ReplyType: g.TypeName(g.ObjectNamed(m.GetOutputType())),
})
callMethodList += out.String()
g.RecordTypeUse(m.GetInputType())
g.RecordTypeUse(m.GetOutputType())
}
// gen all interface code
{
out := bytes.NewBuffer([]byte{})
t := template.Must(template.New("").Parse(serviceInterfaceTmpl))
t.Execute(out, &struct{ ServiceName, CallMethodList string }{
ServiceName: generator.CamelCase(svc.GetName()),
CallMethodList: callMethodList,
})
return out.String()
}
}
func (p *protorpcPlugin) genServiceServer(
g *generator.Generator,
file *generator.FileDescriptor,
svc *descriptor.ServiceDescriptorProto,
) string {
const serviceHelperFunTmpl = `
// Accept{{.ServiceName}}Client accepts connections on the listener and serves requests
// for each incoming connection. Accept blocks; the caller typically
// invokes it in a go statement.
func Accept{{.ServiceName}}Client(lis net.Listener, x {{.ServiceName}}) {
srv := rpc.NewServer()
if err := srv.RegisterName("{{.ServiceRegisterName}}", x); err != nil {
log.Fatal(err)
}
for {
conn, err := lis.Accept()
if err != nil {
log.Fatalf("lis.Accept(): %v\n", err)
}
go srv.ServeConn(conn)
}
}
// Register{{.ServiceName}} publish the given {{.ServiceName}} implementation on the server.
func Register{{.ServiceName}}(srv *rpc.Server, x {{.ServiceName}}) error {
if err := srv.RegisterName("{{.ServiceRegisterName}}", x); err != nil {
return err
}
return nil
}
// New{{.ServiceName}}Server returns a new {{.ServiceName}} Server.
func New{{.ServiceName}}Server(x {{.ServiceName}}) *rpc.Server {
srv := rpc.NewServer()
if err := srv.RegisterName("{{.ServiceRegisterName}}", x); err != nil {
log.Fatal(err)
}
return srv
}
// ListenAndServe{{.ServiceName}} listen announces on the local network address laddr
// and serves the given {{.ServiceName}} implementation.
func ListenAndServe{{.ServiceName}}(network, addr string, x {{.ServiceName}}) error {
lis, err := net.Listen(network, addr)
if err != nil {
return err
}
defer lis.Close()
srv := rpc.NewServer()
if err := srv.RegisterName("{{.ServiceRegisterName}}", x); err != nil {
return err
}
for {
conn, err := lis.Accept()
if err != nil {
log.Fatalf("lis.Accept(): %v\n", err)
}
go srv.ServeConn(conn)
}
}
// Serve{{.ServiceName}} serves the given {{.ServiceName}} implementation.
func Serve{{.ServiceName}}(conn io.ReadWriteCloser, x {{.ServiceName}}) {
srv := rpc.NewServer()
if err := srv.RegisterName("{{.ServiceRegisterName}}", x); err != nil {
log.Fatal(err)
}
srv.ServeConn(conn)
}
`
{
out := bytes.NewBuffer([]byte{})
t := template.Must(template.New("").Parse(serviceHelperFunTmpl))
t.Execute(out, &struct{ PackageName, ServiceName, ServiceRegisterName string }{
PackageName: file.GetPackage(),
ServiceName: generator.CamelCase(svc.GetName()),
ServiceRegisterName: p.makeServiceRegisterName(
file, file.GetPackage(), generator.CamelCase(svc.GetName()),
),
})
return out.String()
}
}
func (p *protorpcPlugin) genServiceClient(
g *generator.Generator,
file *generator.FileDescriptor,
svc *descriptor.ServiceDescriptorProto,
) string {
const clientHelperFuncTmpl = `
type {{.ServiceName}}Client struct {
*rpc.Client
}
// New{{.ServiceName}}Client returns a {{.ServiceName}} stub to handle
// requests to the set of {{.ServiceName}} at the other end of the connection.
func New{{.ServiceName}}Client(conn io.ReadWriteCloser) (*{{.ServiceName}}Client) {
c := rpc.NewClient(conn)
return &{{.ServiceName}}Client{c}
}
{{.MethodList}}
// Dial{{.ServiceName}} connects to an {{.ServiceName}} at the specified network address.
func Dial{{.ServiceName}}(network, addr string) (*{{.ServiceName}}Client, error) {
c, err := rpc.Dial(network, addr)
if err != nil {
return nil, err
}
return &{{.ServiceName}}Client{c}, nil
}
// Dial{{.ServiceName}}Timeout connects to an {{.ServiceName}} at the specified network address.
func Dial{{.ServiceName}}Timeout(network, addr string, timeout time.Duration) (*{{.ServiceName}}Client, error) {
conn, err := net.DialTimeout(network, addr, timeout)
if err != nil {
return nil, err
}
return &{{.ServiceName}}Client{rpc.NewClient(conn)}, nil
}
`
const clientMethodTmpl = `
func (c *{{.ServiceName}}Client) {{.MethodName}}(in *{{.ArgsType}}) (out *{{.ReplyType}}, err error) {
if in == nil {
in = new({{.ArgsType}})
}
type Validator interface {
Validate() error
}
if x, ok := proto.Message(in).(Validator); ok {
if err := x.Validate(); err != nil {
return nil, err
}
}
out = new({{.ReplyType}})
if err = c.Call("{{.ServiceRegisterName}}.{{.MethodName}}", in, out); err != nil {
return nil, err
}
if x, ok := proto.Message(out).(Validator); ok {
if err := x.Validate(); err != nil {
return out, err
}
}
return out, nil
}
func (c *{{.ServiceName}}Client) Async{{.MethodName}}(in *{{.ArgsType}}, out *{{.ReplyType}}, done chan *rpc.Call) *rpc.Call {
if in == nil {
in = new({{.ArgsType}})
}
return c.Go(
"{{.ServiceRegisterName}}.{{.MethodName}}",
in, out,
done,
)
}
`
// gen client method list
var methodList string
for _, m := range svc.Method {
out := bytes.NewBuffer([]byte{})
t := template.Must(template.New("").Parse(clientMethodTmpl))
t.Execute(out, &struct{ ServiceName, ServiceRegisterName, MethodName, ArgsType, ReplyType string }{
ServiceName: generator.CamelCase(svc.GetName()),
ServiceRegisterName: p.makeServiceRegisterName(
file, file.GetPackage(), generator.CamelCase(svc.GetName()),
),
MethodName: generator.CamelCase(m.GetName()),
ArgsType: g.TypeName(g.ObjectNamed(m.GetInputType())),
ReplyType: g.TypeName(g.ObjectNamed(m.GetOutputType())),
})
methodList += out.String()
}
// gen all client code
{
out := bytes.NewBuffer([]byte{})
t := template.Must(template.New("").Parse(clientHelperFuncTmpl))
t.Execute(out, &struct{ PackageName, ServiceName, MethodList string }{
PackageName: file.GetPackage(),
ServiceName: generator.CamelCase(svc.GetName()),
MethodList: methodList,
})
return out.String()
}
}
func (p *protorpcPlugin) makeServiceRegisterName(
file *generator.FileDescriptor,
packageName, serviceName string,
) string {
// return packageName + "." + serviceName
return serviceName
}

View File

@ -0,0 +1,103 @@
// Go support for Protocol Buffers - Google's data interchange format
//
// Copyright 2010 The Go Authors. All rights reserved.
// https://github.com/golang/protobuf
//
// 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.
// protoc-gen-go is a plugin for the Google protocol buffer compiler to generate
// Go code. Run it by building this program and putting it in your path with
// the name
// protoc-gen-go
// That word 'go' at the end becomes part of the option string set for the
// protocol compiler, so once the protocol compiler (protoc) is installed
// you can run
// protoc --go_out=output_directory input_directory/file.proto
// to generate Go bindings for the protocol defined by file.proto.
// With that input, the output will be written to
// output_directory/file.pb.go
//
// The generated code is documented in the package comment for
// the library.
//
// See the README and documentation for protocol buffers to learn more:
// https://developers.google.com/protocol-buffers/
package main
import (
"io/ioutil"
"os"
"strings"
"github.com/golang/protobuf/proto"
"github.com/golang/protobuf/protoc-gen-go/generator"
)
func main() {
// Begin by allocating a generator. The request and response structures are stored there
// so we can do error handling easily - the response structure contains the field to
// report failure.
g := generator.New()
data, err := ioutil.ReadAll(os.Stdin)
if err != nil {
g.Error(err, "reading input")
}
if err := proto.Unmarshal(data, g.Request); err != nil {
g.Error(err, "parsing input proto")
}
if len(g.Request.FileToGenerate) == 0 {
g.Fail("no files to generate")
}
parameter := g.Request.GetParameter()
if !strings.Contains(parameter, "plugins=") {
parameter += ",plugins=" + netrpcPluginName
}
g.CommandLineParameters(parameter)
// Create a wrapped version of the Descriptors and EnumDescriptors that
// point to the file that defines them.
g.WrapTypes()
g.SetPackageNames()
g.BuildTypeNameMap()
g.GenerateAllFiles()
// Send back the results.
data, err = proto.Marshal(g.Response)
if err != nil {
g.Error(err, "failed to marshal output proto")
}
_, err = os.Stdout.Write(data)
if err != nil {
g.Error(err, "failed to write output proto")
}
}

View File

@ -0,0 +1,350 @@
// Copyright 2013 <chaishushan{AT}gmail.com>. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"bytes"
"text/template"
"github.com/golang/protobuf/protoc-gen-go/descriptor"
"github.com/golang/protobuf/protoc-gen-go/generator"
)
const netrpcPluginName = "netrpc"
// netrpcPlugin produce the Service interface.
type netrpcPlugin struct {
*generator.Generator
}
// Name returns the name of the plugin.
func (p *netrpcPlugin) Name() string { return netrpcPluginName }
// Init is called once after data structures are built but before
// code generation begins.
func (p *netrpcPlugin) Init(g *generator.Generator) {
p.Generator = g
}
// Generate produces the code generated by the plugin for this file.
func (p *netrpcPlugin) GenerateImports(file *generator.FileDescriptor) {
if len(file.Service) > 0 {
p.P(`import "bufio"`)
p.P(`import "crypto/tls"`)
p.P(`import "errors"`)
p.P(`import "io"`)
p.P(`import "log"`)
p.P(`import "net"`)
p.P(`import "net/http"`)
p.P(`import "net/rpc"`)
p.P(`import "time"`)
}
}
// Generate generates the Service interface.
// rpc service can't handle other proto message!!!
func (p *netrpcPlugin) Generate(file *generator.FileDescriptor) {
for _, svc := range file.Service {
p.genServiceInterface(file, svc)
p.genServiceServer(file, svc)
p.genServiceClient(file, svc)
}
}
func (p *netrpcPlugin) genServiceInterface(
file *generator.FileDescriptor,
svc *descriptor.ServiceDescriptorProto,
) {
const serviceInterfaceTmpl = `
type {{.ServiceName}} interface {
{{.CallMethodList}}
}
`
const callMethodTmpl = `
{{.MethodName}}(in *{{.ArgsType}}, out *{{.ReplyType}}) error`
// gen call method list
var callMethodList string
for _, m := range svc.Method {
out := bytes.NewBuffer([]byte{})
t := template.Must(template.New("").Parse(callMethodTmpl))
t.Execute(out, &struct{ ServiceName, MethodName, ArgsType, ReplyType string }{
ServiceName: generator.CamelCase(svc.GetName()),
MethodName: generator.CamelCase(m.GetName()),
ArgsType: p.TypeName(p.ObjectNamed(m.GetInputType())),
ReplyType: p.TypeName(p.ObjectNamed(m.GetOutputType())),
})
callMethodList += out.String()
p.RecordTypeUse(m.GetInputType())
p.RecordTypeUse(m.GetOutputType())
}
// gen all interface code
{
out := bytes.NewBuffer([]byte{})
t := template.Must(template.New("").Parse(serviceInterfaceTmpl))
t.Execute(out, &struct{ ServiceName, CallMethodList string }{
ServiceName: generator.CamelCase(svc.GetName()),
CallMethodList: callMethodList,
})
p.P(out.String())
}
}
func (p *netrpcPlugin) genServiceServer(
file *generator.FileDescriptor,
svc *descriptor.ServiceDescriptorProto,
) {
const serviceHelperFunTmpl = `
// Accept{{.ServiceName}}Client accepts connections on the listener and serves requests
// for each incoming connection. Accept blocks; the caller typically
// invokes it in a go statement.
func Accept{{.ServiceName}}Client(lis net.Listener, x {{.ServiceName}}) {
srv := rpc.NewServer()
if err := srv.RegisterName("{{.ServiceRegisterName}}", x); err != nil {
log.Fatal(err)
}
for {
conn, err := lis.Accept()
if err != nil {
log.Fatalf("lis.Accept(): %v\n", err)
}
go srv.ServeConn(conn)
}
}
// Register{{.ServiceName}} publish the given {{.ServiceName}} implementation on the server.
func Register{{.ServiceName}}(srv *rpc.Server, x {{.ServiceName}}) error {
if err := srv.RegisterName("{{.ServiceRegisterName}}", x); err != nil {
return err
}
return nil
}
// New{{.ServiceName}}Server returns a new {{.ServiceName}} Server.
func New{{.ServiceName}}Server(x {{.ServiceName}}) *rpc.Server {
srv := rpc.NewServer()
if err := srv.RegisterName("{{.ServiceRegisterName}}", x); err != nil {
log.Fatal(err)
}
return srv
}
// ListenAndServe{{.ServiceName}} listen announces on the local network address laddr
// and serves the given {{.ServiceName}} implementation.
func ListenAndServe{{.ServiceName}}(network, addr string, x {{.ServiceName}}) error {
lis, err := net.Listen(network, addr)
if err != nil {
return err
}
defer lis.Close()
srv := rpc.NewServer()
if err := srv.RegisterName("{{.ServiceRegisterName}}", x); err != nil {
return err
}
for {
conn, err := lis.Accept()
if err != nil {
log.Fatalf("lis.Accept(): %v\n", err)
}
go srv.ServeConn(conn)
}
}
// Serve{{.ServiceName}} serves the given {{.ServiceName}} implementation.
func Serve{{.ServiceName}}(conn io.ReadWriteCloser, x {{.ServiceName}}) {
srv := rpc.NewServer()
if err := srv.RegisterName("{{.ServiceRegisterName}}", x); err != nil {
log.Fatal(err)
}
srv.ServeConn(conn)
}
`
{
out := bytes.NewBuffer([]byte{})
t := template.Must(template.New("").Parse(serviceHelperFunTmpl))
t.Execute(out, &struct{ PackageName, ServiceName, ServiceRegisterName string }{
PackageName: file.GetPackage(),
ServiceName: generator.CamelCase(svc.GetName()),
ServiceRegisterName: p.makeServiceRegisterName(
file, file.GetPackage(), generator.CamelCase(svc.GetName()),
),
})
p.P(out.String())
}
}
func (p *netrpcPlugin) genServiceClient(
file *generator.FileDescriptor,
svc *descriptor.ServiceDescriptorProto,
) {
const clientHelperFuncTmpl = `
type {{.ServiceName}}Client struct {
*rpc.Client
}
// New{{.ServiceName}}Client returns a {{.ServiceName}} stub to handle
// requests to the set of {{.ServiceName}} at the other end of the connection.
func New{{.ServiceName}}Client(conn io.ReadWriteCloser) (*{{.ServiceName}}Client) {
c := rpc.NewClient(conn)
return &{{.ServiceName}}Client{c}
}
{{.MethodList}}
// Dial{{.ServiceName}} connects to an {{.ServiceName}} at the specified network address.
func Dial{{.ServiceName}}(network, addr string) (*{{.ServiceName}}Client, error) {
c, err := rpc.Dial(network, addr)
if err != nil {
return nil, err
}
return &{{.ServiceName}}Client{c}, nil
}
// Dial{{.ServiceName}}Timeout connects to an {{.ServiceName}} at the specified network address.
func Dial{{.ServiceName}}Timeout(network, addr string, timeout time.Duration) (*{{.ServiceName}}Client, error) {
conn, err := net.DialTimeout(network, addr, timeout)
if err != nil {
return nil, err
}
return &{{.ServiceName}}Client{rpc.NewClient(conn)}, nil
}
// Dial{{.ServiceName}}HTTP connects to an HTTP RPC server at the specified network address
// listening on the default HTTP RPC path.
func Dial{{.ServiceName}}HTTP(network, address string) (*{{.ServiceName}}Client, error) {
return Dial{{.ServiceName}}HTTPPath(network, address, rpc.DefaultRPCPath)
}
// Dial{{.ServiceName}}HTTPPath connects to an HTTP RPC server
// at the specified network address and path.
func Dial{{.ServiceName}}HTTPPath(network, address, path string) (*{{.ServiceName}}Client, error) {
conn, err := net.Dial(network, address)
if err != nil {
return nil, err
}
return dial{{.ServiceName}}Path(network, address, path, conn)
}
// Dial{{.ServiceName}}HTTPS connects to an HTTPS RPC server at the specified network address
// listening on the default HTTP RPC path.
func Dial{{.ServiceName}}HTTPS(network, address string, tlsConfig *tls.Config) (*{{.ServiceName}}Client, error) {
return Dial{{.ServiceName}}HTTPSPath(network, address, rpc.DefaultRPCPath, tlsConfig)
}
// Dial{{.ServiceName}}HTTPSPath connects to an HTTPS RPC server
// at the specified network address and path.
func Dial{{.ServiceName}}HTTPSPath(network, address, path string, tlsConfig *tls.Config) (*{{.ServiceName}}Client, error) {
conn, err := tls.Dial(network, address, tlsConfig)
if err != nil {
return nil, err
}
return dial{{.ServiceName}}Path(network, address, path, conn)
}
func dial{{.ServiceName}}Path(network, address, path string, conn net.Conn) (*{{.ServiceName}}Client, error) {
const net_rpc_connected = "200 Connected to Go RPC"
io.WriteString(conn, "CONNECT "+path+" HTTP/1.0\n\n")
// Require successful HTTP response
// before switching to RPC protocol.
resp, err := http.ReadResponse(bufio.NewReader(conn), &http.Request{Method: "CONNECT"})
if err == nil && resp.Status == net_rpc_connected {
return &{{.ServiceName}}Client{rpc.NewClient(conn)}, nil
}
if err == nil {
err = errors.New("unexpected HTTP response: " + resp.Status)
}
conn.Close()
return nil, &net.OpError{
Op: "dial-http",
Net: network + " " + address,
Addr: nil,
Err: err,
}
}
`
const clientMethodTmpl = `
func (c *{{.ServiceName}}Client) {{.MethodName}}(in *{{.ArgsType}}) (out *{{.ReplyType}}, err error) {
if in == nil {
in = new({{.ArgsType}})
}
type Validator interface {
Validate() error
}
if x, ok := proto.Message(in).(Validator); ok {
if err := x.Validate(); err != nil {
return nil, err
}
}
out = new({{.ReplyType}})
if err = c.Call("{{.ServiceRegisterName}}.{{.MethodName}}", in, out); err != nil {
return nil, err
}
if x, ok := proto.Message(out).(Validator); ok {
if err := x.Validate(); err != nil {
return out, err
}
}
return out, nil
}
func (c *{{.ServiceName}}Client) Async{{.MethodName}}(in *{{.ArgsType}}, out *{{.ReplyType}}, done chan *rpc.Call) *rpc.Call {
if in == nil {
in = new({{.ArgsType}})
}
return c.Go(
"{{.ServiceRegisterName}}.{{.MethodName}}",
in, out,
done,
)
}
`
// gen client method list
var methodList string
for _, m := range svc.Method {
out := bytes.NewBuffer([]byte{})
t := template.Must(template.New("").Parse(clientMethodTmpl))
t.Execute(out, &struct{ ServiceName, ServiceRegisterName, MethodName, ArgsType, ReplyType string }{
ServiceName: generator.CamelCase(svc.GetName()),
ServiceRegisterName: p.makeServiceRegisterName(
file, file.GetPackage(), generator.CamelCase(svc.GetName()),
),
MethodName: generator.CamelCase(m.GetName()),
ArgsType: p.TypeName(p.ObjectNamed(m.GetInputType())),
ReplyType: p.TypeName(p.ObjectNamed(m.GetOutputType())),
})
methodList += out.String()
}
// gen all client code
{
out := bytes.NewBuffer([]byte{})
t := template.Must(template.New("").Parse(clientHelperFuncTmpl))
t.Execute(out, &struct{ PackageName, ServiceName, MethodList string }{
PackageName: file.GetPackage(),
ServiceName: generator.CamelCase(svc.GetName()),
MethodList: methodList,
})
p.P(out.String())
}
}
func (p *netrpcPlugin) makeServiceRegisterName(
file *generator.FileDescriptor,
packageName, serviceName string,
) string {
return packageName + "." + serviceName
}
func init() {
generator.RegisterPlugin(new(netrpcPlugin))
}

256
core/protorpc/rpc_test.go Normal file
View File

@ -0,0 +1,256 @@
// Copyright 2013 <chaishushan{AT}gmail.com>. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package protorpc_test
import (
"errors"
"log"
"net"
"net/rpc"
"testing"
"time"
"github.com/chai2010/protorpc"
msg "github.com/chai2010/protorpc/examples/message.pb"
)
type Arith int
func (t *Arith) Add(args *msg.ArithRequest, reply *msg.ArithResponse) error {
reply.C = args.A + args.B
log.Printf("Arith.Add(%v, %v): %v", args.A, args.B, reply.C)
return nil
}
func (t *Arith) Mul(args *msg.ArithRequest, reply *msg.ArithResponse) error {
reply.C = args.A * args.B
return nil
}
func (t *Arith) Div(args *msg.ArithRequest, reply *msg.ArithResponse) error {
if args.B == 0 {
return errors.New("divide by zero")
}
reply.C = args.A / args.B
return nil
}
func (t *Arith) Error(args *msg.ArithRequest, reply *msg.ArithResponse) error {
return errors.New("ArithError")
}
type Echo int
func (t *Echo) Echo(args *msg.EchoRequest, reply *msg.EchoResponse) error {
reply.Msg = args.Msg
return nil
}
func TestInternalMessagePkg(t *testing.T) {
err := listenAndServeArithAndEchoService("tcp", "127.0.0.1:1414")
if err != nil {
log.Fatalf("listenAndServeArithAndEchoService: %v", err)
}
conn, err := net.Dial("tcp", "127.0.0.1:1414")
if err != nil {
t.Fatalf(`net.Dial("tcp", "127.0.0.1:1414"): %v`, err)
}
client := rpc.NewClientWithCodec(protorpc.NewClientCodec(conn))
defer client.Close()
testArithClient(t, client)
testEchoClient(t, client)
testArithClientAsync(t, client)
testEchoClientAsync(t, client)
}
func listenAndServeArithAndEchoService(network, addr string) error {
clients, err := net.Listen(network, addr)
if err != nil {
return err
}
srv := rpc.NewServer()
if err := srv.RegisterName("ArithService", new(Arith)); err != nil {
return err
}
if err := srv.RegisterName("EchoService", new(Echo)); err != nil {
return err
}
go func() {
for {
conn, err := clients.Accept()
if err != nil {
log.Printf("clients.Accept(): %v\n", err)
continue
}
go srv.ServeCodec(protorpc.NewServerCodec(conn))
}
}()
return nil
}
func testArithClient(t *testing.T, client *rpc.Client) {
var args msg.ArithRequest
var reply msg.ArithResponse
var err error
// Add
args.A = 1
args.B = 2
if err = client.Call("ArithService.Add", &args, &reply); err != nil {
t.Fatalf(`arith.Add: %v`, err)
}
if reply.C != 3 {
t.Fatalf(`arith.Add: expected = %d, got = %d`, 3, reply.C)
}
// Mul
args.A = 2
args.B = 3
if err = client.Call("ArithService.Mul", &args, &reply); err != nil {
t.Fatalf(`arith.Mul: %v`, err)
}
if reply.C != 6 {
t.Fatalf(`arith.Mul: expected = %d, got = %d`, 6, reply.C)
}
// Div
args.A = 13
args.B = 5
if err = client.Call("ArithService.Div", &args, &reply); err != nil {
t.Fatalf(`arith.Div: %v`, err)
}
if reply.C != 2 {
t.Fatalf(`arith.Div: expected = %d, got = %d`, 2, reply.C)
}
// Div zero
args.A = 1
args.B = 0
if err = client.Call("ArithService.Div", &args, &reply); err.Error() != "divide by zero" {
t.Fatalf(`arith.Error: expected = "%s", got = "%s"`, "divide by zero", err.Error())
}
// Error
args.A = 1
args.B = 2
if err = client.Call("ArithService.Error", &args, &reply); err.Error() != "ArithError" {
t.Fatalf(`arith.Error: expected = "%s", got = "%s"`, "ArithError", err.Error())
}
}
func testArithClientAsync(t *testing.T, client *rpc.Client) {
done := make(chan *rpc.Call, 16)
callInfoList := []struct {
method string
args *msg.ArithRequest
reply *msg.ArithResponse
err error
}{
{
"ArithService.Add",
&msg.ArithRequest{A: 1, B: 2},
&msg.ArithResponse{C: 3},
nil,
},
{
"ArithService.Mul",
&msg.ArithRequest{A: 2, B: 3},
&msg.ArithResponse{C: 6},
nil,
},
{
"ArithService.Div",
&msg.ArithRequest{A: 13, B: 5},
&msg.ArithResponse{C: 2},
nil,
},
{
"ArithService.Div",
&msg.ArithRequest{A: 1, B: 0},
&msg.ArithResponse{},
errors.New("divide by zero"),
},
{
"ArithService.Error",
&msg.ArithRequest{A: 1, B: 2},
&msg.ArithResponse{},
errors.New("ArithError"),
},
}
// GoCall list
calls := make([]*rpc.Call, len(callInfoList))
for i := 0; i < len(calls); i++ {
calls[i] = client.Go(callInfoList[i].method,
callInfoList[i].args, callInfoList[i].reply,
done,
)
}
for i := 0; i < len(calls); i++ {
<-calls[i].Done
}
// check result
for i := 0; i < len(calls); i++ {
if callInfoList[i].err != nil {
if calls[i].Error.Error() != callInfoList[i].err.Error() {
t.Fatalf(`%s: expected %v, Got = %v`,
callInfoList[i].method,
callInfoList[i].err,
calls[i].Error,
)
}
continue
}
got := calls[i].Reply.(*msg.ArithResponse).C
expected := callInfoList[i].reply.C
if got != expected {
t.Fatalf(`%v: expected %v, Got = %v`,
callInfoList[i].method, got, expected,
)
}
}
}
func testEchoClient(t *testing.T, client *rpc.Client) {
var args msg.EchoRequest
var reply msg.EchoResponse
var err error
// EchoService.Echo
args.Msg = "Hello, Protobuf-RPC"
if err = client.Call("EchoService.Echo", &args, &reply); err != nil {
t.Fatalf(`EchoService.Echo: %v`, err)
}
if reply.Msg != args.Msg {
t.Fatalf(`EchoService.Echo: expected = "%s", got = "%s"`, args.Msg, reply.Msg)
}
}
func testEchoClientAsync(t *testing.T, client *rpc.Client) {
// EchoService.Echo
args := &msg.EchoRequest{Msg: "Hello, Protobuf-RPC"}
reply := &msg.EchoResponse{}
echoCall := client.Go("EchoService.Echo", args, reply, nil)
// sleep 1s
time.Sleep(time.Second)
// EchoService.Echo reply
echoCall = <-echoCall.Done
if echoCall.Error != nil {
t.Fatalf(`EchoService.Echo: %v`, echoCall.Error)
}
if echoCall.Reply.(*msg.EchoResponse).Msg != args.Msg {
t.Fatalf(`EchoService.Echo: expected = "%s", got = "%s"`,
args.Msg,
echoCall.Reply.(*msg.EchoResponse).Msg,
)
}
}

134
core/protorpc/server.go Normal file
View File

@ -0,0 +1,134 @@
// Copyright 2013 <chaishushan{AT}gmail.com>. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package protorpc
import (
"errors"
"fmt"
"io"
"net/rpc"
"sync"
wire "github.com/chai2010/protorpc/wire.pb"
"github.com/golang/protobuf/proto"
)
type serverCodec struct {
r io.Reader
w io.Writer
c io.Closer
// temporary work space
reqHeader wire.RequestHeader
// Package rpc expects uint64 request IDs.
// We assign uint64 sequence numbers to incoming requests
// but save the original request ID in the pending map.
// When rpc responds, we use the sequence number in
// the response to find the original request ID.
mutex sync.Mutex // protects seq, pending
seq uint64
pending map[uint64]uint64
}
// NewServerCodec returns a serverCodec that communicates with the ClientCodec
// on the other end of the given conn.
func NewServerCodec(conn io.ReadWriteCloser) rpc.ServerCodec {
return &serverCodec{
r: conn,
w: conn,
c: conn,
pending: make(map[uint64]uint64),
}
}
func (c *serverCodec) ReadRequestHeader(r *rpc.Request) error {
header := wire.RequestHeader{}
err := readRequestHeader(c.r, &header)
if err != nil {
return err
}
c.mutex.Lock()
c.seq++
c.pending[c.seq] = *header.Id
r.ServiceMethod = *header.Method
r.Seq = c.seq
c.mutex.Unlock()
c.reqHeader = header
return nil
}
func (c *serverCodec) ReadRequestBody(x interface{}) error {
if x == nil {
return nil
}
request, ok := x.(proto.Message)
if !ok {
return fmt.Errorf(
"protorpc.ServerCodec.ReadRequestBody: %T does not implement proto.Message",
x,
)
}
err := readRequestBody(c.r, &c.reqHeader, request)
if err != nil {
return nil
}
c.reqHeader = wire.RequestHeader{}
return nil
}
// A value sent as a placeholder for the server's response value when the server
// receives an invalid request. It is never decoded by the client since the Response
// contains an error when it is used.
var invalidRequest = struct{}{}
func (c *serverCodec) WriteResponse(r *rpc.Response, x interface{}) error {
var response proto.Message
if x != nil {
var ok bool
if response, ok = x.(proto.Message); !ok {
if _, ok = x.(struct{}); !ok {
c.mutex.Lock()
delete(c.pending, r.Seq)
c.mutex.Unlock()
return fmt.Errorf(
"protorpc.ServerCodec.WriteResponse: %T does not implement proto.Message",
x,
)
}
}
}
c.mutex.Lock()
id, ok := c.pending[r.Seq]
if !ok {
c.mutex.Unlock()
return errors.New("protorpc: invalid sequence number in response")
}
delete(c.pending, r.Seq)
c.mutex.Unlock()
err := writeResponse(c.w, id, r.Error, response)
if err != nil {
return err
}
return nil
}
func (s *serverCodec) Close() error {
return s.c.Close()
}
// ServeConn runs the Protobuf-RPC server on a single connection.
// ServeConn blocks, serving the connection until the client hangs up.
// The caller typically invokes ServeConn in a go statement.
func ServeConn(conn io.ReadWriteCloser) {
rpc.ServeCodec(NewServerCodec(conn))
}

173
core/protorpc/wire.go Normal file
View File

@ -0,0 +1,173 @@
// Copyright 2013 <chaishushan{AT}gmail.com>. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package protorpc
import (
"fmt"
"io"
wire "github.com/chai2010/protorpc/wire.pb"
"github.com/golang/protobuf/proto"
)
func To[T any](v T) *T {
return &v
}
func maxUint32(a, b uint32) uint32 {
if a > b {
return a
}
return b
}
func writeRequest(w io.Writer, id uint64, method string, request proto.Message) error {
// marshal request
pbRequest := []byte{}
if request != nil {
var err error
pbRequest, err = proto.Marshal(request)
if err != nil {
return err
}
}
// generate header
header := &wire.RequestHeader{
Id: To(id),
Method: To(method),
RawRequestLen: To(uint32(len(pbRequest))),
}
// check header size
pbHeader, err := proto.Marshal(header)
if err != err {
return err
}
if len(pbHeader) > int(wire.Const_MAX_REQUEST_HEADER_LEN) {
return fmt.Errorf("protorpc.writeRequest: header larger than max_header_len: %d.", len(pbHeader))
}
// send header (more)
if err := sendFrame(w, pbHeader); err != nil {
return err
}
// send body (end)
if err := sendFrame(w, pbRequest); err != nil {
return err
}
return nil
}
func readRequestHeader(r io.Reader, header *wire.RequestHeader) (err error) {
// recv header (more)
pbHeader, err := recvFrame(r, int(wire.Const_MAX_REQUEST_HEADER_LEN))
if err != nil {
return err
}
// Marshal Header
err = proto.Unmarshal(pbHeader, header)
if err != nil {
return err
}
return nil
}
func readRequestBody(r io.Reader, header *wire.RequestHeader, request proto.Message) error {
// recv body (end)
pbRequest, err := recvFrame(r, int(*header.RawRequestLen))
if err != nil {
return err
}
// Unmarshal to proto message
if request != nil {
err = proto.Unmarshal(pbRequest, request)
if err != nil {
return err
}
}
return nil
}
func writeResponse(w io.Writer, id uint64, serr string, response proto.Message) (err error) {
// clean response if error
if serr != "" {
response = nil
}
// marshal response
pbResponse := []byte{}
if response != nil {
pbResponse, err = proto.Marshal(response)
if err != nil {
return err
}
}
// generate header
header := &wire.ResponseHeader{
Id: To(id),
Error: To(serr),
RawResponseLen: To(uint32(len(pbResponse))),
}
// check header size
pbHeader, err := proto.Marshal(header)
if err != err {
return
}
// send header (more)
if err = sendFrame(w, pbHeader); err != nil {
return
}
// send body (end)
if err = sendFrame(w, pbResponse); err != nil {
return
}
return nil
}
func readResponseHeader(r io.Reader, header *wire.ResponseHeader) error {
// recv header (more)
pbHeader, err := recvFrame(r, 0)
if err != nil {
return err
}
// Marshal Header
err = proto.Unmarshal(pbHeader, header)
if err != nil {
return err
}
return nil
}
func readResponseBody(r io.Reader, header *wire.ResponseHeader, response proto.Message) error {
// recv body (end)
pbResponse, err := recvFrame(r, int(*header.RawResponseLen))
if err != nil {
return err
}
// Unmarshal to proto message
if response != nil {
err = proto.Unmarshal(pbResponse, response)
if err != nil {
return err
}
}
return nil
}

View File

@ -0,0 +1,7 @@
// Copyright 2014 <chaishushan{AT}gmail.com>. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:generate protoc --go_out=. wire.proto
package protorpc_wire

View File

@ -0,0 +1,296 @@
// Copyright 2013 <chaishushan{AT}gmail.com>. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.6
// protoc v6.31.0
// source: wire.proto
//
// protorpc wire format wrapper
//
// 0. Frame Format
// len : uvarint64
// data: byte[len]
//
// 1. Client Send Request
// Send RequestHeader: sendFrame(zsock, hdr, len(hdr))
// Send Request: sendFrame(zsock, body, hdr.snappy_compressed_request_len)
//
// 2. Server Recv Request
// Recv RequestHeader: recvFrame(zsock, hdr, max_hdr_len, 0)
// Recv Request: recvFrame(zsock, body, hdr.snappy_compressed_request_len, 0)
//
// 3. Server Send Response
// Send ResponseHeader: sendFrame(zsock, hdr, len(hdr))
// Send Response: sendFrame(zsock, body, hdr.snappy_compressed_response_len)
//
// 4. Client Recv Response
// Recv ResponseHeader: recvFrame(zsock, hdr, max_hdr_len, 0)
// Recv Response: recvFrame(zsock, body, hdr.snappy_compressed_response_len, 0)
//
package protorpc_wire
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
unsafe "unsafe"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type Const int32
const (
Const_ZERO Const = 0
Const_MAX_REQUEST_HEADER_LEN Const = 1024
)
// Enum value maps for Const.
var (
Const_name = map[int32]string{
0: "ZERO",
1024: "MAX_REQUEST_HEADER_LEN",
}
Const_value = map[string]int32{
"ZERO": 0,
"MAX_REQUEST_HEADER_LEN": 1024,
}
)
func (x Const) Enum() *Const {
p := new(Const)
*p = x
return p
}
func (x Const) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
func (Const) Descriptor() protoreflect.EnumDescriptor {
return file_wire_proto_enumTypes[0].Descriptor()
}
func (Const) Type() protoreflect.EnumType {
return &file_wire_proto_enumTypes[0]
}
func (x Const) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x)
}
// Deprecated: Do not use.
func (x *Const) UnmarshalJSON(b []byte) error {
num, err := protoimpl.X.UnmarshalJSONEnum(x.Descriptor(), b)
if err != nil {
return err
}
*x = Const(num)
return nil
}
// Deprecated: Use Const.Descriptor instead.
func (Const) EnumDescriptor() ([]byte, []int) {
return file_wire_proto_rawDescGZIP(), []int{0}
}
type RequestHeader struct {
state protoimpl.MessageState `protogen:"open.v1"`
Id *uint64 `protobuf:"varint,1,req,name=id" json:"id,omitempty"`
Method *string `protobuf:"bytes,2,req,name=method" json:"method,omitempty"`
RawRequestLen *uint32 `protobuf:"varint,3,req,name=raw_request_len,json=rawRequestLen" json:"raw_request_len,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *RequestHeader) Reset() {
*x = RequestHeader{}
mi := &file_wire_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *RequestHeader) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*RequestHeader) ProtoMessage() {}
func (x *RequestHeader) ProtoReflect() protoreflect.Message {
mi := &file_wire_proto_msgTypes[0]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use RequestHeader.ProtoReflect.Descriptor instead.
func (*RequestHeader) Descriptor() ([]byte, []int) {
return file_wire_proto_rawDescGZIP(), []int{0}
}
func (x *RequestHeader) GetId() uint64 {
if x != nil && x.Id != nil {
return *x.Id
}
return 0
}
func (x *RequestHeader) GetMethod() string {
if x != nil && x.Method != nil {
return *x.Method
}
return ""
}
func (x *RequestHeader) GetRawRequestLen() uint32 {
if x != nil && x.RawRequestLen != nil {
return *x.RawRequestLen
}
return 0
}
type ResponseHeader struct {
state protoimpl.MessageState `protogen:"open.v1"`
Id *uint64 `protobuf:"varint,1,req,name=id" json:"id,omitempty"`
Error *string `protobuf:"bytes,2,req,name=error" json:"error,omitempty"`
RawResponseLen *uint32 `protobuf:"varint,3,req,name=raw_response_len,json=rawResponseLen" json:"raw_response_len,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *ResponseHeader) Reset() {
*x = ResponseHeader{}
mi := &file_wire_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *ResponseHeader) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ResponseHeader) ProtoMessage() {}
func (x *ResponseHeader) ProtoReflect() protoreflect.Message {
mi := &file_wire_proto_msgTypes[1]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ResponseHeader.ProtoReflect.Descriptor instead.
func (*ResponseHeader) Descriptor() ([]byte, []int) {
return file_wire_proto_rawDescGZIP(), []int{1}
}
func (x *ResponseHeader) GetId() uint64 {
if x != nil && x.Id != nil {
return *x.Id
}
return 0
}
func (x *ResponseHeader) GetError() string {
if x != nil && x.Error != nil {
return *x.Error
}
return ""
}
func (x *ResponseHeader) GetRawResponseLen() uint32 {
if x != nil && x.RawResponseLen != nil {
return *x.RawResponseLen
}
return 0
}
var File_wire_proto protoreflect.FileDescriptor
const file_wire_proto_rawDesc = "" +
"\n" +
"\n" +
"wire.proto\x12\rprotorpc.wire\"_\n" +
"\rRequestHeader\x12\x0e\n" +
"\x02id\x18\x01 \x02(\x04R\x02id\x12\x16\n" +
"\x06method\x18\x02 \x02(\tR\x06method\x12&\n" +
"\x0fraw_request_len\x18\x03 \x02(\rR\rrawRequestLen\"`\n" +
"\x0eResponseHeader\x12\x0e\n" +
"\x02id\x18\x01 \x02(\x04R\x02id\x12\x14\n" +
"\x05error\x18\x02 \x02(\tR\x05error\x12(\n" +
"\x10raw_response_len\x18\x03 \x02(\rR\x0erawResponseLen*.\n" +
"\x05Const\x12\b\n" +
"\x04ZERO\x10\x00\x12\x1b\n" +
"\x16MAX_REQUEST_HEADER_LEN\x10\x80\bB\x0fZ\rprotorpc.wire"
var (
file_wire_proto_rawDescOnce sync.Once
file_wire_proto_rawDescData []byte
)
func file_wire_proto_rawDescGZIP() []byte {
file_wire_proto_rawDescOnce.Do(func() {
file_wire_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_wire_proto_rawDesc), len(file_wire_proto_rawDesc)))
})
return file_wire_proto_rawDescData
}
var file_wire_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
var file_wire_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
var file_wire_proto_goTypes = []any{
(Const)(0), // 0: protorpc.wire.Const
(*RequestHeader)(nil), // 1: protorpc.wire.RequestHeader
(*ResponseHeader)(nil), // 2: protorpc.wire.ResponseHeader
}
var file_wire_proto_depIdxs = []int32{
0, // [0:0] is the sub-list for method output_type
0, // [0:0] is the sub-list for method input_type
0, // [0:0] is the sub-list for extension type_name
0, // [0:0] is the sub-list for extension extendee
0, // [0:0] is the sub-list for field type_name
}
func init() { file_wire_proto_init() }
func file_wire_proto_init() {
if File_wire_proto != nil {
return
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_wire_proto_rawDesc), len(file_wire_proto_rawDesc)),
NumEnums: 1,
NumMessages: 2,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_wire_proto_goTypes,
DependencyIndexes: file_wire_proto_depIdxs,
EnumInfos: file_wire_proto_enumTypes,
MessageInfos: file_wire_proto_msgTypes,
}.Build()
File_wire_proto = out.File
file_wire_proto_goTypes = nil
file_wire_proto_depIdxs = nil
}

View File

@ -0,0 +1,50 @@
// Copyright 2013 <chaishushan{AT}gmail.com>. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
syntax = "proto2";
//
// protorpc wire format wrapper
//
// 0. Frame Format
// len : uvarint64
// data: byte[len]
//
// 1. Client Send Request
// Send RequestHeader: sendFrame(zsock, hdr, len(hdr))
// Send Request: sendFrame(zsock, body, hdr.snappy_compressed_request_len)
//
// 2. Server Recv Request
// Recv RequestHeader: recvFrame(zsock, hdr, max_hdr_len, 0)
// Recv Request: recvFrame(zsock, body, hdr.snappy_compressed_request_len, 0)
//
// 3. Server Send Response
// Send ResponseHeader: sendFrame(zsock, hdr, len(hdr))
// Send Response: sendFrame(zsock, body, hdr.snappy_compressed_response_len)
//
// 4. Client Recv Response
// Recv ResponseHeader: recvFrame(zsock, hdr, max_hdr_len, 0)
// Recv Response: recvFrame(zsock, body, hdr.snappy_compressed_response_len, 0)
//
package protorpc.wire;
option go_package = "protorpc.wire";
enum Const {
ZERO = 0;
MAX_REQUEST_HEADER_LEN = 1024;
}
message RequestHeader {
required uint64 id = 1;
required string method = 2;
required uint32 raw_request_len = 3;
}
message ResponseHeader {
required uint64 id = 1;
required string error = 2;
required uint32 raw_response_len = 3;
}

File diff suppressed because it is too large Load Diff

View File

@ -1,8 +1,7 @@
syntax = "proto2"; syntax = "proto2";
package libcore; package libcore;
option go_package = "grpc_server/gen"; option go_package = "./;gen";
option optimize_for = LITE_RUNTIME;
service LibcoreService { service LibcoreService {
rpc Start(LoadConfigReq) returns (ErrorResp); rpc Start(LoadConfigReq) returns (ErrorResp);

View File

@ -1,691 +0,0 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.5.1
// - protoc v5.29.3
// source: libcore.proto
package gen
import (
context "context"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
)
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.64.0 or later.
const _ = grpc.SupportPackageIsVersion9
const (
LibcoreService_Start_FullMethodName = "/libcore.LibcoreService/Start"
LibcoreService_Stop_FullMethodName = "/libcore.LibcoreService/Stop"
LibcoreService_CheckConfig_FullMethodName = "/libcore.LibcoreService/CheckConfig"
LibcoreService_Test_FullMethodName = "/libcore.LibcoreService/Test"
LibcoreService_StopTest_FullMethodName = "/libcore.LibcoreService/StopTest"
LibcoreService_QueryURLTest_FullMethodName = "/libcore.LibcoreService/QueryURLTest"
LibcoreService_QueryStats_FullMethodName = "/libcore.LibcoreService/QueryStats"
LibcoreService_ListConnections_FullMethodName = "/libcore.LibcoreService/ListConnections"
LibcoreService_GetGeoIPList_FullMethodName = "/libcore.LibcoreService/GetGeoIPList"
LibcoreService_GetGeoSiteList_FullMethodName = "/libcore.LibcoreService/GetGeoSiteList"
LibcoreService_CompileGeoIPToSrs_FullMethodName = "/libcore.LibcoreService/CompileGeoIPToSrs"
LibcoreService_CompileGeoSiteToSrs_FullMethodName = "/libcore.LibcoreService/CompileGeoSiteToSrs"
LibcoreService_SetSystemDNS_FullMethodName = "/libcore.LibcoreService/SetSystemDNS"
LibcoreService_IsPrivileged_FullMethodName = "/libcore.LibcoreService/IsPrivileged"
LibcoreService_SpeedTest_FullMethodName = "/libcore.LibcoreService/SpeedTest"
LibcoreService_QuerySpeedTest_FullMethodName = "/libcore.LibcoreService/QuerySpeedTest"
)
// LibcoreServiceClient is the client API for LibcoreService service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
type LibcoreServiceClient interface {
Start(ctx context.Context, in *LoadConfigReq, opts ...grpc.CallOption) (*ErrorResp, error)
Stop(ctx context.Context, in *EmptyReq, opts ...grpc.CallOption) (*ErrorResp, error)
CheckConfig(ctx context.Context, in *LoadConfigReq, opts ...grpc.CallOption) (*ErrorResp, error)
Test(ctx context.Context, in *TestReq, opts ...grpc.CallOption) (*TestResp, error)
StopTest(ctx context.Context, in *EmptyReq, opts ...grpc.CallOption) (*EmptyResp, error)
QueryURLTest(ctx context.Context, in *EmptyReq, opts ...grpc.CallOption) (*QueryURLTestResponse, error)
QueryStats(ctx context.Context, in *EmptyReq, opts ...grpc.CallOption) (*QueryStatsResp, error)
ListConnections(ctx context.Context, in *EmptyReq, opts ...grpc.CallOption) (*ListConnectionsResp, error)
GetGeoIPList(ctx context.Context, in *GeoListRequest, opts ...grpc.CallOption) (*GetGeoIPListResponse, error)
GetGeoSiteList(ctx context.Context, in *GeoListRequest, opts ...grpc.CallOption) (*GetGeoSiteListResponse, error)
CompileGeoIPToSrs(ctx context.Context, in *CompileGeoIPToSrsRequest, opts ...grpc.CallOption) (*EmptyResp, error)
CompileGeoSiteToSrs(ctx context.Context, in *CompileGeoSiteToSrsRequest, opts ...grpc.CallOption) (*EmptyResp, error)
SetSystemDNS(ctx context.Context, in *SetSystemDNSRequest, opts ...grpc.CallOption) (*EmptyResp, error)
IsPrivileged(ctx context.Context, in *EmptyReq, opts ...grpc.CallOption) (*IsPrivilegedResponse, error)
SpeedTest(ctx context.Context, in *SpeedTestRequest, opts ...grpc.CallOption) (*SpeedTestResponse, error)
QuerySpeedTest(ctx context.Context, in *EmptyReq, opts ...grpc.CallOption) (*QuerySpeedTestResponse, error)
}
type libcoreServiceClient struct {
cc grpc.ClientConnInterface
}
func NewLibcoreServiceClient(cc grpc.ClientConnInterface) LibcoreServiceClient {
return &libcoreServiceClient{cc}
}
func (c *libcoreServiceClient) Start(ctx context.Context, in *LoadConfigReq, opts ...grpc.CallOption) (*ErrorResp, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(ErrorResp)
err := c.cc.Invoke(ctx, LibcoreService_Start_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *libcoreServiceClient) Stop(ctx context.Context, in *EmptyReq, opts ...grpc.CallOption) (*ErrorResp, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(ErrorResp)
err := c.cc.Invoke(ctx, LibcoreService_Stop_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *libcoreServiceClient) CheckConfig(ctx context.Context, in *LoadConfigReq, opts ...grpc.CallOption) (*ErrorResp, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(ErrorResp)
err := c.cc.Invoke(ctx, LibcoreService_CheckConfig_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *libcoreServiceClient) Test(ctx context.Context, in *TestReq, opts ...grpc.CallOption) (*TestResp, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(TestResp)
err := c.cc.Invoke(ctx, LibcoreService_Test_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *libcoreServiceClient) StopTest(ctx context.Context, in *EmptyReq, opts ...grpc.CallOption) (*EmptyResp, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(EmptyResp)
err := c.cc.Invoke(ctx, LibcoreService_StopTest_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *libcoreServiceClient) QueryURLTest(ctx context.Context, in *EmptyReq, opts ...grpc.CallOption) (*QueryURLTestResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(QueryURLTestResponse)
err := c.cc.Invoke(ctx, LibcoreService_QueryURLTest_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *libcoreServiceClient) QueryStats(ctx context.Context, in *EmptyReq, opts ...grpc.CallOption) (*QueryStatsResp, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(QueryStatsResp)
err := c.cc.Invoke(ctx, LibcoreService_QueryStats_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *libcoreServiceClient) ListConnections(ctx context.Context, in *EmptyReq, opts ...grpc.CallOption) (*ListConnectionsResp, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(ListConnectionsResp)
err := c.cc.Invoke(ctx, LibcoreService_ListConnections_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *libcoreServiceClient) GetGeoIPList(ctx context.Context, in *GeoListRequest, opts ...grpc.CallOption) (*GetGeoIPListResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(GetGeoIPListResponse)
err := c.cc.Invoke(ctx, LibcoreService_GetGeoIPList_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *libcoreServiceClient) GetGeoSiteList(ctx context.Context, in *GeoListRequest, opts ...grpc.CallOption) (*GetGeoSiteListResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(GetGeoSiteListResponse)
err := c.cc.Invoke(ctx, LibcoreService_GetGeoSiteList_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *libcoreServiceClient) CompileGeoIPToSrs(ctx context.Context, in *CompileGeoIPToSrsRequest, opts ...grpc.CallOption) (*EmptyResp, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(EmptyResp)
err := c.cc.Invoke(ctx, LibcoreService_CompileGeoIPToSrs_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *libcoreServiceClient) CompileGeoSiteToSrs(ctx context.Context, in *CompileGeoSiteToSrsRequest, opts ...grpc.CallOption) (*EmptyResp, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(EmptyResp)
err := c.cc.Invoke(ctx, LibcoreService_CompileGeoSiteToSrs_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *libcoreServiceClient) SetSystemDNS(ctx context.Context, in *SetSystemDNSRequest, opts ...grpc.CallOption) (*EmptyResp, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(EmptyResp)
err := c.cc.Invoke(ctx, LibcoreService_SetSystemDNS_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *libcoreServiceClient) IsPrivileged(ctx context.Context, in *EmptyReq, opts ...grpc.CallOption) (*IsPrivilegedResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(IsPrivilegedResponse)
err := c.cc.Invoke(ctx, LibcoreService_IsPrivileged_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *libcoreServiceClient) SpeedTest(ctx context.Context, in *SpeedTestRequest, opts ...grpc.CallOption) (*SpeedTestResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(SpeedTestResponse)
err := c.cc.Invoke(ctx, LibcoreService_SpeedTest_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *libcoreServiceClient) QuerySpeedTest(ctx context.Context, in *EmptyReq, opts ...grpc.CallOption) (*QuerySpeedTestResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(QuerySpeedTestResponse)
err := c.cc.Invoke(ctx, LibcoreService_QuerySpeedTest_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
// LibcoreServiceServer is the server API for LibcoreService service.
// All implementations must embed UnimplementedLibcoreServiceServer
// for forward compatibility.
type LibcoreServiceServer interface {
Start(context.Context, *LoadConfigReq) (*ErrorResp, error)
Stop(context.Context, *EmptyReq) (*ErrorResp, error)
CheckConfig(context.Context, *LoadConfigReq) (*ErrorResp, error)
Test(context.Context, *TestReq) (*TestResp, error)
StopTest(context.Context, *EmptyReq) (*EmptyResp, error)
QueryURLTest(context.Context, *EmptyReq) (*QueryURLTestResponse, error)
QueryStats(context.Context, *EmptyReq) (*QueryStatsResp, error)
ListConnections(context.Context, *EmptyReq) (*ListConnectionsResp, error)
GetGeoIPList(context.Context, *GeoListRequest) (*GetGeoIPListResponse, error)
GetGeoSiteList(context.Context, *GeoListRequest) (*GetGeoSiteListResponse, error)
CompileGeoIPToSrs(context.Context, *CompileGeoIPToSrsRequest) (*EmptyResp, error)
CompileGeoSiteToSrs(context.Context, *CompileGeoSiteToSrsRequest) (*EmptyResp, error)
SetSystemDNS(context.Context, *SetSystemDNSRequest) (*EmptyResp, error)
IsPrivileged(context.Context, *EmptyReq) (*IsPrivilegedResponse, error)
SpeedTest(context.Context, *SpeedTestRequest) (*SpeedTestResponse, error)
QuerySpeedTest(context.Context, *EmptyReq) (*QuerySpeedTestResponse, error)
mustEmbedUnimplementedLibcoreServiceServer()
}
// UnimplementedLibcoreServiceServer must be embedded to have
// forward compatible implementations.
//
// NOTE: this should be embedded by value instead of pointer to avoid a nil
// pointer dereference when methods are called.
type UnimplementedLibcoreServiceServer struct{}
func (UnimplementedLibcoreServiceServer) Start(context.Context, *LoadConfigReq) (*ErrorResp, error) {
return nil, status.Errorf(codes.Unimplemented, "method Start not implemented")
}
func (UnimplementedLibcoreServiceServer) Stop(context.Context, *EmptyReq) (*ErrorResp, error) {
return nil, status.Errorf(codes.Unimplemented, "method Stop not implemented")
}
func (UnimplementedLibcoreServiceServer) CheckConfig(context.Context, *LoadConfigReq) (*ErrorResp, error) {
return nil, status.Errorf(codes.Unimplemented, "method CheckConfig not implemented")
}
func (UnimplementedLibcoreServiceServer) Test(context.Context, *TestReq) (*TestResp, error) {
return nil, status.Errorf(codes.Unimplemented, "method Test not implemented")
}
func (UnimplementedLibcoreServiceServer) StopTest(context.Context, *EmptyReq) (*EmptyResp, error) {
return nil, status.Errorf(codes.Unimplemented, "method StopTest not implemented")
}
func (UnimplementedLibcoreServiceServer) QueryURLTest(context.Context, *EmptyReq) (*QueryURLTestResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method QueryURLTest not implemented")
}
func (UnimplementedLibcoreServiceServer) QueryStats(context.Context, *EmptyReq) (*QueryStatsResp, error) {
return nil, status.Errorf(codes.Unimplemented, "method QueryStats not implemented")
}
func (UnimplementedLibcoreServiceServer) ListConnections(context.Context, *EmptyReq) (*ListConnectionsResp, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListConnections not implemented")
}
func (UnimplementedLibcoreServiceServer) GetGeoIPList(context.Context, *GeoListRequest) (*GetGeoIPListResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetGeoIPList not implemented")
}
func (UnimplementedLibcoreServiceServer) GetGeoSiteList(context.Context, *GeoListRequest) (*GetGeoSiteListResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetGeoSiteList not implemented")
}
func (UnimplementedLibcoreServiceServer) CompileGeoIPToSrs(context.Context, *CompileGeoIPToSrsRequest) (*EmptyResp, error) {
return nil, status.Errorf(codes.Unimplemented, "method CompileGeoIPToSrs not implemented")
}
func (UnimplementedLibcoreServiceServer) CompileGeoSiteToSrs(context.Context, *CompileGeoSiteToSrsRequest) (*EmptyResp, error) {
return nil, status.Errorf(codes.Unimplemented, "method CompileGeoSiteToSrs not implemented")
}
func (UnimplementedLibcoreServiceServer) SetSystemDNS(context.Context, *SetSystemDNSRequest) (*EmptyResp, error) {
return nil, status.Errorf(codes.Unimplemented, "method SetSystemDNS not implemented")
}
func (UnimplementedLibcoreServiceServer) IsPrivileged(context.Context, *EmptyReq) (*IsPrivilegedResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method IsPrivileged not implemented")
}
func (UnimplementedLibcoreServiceServer) SpeedTest(context.Context, *SpeedTestRequest) (*SpeedTestResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method SpeedTest not implemented")
}
func (UnimplementedLibcoreServiceServer) QuerySpeedTest(context.Context, *EmptyReq) (*QuerySpeedTestResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method QuerySpeedTest not implemented")
}
func (UnimplementedLibcoreServiceServer) mustEmbedUnimplementedLibcoreServiceServer() {}
func (UnimplementedLibcoreServiceServer) testEmbeddedByValue() {}
// UnsafeLibcoreServiceServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to LibcoreServiceServer will
// result in compilation errors.
type UnsafeLibcoreServiceServer interface {
mustEmbedUnimplementedLibcoreServiceServer()
}
func RegisterLibcoreServiceServer(s grpc.ServiceRegistrar, srv LibcoreServiceServer) {
// If the following call pancis, it indicates UnimplementedLibcoreServiceServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
t.testEmbeddedByValue()
}
s.RegisterService(&LibcoreService_ServiceDesc, srv)
}
func _LibcoreService_Start_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(LoadConfigReq)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(LibcoreServiceServer).Start(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: LibcoreService_Start_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(LibcoreServiceServer).Start(ctx, req.(*LoadConfigReq))
}
return interceptor(ctx, in, info, handler)
}
func _LibcoreService_Stop_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(EmptyReq)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(LibcoreServiceServer).Stop(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: LibcoreService_Stop_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(LibcoreServiceServer).Stop(ctx, req.(*EmptyReq))
}
return interceptor(ctx, in, info, handler)
}
func _LibcoreService_CheckConfig_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(LoadConfigReq)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(LibcoreServiceServer).CheckConfig(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: LibcoreService_CheckConfig_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(LibcoreServiceServer).CheckConfig(ctx, req.(*LoadConfigReq))
}
return interceptor(ctx, in, info, handler)
}
func _LibcoreService_Test_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(TestReq)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(LibcoreServiceServer).Test(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: LibcoreService_Test_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(LibcoreServiceServer).Test(ctx, req.(*TestReq))
}
return interceptor(ctx, in, info, handler)
}
func _LibcoreService_StopTest_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(EmptyReq)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(LibcoreServiceServer).StopTest(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: LibcoreService_StopTest_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(LibcoreServiceServer).StopTest(ctx, req.(*EmptyReq))
}
return interceptor(ctx, in, info, handler)
}
func _LibcoreService_QueryURLTest_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(EmptyReq)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(LibcoreServiceServer).QueryURLTest(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: LibcoreService_QueryURLTest_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(LibcoreServiceServer).QueryURLTest(ctx, req.(*EmptyReq))
}
return interceptor(ctx, in, info, handler)
}
func _LibcoreService_QueryStats_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(EmptyReq)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(LibcoreServiceServer).QueryStats(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: LibcoreService_QueryStats_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(LibcoreServiceServer).QueryStats(ctx, req.(*EmptyReq))
}
return interceptor(ctx, in, info, handler)
}
func _LibcoreService_ListConnections_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(EmptyReq)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(LibcoreServiceServer).ListConnections(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: LibcoreService_ListConnections_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(LibcoreServiceServer).ListConnections(ctx, req.(*EmptyReq))
}
return interceptor(ctx, in, info, handler)
}
func _LibcoreService_GetGeoIPList_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GeoListRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(LibcoreServiceServer).GetGeoIPList(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: LibcoreService_GetGeoIPList_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(LibcoreServiceServer).GetGeoIPList(ctx, req.(*GeoListRequest))
}
return interceptor(ctx, in, info, handler)
}
func _LibcoreService_GetGeoSiteList_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GeoListRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(LibcoreServiceServer).GetGeoSiteList(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: LibcoreService_GetGeoSiteList_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(LibcoreServiceServer).GetGeoSiteList(ctx, req.(*GeoListRequest))
}
return interceptor(ctx, in, info, handler)
}
func _LibcoreService_CompileGeoIPToSrs_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(CompileGeoIPToSrsRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(LibcoreServiceServer).CompileGeoIPToSrs(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: LibcoreService_CompileGeoIPToSrs_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(LibcoreServiceServer).CompileGeoIPToSrs(ctx, req.(*CompileGeoIPToSrsRequest))
}
return interceptor(ctx, in, info, handler)
}
func _LibcoreService_CompileGeoSiteToSrs_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(CompileGeoSiteToSrsRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(LibcoreServiceServer).CompileGeoSiteToSrs(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: LibcoreService_CompileGeoSiteToSrs_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(LibcoreServiceServer).CompileGeoSiteToSrs(ctx, req.(*CompileGeoSiteToSrsRequest))
}
return interceptor(ctx, in, info, handler)
}
func _LibcoreService_SetSystemDNS_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(SetSystemDNSRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(LibcoreServiceServer).SetSystemDNS(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: LibcoreService_SetSystemDNS_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(LibcoreServiceServer).SetSystemDNS(ctx, req.(*SetSystemDNSRequest))
}
return interceptor(ctx, in, info, handler)
}
func _LibcoreService_IsPrivileged_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(EmptyReq)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(LibcoreServiceServer).IsPrivileged(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: LibcoreService_IsPrivileged_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(LibcoreServiceServer).IsPrivileged(ctx, req.(*EmptyReq))
}
return interceptor(ctx, in, info, handler)
}
func _LibcoreService_SpeedTest_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(SpeedTestRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(LibcoreServiceServer).SpeedTest(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: LibcoreService_SpeedTest_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(LibcoreServiceServer).SpeedTest(ctx, req.(*SpeedTestRequest))
}
return interceptor(ctx, in, info, handler)
}
func _LibcoreService_QuerySpeedTest_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(EmptyReq)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(LibcoreServiceServer).QuerySpeedTest(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: LibcoreService_QuerySpeedTest_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(LibcoreServiceServer).QuerySpeedTest(ctx, req.(*EmptyReq))
}
return interceptor(ctx, in, info, handler)
}
// LibcoreService_ServiceDesc is the grpc.ServiceDesc for LibcoreService service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var LibcoreService_ServiceDesc = grpc.ServiceDesc{
ServiceName: "libcore.LibcoreService",
HandlerType: (*LibcoreServiceServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "Start",
Handler: _LibcoreService_Start_Handler,
},
{
MethodName: "Stop",
Handler: _LibcoreService_Stop_Handler,
},
{
MethodName: "CheckConfig",
Handler: _LibcoreService_CheckConfig_Handler,
},
{
MethodName: "Test",
Handler: _LibcoreService_Test_Handler,
},
{
MethodName: "StopTest",
Handler: _LibcoreService_StopTest_Handler,
},
{
MethodName: "QueryURLTest",
Handler: _LibcoreService_QueryURLTest_Handler,
},
{
MethodName: "QueryStats",
Handler: _LibcoreService_QueryStats_Handler,
},
{
MethodName: "ListConnections",
Handler: _LibcoreService_ListConnections_Handler,
},
{
MethodName: "GetGeoIPList",
Handler: _LibcoreService_GetGeoIPList_Handler,
},
{
MethodName: "GetGeoSiteList",
Handler: _LibcoreService_GetGeoSiteList_Handler,
},
{
MethodName: "CompileGeoIPToSrs",
Handler: _LibcoreService_CompileGeoIPToSrs_Handler,
},
{
MethodName: "CompileGeoSiteToSrs",
Handler: _LibcoreService_CompileGeoSiteToSrs_Handler,
},
{
MethodName: "SetSystemDNS",
Handler: _LibcoreService_SetSystemDNS_Handler,
},
{
MethodName: "IsPrivileged",
Handler: _LibcoreService_IsPrivileged_Handler,
},
{
MethodName: "SpeedTest",
Handler: _LibcoreService_SpeedTest_Handler,
},
{
MethodName: "QuerySpeedTest",
Handler: _LibcoreService_QuerySpeedTest_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "libcore.proto",
}

View File

@ -2,12 +2,12 @@ module Core
go 1.23.6 go 1.23.6
toolchain go1.24.4
require ( require (
github.com/Mahdi-zarei/speedtest-go v1.7.12 github.com/Mahdi-zarei/speedtest-go v1.7.12
github.com/chai2010/protorpc v1.1.4
github.com/dustin/go-humanize v1.0.1 github.com/dustin/go-humanize v1.0.1
github.com/gofrs/uuid/v5 v5.3.2 github.com/gofrs/uuid/v5 v5.3.2
github.com/golang/protobuf v1.5.4
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
github.com/oschwald/maxminddb-golang v1.13.1 github.com/oschwald/maxminddb-golang v1.13.1
github.com/sagernet/sing v0.6.11 github.com/sagernet/sing v0.6.11
@ -16,7 +16,6 @@ require (
github.com/sagernet/sing-tun v0.6.9 github.com/sagernet/sing-tun v0.6.9
github.com/spf13/cobra v1.9.1 github.com/spf13/cobra v1.9.1
golang.org/x/sys v0.34.0 golang.org/x/sys v0.34.0
google.golang.org/grpc v1.74.2
google.golang.org/protobuf v1.36.6 google.golang.org/protobuf v1.36.6
) )
@ -26,6 +25,8 @@ replace github.com/sagernet/sing-dns => github.com/Mahdi-zarei/sing-dns v0.3.0-b
replace github.com/sagernet/wireguard-go => github.com/throneproj/wireguard-go v0.0.1-beta.7.0.20250728063157-408bba78ad26 replace github.com/sagernet/wireguard-go => github.com/throneproj/wireguard-go v0.0.1-beta.7.0.20250728063157-408bba78ad26
replace github.com/chai2010/protorpc => ../protorpc
require ( require (
github.com/ajg/form v1.5.1 // indirect github.com/ajg/form v1.5.1 // indirect
github.com/andybalholm/brotli v1.0.6 // indirect github.com/andybalholm/brotli v1.0.6 // indirect
@ -97,6 +98,7 @@ require (
golang.org/x/time v0.7.0 // indirect golang.org/x/time v0.7.0 // indirect
golang.org/x/tools v0.28.0 // indirect golang.org/x/tools v0.28.0 // indirect
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de // indirect
google.golang.org/grpc v1.63.2 // indirect
lukechampine.com/blake3 v1.3.0 // indirect lukechampine.com/blake3 v1.3.0 // indirect
) )

View File

@ -24,10 +24,8 @@ github.com/go-chi/chi/v5 v5.2.2 h1:CMwsvRVTbXVytCk1Wd72Zy1LAsAh9GxMmSNWLHCG618=
github.com/go-chi/chi/v5 v5.2.2/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops= github.com/go-chi/chi/v5 v5.2.2/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops=
github.com/go-chi/render v1.0.3 h1:AsXqd2a1/INaIfUSKq3G5uA8weYx20FOsM7uSoCyyt4= github.com/go-chi/render v1.0.3 h1:AsXqd2a1/INaIfUSKq3G5uA8weYx20FOsM7uSoCyyt4=
github.com/go-chi/render v1.0.3/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0= github.com/go-chi/render v1.0.3/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0=
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
@ -48,8 +46,6 @@ github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a h1:fEBsGL/sjAuJrgah5X
github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/hashicorp/yamux v0.1.2 h1:XtB8kyFOyHXYVFnwT5C3+Bdo8gArse7j2AQ0DA0Uey8= github.com/hashicorp/yamux v0.1.2 h1:XtB8kyFOyHXYVFnwT5C3+Bdo8gArse7j2AQ0DA0Uey8=
github.com/hashicorp/yamux v0.1.2/go.mod h1:C+zze2n6e/7wshOZep2A70/aQU6QBRWJO/G6FT1wIns= github.com/hashicorp/yamux v0.1.2/go.mod h1:C+zze2n6e/7wshOZep2A70/aQU6QBRWJO/G6FT1wIns=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
@ -171,18 +167,6 @@ github.com/zeebo/blake3 v0.2.3 h1:TFoLXsjeXqRNFxSbk35Dk4YtszE/MQQGK10BH4ptoTg=
github.com/zeebo/blake3 v0.2.3/go.mod h1:mjJjZpnsyIVtVgTOSpJ9vmRE4wgDeyt2HU3qXvvKCaQ= github.com/zeebo/blake3 v0.2.3/go.mod h1:mjJjZpnsyIVtVgTOSpJ9vmRE4wgDeyt2HU3qXvvKCaQ=
github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo= github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo=
github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4= github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4=
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
go.opentelemetry.io/otel v1.36.0 h1:UumtzIklRBY6cI/lllNZlALOF5nNIzJVb16APdvgTXg=
go.opentelemetry.io/otel v1.36.0/go.mod h1:/TcFMXYjyRNh8khOAO9ybYkqaDBb/70aVwkNML4pP8E=
go.opentelemetry.io/otel/metric v1.36.0 h1:MoWPKVhQvJ+eeXWHFBOPoBOi20jh6Iq2CcCREuTYufE=
go.opentelemetry.io/otel/metric v1.36.0/go.mod h1:zC7Ks+yeyJt4xig9DEw9kuUFe5C3zLbVjV2PzT6qzbs=
go.opentelemetry.io/otel/sdk v1.36.0 h1:b6SYIuLRs88ztox4EyrvRti80uXIFy+Sqzoh9kFULbs=
go.opentelemetry.io/otel/sdk v1.36.0/go.mod h1:+lC+mTgD+MUWfjJubi2vvXWcVxyr9rmlshZni72pXeY=
go.opentelemetry.io/otel/sdk/metric v1.36.0 h1:r0ntwwGosWGaa0CrSt8cuNuTcccMXERFwHX4dThiPis=
go.opentelemetry.io/otel/sdk/metric v1.36.0/go.mod h1:qTNOhFDfKRwX0yXOqJYegL5WRaW376QbB7P4Pb0qva4=
go.opentelemetry.io/otel/trace v1.36.0 h1:ahxWNuqZjpdiFAyrIoQ4GIiAIhxAunQR6MUoKrsNd4w=
go.opentelemetry.io/otel/trace v1.36.0/go.mod h1:gQ+OnDZzrybY4k4seLzPAWNwVBBVlF2szhehOBB/tGA=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
@ -227,10 +211,10 @@ golang.org/x/tools v0.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8=
golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw= golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw=
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg= golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg=
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI= golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a h1:v2PbRU4K3llS09c7zodFpNePeamkAwG3mPrAery9VeE= google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de h1:cZGRis4/ot9uVm639a+rHCUaG0JJHEsdyzSQTMX+suY=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:H4O17MA/PE9BsGx3w+a+W2VOLLD1Qf7oJneAoU6WktY=
google.golang.org/grpc v1.74.2 h1:WoosgB65DlWVC9FqI82dGsZhWFNBSLjQ84bjROOpMu4= google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM=
google.golang.org/grpc v1.74.2/go.mod h1:CtQ+BGjaAIXHs/5YS3i473GqwBBa1zGQNevxdeBEXrM= google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA=
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

View File

@ -6,9 +6,7 @@ import (
"context" "context"
"flag" "flag"
"fmt" "fmt"
"google.golang.org/grpc"
"log" "log"
"net"
"os" "os"
"runtime" "runtime"
runtimeDebug "runtime/debug" runtimeDebug "runtime/debug"
@ -46,22 +44,12 @@ func RunCore() {
}() }()
boxmain.DisableColor() boxmain.DisableColor()
// GRPC // RPC
lis, err := net.Listen("tcp", "127.0.0.1:"+strconv.Itoa(*_port)) fmt.Printf("Core listening at %v\n", "127.0.0.1:"+strconv.Itoa(*_port))
err := gen.ListenAndServeLibcoreService("tcp", "127.0.0.1:"+strconv.Itoa(*_port), new(server))
if err != nil { if err != nil {
log.Fatalf("failed to listen: %v", err) log.Fatalf("failed to listen: %v", err)
} }
s := grpc.NewServer(
grpc.MaxRecvMsgSize(1024*1024*1024), // 1 gigaByte
grpc.MaxSendMsgSize(1024*1024*1024), // 1 gigaByte
)
gen.RegisterLibcoreServiceServer(s, &server{})
fmt.Printf("Core listening at %v\n", lis.Addr())
if err := s.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
} }
func main() { func main() {

View File

@ -31,20 +31,17 @@ var systemProxyAddr metadata.Socksaddr
var instanceCancel context.CancelFunc var instanceCancel context.CancelFunc
var debug bool var debug bool
type server struct { type server int
gen.UnimplementedLibcoreServiceServer
}
// To returns a pointer to the given value. // To returns a pointer to the given value.
func To[T any](v T) *T { func To[T any](v T) *T {
return &v return &v
} }
func (s *server) Start(ctx context.Context, in *gen.LoadConfigReq) (out *gen.ErrorResp, _ error) { func (s *server) Start(in *gen.LoadConfigReq, out *gen.ErrorResp) (_ error) {
var err error var err error
defer func() { defer func() {
out = &gen.ErrorResp{}
if err != nil { if err != nil {
out.Error = To(err.Error()) out.Error = To(err.Error())
boxInstance = nil boxInstance = nil
@ -107,11 +104,10 @@ func (s *server) Start(ctx context.Context, in *gen.LoadConfigReq) (out *gen.Err
return return
} }
func (s *server) Stop(ctx context.Context, in *gen.EmptyReq) (out *gen.ErrorResp, _ error) { func (s *server) Stop(in *gen.EmptyReq, out *gen.ErrorResp) (_ error) {
var err error var err error
defer func() { defer func() {
out = &gen.ErrorResp{}
if err != nil { if err != nil {
out.Error = To(err.Error()) out.Error = To(err.Error())
} }
@ -140,35 +136,35 @@ func (s *server) Stop(ctx context.Context, in *gen.EmptyReq) (out *gen.ErrorResp
return return
} }
func (s *server) CheckConfig(ctx context.Context, in *gen.LoadConfigReq) (*gen.ErrorResp, error) { func (s *server) CheckConfig(in *gen.LoadConfigReq, out *gen.ErrorResp) error {
err := boxmain.Check([]byte(*in.CoreConfig)) err := boxmain.Check([]byte(*in.CoreConfig))
if err != nil { if err != nil {
return &gen.ErrorResp{ out.Error = To(err.Error())
Error: To(err.Error()), return nil
}, nil
} }
return &gen.ErrorResp{}, nil return nil
} }
func (s *server) Test(ctx context.Context, in *gen.TestReq) (*gen.TestResp, error) { func (s *server) Test(in *gen.TestReq, out *gen.TestResp) error {
var testInstance *boxbox.Box var testInstance *boxbox.Box
var cancel context.CancelFunc var cancel context.CancelFunc
var err error var err error
var twice = true var twice = true
if *in.TestCurrent { if *in.TestCurrent {
if boxInstance == nil { if boxInstance == nil {
return &gen.TestResp{Results: []*gen.URLTestResp{{ out.Results = []*gen.URLTestResp{{
OutboundTag: To("proxy"), OutboundTag: To("proxy"),
LatencyMs: To(int32(0)), LatencyMs: To(int32(0)),
Error: To("Instance is not running"), Error: To("Instance is not running"),
}}}, nil }}
return nil
} }
testInstance = boxInstance testInstance = boxInstance
twice = false twice = false
} else { } else {
testInstance, cancel, err = boxmain.Create([]byte(*in.Config)) testInstance, cancel, err = boxmain.Create([]byte(*in.Config))
if err != nil { if err != nil {
return nil, err return err
} }
defer cancel() defer cancel()
defer testInstance.Close() defer testInstance.Close()
@ -199,82 +195,79 @@ func (s *server) Test(ctx context.Context, in *gen.TestReq) (*gen.TestResp, erro
}) })
} }
return &gen.TestResp{Results: res}, nil out.Results = res
return nil
} }
func (s *server) StopTest(ctx context.Context, in *gen.EmptyReq) (*gen.EmptyResp, error) { func (s *server) StopTest(in *gen.EmptyReq, out *gen.EmptyResp) error {
cancelTests() cancelTests()
testCtx, cancelTests = context.WithCancel(context.Background()) testCtx, cancelTests = context.WithCancel(context.Background())
return &gen.EmptyResp{}, nil return nil
} }
func (s *server) QueryURLTest(ctx context.Context, in *gen.EmptyReq) (*gen.QueryURLTestResponse, error) { func (s *server) QueryURLTest(in *gen.EmptyReq, out *gen.QueryURLTestResponse) error {
results := URLReporter.Results() results := URLReporter.Results()
resp := &gen.QueryURLTestResponse{}
for _, r := range results { for _, r := range results {
errStr := "" errStr := ""
if r.Error != nil { if r.Error != nil {
errStr = r.Error.Error() errStr = r.Error.Error()
} }
resp.Results = append(resp.Results, &gen.URLTestResp{ out.Results = append(out.Results, &gen.URLTestResp{
OutboundTag: To(r.Tag), OutboundTag: To(r.Tag),
LatencyMs: To(int32(r.Duration.Milliseconds())), LatencyMs: To(int32(r.Duration.Milliseconds())),
Error: To(errStr), Error: To(errStr),
}) })
} }
return resp, nil return nil
} }
func (s *server) QueryStats(ctx context.Context, _ *gen.EmptyReq) (*gen.QueryStatsResp, error) { func (s *server) QueryStats(in *gen.EmptyReq, out *gen.QueryStatsResp) error {
resp := &gen.QueryStatsResp{ out.Ups = make(map[string]int64)
Ups: make(map[string]int64), out.Downs = make(map[string]int64)
Downs: make(map[string]int64),
}
if boxInstance != nil { if boxInstance != nil {
clash := service.FromContext[adapter.ClashServer](boxInstance.Context()) clash := service.FromContext[adapter.ClashServer](boxInstance.Context())
if clash != nil { if clash != nil {
cApi, ok := clash.(*clashapi.Server) cApi, ok := clash.(*clashapi.Server)
if !ok { if !ok {
log.Println("Failed to assert clash server") log.Println("Failed to assert clash server")
return nil, E.New("invalid clash server type") return E.New("invalid clash server type")
} }
outbounds := service.FromContext[adapter.OutboundManager](boxInstance.Context()) outbounds := service.FromContext[adapter.OutboundManager](boxInstance.Context())
if outbounds == nil { if outbounds == nil {
log.Println("Failed to get outbound manager") log.Println("Failed to get outbound manager")
return nil, E.New("nil outbound manager") return E.New("nil outbound manager")
} }
endpoints := service.FromContext[adapter.EndpointManager](boxInstance.Context()) endpoints := service.FromContext[adapter.EndpointManager](boxInstance.Context())
if endpoints == nil { if endpoints == nil {
log.Println("Failed to get endpoint manager") log.Println("Failed to get endpoint manager")
return nil, E.New("nil endpoint manager") return E.New("nil endpoint manager")
} }
for _, out := range outbounds.Outbounds() { for _, ob := range outbounds.Outbounds() {
u, d := cApi.TrafficManager().TotalOutbound(out.Tag()) u, d := cApi.TrafficManager().TotalOutbound(ob.Tag())
resp.Ups[out.Tag()] = u out.Ups[ob.Tag()] = u
resp.Downs[out.Tag()] = d out.Downs[ob.Tag()] = d
} }
for _, ep := range endpoints.Endpoints() { for _, ep := range endpoints.Endpoints() {
u, d := cApi.TrafficManager().TotalOutbound(ep.Tag()) u, d := cApi.TrafficManager().TotalOutbound(ep.Tag())
resp.Ups[ep.Tag()] = u out.Ups[ep.Tag()] = u
resp.Downs[ep.Tag()] = d out.Downs[ep.Tag()] = d
} }
} }
} }
return nil
return resp, nil
} }
func (s *server) ListConnections(ctx context.Context, in *gen.EmptyReq) (*gen.ListConnectionsResp, error) { func (s *server) ListConnections(in *gen.EmptyReq, out *gen.ListConnectionsResp) error {
if boxInstance == nil { if boxInstance == nil {
return &gen.ListConnectionsResp{}, nil return nil
} }
if service.FromContext[adapter.ClashServer](boxInstance.Context()) == nil { if service.FromContext[adapter.ClashServer](boxInstance.Context()) == nil {
return nil, errors.New("no clash server found") return errors.New("no clash server found")
} }
clash, ok := service.FromContext[adapter.ClashServer](boxInstance.Context()).(*clashapi.Server) clash, ok := service.FromContext[adapter.ClashServer](boxInstance.Context()).(*clashapi.Server)
if !ok { if !ok {
return nil, errors.New("invalid state, should not be here") return errors.New("invalid state, should not be here")
} }
connections := clash.TrafficManager().Connections() connections := clash.TrafficManager().Connections()
@ -299,16 +292,14 @@ func (s *server) ListConnections(ctx context.Context, in *gen.EmptyReq) (*gen.Li
} }
res = append(res, r) res = append(res, r)
} }
out := &gen.ListConnectionsResp{ out.Connections = res
Connections: res, return nil
}
return out, nil
} }
func (s *server) GetGeoIPList(ctx context.Context, in *gen.GeoListRequest) (*gen.GetGeoIPListResponse, error) { func (s *server) GetGeoIPList(in *gen.GeoListRequest, out *gen.GetGeoIPListResponse) error {
resp, err := boxmain.ListGeoip(*in.Path + string(os.PathSeparator) + "geoip.db") resp, err := boxmain.ListGeoip(*in.Path + string(os.PathSeparator) + "geoip.db")
if err != nil { if err != nil {
return nil, err return err
} }
res := make([]string, 0) res := make([]string, 0)
@ -317,13 +308,14 @@ func (s *server) GetGeoIPList(ctx context.Context, in *gen.GeoListRequest) (*gen
res = append(res, r) res = append(res, r)
} }
return &gen.GetGeoIPListResponse{Items: res}, nil out.Items = res
return nil
} }
func (s *server) GetGeoSiteList(ctx context.Context, in *gen.GeoListRequest) (*gen.GetGeoSiteListResponse, error) { func (s *server) GetGeoSiteList(in *gen.GeoListRequest, out *gen.GetGeoSiteListResponse) error {
resp, err := boxmain.GeositeList(*in.Path + string(os.PathSeparator) + "geosite.db") resp, err := boxmain.GeositeList(*in.Path + string(os.PathSeparator) + "geosite.db")
if err != nil { if err != nil {
return nil, err return err
} }
res := make([]string, 0) res := make([]string, 0)
@ -332,42 +324,43 @@ func (s *server) GetGeoSiteList(ctx context.Context, in *gen.GeoListRequest) (*g
res = append(res, r) res = append(res, r)
} }
return &gen.GetGeoSiteListResponse{Items: res}, nil out.Items = res
return nil
} }
func (s *server) CompileGeoIPToSrs(ctx context.Context, in *gen.CompileGeoIPToSrsRequest) (*gen.EmptyResp, error) { func (s *server) CompileGeoIPToSrs(in *gen.CompileGeoIPToSrsRequest, out *gen.EmptyResp) error {
category := strings.TrimSuffix(*in.Item, "_IP") category := strings.TrimSuffix(*in.Item, "_IP")
err := boxmain.CompileRuleSet(*in.Path+string(os.PathSeparator)+"geoip.db", category, boxmain.IpRuleSet, "./rule_sets/"+*in.Item+".srs") err := boxmain.CompileRuleSet(*in.Path+string(os.PathSeparator)+"geoip.db", category, boxmain.IpRuleSet, "./rule_sets/"+*in.Item+".srs")
if err != nil { if err != nil {
return nil, err return err
} }
return &gen.EmptyResp{}, nil return nil
} }
func (s *server) CompileGeoSiteToSrs(ctx context.Context, in *gen.CompileGeoSiteToSrsRequest) (*gen.EmptyResp, error) { func (s *server) CompileGeoSiteToSrs(in *gen.CompileGeoSiteToSrsRequest, out *gen.EmptyResp) error {
category := strings.TrimSuffix(*in.Item, "_SITE") category := strings.TrimSuffix(*in.Item, "_SITE")
err := boxmain.CompileRuleSet(*in.Path+string(os.PathSeparator)+"geosite.db", category, boxmain.SiteRuleSet, "./rule_sets/"+*in.Item+".srs") err := boxmain.CompileRuleSet(*in.Path+string(os.PathSeparator)+"geosite.db", category, boxmain.SiteRuleSet, "./rule_sets/"+*in.Item+".srs")
if err != nil { if err != nil {
return nil, err return err
} }
return &gen.EmptyResp{}, nil return nil
} }
func (s *server) IsPrivileged(ctx context.Context, _ *gen.EmptyReq) (*gen.IsPrivilegedResponse, error) { func (s *server) IsPrivileged(in *gen.EmptyReq, out *gen.IsPrivilegedResponse) error {
if runtime.GOOS == "windows" { if runtime.GOOS == "windows" {
return &gen.IsPrivilegedResponse{ out.HasPrivilege = To(false)
HasPrivilege: To(false), return nil
}, nil
} }
return &gen.IsPrivilegedResponse{HasPrivilege: To(os.Geteuid() == 0)}, nil out.HasPrivilege = To(os.Geteuid() == 0)
return nil
} }
func (s *server) SpeedTest(ctx context.Context, in *gen.SpeedTestRequest) (*gen.SpeedTestResponse, error) { func (s *server) SpeedTest(in *gen.SpeedTestRequest, out *gen.SpeedTestResponse) error {
if !*in.TestDownload && !*in.TestUpload && !*in.SimpleDownload { if !*in.TestDownload && !*in.TestUpload && !*in.SimpleDownload {
return nil, errors.New("cannot run empty test") return errors.New("cannot run empty test")
} }
var testInstance *boxbox.Box var testInstance *boxbox.Box
var cancel context.CancelFunc var cancel context.CancelFunc
@ -375,16 +368,17 @@ func (s *server) SpeedTest(ctx context.Context, in *gen.SpeedTestRequest) (*gen.
var err error var err error
if *in.TestCurrent { if *in.TestCurrent {
if boxInstance == nil { if boxInstance == nil {
return &gen.SpeedTestResponse{Results: []*gen.SpeedTestResult{{ out.Results = []*gen.SpeedTestResult{{
OutboundTag: To("proxy"), OutboundTag: To("proxy"),
Error: To("Instance is not running"), Error: To("Instance is not running"),
}}}, nil }}
return nil
} }
testInstance = boxInstance testInstance = boxInstance
} else { } else {
testInstance, cancel, err = boxmain.Create([]byte(*in.Config)) testInstance, cancel, err = boxmain.Create([]byte(*in.Config))
if err != nil { if err != nil {
return nil, err return err
} }
defer cancel() defer cancel()
defer testInstance.Close() defer testInstance.Close()
@ -415,17 +409,17 @@ func (s *server) SpeedTest(ctx context.Context, in *gen.SpeedTestRequest) (*gen.
}) })
} }
return &gen.SpeedTestResponse{Results: res}, nil out.Results = res
return nil
} }
func (s *server) QuerySpeedTest(context.Context, *gen.EmptyReq) (*gen.QuerySpeedTestResponse, error) { func (s *server) QuerySpeedTest(in *gen.EmptyReq, out *gen.QuerySpeedTestResponse) error {
res, isRunning := SpTQuerier.Result() res, isRunning := SpTQuerier.Result()
errStr := "" errStr := ""
if res.Error != nil { if res.Error != nil {
errStr = res.Error.Error() errStr = res.Error.Error()
} }
return &gen.QuerySpeedTestResponse{ out.Result = &gen.SpeedTestResult{
Result: &gen.SpeedTestResult{
DlSpeed: To(res.DlSpeed), DlSpeed: To(res.DlSpeed),
UlSpeed: To(res.UlSpeed), UlSpeed: To(res.UlSpeed),
Latency: To(res.Latency), Latency: To(res.Latency),
@ -434,7 +428,7 @@ func (s *server) QuerySpeedTest(context.Context, *gen.EmptyReq) (*gen.QuerySpeed
ServerName: To(res.ServerName), ServerName: To(res.ServerName),
ServerCountry: To(res.ServerCountry), ServerCountry: To(res.ServerCountry),
Cancelled: To(res.Cancelled), Cancelled: To(res.Cancelled),
}, }
IsRunning: To(isRunning), out.IsRunning = To(isRunning)
}, nil return nil
} }

View File

@ -0,0 +1,9 @@
package main
import (
"Core/gen"
)
func (s *server) SetSystemDNS(in *gen.SetSystemDNSRequest, out *gen.EmptyResp) error {
return nil
}

View File

@ -0,0 +1,9 @@
package main
import (
"Core/gen"
)
func (s *server) SetSystemDNS(in *gen.SetSystemDNSRequest, out *gen.EmptyResp) error {
return nil
}

View File

@ -3,14 +3,13 @@ package main
import ( import (
"Core/gen" "Core/gen"
"Core/internal/boxdns" "Core/internal/boxdns"
"context"
) )
func (s *server) SetSystemDNS(ctx context.Context, in *gen.SetSystemDNSRequest) (*gen.EmptyResp, error) { func (s *server) SetSystemDNS(in *gen.SetSystemDNSRequest, out *gen.EmptyResp) error {
err := boxdns.DnsManagerInstance.SetSystemDNS(nil, *in.Clear) err := boxdns.DnsManagerInstance.SetSystemDNS(nil, *in.Clear)
if err != nil { if err != nil {
return nil, err return err
} }
return &gen.EmptyResp{}, nil return nil
} }

View File

@ -4,17 +4,14 @@
#include "libcore.pb.h" #include "libcore.pb.h"
#endif #endif
#include <QString> #include <QString>
#include "3rdparty/protorpc/rpc_client.h"
namespace QtGrpc {
class Http2GrpcChannelPrivate;
}
namespace API { namespace API {
enum GeoRuleSetType {ip, site}; enum GeoRuleSetType {ip, site};
class Client { class Client {
public: public:
explicit Client(std::function<void(const QString &)> onError, const QString &target); explicit Client(std::function<void(const QString &)> onError, const QString &host, int port);
// QString returns is error string // QString returns is error string
@ -47,8 +44,7 @@ namespace API {
libcore::QuerySpeedTestResponse QueryCurrentSpeedTests(bool *rpcOK); libcore::QuerySpeedTestResponse QueryCurrentSpeedTests(bool *rpcOK);
private: private:
std::function<std::unique_ptr<QtGrpc::Http2GrpcChannelPrivate>()> make_grpc_channel; std::function<std::unique_ptr<protorpc::Client>()> make_rpc_client;
std::unique_ptr<QtGrpc::Http2GrpcChannelPrivate> default_grpc_channel;
std::function<void(const QString &)> onError; std::function<void(const QString &)> onError;
}; };

View File

@ -39,7 +39,7 @@ popd
#### Go: core #### #### Go: core ####
pushd core/server pushd core/server
pushd gen pushd gen
protoc -I . --go_out=. --go_opt paths=source_relative --go-grpc_out=. --go-grpc_opt paths=source_relative libcore.proto protoc -I . --go_out=. --protorpc_out=. libcore.proto
popd popd
VERSION_SINGBOX=$(go list -m -f '{{.Version}}' github.com/sagernet/sing-box) VERSION_SINGBOX=$(go list -m -f '{{.Version}}' github.com/sagernet/sing-box)
$GOCMD build -v -o $DEST -trimpath -ldflags "-w -s -X 'github.com/sagernet/sing-box/constant.Version=${VERSION_SINGBOX}'" -tags "with_clash_api,with_gvisor,with_quic,with_wireguard,with_utls,with_ech,with_dhcp" $GOCMD build -v -o $DEST -trimpath -ldflags "-w -s -X 'github.com/sagernet/sing-box/constant.Version=${VERSION_SINGBOX}'" -tags "with_clash_api,with_gvisor,with_quic,with_wireguard,with_utls,with_ech,with_dhcp"

295
src/api/RPC.cpp Normal file
View File

@ -0,0 +1,295 @@
#include "include/api/RPC.h"
#include "include/global/Configs.hpp"
namespace API {
Client::Client(std::function<void(const QString &)> onError, const QString &host, int port) {
this->make_rpc_client = [=]() { return std::make_unique<protorpc::Client>(host.toStdString().c_str(), port); };
this->onError = std::move(onError);
}
#define NOT_OK \
*rpcOK = false; \
onError(QString("LibcoreService error: %1\n").arg(QString::fromStdString(err.String())));
QString Client::Start(bool *rpcOK, const libcore::LoadConfigReq &request) {
libcore::ErrorResp reply;
std::string resp, req = spb::pb::serialize<std::string>(request);
auto err = make_rpc_client()->CallMethod("LibcoreService.Start", &req, &resp);
if(err.IsNil()) {
reply = spb::pb::deserialize< libcore::ErrorResp >(resp);
*rpcOK = true;
return QString::fromStdString(reply.error.value());
} else {
NOT_OK
return "";
}
}
QString Client::Stop(bool *rpcOK) {
libcore::EmptyReq request;
libcore::ErrorResp reply;
std::string resp, req = spb::pb::serialize<std::string>(request);
auto err = make_rpc_client()->CallMethod("LibcoreService.Stop", &req, &resp);
if(err.IsNil()) {
reply = spb::pb::deserialize< libcore::ErrorResp >( resp );
*rpcOK = true;
return QString::fromStdString(reply.error.value());
} else {
NOT_OK
return "";
}
}
libcore::QueryStatsResp Client::QueryStats() {
libcore::EmptyReq request;
libcore::QueryStatsResp reply;
std::string resp, req = spb::pb::serialize<std::string>(request);
auto err = make_rpc_client()->CallMethod("LibcoreService.QueryStats", &req, &resp);
if(err.IsNil()) {
reply = spb::pb::deserialize< libcore::QueryStatsResp >( resp );
return reply;
} else {
return {};
}
}
libcore::TestResp Client::Test(bool *rpcOK, const libcore::TestReq &request) {
libcore::TestResp reply;
std::string resp, req = spb::pb::serialize<std::string>(request);
auto err = make_rpc_client()->CallMethod("LibcoreService.Test", &req, &resp);
if(err.IsNil()) {
reply = spb::pb::deserialize< libcore::TestResp >( resp );
*rpcOK = true;
return reply;
} else {
NOT_OK
return reply;
}
}
void Client::StopTests(bool *rpcOK) {
const libcore::EmptyReq request;
std::string resp, req = spb::pb::serialize<std::string>(request);
auto err = make_rpc_client()->CallMethod("LibcoreService.StopTest", &req, &resp);
if(err.IsNil()) {
*rpcOK = true;
} else {
NOT_OK
}
}
libcore::QueryURLTestResponse Client::QueryURLTest(bool *rpcOK)
{
libcore::EmptyReq request;
libcore::QueryURLTestResponse reply;
std::string resp, req = spb::pb::serialize<std::string>(request);
auto err = make_rpc_client()->CallMethod("LibcoreService.QueryURLTest", &req, &resp);
if(err.IsNil()) {
reply = spb::pb::deserialize< libcore::QueryURLTestResponse >( resp );
*rpcOK = true;
return reply;
} else {
NOT_OK
return reply;
}
}
QStringList Client::GetGeoList(bool *rpcOK, GeoRuleSetType mode, const QString& basePath) {
switch (mode) {
case GeoRuleSetType::ip: {
libcore::GeoListRequest request;
libcore::GetGeoIPListResponse reply;
request.path = basePath.toStdString();
std::string resp, req = spb::pb::serialize<std::string>(request);
auto err = make_rpc_client()->CallMethod("LibcoreService.GetGeoIPList", &req, &resp);
if(err.IsNil()) {
QStringList res;
reply = spb::pb::deserialize< libcore::GetGeoIPListResponse >( resp );
for (const auto & i : reply.items) {
res.append(QString::fromStdString(i));
}
*rpcOK = true;
return res;
} else {
NOT_OK
return {};
}
}
case GeoRuleSetType::site: {
libcore::GeoListRequest request;
libcore::GetGeoSiteListResponse reply;
request.path = basePath.toStdString();
std::string resp, req = spb::pb::serialize<std::string>(request);
auto err = make_rpc_client()->CallMethod("LibcoreService.GetGeoSiteList", &req, &resp);
if(err.IsNil()) {
QStringList res;
reply = spb::pb::deserialize< libcore::GetGeoSiteListResponse >( resp );
for (const auto & i : reply.items) {
res.append(QString::fromStdString(i));
}
*rpcOK = true;
return res;
} else {
NOT_OK
return {};
}
}
}
return {};
}
QString Client::CompileGeoSet(bool *rpcOK, GeoRuleSetType mode, std::string category, const QString& basePath) {
switch (mode) {
case ip: {
libcore::CompileGeoIPToSrsRequest request;
libcore::EmptyResp reply;
request.item = category;
request.path = basePath.toStdString();
std::string resp, req = spb::pb::serialize<std::string>(request);
auto err = make_rpc_client()->CallMethod("LibcoreService.CompileGeoIPToSrs", &req, &resp);
if(err.IsNil()) {
*rpcOK = true;
return "";
} else {
NOT_OK
return QString::fromStdString(err.String());
}
}
case site: {
libcore::CompileGeoSiteToSrsRequest request;
libcore::EmptyResp reply;
request.item = category;
request.path = basePath.toStdString();
std::string resp, req = spb::pb::serialize<std::string>(request);
auto err = make_rpc_client()->CallMethod("LibcoreService.CompileGeoSiteToSrs", &req, &resp);
if(err.IsNil()) {
*rpcOK = true;
return "";
} else {
NOT_OK
return QString::fromStdString(err.String());
}
}
}
return "";
}
QString Client::SetSystemDNS(bool *rpcOK, const bool clear) const {
libcore::SetSystemDNSRequest request;
request.clear = clear;
std::string resp, req = spb::pb::serialize<std::string>(request);
auto err = make_rpc_client()->CallMethod("LibcoreService.SetSystemDNS", &req, &resp);
if(err.IsNil()) {
*rpcOK = true;
return "";
} else {
NOT_OK
return QString::fromStdString(err.String());
}
}
libcore::ListConnectionsResp Client::ListConnections(bool* rpcOK) const
{
libcore::EmptyReq request;
libcore::ListConnectionsResp reply;
std::string resp, req = spb::pb::serialize<std::string>(request);
auto err = make_rpc_client()->CallMethod("LibcoreService.ListConnections", &req, &resp);
if(err.IsNil()) {
reply = spb::pb::deserialize< libcore::ListConnectionsResp >( resp );
*rpcOK = true;
return reply;
} else {
NOT_OK
MW_show_log(QString("Failed to list connections: ") + QString::fromStdString(err.String()));
return {};
}
}
QString Client::CheckConfig(bool* rpcOK, const QString& config) const
{
libcore::LoadConfigReq request;
libcore::ErrorResp reply;
request.core_config = config.toStdString();
std::string resp, req = spb::pb::serialize<std::string>(request);
auto err = make_rpc_client()->CallMethod("LibcoreService.CheckConfig", &req, &resp);
if(err.IsNil())
{
reply = spb::pb::deserialize< libcore::ErrorResp >( resp );
*rpcOK = true;
return QString::fromStdString(reply.error.value());
} else
{
NOT_OK
return QString::fromStdString(err.String());
}
}
bool Client::IsPrivileged(bool* rpcOK) const
{
libcore::EmptyReq request;
libcore::IsPrivilegedResponse reply;
std::string resp, req = spb::pb::serialize<std::string>(request);
auto err = make_rpc_client()->CallMethod("LibcoreService.IsPrivileged", &req, &resp);
if(err.IsNil())
{
reply = spb::pb::deserialize< libcore::IsPrivilegedResponse >( resp );
*rpcOK = true;
return reply.has_privilege.value();
} else
{
NOT_OK
return false;
}
}
libcore::SpeedTestResponse Client::SpeedTest(bool *rpcOK, const libcore::SpeedTestRequest &request)
{
libcore::SpeedTestResponse reply;
std::string resp, req = spb::pb::serialize<std::string>(request);
auto err = make_rpc_client()->CallMethod("LibcoreService.SpeedTest", &req, &resp);
if(err.IsNil()) {
reply = spb::pb::deserialize< libcore::SpeedTestResponse >( resp );
*rpcOK = true;
return reply;
} else {
NOT_OK
return reply;
}
}
libcore::QuerySpeedTestResponse Client::QueryCurrentSpeedTests(bool *rpcOK)
{
const libcore::EmptyReq request;
libcore::QuerySpeedTestResponse reply;
std::string resp, req = spb::pb::serialize<std::string>(request);
auto err = make_rpc_client()->CallMethod("LibcoreService.QuerySpeedTest", &req, &resp);
if(err.IsNil()) {
reply = spb::pb::deserialize< libcore::QuerySpeedTestResponse >( resp );
*rpcOK = true;
return reply;
} else {
NOT_OK
return reply;
}
}
} // namespace API

View File

@ -1,474 +0,0 @@
#include "include/api/gRPC.h"
#include <utility>
#include "include/global/Configs.hpp"
#include <QCoreApplication>
#include <QNetworkReply>
#include <QTimer>
#include <QtEndian>
#include <QThread>
#include <QMutex>
#include <QAbstractNetworkCache>
namespace QtGrpc {
const char *GrpcAcceptEncodingHeader = "grpc-accept-encoding";
const char *AcceptEncodingHeader = "accept-encoding";
const char *TEHeader = "te";
const char *GrpcStatusHeader = "grpc-status";
const char *GrpcStatusMessage = "grpc-message";
const int GrpcMessageSizeHeaderSize = 5;
class NoCache : public QAbstractNetworkCache {
public:
QNetworkCacheMetaData metaData(const QUrl &url) override {
return {};
}
void updateMetaData(const QNetworkCacheMetaData &metaData) override {
}
QIODevice *data(const QUrl &url) override {
return nullptr;
}
bool remove(const QUrl &url) override {
return false;
}
[[nodiscard]] qint64 cacheSize() const override {
return 0;
}
QIODevice *prepare(const QNetworkCacheMetaData &metaData) override {
return nullptr;
}
void insert(QIODevice *device) override {
}
void clear() override {
}
};
class Http2GrpcChannelPrivate {
private:
QThread *thread;
QNetworkAccessManager *nm;
QString url_base;
QString serviceName;
// async
QNetworkReply *post(const QString &method, const QString &service, const QByteArray &args) {
QUrl callUrl = url_base + "/" + service + "/" + method;
QNetworkRequest request(callUrl);
request.setAttribute(QNetworkRequest::Http2DirectAttribute, true);
request.setHeader(QNetworkRequest::ContentTypeHeader, QLatin1String{"application/grpc"});
request.setRawHeader("Cache-Control", "no-store");
request.setRawHeader(GrpcAcceptEncodingHeader, QByteArray{"identity,deflate,gzip"});
request.setRawHeader(AcceptEncodingHeader, QByteArray{"identity,gzip"});
request.setRawHeader(TEHeader, QByteArray{"trailers"});
QByteArray msg(GrpcMessageSizeHeaderSize, '\0');
*reinterpret_cast<int *>(msg.data() + 1) = qToBigEndian((int) args.size());
msg += args;
QNetworkReply *networkReply = nm->post(request, msg);
return networkReply;
}
static QByteArray processReply(QNetworkReply *networkReply, QNetworkReply::NetworkError &statusCode) {
// Check if no network error occured
if (networkReply->error() != QNetworkReply::NoError) {
statusCode = networkReply->error();
return {};
}
// Check if server answer with error
auto errCode = networkReply->rawHeader(GrpcStatusHeader).toInt();
if (errCode != 0) {
QStringList errstr;
errstr << "grpc-status error code:" << Int2String(errCode) << ", error msg:"
<< QLatin1String(networkReply->rawHeader(GrpcStatusMessage));
MW_show_log(errstr.join(" "));
statusCode = QNetworkReply::NetworkError::ProtocolUnknownError;
return {};
}
statusCode = QNetworkReply::NetworkError::NoError;
return networkReply->readAll().mid(GrpcMessageSizeHeaderSize);
}
QNetworkReply::NetworkError call(const QString &method, const QString &service, const QByteArray &args, QByteArray &qByteArray, int timeout_ms) {
QNetworkReply *networkReply = post(method, service, args);
QTimer *abortTimer = nullptr;
if (timeout_ms > 0) {
abortTimer = new QTimer;
abortTimer->setSingleShot(true);
abortTimer->setInterval(timeout_ms);
QObject::connect(abortTimer, &QTimer::timeout, networkReply, &QNetworkReply::abort);
abortTimer->start();
}
{
QEventLoop loop;
QObject::connect(networkReply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
loop.exec();
}
if (abortTimer != nullptr) {
abortTimer->stop();
abortTimer->deleteLater();
}
auto grpcStatus = QNetworkReply::NetworkError::ProtocolUnknownError;
qByteArray = processReply(networkReply, grpcStatus);
networkReply->deleteLater();
return grpcStatus;
}
public:
Http2GrpcChannelPrivate(const QString &url_, const QString &serviceName_) {
url_base = "http://" + url_;
serviceName = serviceName_;
//
thread = new QThread;
nm = new QNetworkAccessManager();
nm->setCache(new NoCache);
nm->moveToThread(thread);
thread->start();
}
~Http2GrpcChannelPrivate() {
nm->deleteLater();
thread->quit();
thread->wait();
thread->deleteLater();
}
QNetworkReply::NetworkError Call(const QString &methodName,
const std::string req, std::vector<uint8_t> &rsp,
int timeout_ms = 0) {
if (!Configs::dataStore->core_running) return QNetworkReply::NetworkError(-1919);
auto requestArray = QByteArray::fromStdString(req);
QByteArray responseArray;
QNetworkReply::NetworkError err;
QMutex lock;
lock.lock();
runOnThread(
[&] {
err = call(methodName, serviceName, requestArray, responseArray, timeout_ms);
lock.unlock();
},
nm);
lock.lock();
lock.unlock();
// qDebug() << "rsp err" << err;
// qDebug() << "rsp array" << responseArray;
if (err != QNetworkReply::NetworkError::NoError) {
return err;
}
rsp.assign(responseArray.begin(), responseArray.end());
return QNetworkReply::NetworkError::NoError;
}
};
} // namespace QtGrpc
namespace API {
Client::Client(std::function<void(const QString &)> onError, const QString &target) {
this->make_grpc_channel = [=]() { return std::make_unique<QtGrpc::Http2GrpcChannelPrivate>(target, "libcore.LibcoreService"); };
this->default_grpc_channel = make_grpc_channel();
this->onError = std::move(onError);
}
#define NOT_OK \
*rpcOK = false; \
onError(QString("QNetworkReply::NetworkError code: %1\n").arg(status));
QString Client::Start(bool *rpcOK, const libcore::LoadConfigReq &request) {
libcore::ErrorResp reply;
std::vector<uint8_t> rsp;
auto status = default_grpc_channel->Call("Start", spb::pb::serialize< std::string >( request ), rsp);
if (status == QNetworkReply::NoError) {
reply = spb::pb::deserialize< libcore::ErrorResp >( rsp );
*rpcOK = true;
return {reply.error.value().c_str()};
} else {
NOT_OK
return reply.error.value().c_str();
}
}
QString Client::Stop(bool *rpcOK) {
libcore::EmptyReq request;
libcore::ErrorResp reply;
std::vector<uint8_t> rsp;
auto status = default_grpc_channel->Call("Stop", spb::pb::serialize< std::string >( request ), rsp);
if (status == QNetworkReply::NoError) {
reply = spb::pb::deserialize< libcore::ErrorResp >( rsp );
*rpcOK = true;
return {reply.error.value().c_str()};
} else {
NOT_OK
return "";
}
}
libcore::QueryStatsResp Client::QueryStats() {
libcore::EmptyReq request;
libcore::QueryStatsResp reply;
std::vector<uint8_t> rsp;
auto status = default_grpc_channel->Call("QueryStats", spb::pb::serialize< std::string >( request ), rsp, 500);
if (status == QNetworkReply::NoError) {
reply = spb::pb::deserialize< libcore::QueryStatsResp >( rsp );
return reply;
} else {
return {};
}
}
libcore::TestResp Client::Test(bool *rpcOK, const libcore::TestReq &request) {
libcore::TestResp reply;
std::vector<uint8_t> rsp;
auto status = make_grpc_channel()->Call("Test", spb::pb::serialize< std::string >( request ), rsp);
if (status == QNetworkReply::NoError) {
reply = spb::pb::deserialize< libcore::TestResp >( rsp );
*rpcOK = true;
return reply;
} else {
NOT_OK
return reply;
}
}
void Client::StopTests(bool *rpcOK) {
const libcore::EmptyReq req;
libcore::EmptyResp resp;
std::vector<uint8_t> rsp;
auto status = make_grpc_channel()->Call("StopTest", spb::pb::serialize< std::string >( req ), rsp);
if (status == QNetworkReply::NoError) {
*rpcOK = true;
} else {
NOT_OK
}
}
libcore::QueryURLTestResponse Client::QueryURLTest(bool *rpcOK)
{
libcore::EmptyReq request;
libcore::QueryURLTestResponse resp;
std::vector<uint8_t> rsp;
auto status = make_grpc_channel()->Call("QueryURLTest", spb::pb::serialize< std::string >( request ), rsp);
if (status == QNetworkReply::NoError) {
resp = spb::pb::deserialize< libcore::QueryURLTestResponse >( rsp );
*rpcOK = true;
return resp;
} else {
NOT_OK
return resp;
}
}
QStringList Client::GetGeoList(bool *rpcOK, GeoRuleSetType mode, const QString& basePath) {
switch (mode) {
case GeoRuleSetType::ip: {
libcore::GeoListRequest req;
libcore::GetGeoIPListResponse resp;
std::vector<uint8_t> rsp;
req.path = basePath.toStdString();
auto status = default_grpc_channel->Call("GetGeoIPList", spb::pb::serialize< std::string >( req ), rsp);
if (status == QNetworkReply::NoError) {
QStringList res;
resp = spb::pb::deserialize< libcore::GetGeoIPListResponse >( rsp );
for (const auto & i : resp.items) {
res.append(QString::fromStdString(i));
}
*rpcOK = true;
return res;
} else {
NOT_OK
return {};
}
}
case GeoRuleSetType::site: {
libcore::GeoListRequest req;
libcore::GetGeoSiteListResponse resp;
std::vector<uint8_t> rsp;
req.path = basePath.toStdString();
auto status = default_grpc_channel->Call("GetGeoSiteList", spb::pb::serialize< std::string >( req ), rsp);
if (status == QNetworkReply::NoError) {
QStringList res;
resp = spb::pb::deserialize< libcore::GetGeoSiteListResponse >( rsp );
for (const auto & i : resp.items) {
res.append(QString::fromStdString(i));
}
*rpcOK = true;
return res;
} else {
NOT_OK
return {};
}
}
}
return {};
}
QString Client::CompileGeoSet(bool *rpcOK, GeoRuleSetType mode, std::string category, const QString& basePath) {
switch (mode) {
case ip: {
libcore::CompileGeoIPToSrsRequest req;
libcore::EmptyResp resp;
std::vector<uint8_t> rsp;
req.item = category;
req.path = basePath.toStdString();
auto status = default_grpc_channel->Call("CompileGeoIPToSrs", spb::pb::serialize< std::string >( req ), rsp);
if (status == QNetworkReply::NoError) {
*rpcOK = true;
return "";
} else {
NOT_OK
return qt_error_string(status);
}
}
case site: {
libcore::CompileGeoSiteToSrsRequest req;
libcore::EmptyResp resp;
std::vector<uint8_t> rsp;
req.item = category;
req.path = basePath.toStdString();
auto status = default_grpc_channel->Call("CompileGeoSiteToSrs", spb::pb::serialize< std::string >( req ), rsp);
if (status == QNetworkReply::NoError) {
*rpcOK = true;
return "";
} else {
NOT_OK
return qt_error_string(status);
}
}
}
return "";
}
QString Client::SetSystemDNS(bool *rpcOK, const bool clear) const {
libcore::SetSystemDNSRequest req;
libcore::EmptyResp resp;
std::vector<uint8_t> rsp;
req.clear = clear;
auto status = default_grpc_channel->Call("SetSystemDNS", spb::pb::serialize< std::string >( req ), rsp);
if (status == QNetworkReply::NoError) {
*rpcOK = true;
return "";
} else {
NOT_OK
return qt_error_string(status);
}
}
libcore::ListConnectionsResp Client::ListConnections(bool* rpcOK) const
{
libcore::EmptyReq req;
libcore::ListConnectionsResp resp;
std::vector<uint8_t> rsp;
auto status = default_grpc_channel->Call("ListConnections", spb::pb::serialize< std::string >( req ), rsp);
if (status == QNetworkReply::NoError) {
resp = spb::pb::deserialize< libcore::ListConnectionsResp >( rsp );
*rpcOK = true;
return resp;
} else {
NOT_OK
MW_show_log(QString("Failed to list connections: " + qt_error_string(status)));
return {};
}
}
QString Client::CheckConfig(bool* rpcOK, const QString& config) const
{
libcore::LoadConfigReq req;
libcore::ErrorResp resp;
std::vector<uint8_t> rsp;
req.core_config = config.toStdString();
auto status = default_grpc_channel->Call("CheckConfig", spb::pb::serialize< std::string >( req ), rsp);
if (status == QNetworkReply::NoError)
{
resp = spb::pb::deserialize< libcore::ErrorResp >( rsp );
*rpcOK = true;
return {resp.error.value().c_str()};
} else
{
NOT_OK
return qt_error_string(status);
}
}
bool Client::IsPrivileged(bool* rpcOK) const
{
auto req = libcore::EmptyReq();
auto resp = libcore::IsPrivilegedResponse();
std::vector<uint8_t> rsp;
auto status = default_grpc_channel->Call("IsPrivileged", spb::pb::serialize< std::string >( req ), rsp);
if (status == QNetworkReply::NoError)
{
resp = spb::pb::deserialize< libcore::IsPrivilegedResponse >( rsp );
*rpcOK = true;
return resp.has_privilege.value();
} else
{
NOT_OK
return false;
}
}
libcore::SpeedTestResponse Client::SpeedTest(bool *rpcOK, const libcore::SpeedTestRequest &request)
{
libcore::SpeedTestResponse reply;
std::vector<uint8_t> rsp;
auto status = make_grpc_channel()->Call("SpeedTest", spb::pb::serialize< std::string >( request ), rsp);
if (status == QNetworkReply::NoError) {
reply = spb::pb::deserialize< libcore::SpeedTestResponse >( rsp );
*rpcOK = true;
return reply;
} else {
NOT_OK
return reply;
}
}
libcore::QuerySpeedTestResponse Client::QueryCurrentSpeedTests(bool *rpcOK)
{
const libcore::EmptyReq req;
libcore::QuerySpeedTestResponse reply;
std::vector<uint8_t> rsp;
auto status = make_grpc_channel()->Call("QuerySpeedTest", spb::pb::serialize< std::string >( req ), rsp);
if (status == QNetworkReply::NoError) {
reply = spb::pb::deserialize< libcore::QuerySpeedTestResponse >( rsp );
*rpcOK = true;
return reply;
} else {
NOT_OK
return reply;
}
}
} // namespace API

View File

@ -2,7 +2,7 @@
#include "include/dataStore/Database.hpp" #include "include/dataStore/Database.hpp"
#include "include/configs/proxy/includes.h" #include "include/configs/proxy/includes.h"
#include "include/configs/proxy/Preset.hpp" #include "include/configs/proxy/Preset.hpp"
#include "include/api/gRPC.h" #include "include/api/RPC.h"
#include <QApplication> #include <QApplication>
#include <QFile> #include <QFile>

View File

@ -9,7 +9,7 @@
#include <QJsonDocument> #include <QJsonDocument>
#include <QJsonObject> #include <QJsonObject>
#include <QStandardPaths> #include <QStandardPaths>
#include <include/api/gRPC.h> #include <include/api/RPC.h>
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
#include "include/sys/windows/guihelper.h" #include "include/sys/windows/guihelper.h"

View File

@ -1,6 +1,6 @@
#include <QThread> #include <QThread>
#include <libcore.pb.h> #include <libcore.pb.h>
#include <include/api/gRPC.h> #include <include/api/RPC.h>
#include "include/ui/mainwindow_interface.h" #include "include/ui/mainwindow_interface.h"
#include <include/stats/connections/connectionLister.hpp> #include <include/stats/connections/connectionLister.hpp>

View File

@ -1,6 +1,6 @@
#include "include/stats/traffic/TrafficLooper.hpp" #include "include/stats/traffic/TrafficLooper.hpp"
#include "include/api/gRPC.h" #include "include/api/RPC.h"
#include "include/ui/mainwindow_interface.h" #include "include/ui/mainwindow_interface.h"
#include <QThread> #include <QThread>

View File

@ -53,7 +53,6 @@
#include <random> #include <random>
#include <3rdparty/QHotkey/qhotkey.h> #include <3rdparty/QHotkey/qhotkey.h>
#include <3rdparty/qv2ray/v2/proxy/QvProxyConfigurator.hpp> #include <3rdparty/qv2ray/v2/proxy/QvProxyConfigurator.hpp>
#include <include/api/gRPC.h>
#include <include/global/HTTPRequestHelper.hpp> #include <include/global/HTTPRequestHelper.hpp>
#include "include/sys/macos/MacOS.h" #include "include/sys/macos/MacOS.h"

View File

@ -3,7 +3,7 @@
#include "include/dataStore/Database.hpp" #include "include/dataStore/Database.hpp"
#include "include/configs/ConfigBuilder.hpp" #include "include/configs/ConfigBuilder.hpp"
#include "include/stats/traffic/TrafficLooper.hpp" #include "include/stats/traffic/TrafficLooper.hpp"
#include "include/api/gRPC.h" #include "include/api/RPC.h"
#include "include/ui/utils//MessageBoxTimer.h" #include "include/ui/utils//MessageBoxTimer.h"
#include "3rdparty/qv2ray/v2/proxy/QvProxyConfigurator.hpp" #include "3rdparty/qv2ray/v2/proxy/QvProxyConfigurator.hpp"
@ -22,7 +22,7 @@ void MainWindow::setup_grpc() {
[=](const QString &errStr) { [=](const QString &errStr) {
MW_show_log("[Error] Core: " + errStr); MW_show_log("[Error] Core: " + errStr);
}, },
"127.0.0.1:" + Int2String(Configs::dataStore->core_port)); "127.0.0.1", Configs::dataStore->core_port);
// Looper // Looper
runOnNewThread([=] { Stats::trafficLooper->Loop(); }); runOnNewThread([=] { Stats::trafficLooper->Loop(); });
@ -64,7 +64,7 @@ void MainWindow::runURLTest(const QString& config, bool useDefault, const QStrin
{ {
int entid = -1; int entid = -1;
if (!tag2entID.empty()) { if (!tag2entID.empty()) {
entid = tag2entID.count(QString(res.outbound_tag.value().c_str())) == 0 ? -1 : tag2entID[QString(res.outbound_tag.value().c_str())]; entid = tag2entID.count(QString::fromStdString(res.outbound_tag.value())) == 0 ? -1 : tag2entID[QString::fromStdString(res.outbound_tag.value())];
} }
if (entid == -1) { if (entid == -1) {
continue; continue;
@ -76,11 +76,11 @@ void MainWindow::runURLTest(const QString& config, bool useDefault, const QStrin
if (res.error.value().empty()) { if (res.error.value().empty()) {
ent->latency = res.latency_ms.value(); ent->latency = res.latency_ms.value();
} else { } else {
if (QString(res.error.value().c_str()).contains("test aborted") || if (QString::fromStdString(res.error.value()).contains("test aborted") ||
QString(res.error.value().c_str()).contains("context canceled")) ent->latency=0; QString::fromStdString(res.error.value()).contains("context canceled")) ent->latency=0;
else { else {
ent->latency = -1; ent->latency = -1;
MW_show_log(tr("[%1] test error: %2").arg(ent->bean->DisplayTypeAndName(), res.error.value().c_str())); MW_show_log(tr("[%1] test error: %2").arg(ent->bean->DisplayTypeAndName(), QString::fromStdString(res.error.value())));
} }
} }
ent->Save(); ent->Save();
@ -104,7 +104,7 @@ void MainWindow::runURLTest(const QString& config, bool useDefault, const QStrin
for (const auto &res: result.results) { for (const auto &res: result.results) {
if (!tag2entID.empty()) { if (!tag2entID.empty()) {
entID = tag2entID.count(QString(res.outbound_tag.value().c_str())) == 0 ? -1 : tag2entID[QString(res.outbound_tag.value().c_str())]; entID = tag2entID.count(QString::fromStdString(res.outbound_tag.value())) == 0 ? -1 : tag2entID[QString::fromStdString(res.outbound_tag.value())];
} }
if (entID == -1) { if (entID == -1) {
MW_show_log(tr("Something is very wrong, the subject ent cannot be found!")); MW_show_log(tr("Something is very wrong, the subject ent cannot be found!"));
@ -120,11 +120,11 @@ void MainWindow::runURLTest(const QString& config, bool useDefault, const QStrin
if (res.error.value().empty()) { if (res.error.value().empty()) {
ent->latency = res.latency_ms.value(); ent->latency = res.latency_ms.value();
} else { } else {
if (QString(res.error.value().c_str()).contains("test aborted") || if (QString::fromStdString(res.error.value()).contains("test aborted") ||
QString(res.error.value().c_str()).contains("context canceled")) ent->latency=0; QString::fromStdString(res.error.value()).contains("context canceled")) ent->latency=0;
else { else {
ent->latency = -1; ent->latency = -1;
MW_show_log(tr("[%1] test error: %2").arg(ent->bean->DisplayTypeAndName(), res.error.value().c_str())); MW_show_log(tr("[%1] test error: %2").arg(ent->bean->DisplayTypeAndName(), QString::fromStdString(res.error.value())));
} }
} }
ent->Save(); ent->Save();
@ -212,7 +212,7 @@ void MainWindow::url_test_current() {
runOnUiThread([=] { runOnUiThread([=] {
if (!result.results[0].error.value().empty()) { if (!result.results[0].error.value().empty()) {
MW_show_log(QString("UrlTest error: %1").arg(result.results[0].error.value().c_str())); MW_show_log(QString("UrlTest error: %1").arg(QString::fromStdString(result.results[0].error.value())));
} }
if (latency <= 0) { if (latency <= 0) {
ui->label_running->setText(tr("Test Result") + ": " + tr("Unavailable")); ui->label_running->setText(tr("Test Result") + ": " + tr("Unavailable"));
@ -304,7 +304,7 @@ void MainWindow::runSpeedTest(const QString& config, bool useDefault, bool testC
{ {
continue; continue;
} }
auto profile = testCurrent ? running : Configs::profileManager->GetProfile(tag2entID[res.result.value().outbound_tag.value().c_str()]); auto profile = testCurrent ? running : Configs::profileManager->GetProfile(tag2entID[QString::fromStdString(res.result.value().outbound_tag.value())]);
if (profile == nullptr) if (profile == nullptr)
{ {
continue; continue;
@ -318,8 +318,8 @@ void MainWindow::runSpeedTest(const QString& config, bool useDefault, bool testC
if (res.result.value().error.value().empty() && !res.result.value().cancelled.value() && lastProxyListUpdate.msecsTo(QDateTime::currentDateTime()) >= 500) if (res.result.value().error.value().empty() && !res.result.value().cancelled.value() && lastProxyListUpdate.msecsTo(QDateTime::currentDateTime()) >= 500)
{ {
if (!res.result.value().dl_speed.value().empty()) profile->dl_speed = res.result.value().dl_speed.value().c_str(); if (!res.result.value().dl_speed.value().empty()) profile->dl_speed = QString::fromStdString(res.result.value().dl_speed.value());
if (!res.result.value().ul_speed.value().empty()) profile->ul_speed = res.result.value().ul_speed.value().c_str(); if (!res.result.value().ul_speed.value().empty()) profile->ul_speed = QString::fromStdString(res.result.value().ul_speed.value());
if (profile->latency <= 0 && res.result.value().latency.value() > 0) profile->latency = res.result.value().latency.value(); if (profile->latency <= 0 && res.result.value().latency.value() > 0) profile->latency = res.result.value().latency.value();
refresh_proxy_list(profile->id); refresh_proxy_list(profile->id);
lastProxyListUpdate = QDateTime::currentDateTime(); lastProxyListUpdate = QDateTime::currentDateTime();
@ -343,7 +343,7 @@ void MainWindow::runSpeedTest(const QString& config, bool useDefault, bool testC
for (const auto &res: result.results) { for (const auto &res: result.results) {
if (testCurrent) entID = running ? running->id : -1; if (testCurrent) entID = running ? running->id : -1;
else { else {
entID = tag2entID.count(QString(res.outbound_tag.value().c_str())) == 0 ? -1 : tag2entID[QString(res.outbound_tag.value().c_str())]; entID = tag2entID.count(QString::fromStdString(res.outbound_tag.value())) == 0 ? -1 : tag2entID[QString::fromStdString(res.outbound_tag.value())];
} }
if (entID == -1) { if (entID == -1) {
MW_show_log(tr("Something is very wrong, the subject ent cannot be found!")); MW_show_log(tr("Something is very wrong, the subject ent cannot be found!"));
@ -359,14 +359,14 @@ void MainWindow::runSpeedTest(const QString& config, bool useDefault, bool testC
if (res.cancelled.value()) continue; if (res.cancelled.value()) continue;
if (res.error.value().empty()) { if (res.error.value().empty()) {
ent->dl_speed = res.dl_speed.value().c_str(); ent->dl_speed = QString::fromStdString(res.dl_speed.value());
ent->ul_speed = res.ul_speed.value().c_str(); ent->ul_speed = QString::fromStdString(res.ul_speed.value());
if (ent->latency <= 0 && res.latency.value() > 0) ent->latency = res.latency.value(); if (ent->latency <= 0 && res.latency.value() > 0) ent->latency = res.latency.value();
} else { } else {
ent->dl_speed = "N/A"; ent->dl_speed = "N/A";
ent->ul_speed = "N/A"; ent->ul_speed = "N/A";
ent->latency = -1; ent->latency = -1;
MW_show_log(tr("[%1] speed test error: %2").arg(ent->bean->DisplayTypeAndName(), res.error.value().c_str())); MW_show_log(tr("[%1] speed test error: %2").arg(ent->bean->DisplayTypeAndName(), QString::fromStdString(res.error.value())));
} }
ent->Save(); ent->Save();
} }

View File

@ -1,7 +1,7 @@
#include "include/ui/setting/RouteItem.h" #include "include/ui/setting/RouteItem.h"
#include "include/dataStore/RouteEntity.h" #include "include/dataStore/RouteEntity.h"
#include "include/dataStore/Database.hpp" #include "include/dataStore/Database.hpp"
#include "include/api/gRPC.h" #include "include/api/RPC.h"
void adjustComboBoxWidth(const QComboBox *comboBox) { void adjustComboBoxWidth(const QComboBox *comboBox) {
int maxWidth = 0; int maxWidth = 0;

View File

@ -13,7 +13,7 @@
#include <QShortcut> #include <QShortcut>
#include <QTimer> #include <QTimer>
#include <QToolTip> #include <QToolTip>
#include <include/api/gRPC.h> #include <include/api/RPC.h>
void DialogManageRoutes::reloadProfileItems() { void DialogManageRoutes::reloadProfileItems() {
if (chainList.empty()) { if (chainList.empty()) {