in src/brpc/server.cpp [1365:1595]
int Server::AddServiceInternal(google::protobuf::Service* service,
bool is_builtin_service,
const ServiceOptions& svc_opt) {
if (NULL == service) {
LOG(ERROR) << "Parameter[service] is NULL!";
return -1;
}
const google::protobuf::ServiceDescriptor* sd = service->GetDescriptor();
if (sd->method_count() == 0) {
LOG(ERROR) << "service=" << sd->full_name()
<< " does not have any method.";
return -1;
}
if (InitializeOnce() != 0) {
LOG(ERROR) << "Fail to initialize Server[" << version() << ']';
return -1;
}
if (status() != READY) {
LOG(ERROR) << "Can't add service=" << sd->full_name() << " to Server["
<< version() << "] which is " << status_str(status());
return -1;
}
if (_fullname_service_map.seek(sd->full_name()) != NULL) {
LOG(ERROR) << "service=" << sd->full_name() << " already exists";
return -1;
}
ServiceProperty* old_ss = _service_map.seek(sd->name());
if (old_ss != NULL) {
// names conflict.
LOG(ERROR) << "Conflict service name between "
<< sd->full_name() << " and "
<< old_ss->service_name();
return -1;
}
// defined `option (idl_support) = true' or not.
const bool is_idl_support = sd->file()->options().GetExtension(idl_support);
Tabbed* tabbed = dynamic_cast<Tabbed*>(service);
for (int i = 0; i < sd->method_count(); ++i) {
const google::protobuf::MethodDescriptor* md = sd->method(i);
MethodProperty mp;
mp.is_builtin_service = is_builtin_service;
mp.own_method_status = true;
mp.params.is_tabbed = !!tabbed;
mp.params.allow_default_url = svc_opt.allow_default_url;
mp.params.allow_http_body_to_pb = svc_opt.allow_http_body_to_pb;
mp.params.pb_bytes_to_base64 = svc_opt.pb_bytes_to_base64;
mp.params.pb_single_repeated_to_array = svc_opt.pb_single_repeated_to_array;
mp.params.enable_progressive_read = svc_opt.enable_progressive_read;
if (mp.params.enable_progressive_read) {
_has_progressive_read_method = true;
}
mp.service = service;
mp.method = md;
mp.status = new MethodStatus;
_method_map[md->full_name()] = mp;
if (is_idl_support && sd->name() != sd->full_name()/*has ns*/) {
MethodProperty mp2 = mp;
mp2.own_method_status = false;
// have to map service_name + method_name as well because ubrpc
// does not send the namespace before service_name.
std::string full_name_wo_ns;
full_name_wo_ns.reserve(sd->name().size() + 1 + md->name().size());
full_name_wo_ns.append(sd->name());
full_name_wo_ns.push_back('.');
full_name_wo_ns.append(md->name());
if (_method_map.seek(full_name_wo_ns) == NULL) {
_method_map[full_name_wo_ns] = mp2;
} else {
LOG(ERROR) << '`' << full_name_wo_ns << "' already exists";
RemoveMethodsOf(service);
return -1;
}
}
}
const ServiceProperty ss = {
is_builtin_service, svc_opt.ownership, service, NULL };
_fullname_service_map[sd->full_name()] = ss;
_service_map[sd->name()] = ss;
if (is_builtin_service) {
++_builtin_service_count;
} else {
if (_first_service == NULL) {
_first_service = service;
}
}
butil::StringPiece restful_mappings = svc_opt.restful_mappings;
restful_mappings.trim_spaces();
if (!restful_mappings.empty()) {
// Parse the mappings.
std::vector<RestfulMapping> mappings;
if (!ParseRestfulMappings(restful_mappings, &mappings)) {
LOG(ERROR) << "Fail to parse mappings `" << restful_mappings << '\'';
RemoveService(service);
return -1;
}
if (mappings.empty()) {
// we already trimmed at the beginning, this is impossible.
LOG(ERROR) << "Impossible: Nothing in restful_mappings";
RemoveService(service);
return -1;
}
// Due the flexibility of URL matching, it's almost impossible to
// dispatch all kinds of URL to different methods *efficiently* just
// inside the HTTP protocol impl. We would like to match most-
// frequently-used URLs(/Service/Method) fastly and match more complex
// URLs inside separate functions.
// The trick is adding some entries inside the service maps without
// real services, mapping from the first component in the URL to a
// RestfulMap which does the complex matchings. For example:
// "/v1/send => SendFn, /v1/recv => RecvFn, /v2/check => CheckFn"
// We'll create 2 entries in service maps (_fullname_service_map and
// _service_map) mapping from "v1" and "v2" to 2 different RestfulMap
// respectively. When the URL is accessed, we extract the first
// component, find the RestfulMap and do url matchings. Regular url
// handling is not affected.
for (size_t i = 0; i < mappings.size(); ++i) {
const std::string full_method_name =
sd->full_name() + "." + mappings[i].method_name;
MethodProperty* mp = _method_map.seek(full_method_name);
if (mp == NULL) {
LOG(ERROR) << "Unknown method=`" << full_method_name << '\'';
RemoveService(service);
return -1;
}
const std::string& svc_name = mappings[i].path.service_name;
if (svc_name.empty()) {
if (_global_restful_map == NULL) {
_global_restful_map = new RestfulMap("");
}
MethodProperty::OpaqueParams params;
params.is_tabbed = !!tabbed;
params.allow_default_url = svc_opt.allow_default_url;
params.allow_http_body_to_pb = svc_opt.allow_http_body_to_pb;
params.pb_bytes_to_base64 = svc_opt.pb_bytes_to_base64;
params.pb_single_repeated_to_array = svc_opt.pb_single_repeated_to_array;
if (!_global_restful_map->AddMethod(
mappings[i].path, service, params,
mappings[i].method_name, mp->status)) {
LOG(ERROR) << "Fail to map `" << mappings[i].path
<< "' to `" << full_method_name << '\'';
RemoveService(service);
return -1;
}
if (mp->http_url == NULL) {
mp->http_url = new std::string(mappings[i].path.to_string());
} else {
if (!mp->http_url->empty()) {
mp->http_url->append(" @");
}
mp->http_url->append(mappings[i].path.to_string());
}
continue;
}
ServiceProperty* sp = _fullname_service_map.seek(svc_name);
ServiceProperty* sp2 = _service_map.seek(svc_name);
if (((!!sp) != (!!sp2)) ||
(sp != NULL && sp->service != sp2->service)) {
LOG(ERROR) << "Impossible: _fullname_service and _service_map are"
" inconsistent before inserting " << svc_name;
RemoveService(service);
return -1;
}
RestfulMap* m = NULL;
if (sp == NULL) {
m = new RestfulMap(mappings[i].path.service_name);
} else {
m = sp->restful_map;
}
MethodProperty::OpaqueParams params;
params.is_tabbed = !!tabbed;
params.allow_default_url = svc_opt.allow_default_url;
params.allow_http_body_to_pb = svc_opt.allow_http_body_to_pb;
params.pb_bytes_to_base64 = svc_opt.pb_bytes_to_base64;
params.pb_single_repeated_to_array = svc_opt.pb_single_repeated_to_array;
if (!m->AddMethod(mappings[i].path, service, params,
mappings[i].method_name, mp->status)) {
LOG(ERROR) << "Fail to map `" << mappings[i].path << "' to `"
<< sd->full_name() << '.' << mappings[i].method_name
<< '\'';
if (sp == NULL) {
delete m;
}
RemoveService(service);
return -1;
}
if (mp->http_url == NULL) {
mp->http_url = new std::string(mappings[i].path.to_string());
} else {
if (!mp->http_url->empty()) {
mp->http_url->append(" @");
}
mp->http_url->append(mappings[i].path.to_string());
}
if (sp == NULL) {
ServiceProperty ss =
{ false, SERVER_DOESNT_OWN_SERVICE, NULL, m };
_fullname_service_map[svc_name] = ss;
_service_map[svc_name] = ss;
++_virtual_service_count;
}
}
}
if (tabbed) {
if (_tab_info_list == NULL) {
_tab_info_list = new TabInfoList;
}
const size_t last_size = _tab_info_list->size();
tabbed->GetTabInfo(_tab_info_list);
const size_t cur_size = _tab_info_list->size();
for (size_t i = last_size; i != cur_size; ++i) {
const TabInfo& info = (*_tab_info_list)[i];
if (!info.valid()) {
LOG(ERROR) << "Invalid TabInfo: path=" << info.path
<< " tab_name=" << info.tab_name;
_tab_info_list->resize(last_size);
RemoveService(service);
return -1;
}
}
}
return 0;
}