Files
workflow/tutorial/tutorial-15-name_service.cc
2025-05-08 15:16:10 +08:00

176 lines
3.7 KiB
C++

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <string>
#include "workflow/WFGlobal.h"
#include "workflow/WFNameService.h"
#include "workflow/WFTaskFactory.h"
#include "workflow/WFFacilities.h"
#include "workflow/HttpUtil.h"
// The example domonstrate the simplest user defined naming policy.
/* 'MyNSPolicy' is a naming policy, which use local file for naming.
* The format of naming file is similar to 'hosts' file, but we allow
* domain name and IP address as destination. For example:
*
* 127.0.0.1 localhost
* 127.0.0.1 mydomain # another alias for 127.0.0.1
* www.sogou.com sogou # sogou -> www.sogou.com
*/
class MyNSPolicy : public WFNSPolicy
{
public:
WFRouterTask *create_router_task(const struct WFNSParams *params,
router_callback_t callback) override;
private:
std::string path;
private:
std::string read_from_fp(FILE *fp, const char *name);
std::string parse_line(char *p, const char *name);
public:
MyNSPolicy(const char *naming_file) : path(naming_file) { }
};
std::string MyNSPolicy::parse_line(char *p, const char *name)
{
const char *dest = NULL;
char *start;
start = p;
while (*start != '\0' && *start != '#')
start++;
*start = '\0';
while (1)
{
while (isspace(*p))
p++;
start = p;
while (*p != '\0' && !isspace(*p))
p++;
if (start == p)
break;
if (*p != '\0')
*p++ = '\0';
if (dest == NULL)
{
dest = start;
continue;
}
if (strcasecmp(name, start) == 0)
return std::string(dest);
}
return std::string();
}
std::string MyNSPolicy::read_from_fp(FILE *fp, const char *name)
{
char *line = NULL;
size_t bufsize = 0;
std::string result;
while (getline(&line, &bufsize, fp) > 0)
{
result = this->parse_line(line, name);
if (result.size() > 0)
break;
}
free(line);
return result;
}
WFRouterTask *MyNSPolicy::create_router_task(const struct WFNSParams *params,
router_callback_t callback)
{
WFDnsResolver *dns_resolver = WFGlobal::get_dns_resolver();
if (params->uri.host)
{
FILE *fp = fopen(this->path.c_str(), "r");
if (fp)
{
std::string dest = this->read_from_fp(fp, params->uri.host);
if (dest.size() > 0)
{
/* Update the uri structure's 'host' field directly.
* You can also update the 'port' field if needed. */
free(params->uri.host);
params->uri.host = strdup(dest.c_str());
}
fclose(fp);
}
}
/* Simply, use the global dns resolver to create a router task. */
return dns_resolver->create_router_task(params, std::move(callback));
}
int main(int argc, char *argv[])
{
if (argc != 3)
{
fprintf(stderr, "USAGE: %s <http url> <naming file>\n", argv[0]);
exit(1);
}
ParsedURI uri;
URIParser::parse(argv[1], uri);
char *name = uri.host;
if (name == NULL)
{
fprintf(stderr, "Invalid http URI\n");
exit(1);
}
/* Create an naming policy. */
MyNSPolicy *policy = new MyNSPolicy(argv[2]);
/* Get the global name service object.*/
WFNameService *ns = WFGlobal::get_name_service();
/* Add the our name with policy to global name service.
* You can add mutilply names with one policy object. */
ns->add_policy(name, policy);
WFFacilities::WaitGroup wg(1);
WFHttpTask *task = WFTaskFactory::create_http_task(argv[1], 2, 3,
[&wg](WFHttpTask *task) {
int state = task->get_state();
int error = task->get_error();
if (state != WFT_STATE_SUCCESS)
{
fprintf(stderr, "error: %s\n",
WFGlobal::get_error_string(state, error));
}
else
{
auto *r = task->get_resp();
std::string body = protocol::HttpUtil::decode_chunked_body(r);
fwrite(body.c_str(), 1, body.size(), stdout);
}
wg.done();
});
task->start();
wg.wait();
/* clean up */
ns->del_policy(name);
delete policy;
return 0;
}