-
-
Notifications
You must be signed in to change notification settings - Fork 80
/
Copy pathNPLMsgIn_parser.h
233 lines (197 loc) · 6.61 KB
/
NPLMsgIn_parser.h
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
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
#pragma once
#include "NPLMsgIn.h"
#include <boost/logic/tribool.hpp>
#include <boost/tuple/tuple.hpp>
namespace NPL
{
struct NPLMsgIn;
/** Parser for incoming requests.
---++ NPL Message Format
ALL NPL messages are created to loosely fit the HTTP message structure that the standard calls the generic message format.
I say loosely fit, because NPL messages are made as compact as possible, such that the minimum overhead for
sending a message is less than 15 bytes, which is negligible compared to the TCP/IP overhead.
*quick sample*
<verbatim>
A (g1)script/hello.lua NPL/1.0
rts:r1
User-Agent:NPL
16:{"hello world!"}
</verbatim>
*a concise message*
<verbatim>
A (g1)12
16:{"hello world!"}
</verbatim>
The NPL generic message format is as follows:
<verbatim>
<start-line>
[<message-headers>]
<empty-line>
[<message-body>]
</verbatim>
You can see that this is pretty much the same as the format used for e-mail messages and for Usenet newsgroup messages too: headers, an empty line and then a message body.
All text lines are terminated with the "\n" CRLF control character (NOT the standard "\r\n").
the empty line contains just those two characters and nothing else. The headers are always sent as regular text;
the body, however, may be either text or 8-bit binary information, depending on the nature of the data to be sent.
---+++ start line
The start line is a special text within the first line of that message that conveys the nature of the message.
It consists of the method to be applied to the NPL file, the identifier of the NPL file, and the protocol version in use.
<verbatim>
Method SP NPLFileName SP NPL-Version CRLF
</verbatim>
See some examples below
<verbatim>
-- ACTivate a given file in a given runtime state on the target machine using NPL protocol 1.0.
ACT (runtime_name)script/hello.lua NPL/1.0
-- this is a shortcut to above, where A means activate method; The file name is replaced by its Numeric identifier(N), which is dynamically known by both the sender and receiver; the NPL version is omitted.
A (g1)12
-- This is like HTTP, but is NOT supported in current version.
GET script/hello.lua NPL/1.0
</verbatim>
---++++ ACT method
The ACT method activates a given file in a given runtime state on the target machine.
---+++ message-headers
Message-headers are just name value pairs and one pair on each line. There are many meaningful names that the NPL used to interprete the message.
For example, we can specify the message length and the type.
<verbatim>
From:r1
Length:100
</verbatim>
In most cases, message-length are omitted. And the message-body length is determined by parsing the message-body as NPL table string.
---+++ message-body
the message-body contains the length (number of bytes after ":" or ">") and the binary or text data.
If the character after number of bytes is ":", the message body is a non-compressed plain-text message
If the character after number of bytes is ">", the message body is a compressed message and NPLCodec should be used to decode the message body.
Non-compressed:
<verbatim>
msg_length:{name=value, value, value, {name=value, value}}
</verbatim>
Compressed:
<verbatim>
msg_length>XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX}
</verbatim>
Some examples are "0:", "16:{\"hello world!\"}"
---+++ Example messages
---++++ Example 1: a simple hello world
<verbatim>
NPL.activate("(r1)script/hello.lua", {"hello world!"});
-- Output Message with NPL identifier
A (r1)12 NPL/1.0
16:{"hello world!"}
-- Output Message without preinstalled NPL identifier
A (r1)script/hello.lua
16:{"hello world!"}
</verbatim>
---++++ Example 2: a hello world with user-defined headers
<verbatim>
NPL.activate("(g1)script/hello.lua", {header={rts="r1",User-Agent="NPL"}, "hello world!"});
-- Output Message
A (g1)script/hello.lua NPL/1.0
rts:r1
User-Agent:NPL
16:{"hello world!"}
</verbatim>
*/
class NPLMsgIn_parser
{
public:
enum Consume_Result
{
c_res_indeterminate,
c_res_code_body,
c_res_true,
c_res_false,
};
/// Construct ready to parse the NPLMsgIn method.
NPLMsgIn_parser();
/// Reset to initial parser state.
void reset();
/// Parse some data. The tribool return value is true when a complete NPLMsgIn
/// has been parsed, false if the data is invalid, indeterminate when more
/// data is required. The InputIterator return value indicates how much of the
/// input has been consumed.
template <typename InputIterator>
boost::tuple<boost::tribool, InputIterator> parse(NPLMsgIn& req,
InputIterator begin, InputIterator end)
{
Consume_Result result = c_res_indeterminate;
while (begin != end && (result == c_res_indeterminate))
{
result = consume(req, *begin++);
}
if(begin != end && result == c_res_code_body)
{
// we can read to end or length instead of doing the lexical work one char at a time.
int nOldSize = req.m_code.size();
int nByteCount = end-begin;
if(req.m_nLength < (nOldSize+nByteCount))
{
nByteCount = req.m_nLength - nOldSize;
}
req.m_code.resize(nOldSize+nByteCount);
memcpy(&(req.m_code[nOldSize]), begin, nByteCount);
begin = begin + nByteCount;
if (req.m_nLength ==(int)req.m_code.size())
{
result = c_res_true;
}
}
if(result == c_res_true)
{
if(m_bCompressed)
Decompress(req);
reset();
boost::tribool result_ = true;
return boost::make_tuple(result_, begin);
}
else if(result == c_res_false)
{
boost::tribool result_ = false;
return boost::make_tuple(result_, begin);
}
else
{
boost::tribool result_ = boost::indeterminate;
return boost::make_tuple(result_, begin);
}
}
private:
/// Handle the next character of input.
Consume_Result consume(NPLMsgIn& req, char input);
/** decompress the message body*/
void Decompress(NPLMsgIn& req);
/** Decode the message body*/
void Decode(NPLMsgIn& req);
/// Check if a byte is an HTTP character.
static bool is_char(int c);
/// Check if a byte is an HTTP control character.
static bool is_ctl(int c);
/// Check if a byte is defined as an HTTP tspecial character.
static bool is_tspecial(int c);
/// Check if a byte is a digit.
static bool is_digit(int c);
/// The current state of the parser.
enum state
{
method_start,
method,
uri,
uri_rts_name,
npl_version_n,
npl_version_p,
npl_version_l,
npl_version_slash,
npl_version_major,
npl_version_minor,
header_line_start,
header_lws,
header_name,
header_value,
code_length,
code_body,
uri_http,
} state_;
/** whether the message body is compressed. */
bool m_bCompressed;
};
}