_site/cover/mcd_protocol.COVER.html

1 %% Copyright (c) 2022 Peter Morgan <peter.james.morgan@gmail.com>
2 %%
3 %% Licensed under the Apache License, Version 2.0 (the "License");
4 %% you may not use this file except in compliance with the License.
5 %% You may obtain a copy of the License at
6 %%
7 %% http://www.apache.org/licenses/LICENSE-2.0
8 %%
9 %% Unless required by applicable law or agreed to in writing, software
10 %% distributed under the License is distributed on an "AS IS" BASIS,
11 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 %% See the License for the specific language governing permissions and
13 %% limitations under the License.
14
15
16 -module(mcd_protocol).
17
18
19 -export([decode/1]).
20 -export([encode/1]).
21 -export([reply_expected/1]).
22 -import(mcd_util, [split/1]).
23 -include("mcd.hrl").
24 -include_lib("kernel/include/logger.hrl").
25
26
27 -callback init([]) -> {ok, callback_data()}.
28
29 -type callback_data() :: any().
30
31 -callback recv(recv_request()) -> recv_response().
32
33 -type recv_request() :: #{message := mcd:protocol(),
34 data := callback_data()}.
35
36
37 -type recv_response() :: {continue, continue_response()}
38 | {continue, [continue_response()]}
39 | {stop, any()}
40 | stop.
41
42 -type continue_response() :: {encode, mcd:protocol()}
43 | {expire, #{key := binary(), seconds := integer()}}.
44
45 -callback expire(expire_request()) -> expire_response().
46
47 -type expire_request() :: #{key := binary(), data := callback_data()}.
48
49 -type expire_response() :: ok
50 | {stop, any()}
51 | stop.
52
53 -callback flush_all(flush_all_request()) -> flush_all_response().
54
55 -type flush_all_request() :: #{data := callback_data()}.
56
57 -type flush_all_response() :: ok
58 | {stop, any()}
59 | stop.
60
61 -spec decode(binary()) -> {mcd:protocol(), binary()} | partial | {error, atom()}.
62
63 decode(<<"STAT ", Remainder/bytes>>) ->
64 3 mcd_protocol_text:decode(stat, Remainder);
65
66 decode(<<"stats", Remainder/bytes>>) ->
67 2 mcd_protocol_text:decode(stats, Remainder);
68
69 decode(<<"stat", Remainder/bytes>>) ->
70
:-(
mcd_protocol_text:decode(stat, Remainder);
71
72 decode(<<"quit", Remainder/bytes>>) ->
73 2 mcd_protocol_text:decode(quit, Remainder);
74
75 decode(<<"flush_all", Remainder/bytes>>) ->
76 8 mcd_protocol_text:decode(flush_all, Remainder);
77
78 decode(<<"verbosity ", Remainder/bytes>>) ->
79 4 mcd_protocol_text:decode(verbosity, Remainder);
80
81 decode(<<"set ", Remainder/bytes>>) ->
82 41 mcd_protocol_text:decode(set, Remainder);
83
84 decode(<<"cas ", Remainder/bytes>>) ->
85 9 mcd_protocol_text:decode(cas, Remainder);
86
87 decode(<<"add ", Remainder/bytes>>) ->
88 10 mcd_protocol_text:decode(add, Remainder);
89
90 decode(<<"replace ", Remainder/bytes>>) ->
91 5 mcd_protocol_text:decode(replace, Remainder);
92
93 decode(<<"append ", Remainder/bytes>>) ->
94 3 mcd_protocol_text:decode(append, Remainder);
95
96 decode(<<"prepend ", Remainder/bytes>>) ->
97 3 mcd_protocol_text:decode(prepend, Remainder);
98
99 decode(<<"get ", Remainder/bytes>>) ->
100 37 mcd_protocol_text:decode(get, Remainder);
101
102 decode(<<"gets ", Remainder/bytes>>) ->
103 11 mcd_protocol_text:decode(gets, Remainder);
104
105 decode(<<"gat ", Remainder/bytes>>) ->
106 4 mcd_protocol_text:decode(gat, Remainder);
107
108 decode(<<"gats ", Remainder/bytes>>) ->
109 3 mcd_protocol_text:decode(gats, Remainder);
110
111 decode(<<"delete ", Remainder/bytes>>) ->
112 11 mcd_protocol_text:decode(delete, Remainder);
113
114 decode(<<"incr ", Remainder/bytes>>) ->
115 18 mcd_protocol_text:decode(incr, Remainder);
116
117 decode(<<"decr ", Remainder/bytes>>) ->
118 13 mcd_protocol_text:decode(decr, Remainder);
119
120 decode(<<"touch ", Remainder/bytes>>) ->
121 5 mcd_protocol_text:decode(touch, Remainder);
122
123 decode(<<"VALUE ", Remainder/bytes>>) ->
124 46 mcd_protocol_text:decode(value, Remainder);
125
126 decode(<<"CLIENT_ERROR ", Remainder/bytes>>) ->
127 5 mcd_protocol_text:decode(client_error, Remainder);
128
129 decode(<<"SERVER_ERROR ", Remainder/bytes>>) ->
130 1 mcd_protocol_text:decode(server_error, Remainder);
131
132 decode(<<"OK\r\n", Remainder/bytes>>) ->
133 3 {#{command => ok}, Remainder};
134
135 decode(<<"ERROR\r\n", Remainder/bytes>>) ->
136 1 {#{command => error}, Remainder};
137
138 decode(<<"TOUCHED\r\n", Remainder/bytes>>) ->
139 2 {#{command => touched}, Remainder};
140
141 decode(<<"STORED\r\n", Remainder/bytes>>) ->
142 40 {#{command => stored}, Remainder};
143
144 decode(<<"NOT_STORED\r\n", Remainder/bytes>>) ->
145 3 {#{command => not_stored}, Remainder};
146
147 decode(<<"EXISTS\r\n", Remainder/bytes>>) ->
148 4 {#{command => exists}, Remainder};
149
150 decode(<<"NOT_FOUND\r\n", Remainder/bytes>>) ->
151 5 {#{command => not_found}, Remainder};
152
153 decode(<<"END\r\n", Remainder/bytes>>) ->
154 49 {#{command => 'end'}, Remainder};
155
156 decode(<<"DELETED\r\n", Remainder/bytes>>) ->
157 5 {#{command => deleted}, Remainder};
158
159 decode(<<"ma ", Remainder/bytes>>) ->
160 18 mcd_protocol_meta:decode(arithmetic, Remainder);
161
162 decode(<<"me ", Remainder/bytes>>) ->
163 4 mcd_protocol_meta:decode(debug, Remainder);
164
165 decode(<<"ME ", Remainder/bytes>>) ->
166 1 mcd_protocol_meta:decode(debug_reply, Remainder);
167
168 decode(<<"mg ", Remainder/bytes>>) ->
169 15 mcd_protocol_meta:decode(get, Remainder);
170
171 decode(<<"mn", Remainder/bytes>>) ->
172 4 mcd_protocol_meta:decode(no_op, Remainder);
173
174 decode(<<"ms ", Remainder/bytes>>) ->
175 25 mcd_protocol_meta:decode(set, Remainder);
176
177 decode(<<"md ", Remainder/bytes>>) ->
178 4 mcd_protocol_meta:decode(delete, Remainder);
179
180 decode(<<"VA ", Remainder/bytes>>) ->
181 19 mcd_protocol_meta:decode(value, Remainder);
182
183 decode(<<"HD", Remainder/bytes>>) ->
184 21 mcd_protocol_meta:decode(head, Remainder);
185
186 decode(<<"NS", Remainder/bytes>>) ->
187 3 mcd_protocol_meta:decode(not_stored, Remainder);
188
189 decode(<<"EX", Remainder/bytes>>) ->
190 3 mcd_protocol_meta:decode(exists, Remainder);
191
192 decode(<<"NF", Remainder/bytes>>) ->
193 3 mcd_protocol_meta:decode(not_found, Remainder);
194
195 decode(<<"MN", Remainder/bytes>>) ->
196 3 mcd_protocol_meta:decode(no_op_reply, Remainder);
197
198 decode(<<"EN\r\n", Remainder/bytes>>) ->
199 1 {#{meta => miss}, Remainder};
200
201 decode(<<Magic:8,
202 _:8,
203 _:16,
204 _:8,
205 ?RAW:8,
206 _:16,
207 TotalBodyLength:32,
208 _:32,
209 _:64,
210 _:TotalBodyLength/bytes,
211 _/bytes>> = Arg) when Magic == ?REQUEST; Magic == ?RESPONSE ->
212 59 mcd_protocol_binary:decode(Arg);
213
214 decode(Arg) ->
215 14 [CommandLine, _] = split(Arg),
216 14 case re:run(CommandLine, "\\d+") of
217 {match, _} ->
218 14 mcd_protocol_text:decode(incrdecr, Arg);
219
220 nomatch ->
221
:-(
?LOG_ERROR(#{arg => Arg}),
222
:-(
error(client_error)
223 end.
224
225
226 encode(L) when is_list(L) ->
227 184 lists:map(fun encode/1, L);
228
229 encode(#{meta := _} = Arg) ->
230 114 mcd_protocol_meta:encode(Arg);
231
232 encode(#{command := _} = Arg) ->
233 345 mcd_protocol_text:encode(Arg);
234
235 encode(#{header := #{opcode := _}} = Arg) ->
236 49 mcd_protocol_binary:encode(Arg).
237
238
239 reply_expected(L) when is_list(L) ->
240 176 lists:filter(fun ?FUNCTION_NAME/1, L);
241
242 reply_expected(#{command := _, noreply := Noreply}) ->
243 111 not(Noreply);
244
245 reply_expected(#{meta := _, flags := Flags}) ->
246 57 not(lists:member(noreply, Flags));
247
248 reply_expected(#{}) ->
249 81 true.
Line Hits Source