Line data Source code
1 : /*
2 : * Copyright (c) 2022 Cisco and/or its affiliates.
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 : #include <vnet/session/application.h>
17 : #include <vnet/session/application_interface.h>
18 : #include <vnet/session/session.h>
19 : #include <http/http.h>
20 : #include <hs_apps/http_cli.h>
21 :
22 : typedef struct
23 : {
24 : CLIB_CACHE_LINE_ALIGN_MARK (cacheline0);
25 : u32 session_index;
26 : u32 thread_index;
27 : u32 rx_offset;
28 : u32 vpp_session_index;
29 : u32 to_recv;
30 : u8 is_closed;
31 : } hcc_session_t;
32 :
33 : typedef struct
34 : {
35 : hcc_session_t *sessions;
36 : u8 *rx_buf;
37 : u32 thread_index;
38 : } hcc_worker_t;
39 :
40 : typedef struct
41 : {
42 : hcc_worker_t *wrk;
43 : u32 app_index;
44 :
45 : u32 prealloc_fifos;
46 : u32 private_segment_size;
47 : u32 fifo_size;
48 : u8 *uri;
49 : u8 *http_query;
50 : session_endpoint_cfg_t connect_sep;
51 :
52 : u8 test_client_attached;
53 : vlib_main_t *vlib_main;
54 : u32 cli_node_index;
55 : u8 *http_response;
56 : u8 *appns_id;
57 : u64 appns_secret;
58 : } hcc_main_t;
59 :
60 : typedef enum
61 : {
62 : HCC_REPLY_RECEIVED = 100,
63 : } hcc_cli_signal_t;
64 :
65 : static hcc_main_t hcc_main;
66 :
67 : static hcc_worker_t *
68 0 : hcc_worker_get (u32 thread_index)
69 : {
70 0 : return vec_elt_at_index (hcc_main.wrk, thread_index);
71 : }
72 :
73 : static hcc_session_t *
74 0 : hcc_session_alloc (hcc_worker_t *wrk)
75 : {
76 : hcc_session_t *hs;
77 0 : pool_get_zero (wrk->sessions, hs);
78 0 : hs->session_index = hs - wrk->sessions;
79 0 : hs->thread_index = wrk->thread_index;
80 0 : return hs;
81 : }
82 :
83 : static hcc_session_t *
84 0 : hcc_session_get (u32 hs_index, u32 thread_index)
85 : {
86 0 : hcc_worker_t *wrk = hcc_worker_get (thread_index);
87 0 : return pool_elt_at_index (wrk->sessions, hs_index);
88 : }
89 :
90 : static void
91 0 : hcc_session_free (u32 thread_index, hcc_session_t *hs)
92 : {
93 0 : hcc_worker_t *wrk = hcc_worker_get (thread_index);
94 0 : pool_put (wrk->sessions, hs);
95 0 : }
96 :
97 : static int
98 0 : hcc_ts_accept_callback (session_t *ts)
99 : {
100 0 : clib_warning ("bug");
101 0 : return -1;
102 : }
103 :
104 : static void
105 0 : hcc_ts_disconnect_callback (session_t *s)
106 : {
107 0 : hcc_main_t *hcm = &hcc_main;
108 0 : vnet_disconnect_args_t _a = { 0 }, *a = &_a;
109 :
110 0 : a->handle = session_handle (s);
111 0 : a->app_index = hcm->app_index;
112 0 : vnet_disconnect_session (a);
113 0 : }
114 :
115 : static int
116 0 : hcc_ts_connected_callback (u32 app_index, u32 hc_index, session_t *as,
117 : session_error_t err)
118 : {
119 0 : hcc_main_t *hcm = &hcc_main;
120 : hcc_session_t *hs, *new_hs;
121 : hcc_worker_t *wrk;
122 : http_msg_t msg;
123 : int rv;
124 :
125 0 : if (err)
126 : {
127 0 : clib_warning ("connected error: hc_index(%d): %U", hc_index,
128 : format_session_error, err);
129 0 : return -1;
130 : }
131 :
132 : /* TODO delete half open session once the support is added in http layer */
133 0 : hs = hcc_session_get (hc_index, 0);
134 0 : wrk = hcc_worker_get (as->thread_index);
135 0 : new_hs = hcc_session_alloc (wrk);
136 0 : clib_memcpy_fast (new_hs, hs, sizeof (*hs));
137 :
138 0 : hs->vpp_session_index = as->session_index;
139 :
140 0 : msg.type = HTTP_MSG_REQUEST;
141 0 : msg.method_type = HTTP_REQ_GET;
142 0 : msg.content_type = HTTP_CONTENT_TEXT_HTML;
143 0 : msg.data.type = HTTP_MSG_DATA_INLINE;
144 0 : msg.data.len = vec_len (hcm->http_query);
145 :
146 0 : svm_fifo_seg_t segs[2] = { { (u8 *) &msg, sizeof (msg) },
147 0 : { hcm->http_query, vec_len (hcm->http_query) } };
148 :
149 0 : rv = svm_fifo_enqueue_segments (as->tx_fifo, segs, 2, 0 /* allow partial */);
150 0 : if (rv < 0 || rv != sizeof (msg) + vec_len (hcm->http_query))
151 : {
152 0 : clib_warning ("failed app enqueue");
153 0 : return -1;
154 : }
155 :
156 0 : if (svm_fifo_set_event (as->tx_fifo))
157 0 : session_send_io_evt_to_thread (as->tx_fifo, SESSION_IO_EVT_TX);
158 :
159 0 : return 0;
160 : }
161 :
162 : static void
163 0 : hcc_ts_reset_callback (session_t *s)
164 : {
165 0 : hcc_main_t *hcm = &hcc_main;
166 : hcc_session_t *hs;
167 0 : vnet_disconnect_args_t _a = { 0 }, *a = &_a;
168 :
169 0 : hs = hcc_session_get (s->opaque, s->thread_index);
170 0 : hs->is_closed = 1;
171 :
172 0 : a->handle = session_handle (s);
173 0 : a->app_index = hcm->app_index;
174 0 : vnet_disconnect_session (a);
175 0 : }
176 :
177 : static int
178 0 : hcc_ts_tx_callback (session_t *ts)
179 : {
180 0 : clib_warning ("bug");
181 0 : return -1;
182 : }
183 :
184 : static void
185 0 : hcc_session_disconnect (session_t *s)
186 : {
187 0 : hcc_main_t *hcm = &hcc_main;
188 0 : vnet_disconnect_args_t _a = { 0 }, *a = &_a;
189 0 : a->handle = session_handle (s);
190 0 : a->app_index = hcm->app_index;
191 0 : vnet_disconnect_session (a);
192 0 : }
193 :
194 : static int
195 0 : hcc_ts_rx_callback (session_t *ts)
196 : {
197 0 : hcc_main_t *hcm = &hcc_main;
198 : hcc_session_t *hs;
199 : http_msg_t msg;
200 : int rv;
201 :
202 0 : hs = hcc_session_get (ts->opaque, ts->thread_index);
203 :
204 0 : if (hs->is_closed)
205 : {
206 0 : clib_warning ("session is closed");
207 0 : return 0;
208 : }
209 :
210 0 : if (!hs->to_recv)
211 : {
212 0 : rv = svm_fifo_dequeue (ts->rx_fifo, sizeof (msg), (u8 *) &msg);
213 0 : ASSERT (rv == sizeof (msg));
214 :
215 0 : if (msg.type != HTTP_MSG_REPLY || msg.code != HTTP_STATUS_OK)
216 : {
217 0 : clib_warning ("unexpected msg type %d", msg.type);
218 0 : return 0;
219 : }
220 0 : vec_validate (hcm->http_response, msg.data.len - 1);
221 0 : vec_reset_length (hcm->http_response);
222 0 : hs->to_recv = msg.data.len;
223 : }
224 :
225 0 : u32 max_deq = svm_fifo_max_dequeue (ts->rx_fifo);
226 :
227 0 : u32 n_deq = clib_min (hs->to_recv, max_deq);
228 0 : u32 curr = vec_len (hcm->http_response);
229 0 : rv = svm_fifo_dequeue (ts->rx_fifo, n_deq, hcm->http_response + curr);
230 0 : if (rv < 0)
231 : {
232 0 : clib_warning ("app dequeue failed");
233 0 : return -1;
234 : }
235 :
236 0 : if (rv != n_deq)
237 0 : return -1;
238 :
239 0 : vec_set_len (hcm->http_response, curr + n_deq);
240 0 : ASSERT (hs->to_recv >= rv);
241 0 : hs->to_recv -= rv;
242 :
243 0 : if (hs->to_recv == 0)
244 : {
245 0 : hcc_session_disconnect (ts);
246 0 : vlib_process_signal_event_mt (hcm->vlib_main, hcm->cli_node_index,
247 : HCC_REPLY_RECEIVED, 0);
248 : }
249 :
250 0 : return 0;
251 : }
252 :
253 : static void
254 0 : hcc_ts_cleanup_callback (session_t *s, session_cleanup_ntf_t ntf)
255 : {
256 : hcc_session_t *hs;
257 :
258 0 : hs = hcc_session_get (s->thread_index, s->opaque);
259 0 : if (!hs)
260 0 : return;
261 :
262 0 : hcc_session_free (s->thread_index, hs);
263 : }
264 :
265 : static session_cb_vft_t hcc_session_cb_vft = {
266 : .session_accept_callback = hcc_ts_accept_callback,
267 : .session_disconnect_callback = hcc_ts_disconnect_callback,
268 : .session_connected_callback = hcc_ts_connected_callback,
269 : .builtin_app_rx_callback = hcc_ts_rx_callback,
270 : .builtin_app_tx_callback = hcc_ts_tx_callback,
271 : .session_reset_callback = hcc_ts_reset_callback,
272 : .session_cleanup_callback = hcc_ts_cleanup_callback,
273 : };
274 :
275 : static clib_error_t *
276 0 : hcc_attach ()
277 : {
278 0 : hcc_main_t *hcm = &hcc_main;
279 0 : vnet_app_attach_args_t _a, *a = &_a;
280 : u64 options[18];
281 0 : u32 segment_size = 128 << 20;
282 : int rv;
283 :
284 0 : if (hcm->private_segment_size)
285 0 : segment_size = hcm->private_segment_size;
286 :
287 0 : clib_memset (a, 0, sizeof (*a));
288 0 : clib_memset (options, 0, sizeof (options));
289 :
290 0 : a->api_client_index = ~0;
291 0 : a->name = format (0, "http_cli_client");
292 0 : a->session_cb_vft = &hcc_session_cb_vft;
293 0 : a->options = options;
294 0 : a->options[APP_OPTIONS_SEGMENT_SIZE] = segment_size;
295 0 : a->options[APP_OPTIONS_ADD_SEGMENT_SIZE] = segment_size;
296 0 : a->options[APP_OPTIONS_RX_FIFO_SIZE] =
297 0 : hcm->fifo_size ? hcm->fifo_size : 8 << 10;
298 0 : a->options[APP_OPTIONS_TX_FIFO_SIZE] =
299 0 : hcm->fifo_size ? hcm->fifo_size : 32 << 10;
300 0 : a->options[APP_OPTIONS_FLAGS] = APP_OPTIONS_FLAGS_IS_BUILTIN;
301 0 : a->options[APP_OPTIONS_PREALLOC_FIFO_PAIRS] = hcm->prealloc_fifos;
302 0 : if (hcm->appns_id)
303 : {
304 0 : a->namespace_id = hcm->appns_id;
305 0 : a->options[APP_OPTIONS_NAMESPACE_SECRET] = hcm->appns_secret;
306 : }
307 :
308 0 : if ((rv = vnet_application_attach (a)))
309 0 : return clib_error_return (0, "attach returned %d", rv);
310 :
311 0 : hcm->app_index = a->app_index;
312 0 : vec_free (a->name);
313 0 : hcm->test_client_attached = 1;
314 0 : return 0;
315 : }
316 :
317 : static int
318 0 : hcc_connect_rpc (void *rpc_args)
319 : {
320 0 : vnet_connect_args_t *a = rpc_args;
321 : int rv;
322 :
323 0 : rv = vnet_connect (a);
324 0 : if (rv)
325 0 : clib_warning (0, "connect returned: %U", format_session_error, rv);
326 :
327 0 : vec_free (a);
328 0 : return rv;
329 : }
330 :
331 : static void
332 0 : hcc_program_connect (vnet_connect_args_t *a)
333 : {
334 0 : session_send_rpc_evt_to_thread_force (transport_cl_thread (),
335 : hcc_connect_rpc, a);
336 0 : }
337 :
338 : static clib_error_t *
339 0 : hcc_connect ()
340 : {
341 0 : vnet_connect_args_t *a = 0;
342 0 : hcc_main_t *hcm = &hcc_main;
343 : hcc_worker_t *wrk;
344 : hcc_session_t *hs;
345 :
346 0 : vec_validate (a, 0);
347 0 : clib_memset (a, 0, sizeof (a[0]));
348 :
349 0 : clib_memcpy (&a->sep_ext, &hcm->connect_sep, sizeof (hcm->connect_sep));
350 0 : a->app_index = hcm->app_index;
351 :
352 : /* allocate http session on main thread */
353 0 : wrk = hcc_worker_get (0);
354 0 : hs = hcc_session_alloc (wrk);
355 0 : a->api_context = hs->session_index;
356 :
357 0 : hcc_program_connect (a);
358 0 : return 0;
359 : }
360 :
361 : static clib_error_t *
362 0 : hcc_run (vlib_main_t *vm)
363 : {
364 0 : vlib_thread_main_t *vtm = vlib_get_thread_main ();
365 0 : hcc_main_t *hcm = &hcc_main;
366 0 : uword event_type, *event_data = 0;
367 : u32 num_threads;
368 0 : clib_error_t *err = 0;
369 : hcc_worker_t *wrk;
370 :
371 0 : num_threads = 1 /* main thread */ + vtm->n_threads;
372 0 : vec_validate (hcm->wrk, num_threads);
373 0 : vec_foreach (wrk, hcm->wrk)
374 : {
375 0 : wrk->thread_index = wrk - hcm->wrk;
376 : }
377 :
378 0 : if ((err = hcc_attach ()))
379 : {
380 0 : return clib_error_return (0, "http client attach: %U", format_clib_error,
381 : err);
382 : }
383 :
384 0 : if ((err = hcc_connect ()))
385 : {
386 0 : return clib_error_return (0, "http client connect: %U",
387 : format_clib_error, err);
388 : }
389 :
390 0 : vlib_process_wait_for_event_or_clock (vm, 10);
391 0 : event_type = vlib_process_get_events (vm, &event_data);
392 0 : switch (event_type)
393 : {
394 0 : case ~0:
395 0 : err = clib_error_return (0, "timeout");
396 0 : goto cleanup;
397 :
398 0 : case HCC_REPLY_RECEIVED:
399 0 : vlib_cli_output (vm, "%v", hcm->http_response);
400 0 : vec_free (hcm->http_response);
401 0 : break;
402 0 : default:
403 0 : clib_error_return (0, "unexpected event %d", event_type);
404 0 : break;
405 : }
406 :
407 0 : cleanup:
408 0 : vec_free (event_data);
409 0 : return err;
410 : }
411 :
412 : static int
413 0 : hcc_detach ()
414 : {
415 0 : hcc_main_t *hcm = &hcc_main;
416 0 : vnet_app_detach_args_t _da, *da = &_da;
417 : int rv;
418 :
419 0 : if (!hcm->test_client_attached)
420 0 : return 0;
421 :
422 0 : da->app_index = hcm->app_index;
423 0 : da->api_client_index = ~0;
424 0 : rv = vnet_application_detach (da);
425 0 : hcm->test_client_attached = 0;
426 0 : hcm->app_index = ~0;
427 :
428 0 : return rv;
429 : }
430 :
431 : static clib_error_t *
432 0 : hcc_command_fn (vlib_main_t *vm, unformat_input_t *input,
433 : vlib_cli_command_t *cmd)
434 : {
435 0 : unformat_input_t _line_input, *line_input = &_line_input;
436 0 : hcc_main_t *hcm = &hcc_main;
437 : u64 seg_size;
438 0 : u8 *appns_id = 0;
439 0 : clib_error_t *err = 0;
440 : int rv;
441 :
442 0 : hcm->prealloc_fifos = 0;
443 0 : hcm->private_segment_size = 0;
444 0 : hcm->fifo_size = 0;
445 :
446 0 : if (hcm->test_client_attached)
447 0 : return clib_error_return (0, "failed: already running!");
448 :
449 : /* Get a line of input. */
450 0 : if (!unformat_user (input, unformat_line_input, line_input))
451 0 : return clib_error_return (0, "expected URI");
452 :
453 0 : while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
454 : {
455 0 : if (unformat (line_input, "prealloc-fifos %d", &hcm->prealloc_fifos))
456 : ;
457 0 : else if (unformat (line_input, "private-segment-size %U",
458 : unformat_memory_size, &seg_size))
459 0 : hcm->private_segment_size = seg_size;
460 0 : else if (unformat (line_input, "fifo-size %d", &hcm->fifo_size))
461 0 : hcm->fifo_size <<= 10;
462 0 : else if (unformat (line_input, "uri %s", &hcm->uri))
463 : ;
464 0 : else if (unformat (line_input, "appns %_%v%_", &appns_id))
465 : ;
466 0 : else if (unformat (line_input, "secret %lu", &hcm->appns_secret))
467 : ;
468 0 : else if (unformat (line_input, "query %s", &hcm->http_query))
469 : ;
470 : else
471 : {
472 0 : err = clib_error_return (0, "unknown input `%U'",
473 : format_unformat_error, line_input);
474 0 : goto done;
475 : }
476 : }
477 :
478 0 : vec_free (hcm->appns_id);
479 0 : hcm->appns_id = appns_id;
480 0 : hcm->cli_node_index = vlib_get_current_process (vm)->node_runtime.node_index;
481 :
482 0 : if (!hcm->uri)
483 : {
484 0 : err = clib_error_return (0, "URI not defined");
485 0 : goto done;
486 : }
487 :
488 0 : if ((rv = parse_uri ((char *) hcm->uri, &hcm->connect_sep)))
489 : {
490 0 : err = clib_error_return (0, "Uri parse error: %d", rv);
491 0 : goto done;
492 : }
493 :
494 0 : vlib_worker_thread_barrier_sync (vm);
495 0 : vnet_session_enable_disable (vm, 1 /* turn on TCP, etc. */);
496 0 : vlib_worker_thread_barrier_release (vm);
497 :
498 0 : err = hcc_run (vm);
499 :
500 0 : if (hcc_detach ())
501 : {
502 : /* don't override last error */
503 0 : if (!err)
504 0 : err = clib_error_return (0, "failed: app detach");
505 0 : clib_warning ("WARNING: app detach failed...");
506 : }
507 :
508 0 : done:
509 0 : vec_free (hcm->uri);
510 0 : vec_free (hcm->http_query);
511 0 : unformat_free (line_input);
512 0 : return err;
513 : }
514 :
515 203447 : VLIB_CLI_COMMAND (hcc_command, static) = {
516 : .path = "http cli client",
517 : .short_help = "[appns <app-ns> secret <appns-secret>] uri http://<ip-addr> "
518 : "query <query-string>",
519 : .function = hcc_command_fn,
520 : .is_mp_safe = 1,
521 : };
522 :
523 : static clib_error_t *
524 559 : hcc_main_init (vlib_main_t *vm)
525 : {
526 559 : hcc_main_t *hcm = &hcc_main;
527 :
528 559 : hcm->app_index = ~0;
529 559 : hcm->vlib_main = vm;
530 559 : return 0;
531 : }
532 :
533 2239 : VLIB_INIT_FUNCTION (hcc_main_init);
534 :
535 : /*
536 : * fd.io coding-style-patch-verification: ON
537 : *
538 : * Local Variables:
539 : * eval: (c-set-style "gnu")
540 : * End:
541 : */
|