aboutsummaryrefslogtreecommitdiffstats
path: root/messagebus/src/vespa/messagebus/routing/routeparser.cpp
blob: 52fd189cbeda99f075f0038fac325af98a5da24f (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#include "errordirective.h"
#include "policydirective.h"
#include "routedirective.h"
#include "routeparser.h"
#include "tcpdirective.h"
#include "verbatimdirective.h"
#include <vespa/vespalib/util/stringfmt.h>

using vespalib::stringref;

namespace mbus {

bool
RouteParser::isWhitespace(char c)
{
    return c == ' ' || c == '\f' || c == '\n' || c == '\r' || c == '\t';
}

IHopDirective::SP
RouteParser::createRouteDirective(stringref str)
{
    return IHopDirective::SP(new RouteDirective(str));
}

IHopDirective::SP
RouteParser::createTcpDirective(stringref str)
{
    size_t posP = str.find(":");
    if (posP == string::npos || posP == 0) {
        return IHopDirective::SP(); // no host
    }
    size_t posS = str.find("/", posP);
    if (posS == string::npos || posS == posP + 1) {
        return IHopDirective::SP(); // no port
    }
    // FIXME C++17 range-safe from_chars() instead of atoi()
    return IHopDirective::SP(new TcpDirective(str.substr(0, posP),
                                              atoi(str.substr(posP + 1, posS - 1).data()),
                                              str.substr(posS + 1)));
}

IHopDirective::SP
RouteParser::createPolicyDirective(stringref str)
{
    size_t pos = str.find(":");
    if (pos == string::npos) {
        return IHopDirective::SP(new PolicyDirective(str, ""));
    }
    return IHopDirective::SP(new PolicyDirective(str.substr(0, pos), str.substr(pos + 1)));
}

IHopDirective::SP
RouteParser::createVerbatimDirective(stringref str)
{
    return IHopDirective::SP(new VerbatimDirective(str));
}

IHopDirective::SP
RouteParser::createErrorDirective(stringref str)
{
    return IHopDirective::SP(new ErrorDirective(str));
}

IHopDirective::SP
RouteParser::createDirective(stringref str)
{
    if (str.size() > 2 && str[0] == '[') {
        return createPolicyDirective(str.substr(1, str.size() - 2));
    }
    return createVerbatimDirective(str);
}

Hop
RouteParser::createHop(stringref str)
{
    if (str.empty()) {
        return Hop().addDirective(createErrorDirective("Failed to parse empty string."));
    }
    size_t len = str.size();
    if (len > 1 && str[0] == '?') {
        Hop hop = createHop(str.substr(1));
        hop.setIgnoreResult(true);
        return hop;
    }
    if (len > 4 && str.substr(0, 4) == "tcp/") {
        IHopDirective::SP tcp = createTcpDirective(str.substr(4));
        if (tcp) {
            return Hop().addDirective(std::move(tcp));
        }
    }
    if (len > 6 && str.substr(0, 6) == "route:") {
        return Hop().addDirective(createRouteDirective(str.substr(6)));
    }
    Hop ret;
    for (size_t from = 0, at = 0, depth = 0; at <= len; ++at) {
        if (at == len || (depth == 0 && str[at] == '/')) {
            if (depth > 0) {
                return Hop().addDirective(createErrorDirective(
                                "Unexpected token '': syntax error"));
            }
            ret.addDirective(createDirective(str.substr(from, at - from)));
            from = at + 1;
        } else if (isWhitespace(str[at]) && depth == 0) {
            return Hop().addDirective(createErrorDirective(
                            vespalib::make_string(
                                    "Failed to completely parse '%s'.",
                                    vespalib::string(str).c_str())));
        } else if (str[at] == '[') {
            ++depth;
        } else if (str[at] == ']') {
            if (depth == 0) {
                return Hop().addDirective(createErrorDirective(
                                "Unexpected token ']': syntax error"));
            }
            --depth;
        }
    }
    return ret;
}

Route
RouteParser::createRoute(stringref str)
{
    Route ret;
    for (size_t from = 0, at = 0, depth = 0; at <= str.size(); ++at) {
        if (at == str.size() || (depth == 0 && isWhitespace(str[at]))) {
            if (from < at - 1) {
                Hop hop = createHop(str.substr(from, at - from));
                if (hop.hasDirectives() &&
                    hop.getDirective(0).getType() == IHopDirective::TYPE_ERROR)
                {
                    return std::move(Route().addHop(std::move(hop)));
                }
                ret.addHop(std::move(hop));
            }
            from = at + 1;
        } else if (str[at] == '[') {
            ++depth;
        } else if (str[at] == ']') {
            --depth;
        }
    }
    return ret;
}

} // mbus