uhttpd add new rest mechanism. rest like.

This commit is contained in:
jbnadal
2018-05-22 14:50:36 +02:00
parent 81442524a3
commit 68c0d02078
10 changed files with 194 additions and 170 deletions

77
docs/api/test-rest-api.md Normal file
View File

@@ -0,0 +1,77 @@
# Test all the REST API of domo.
## Outlets
### list
```
curl -v http://localhost:34000/v1/outlets
```
```json
{
"id": "",
"outlets": [
{
"id": 1,
"name": "Bureau JB",
"sender": 12797322,
"speech_name": "bureau",
"state": true,
"switch": 0
},
{
"id": 2,
"name": "Salon",
"sender": 12797322,
"speech_name": "salon",
"state": false,
"switch": 1
},
{
"id": 3,
"name": "Sapin",
"sender": 12797322,
"speech_name": "sapin",
"state": false,
"switch": 2
}
],
"response_code": 200,
"status": "passed"
}
```
### read
```
curl -v http://localhost:34000/v1/outlets/2
```
```json
{
"id": 2,
"name": "Salon",
"sender": 12797322,
"speech_name": "salon",
"state": false,
"switch": 1
}
```
### create
```
curl -X POST -v http://localhost:34000/v1/outlets -d "{\"name\":\"test\", \"sender\": 12797322, \"switch\": 4}"
```
### update
```
curl -X PUT -v http://localhost:34000/v1/outlets/4 -d "{\"name\":\"test1\", \"sender\": 1, \"switch\": 2}"
```
### delete
```
curl -v http://localhost:34000/v1/outlets/2
```

View File

@@ -52,15 +52,12 @@ HttpParameter::HttpParameter(void) : m_request_timeout(-1)
* *
* @brief parse parameters from the uri. * @brief parse parameters from the uri.
*/ */
std::string HttpParameter::parse_from_uri(const std::string &a_parameters) void HttpParameter::parse_from_uri(struct json_object *a_root_node, const std::string &a_parameters)
{ {
std::string the_result;
std::vector<std::string> the_list; std::vector<std::string> the_list;
std::vector<std::string>::iterator the_it; std::vector<std::string>::iterator the_it;
std::string the_key, the_value, the_value_decoded; std::string the_key, the_value, the_value_decoded;
std::size_t the_pos; std::size_t the_pos;
struct json_object *the_root_node = NULL;
the_root_node = json_object_new_object();
the_list = split_params(a_parameters, '&'); the_list = split_params(a_parameters, '&');
for (the_it = the_list.begin(); the_it != the_list.end(); ++the_it) for (the_it = the_list.begin(); the_it != the_list.end(); ++the_it)
@@ -79,15 +76,30 @@ std::string HttpParameter::parse_from_uri(const std::string &a_parameters)
printf(" - valuedecode = %s\n", the_value_decoded.c_str()); printf(" - valuedecode = %s\n", the_value_decoded.c_str());
#endif #endif
if (is_number(the_value_decoded)) if (is_number(the_value_decoded))
json_object_object_add(the_root_node, the_key.c_str(), json_object_new_int(std::stoi(the_value_decoded))); json_object_object_add(a_root_node, the_key.c_str(), json_object_new_int(std::stoi(the_value_decoded)));
else if (the_value_decoded == "true") else if (the_value_decoded == "true")
json_object_object_add(the_root_node, the_key.c_str(), json_object_new_boolean(true)); json_object_object_add(a_root_node, the_key.c_str(), json_object_new_boolean(true));
else if (the_value_decoded == "false") else if (the_value_decoded == "false")
json_object_object_add(the_root_node, the_key.c_str(), json_object_new_boolean(false)); json_object_object_add(a_root_node, the_key.c_str(), json_object_new_boolean(false));
else else
json_object_object_add(the_root_node, the_key.c_str(), json_object_new_string(the_value_decoded.c_str())); json_object_object_add(a_root_node, the_key.c_str(), json_object_new_string(the_value_decoded.c_str()));
} }
} }
}
/*! ----------------------------------------------------------------------------
* @fn parse_from_uri
*
* @brief parse parameters from the uri.
*/
std::string HttpParameter::parse_from_uri(const std::string &a_parameters)
{
std::string the_result;
struct json_object *the_root_node;
the_root_node = json_object_new_object();
parse_from_uri(the_root_node, a_parameters);
the_result = json_object_to_json_string(the_root_node); the_result = json_object_to_json_string(the_root_node);

View File

@@ -33,6 +33,7 @@
/*----------------------------- Dependencies --------------------------------*/ /*----------------------------- Dependencies --------------------------------*/
struct client; struct client;
struct json_object;
/*--------------------------------- CLASS ----------------------------------*/ /*--------------------------------- CLASS ----------------------------------*/
@@ -41,6 +42,7 @@ class HttpParameter
public: public:
HttpParameter(void); HttpParameter(void);
void parse_from_uri(struct json_object *a_root_node, const std::string &a_parameters);
std::string parse_from_uri(const std::string &a_parameters); std::string parse_from_uri(const std::string &a_parameters);
bool parse(client *a_client); bool parse(client *a_client);

View File

@@ -47,7 +47,7 @@ class WebConnection
WebConnection(struct uhttpd_ops *an_ops, struct client *a_client); WebConnection(struct uhttpd_ops *an_ops, struct client *a_client);
virtual ~WebConnection(void); virtual ~WebConnection(void);
int add_data(const char *a_data, int a_len); virtual int add_data(const char *a_data, int a_len);
struct client *get_client(void); struct client *get_client(void);
virtual void invoke(struct ubus_context *a_ctx); virtual void invoke(struct ubus_context *a_ctx);

View File

@@ -51,10 +51,11 @@ extern "C" {
RestConnection::RestConnection(struct uhttpd_ops *an_ops, struct client *a_client, RestController *a_controller, const std::string &a_parameters) : WebConnection(an_ops, a_client), RestConnection::RestConnection(struct uhttpd_ops *an_ops, struct client *a_client, RestController *a_controller, const std::string &a_parameters) : WebConnection(an_ops, a_client),
m_controller(a_controller), m_ctx(0) m_controller(a_controller), m_ctx(0)
{ {
m_param_node = json_object_new_object();
if (!a_parameters.empty()) if (!a_parameters.empty())
{ {
HttpParameter the_http_parm; HttpParameter the_http_parm;
m_connection_data = the_http_parm.parse_from_uri(a_parameters); the_http_parm.parse_from_uri(m_param_node, a_parameters);
} }
// printf("RestConnection:: constructor....\n"); // printf("RestConnection:: constructor....\n");
} }
@@ -66,6 +67,7 @@ RestConnection::RestConnection(struct uhttpd_ops *an_ops, struct client *a_clien
*/ */
RestConnection::~RestConnection(void) RestConnection::~RestConnection(void)
{ {
json_object_put(m_param_node);
abort(m_ctx); abort(m_ctx);
} }
@@ -96,23 +98,17 @@ void RestConnection::invoke(struct ubus_context *a_ctx)
m_ctx = a_ctx; m_ctx = a_ctx;
printf("RestConnection::invoke(object: %s)....\n", m_controller->get_path().c_str()); // printf("RestConnection::invoke(object: %s)....\n", m_controller->get_path().c_str());
if (!ubus_lookup_id(a_ctx, m_controller->get_ubus().get_path().c_str(), &the_id)) if (!ubus_lookup_id(a_ctx, m_controller->get_ubus().get_path().c_str(), &the_id))
{ {
json_object *the_parameter_doc; #if 0
printf("RestConnection::invoke method: (%s)- param:(%s)....\n",
the_parameter_doc = json_tokener_parse(m_connection_data.c_str()); m_controller->get_ubus().get_method().c_str(),
json_object_to_json_string(m_param_node));
if (the_parameter_doc == 0) #endif
the_parameter_doc = parse_form_encoded_data();
// printf("RestConnection::invoke launch async call (%d)....\n", the_id);
printf("RestConnection::invoke - param:(%s)....\n", json_object_to_json_string(the_parameter_doc));
the_cmd.exec_async(a_ctx, the_id, m_controller->get_ubus().get_method().c_str(), the_cmd.exec_async(a_ctx, the_id, m_controller->get_ubus().get_method().c_str(),
json_object_to_json_string(the_parameter_doc), this); json_object_to_json_string(m_param_node), this);
json_object_put(the_parameter_doc);
} }
else else
{ {
@@ -120,21 +116,73 @@ void RestConnection::invoke(struct ubus_context *a_ctx)
} }
} }
/*! ----------------------------------------------------------------------------
* @fn add_data
*
* @brief add data to the connection parameter.
*/
int RestConnection::add_data(const char *a_data, int a_len)
{
struct json_object *the_root_node;
the_root_node = json_tokener_parse(a_data);
// the data is not a json object.
if (the_root_node == NULL)
{
char *the_parameters = strdup(a_data);
const char *the_encoded_name;
const char *the_encoded_value;
for (the_encoded_name = strtok(the_parameters, "="); the_encoded_name != NULL; the_encoded_name = strtok(NULL, "="))
{
std::string the_name, the_value;
the_encoded_value = strtok(NULL, "&");
if (the_encoded_value == NULL)
the_encoded_value = "";
the_name = UriTransform::decode(std::string(the_encoded_name));
the_value = UriTransform::decode(std::string(the_encoded_value));
json_object_object_add(m_param_node, the_name.c_str(), json_object_new_string(the_value.c_str()));
}
free(the_parameters);
}
else // C'est un document json.
{
struct json_object_iterator the_it;
struct json_object_iterator the_it_end;
the_it = json_object_iter_begin(the_root_node);
the_it_end = json_object_iter_end(the_root_node);
while (!json_object_iter_equal(&the_it, &the_it_end))
{
json_object_object_add(m_param_node, json_object_iter_peek_name(&the_it),
json_object_iter_peek_value(&the_it));
json_object_iter_next(&the_it);
}
}
return a_len;
}
/*! ---------------------------------------------------------------------------- /*! ----------------------------------------------------------------------------
* @fn parse_form_encoded_data * @fn parse_form_encoded_data
* *
* @brief Parse the document content as form encoded parameters * @brief Parse the document content as form encoded parameters
* and return them as a json object with an attribute for each parameter. * and return them as a json object with an attribute for each parameter.
*/ */
json_object *RestConnection::parse_form_encoded_data() bool RestConnection::parse_form_encoded_data(json_object *a_root_document)
{ {
json_object *the_parsed_document; // removed !!!!!
char *the_parameters = strdup(m_connection_data.c_str()); char *the_parameters = strdup(m_connection_data.c_str());
const char *the_encoded_name; const char *the_encoded_name;
const char *the_encoded_value; const char *the_encoded_value;
the_parsed_document = json_object_new_object();
for (the_encoded_name = strtok(the_parameters, "="); the_encoded_name != NULL; the_encoded_name = strtok(NULL, "=")) for (the_encoded_name = strtok(the_parameters, "="); the_encoded_name != NULL; the_encoded_name = strtok(NULL, "="))
{ {
std::string the_name, the_value; std::string the_name, the_value;
@@ -147,12 +195,12 @@ json_object *RestConnection::parse_form_encoded_data()
the_name = UriTransform::decode(std::string(the_encoded_name)); the_name = UriTransform::decode(std::string(the_encoded_name));
the_value = UriTransform::decode(std::string(the_encoded_value)); the_value = UriTransform::decode(std::string(the_encoded_value));
json_object_object_add(the_parsed_document, the_name.c_str(), json_object_new_string(the_value.c_str())); json_object_object_add(a_root_document, the_name.c_str(), json_object_new_string(the_value.c_str()));
} }
free(the_parameters); free(the_parameters);
return the_parsed_document; return true;
} }
/*! ---------------------------------------------------------------------------- /*! ----------------------------------------------------------------------------

View File

@@ -49,11 +49,14 @@ class RestConnection : public WebConnection, public UBusExecReceiver
void invoke(struct ubus_context *a_ctx); void invoke(struct ubus_context *a_ctx);
int add_data(const char *a_data, int a_len);
private: private:
RestController *m_controller; RestController *m_controller;
struct ubus_context *m_ctx; struct ubus_context *m_ctx;
struct json_object *m_param_node;
json_object *parse_form_encoded_data(void); bool parse_form_encoded_data(json_object *a_root_document);
void send_response(int a_result_code, std::string a_content_document = ""); void send_response(int a_result_code, std::string a_content_document = "");
}; };

View File

@@ -46,9 +46,10 @@
* *
* @brief constructor of the rest controller object. * @brief constructor of the rest controller object.
*/ */
RestController::RestController(void) : m_parameter_position(std::string::npos) RestController::RestController(void) :
mf_raw_response(true),
m_parameter_position(std::string::npos)
{ {
printf("constructor empty.\n");
} }
/*! ---------------------------------------------------------------------------- /*! ----------------------------------------------------------------------------
@@ -58,12 +59,6 @@ RestController::RestController(void) : m_parameter_position(std::string::npos)
*/ */
RestController::RestController(const std::string &a_path, const std::string &a_method_get, const std::string &a_method_put, int a_timeout, bool a_raw_response) : WebController(a_path), RestController::RestController(const std::string &a_path, const std::string &a_method_get, const std::string &a_method_put, int a_timeout, bool a_raw_response) : WebController(a_path),
mf_raw_response(false) mf_raw_response(false)
#if 0
m_method_get(a_method_get),
m_method_put(a_method_put),
m_timeout(a_timeout),
mf_raw_response(a_raw_response)
#endif
{ {
} }
@@ -146,7 +141,7 @@ bool RestController::find(uint8_t a_method, const std::string &an_url)
std::vector<std::string>::iterator the_it; std::vector<std::string>::iterator the_it;
std::size_t the_pos; std::size_t the_pos;
printf("find : url: %s, endpoint:%s method: %d ubus:%s\n", an_url.c_str(), m_endpoint.c_str(), a_method, get_ubus().get_method().c_str()); // printf("find : url: %s, endpoint:%s method: %d ubus:%s\n", an_url.c_str(), m_endpoint.c_str(), a_method, get_ubus().get_method().c_str());
if ((m_endpoint == an_url) && (m_method == a_method)) if ((m_endpoint == an_url) && (m_method == a_method))
{ {
return true; return true;
@@ -167,8 +162,6 @@ bool RestController::find(uint8_t a_method, const std::string &an_url)
the_endpoint.erase(the_pos); the_endpoint.erase(the_pos);
} }
printf("endpoint: %s\n", the_endpoint.c_str());
the_pos = an_url.find(the_endpoint); the_pos = an_url.find(the_endpoint);
if (the_pos == std::string::npos) if (the_pos == std::string::npos)
{ {
@@ -177,11 +170,10 @@ bool RestController::find(uint8_t a_method, const std::string &an_url)
if (m_method != a_method) if (m_method != a_method)
{ {
printf ("pas la bonne methode.(%d) != (%d)\n", m_method, a_method); // printf ("pas la bonne methode.(%d) != (%d)\n", m_method, a_method);
return false; return false;
} }
printf("!!!!!! c'est bon. c'est le meme.\n");
return true; return true;
} }
@@ -199,13 +191,16 @@ std::string RestController::get_parameter(const std::string &an_uri)
if (m_parameter_position != std::string::npos) if (m_parameter_position != std::string::npos)
{ {
the_tmp = an_uri.substr(m_parameter_position); the_tmp = an_uri.substr(m_parameter_position);
printf("uri: %s, tmp: %s pos: %d\n", an_uri.c_str(), the_tmp.c_str(), (int)m_parameter_position); // printf("uri: %s, tmp: %s pos: %d\n", an_uri.c_str(), the_tmp.c_str(), (int)m_parameter_position);
} }
the_pos = the_tmp.find_first_of("/"); the_pos = the_tmp.find_first_of("/");
if (the_pos != std::string::npos) if (the_pos != std::string::npos)
{ {
the_tmp = the_tmp.substr(0, the_pos); the_tmp = the_tmp.substr(0, the_pos);
}
if (!the_tmp.empty()) {
the_parameter = fmt::format("{}={}", m_parameter_name, the_tmp); the_parameter = fmt::format("{}={}", m_parameter_name, the_tmp);
} }
@@ -279,86 +274,5 @@ void RestController::manage_endpoint(void)
m_controller = m_parameter_name.substr(the_pos + 1); m_controller = m_parameter_name.substr(the_pos + 1);
m_parameter_name = m_parameter_name.substr(0, the_pos); m_parameter_name = m_parameter_name.substr(0, the_pos);
} }
printf ("endpoint: %s pos: %d ubus: %s\n", m_endpoint.c_str(), (int)m_parameter_position, get_ubus().get_method().c_str()); // printf ("endpoint: %s pos: %d ubus: %s\n", m_endpoint.c_str(), (int)m_parameter_position, get_ubus().get_method().c_str());
} }
#if 0
std::string
// Remove the last char if it's a /
the_pos = the_endpoint.find_last_of("/");
if(the_pos != std::string::npos)
{
the_endpoint.erase(the_pos);
}
printf("endpoint: %s\n", the_endpoint.c_str());
the_pos = an_url.find(the_endpoint);
if (the_pos == std::string::npos)
{
return false;
}
printf("c'est bon. c'est le meme.\n");
return true;
#endif
#if 0
/*! ----------------------------------------------------------------------------
* @fn get_method_get
*
* @brief return the get method of the controller.
*/
std::string RestController::get_method_get(void)
{
return ""; //m_method_get;
}
/*! ----------------------------------------------------------------------------
* @fn get_method_put
*
* @brief return the put method of the controller.
*/
std::string RestController::get_method_put(void)
{
return ""; //m_method_put;
}
/*! ----------------------------------------------------------------------------
* @fn get_timeout
*
* @brief return the wanted timeout of the controller.
*/
uint16_t RestController::get_timeout(void)
{
return 0; //m_timeout;
}
/*! ----------------------------------------------------------------------------
* @fn get_method
*
* @brief return the right method in function of the client connection.
*/
std::string RestController::get_method(uint8_t a_method)
{
#if 0
switch (a_method)
{
case UH_HTTP_MSG_GET:
return m_method_get;
break;
case UH_HTTP_MSG_PUT:
case UH_HTTP_MSG_POST:
return m_method_put;
break;
default:
// TODO ERROR
break;
};
#endif
return "";
}
#endif

View File

@@ -49,7 +49,7 @@ class RestController : public WebController
public: public:
RestController(void); RestController(void);
RestController(const std::string &a_path, const std::string &a_method_get = "", const std::string &a_method_put = "", int a_timeout = kDefaultTimeout, bool a_raw_response = false); RestController(const std::string &a_path, const std::string &a_method_get = "", const std::string &a_method_put = "", int a_timeout = kDefaultTimeout, bool a_raw_response = false);
virtual ~RestController(void); ~RestController(void);
WebConnection *new_connection(struct uhttpd_ops *an_ops, struct client *a_client, const std::string &a_parameters); WebConnection *new_connection(struct uhttpd_ops *an_ops, struct client *a_client, const std::string &a_parameters);
@@ -65,33 +65,17 @@ class RestController : public WebController
bool is_raw_response(void); bool is_raw_response(void);
/*
std::string get_method_get(void);
std::string get_method_put(void);
uint16_t get_timeout(void);
std::string get_method(uint8_t a_method);
*/
protected: protected:
uint8_t m_method; uint8_t m_method;
std::string m_endpoint; std::string m_endpoint;
Ubus m_ubus; Ubus m_ubus;
bool mf_raw_response; bool mf_raw_response;
/* private:
std::string m_name;
std::string m_path;
std::string m_method_get;
std::string m_method_put;
uint16_t m_timeout;
*/
private:
void manage_endpoint(void); void manage_endpoint(void);
std::string m_parameter_name; std::string m_parameter_name;
std::size_t m_parameter_position; std::size_t m_parameter_position;
std::string m_controller; std::string m_controller;
}; };
#endif /* _REST_CONTROLLER_H */ #endif /* _REST_CONTROLLER_H */

View File

@@ -283,7 +283,7 @@ struct ubus_context *UhttpServer::get_context(void)
bool UhttpServer::check_url(const std::string &an_url) bool UhttpServer::check_url(const std::string &an_url)
{ {
ControllerIterator the_it; ControllerIterator the_it;
printf("UhttpServer::check_url: %s\n", an_url.c_str()); // printf("UhttpServer::check_url: %s\n", an_url.c_str());
for (the_it = m_controllers.begin(); the_it != m_controllers.end(); ++the_it) for (the_it = m_controllers.begin(); the_it != m_controllers.end(); ++the_it)
{ {
@@ -306,7 +306,7 @@ void UhttpServer::handle_request(struct client *a_cl, const std::string &an_url,
std::string the_url, the_parameters; std::string the_url, the_parameters;
std::size_t the_pos; std::size_t the_pos;
WebController *the_controller; WebController *the_controller;
printf("uhttp_server_handle_request : url: <%s> client: %p\n", an_url.c_str(), a_cl); // printf("uhttp_server_handle_request : url: <%s> client: %p\n", an_url.c_str(), a_cl);
// Check if parameters are present on the url. // Check if parameters are present on the url.
the_pos = an_url.find_first_of("?", 0); the_pos = an_url.find_first_of("?", 0);
@@ -325,7 +325,6 @@ void UhttpServer::handle_request(struct client *a_cl, const std::string &an_url,
the_controller = get_controller(a_cl->request.method, the_url); the_controller = get_controller(a_cl->request.method, the_url);
if (the_controller == NULL) if (the_controller == NULL)
{ {
printf ("not found :( \n");
std::string the_msg; std::string the_msg;
the_msg = fmt::format(kJsonControlerNotFound, the_url); the_msg = fmt::format(kJsonControlerNotFound, the_url);
send_error(a_cl, 404, "Not Found", the_msg); send_error(a_cl, 404, "Not Found", the_msg);
@@ -333,7 +332,7 @@ void UhttpServer::handle_request(struct client *a_cl, const std::string &an_url,
} }
the_parameters = the_controller->get_parameter(the_url); the_parameters = the_controller->get_parameter(the_url);
printf ("the parameter: %s\n", the_parameters.c_str()); // printf ("the parameter: %s\n", the_parameters.c_str());
// We found the controller. // We found the controller.
// printf("method: %d\n", a_cl->request.method); // printf("method: %d\n", a_cl->request.method);
struct dispatch *d = &a_cl->dispatch; struct dispatch *d = &a_cl->dispatch;
@@ -362,7 +361,7 @@ void UhttpServer::handle_request(struct client *a_cl, const std::string &an_url,
send_error(a_cl, 400, "Bad Request", "Invalid Request"); send_error(a_cl, 400, "Bad Request", "Invalid Request");
} }
printf("uhttp_server_handle_request -done.\n"); // printf("uhttp_server_handle_request -done.\n");
} }
/*! ---------------------------------------------------------------------------- /*! ----------------------------------------------------------------------------
@@ -425,6 +424,12 @@ int UhttpServer::load_config_dir(const char *a_config_dir_path)
struct dirent *the_dir_ent = NULL; struct dirent *the_dir_ent = NULL;
std::string the_path; std::string the_path;
struct json_object *the_root_node; struct json_object *the_root_node;
if (a_config_dir_path == NULL) {
fprintf (stderr, "ERROR: a config dir is mandatory.\n");
return -1;
}
the_rep = opendir(a_config_dir_path); the_rep = opendir(a_config_dir_path);
if (the_rep == NULL) { if (the_rep == NULL) {
fprintf (stderr, "Impossible to open the config directory (%s).\n", a_config_dir_path); fprintf (stderr, "Impossible to open the config directory (%s).\n", a_config_dir_path);
@@ -661,32 +666,12 @@ struct json_object *UhttpServer::load(const std::string &a_file)
int UhttpServer::add_controller(ControllerKey a_key, WebController *a_controller) int UhttpServer::add_controller(ControllerKey a_key, WebController *a_controller)
{ {
std::string the_path; std::string the_path;
std::size_t the_pos;
if (a_controller == NULL) if (a_controller == NULL)
return -1; return -1;
// a_controller->set_name(an_uri);
m_controllers[a_key] = std::unique_ptr<WebController>(a_controller); m_controllers[a_key] = std::unique_ptr<WebController>(a_controller);
//m_controller_array[an_uri] = a_controller;
#if 0
printf("add: an_uri:%s\n", an_uri.c_str());
// Keep a list of the API root. to check if a controller is managed by this plugin or not.
the_pos = an_uri.find_first_of("/", 1);
if (the_pos != std::string::npos)
{
the_path = an_uri.substr(0, the_pos + 1);
if (std::find(m_path_list.begin(), m_path_list.end(), the_path) == m_path_list.end())
{
// uniq version. we add it.
m_path_list.push_back(the_path);
}
}
#endif
return 0; return 0;
} }
@@ -704,7 +689,6 @@ WebController *UhttpServer::get_controller(uint8_t a_method, const std::string &
{ {
if (the_it->second.get()->find(a_method, an_url)) if (the_it->second.get()->find(a_method, an_url))
{ {
printf ("return the controller.\n");
return the_it->second.get(); return the_it->second.get();
} }
} }