int Server::AddServiceInternal()

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;
}