twister_api.c (19059B)
1 /* 2 This file is part of Taler. 3 Copyright (C) 2009, 2010, 2011, 2016 GNUnet e.V. 4 Copyright (C) 2018 Taler Systems SA 5 6 Taler is free software; you can redistribute it and/or modify 7 it under the terms of the GNU General Public License as 8 published by the Free Software Foundation; either version 3, 9 or (at your option) any later version. 10 11 Taler is distributed in the hope that it will be useful, but 12 WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 14 See the GNU General Public License for more details. 15 16 You should have received a copy of the GNU General Public 17 License along with Taler; see the file COPYING. If not, 18 write to the Free Software Foundation, Inc., 51 Franklin 19 Street, Fifth Floor, Boston, MA 02110-1301, USA. */ 20 21 /** 22 * @file twister_api.c 23 * @brief api to control twister proxy 24 * @author Christian Grothoff 25 * @author Marcello Stanisci 26 */ 27 #include "platform.h" 28 #include <gnunet/gnunet_util_lib.h> 29 #include <microhttpd.h> 30 #include "taler_twister_service.h" 31 #include "twister.h" 32 33 #define LOG(kind,...) \ 34 GNUNET_log_from (kind, "twister-api",__VA_ARGS__) 35 36 37 /** 38 * Opaque handle for asynchronous operation. 39 */ 40 struct TALER_TWISTER_Operation 41 { 42 43 /** 44 * Pointer to next operation. 45 */ 46 struct TALER_TWISTER_Operation *next; 47 48 /** 49 * Pointer to previous operation. 50 */ 51 struct TALER_TWISTER_Operation *prev; 52 53 /** 54 * Pointer to main handle (= connection to the twister). 55 */ 56 struct TALER_TWISTER_Handle *h; 57 58 /** 59 * Callback for this operation. 60 */ 61 GNUNET_SCHEDULER_TaskCallback cb; 62 63 /** 64 * Closure to pass the callback above. 65 */ 66 void *cb_cls; 67 }; 68 69 70 /** 71 * Handle for talking with the Twister service. 72 */ 73 struct TALER_TWISTER_Handle 74 { 75 /** 76 * Configuration to use. 77 */ 78 const struct GNUNET_CONFIGURATION_Handle *cfg; 79 80 /** 81 * Message queue (if available). 82 */ 83 struct GNUNET_MQ_Handle *mq; 84 85 /** 86 * First pending operation. 87 */ 88 struct TALER_TWISTER_Operation *op_head; 89 90 /** 91 * Last pending operation. 92 */ 93 struct TALER_TWISTER_Operation *op_tail; 94 }; 95 96 97 /** 98 * Generic error handler, called with the appropriate 99 * error code and the same closure specified at the creation of 100 * the message queue. 101 * Not every message queue implementation supports an error handler. 102 * 103 * @param cls closure with the `struct TALER_TWISTER_Handle *` 104 * @param error error code 105 */ 106 static void 107 mq_error_handler (void *cls, 108 enum GNUNET_MQ_Error error) 109 { 110 struct TALER_TWISTER_Handle *h = cls; 111 112 GNUNET_MQ_destroy (h->mq); 113 h->mq = NULL; 114 /* FIXME: maybe give test case nicer way to shut down... */ 115 GNUNET_assert (0); 116 } 117 118 119 /** 120 * Type of a function to call when we receive a message 121 * from the service. 122 * 123 * @param cls closure 124 * @param client_msg message received 125 */ 126 static void 127 handle_acknowledgement (void *cls, 128 const struct GNUNET_MessageHeader *ack) 129 { 130 struct TALER_TWISTER_Handle *h = cls; 131 struct TALER_TWISTER_Operation *op; 132 133 op = h->op_head; 134 GNUNET_assert (NULL != op); /* twister very wrong, fail test */ 135 GNUNET_CONTAINER_DLL_remove (h->op_head, 136 h->op_tail, 137 op); 138 if (NULL != op->cb) 139 op->cb (op->cb_cls); 140 GNUNET_free (op); 141 } 142 143 144 /** 145 * Connect to the twister service. 146 * 147 * @param cfg the configuration to use 148 * @return handle to use in #TALER_TWISTER_disconnect to disconnect 149 */ 150 struct TALER_TWISTER_Handle * 151 TALER_TWISTER_connect (const struct GNUNET_CONFIGURATION_Handle *cfg) 152 { 153 struct TALER_TWISTER_Handle *h; 154 155 h = GNUNET_new (struct TALER_TWISTER_Handle); 156 h->cfg = cfg; 157 { 158 struct GNUNET_MQ_MessageHandler handlers[] = { 159 GNUNET_MQ_hd_fixed_size 160 (acknowledgement, 161 TWISTER_MESSAGE_TYPE_ACKNOWLEDGEMENT, 162 struct GNUNET_MessageHeader, 163 h), 164 GNUNET_MQ_handler_end () 165 }; 166 167 LOG (GNUNET_ERROR_TYPE_DEBUG, 168 "Connecting to twister service.\n"); 169 h->mq = GNUNET_CLIENT_connect (h->cfg, 170 "twister", 171 handlers, 172 &mq_error_handler, 173 h); 174 } 175 if (NULL == h->mq) 176 { 177 LOG (GNUNET_ERROR_TYPE_ERROR, 178 "Could not connect to twister service\n"); 179 GNUNET_free (h); 180 return NULL; 181 } 182 return h; 183 } 184 185 186 /** 187 * Disconnect from twister service. 188 * 189 * @param h handle to destroy 190 */ 191 void 192 TALER_TWISTER_disconnect (struct TALER_TWISTER_Handle *h) 193 { 194 struct TALER_TWISTER_Operation *op; 195 196 while (NULL != (op = h->op_head)) 197 { 198 GNUNET_CONTAINER_DLL_remove (h->op_head, 199 h->op_tail, 200 op); 201 GNUNET_free (op); 202 } 203 if (NULL != h->mq) 204 { 205 GNUNET_MQ_destroy (h->mq); 206 h->mq = NULL; 207 } 208 GNUNET_free (h); 209 } 210 211 212 /** 213 * Abort operation. Twister behavior may then include the 214 * changes requested by the operation, or not! Must be called 215 * before the operation callback was invoked. 216 * 217 * @param op operation to cancel, 218 * operation's callback will not be called 219 */ 220 void 221 TALER_TWISTER_cancel (struct TALER_TWISTER_Operation *op) 222 { 223 /* Just don't call the callback anymore */ 224 op->cb = NULL; 225 } 226 227 228 /** 229 * Randomly truncate the request. 230 * 231 * @param h twister instance to control 232 * @param cb function to call once twister is ready; typically 233 * a acknowledge function. 234 * @param cb_cls closure for @a cb 235 * @return operation handle (to possibly abort) 236 */ 237 struct TALER_TWISTER_Operation * 238 TALER_TWISTER_malform_upload (struct TALER_TWISTER_Handle *h, 239 GNUNET_SCHEDULER_TaskCallback cb, 240 void *cb_cls) 241 { 242 struct TALER_TWISTER_Operation *op; 243 struct GNUNET_MQ_Envelope *env; 244 struct TWISTER_Malform *src; 245 246 op = GNUNET_new (struct TALER_TWISTER_Operation); 247 op->h = h; 248 op->cb = cb; 249 op->cb_cls = cb_cls; 250 GNUNET_CONTAINER_DLL_insert_tail (h->op_head, 251 h->op_tail, 252 op); 253 /* Prepare *env*elope. */ 254 env = GNUNET_MQ_msg 255 (src, TWISTER_MESSAGE_TYPE_MALFORM_UPLOAD); 256 /* Send message. */ 257 GNUNET_MQ_send (h->mq, env); 258 LOG (GNUNET_ERROR_TYPE_DEBUG, 259 "Batching a (upload) body malformation\n"); 260 return op; 261 } 262 263 264 /** 265 * Randomly truncate the response. 266 * 267 * @param h twister instance to control 268 * @param cb function to call once twister has processed this 269 * request. 270 * @param cb_cls closure for @a cb 271 * @return operation handle (to possibly abort) 272 */ 273 struct TALER_TWISTER_Operation * 274 TALER_TWISTER_malform (struct TALER_TWISTER_Handle *h, 275 GNUNET_SCHEDULER_TaskCallback cb, 276 void *cb_cls) 277 { 278 struct TALER_TWISTER_Operation *op; 279 struct GNUNET_MQ_Envelope *env; 280 struct TWISTER_Malform *src; 281 282 op = GNUNET_new (struct TALER_TWISTER_Operation); 283 op->h = h; 284 op->cb = cb; 285 op->cb_cls = cb_cls; 286 GNUNET_CONTAINER_DLL_insert_tail (h->op_head, 287 h->op_tail, 288 op); 289 /* Prepare *env*elope. */ 290 env = GNUNET_MQ_msg 291 (src, TWISTER_MESSAGE_TYPE_MALFORM); 292 /* Send message. */ 293 GNUNET_MQ_send (h->mq, env); 294 LOG (GNUNET_ERROR_TYPE_DEBUG, 295 "Batching a body malformation\n"); 296 return op; 297 } 298 299 300 /** 301 * Instruct the twister to flip a character into 302 * the string JSON field that belongs to the object 303 * being returned to the HTTP client. 304 * 305 * @param h twister instance to control 306 * @param path object-like notation to point the string 307 * object where we seek a character to flip. 308 * @param cb function to call once twister has processed this 309 * request 310 * @param cb_cls closure for @a cb 311 * @return operation handle (to possibly abort) 312 */ 313 struct TALER_TWISTER_Operation * 314 TALER_TWISTER_flip_download (struct TALER_TWISTER_Handle *h, 315 const char *path, 316 GNUNET_SCHEDULER_TaskCallback cb, 317 void *cb_cls) 318 { 319 struct TALER_TWISTER_Operation *op; 320 struct GNUNET_MQ_Envelope *env; 321 struct TWISTER_FlipPath *src; // FIXME 'src' right name? 322 uint16_t stralloc; 323 324 stralloc = strlen (path) + 1; 325 if (stralloc + sizeof (struct TWISTER_FlipPath) > UINT16_MAX) 326 { 327 GNUNET_break (0); 328 return NULL; 329 } 330 op = GNUNET_new (struct TALER_TWISTER_Operation); 331 op->h = h; 332 op->cb = cb; 333 op->cb_cls = cb_cls; 334 GNUNET_CONTAINER_DLL_insert_tail (h->op_head, 335 h->op_tail, 336 op); 337 env = GNUNET_MQ_msg_extra (src, 338 stralloc, 339 TWISTER_MESSAGE_TYPE_FLIP_PATH_DL); 340 GNUNET_assert 341 (stralloc == GNUNET_STRINGS_buffer_fill ((char *) &src[1], 342 stralloc, 343 1, 344 path)); 345 GNUNET_MQ_send (h->mq, env); 346 return op; 347 } 348 349 350 /** 351 * Instruct the twister to flip a character into 352 * the string JSON field that belongs to the object 353 * being uploaded to the proxied service. 354 * 355 * @param h twister instance to control 356 * @param path object-like notation to point the string 357 * object where we seek a character to flip. 358 * @param cb function to call once twister has batched this 359 * request 360 * @param cb_cls closure for @a cb 361 * @return operation handle (to possibly abort) 362 */ 363 struct TALER_TWISTER_Operation * 364 TALER_TWISTER_flip_upload (struct TALER_TWISTER_Handle *h, 365 const char *path, 366 GNUNET_SCHEDULER_TaskCallback cb, 367 void *cb_cls) 368 { 369 struct TALER_TWISTER_Operation *op; 370 struct GNUNET_MQ_Envelope *env; 371 struct TWISTER_FlipPath *src; // FIXME 'src' right name? 372 uint16_t stralloc = strlen (path) + 1; 373 374 if (stralloc + sizeof (struct TWISTER_FlipPath) > UINT16_MAX) 375 { 376 GNUNET_break (0); 377 return NULL; 378 } 379 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 380 "Will UL-flip: %s\n", 381 path); 382 383 op = GNUNET_new (struct TALER_TWISTER_Operation); 384 op->h = h; 385 op->cb = cb; 386 op->cb_cls = cb_cls; 387 GNUNET_CONTAINER_DLL_insert_tail (h->op_head, 388 h->op_tail, 389 op); 390 391 env = GNUNET_MQ_msg_extra (src, 392 stralloc, 393 TWISTER_MESSAGE_TYPE_FLIP_PATH_UL); 394 GNUNET_assert 395 (stralloc == GNUNET_STRINGS_buffer_fill ((char *) &src[1], 396 stralloc, 397 1, 398 path)); 399 GNUNET_MQ_send (h->mq, env); 400 return op; 401 } 402 403 404 /** 405 * Delete the object pointed to by @a path. Note, this 406 * object belongs to the JSON response object. 407 * 408 * @param h twister instance to control 409 * @param path object-like notation to point the object to be 410 deleted. E.g., the path "f1.f2.0" will delete the object 411 {"f1": {"f2": [{"to be": "deleted"}]}}. 412 * @param cb function to call once twister is ready 413 * @param cb_cls closure for @a cb 414 * @return operation handle (to possibly abort) 415 */ 416 struct TALER_TWISTER_Operation * 417 TALER_TWISTER_delete_path (struct TALER_TWISTER_Handle *h, 418 const char *path, 419 GNUNET_SCHEDULER_TaskCallback cb, 420 void *cb_cls) 421 { 422 struct TALER_TWISTER_Operation *op; 423 struct GNUNET_MQ_Envelope *env; 424 struct TWISTER_DeletePath *src; // FIXME 'src' right name? 425 uint16_t stralloc; 426 427 stralloc = strlen (path) + 1; 428 if (stralloc + sizeof (struct TWISTER_DeletePath) > UINT16_MAX) 429 { 430 GNUNET_break (0); 431 return NULL; 432 } 433 op = GNUNET_new (struct TALER_TWISTER_Operation); 434 op->h = h; 435 op->cb = cb; 436 op->cb_cls = cb_cls; 437 GNUNET_CONTAINER_DLL_insert_tail (h->op_head, 438 h->op_tail, 439 op); 440 env = GNUNET_MQ_msg_extra (src, 441 stralloc, 442 TWISTER_MESSAGE_TYPE_DELETE_PATH); 443 GNUNET_assert 444 (stralloc == GNUNET_STRINGS_buffer_fill ((char *) &src[1], 445 stralloc, 446 1, 447 path)); 448 GNUNET_MQ_send (h->mq, env); 449 return op; 450 } 451 452 453 /** 454 * Change the response field pointed by @a modify_path with 455 * @a modify_value. 456 * 457 * @param h twister instance to control 458 * @param path object-like notation path to the object to modify 459 * @param value value to use for @a modify_path 460 * @param cb callback to call once twister gets this instruction. 461 * @param cb_cls closure for @a cb_callback 462 * 463 * @return operation handle. 464 */ 465 struct TALER_TWISTER_Operation * 466 TALER_TWISTER_modify_path_dl (struct TALER_TWISTER_Handle *h, 467 const char *path, 468 const char *value, 469 GNUNET_SCHEDULER_TaskCallback cb, 470 void *cb_cls) 471 { 472 struct TALER_TWISTER_Operation *op; 473 struct GNUNET_MQ_Envelope *env; 474 struct TWISTER_ModifyPath *src; 475 uint16_t stralloc; 476 477 stralloc = strlen (path) + strlen (value) + 2; 478 if (sizeof (*src) + stralloc > UINT16_MAX) 479 { 480 GNUNET_break (0); 481 return NULL; 482 } 483 op = GNUNET_new (struct TALER_TWISTER_Operation); 484 op->h = h; 485 op->cb = cb; 486 op->cb_cls = cb_cls; 487 GNUNET_CONTAINER_DLL_insert_tail (h->op_head, 488 h->op_tail, 489 op); 490 env = GNUNET_MQ_msg_extra (src, 491 stralloc, 492 TWISTER_MESSAGE_TYPE_MODIFY_PATH_DL); 493 494 GNUNET_assert (stralloc == 495 GNUNET_STRINGS_buffer_fill ((char *) &src[1], 496 stralloc, 497 2, 498 path, 499 value)); 500 GNUNET_MQ_send (h->mq, env); 501 return op; 502 } 503 504 505 /** 506 * Change the JSON field pointed by @a path to the new @a value. 507 * It only applies to upload objects. 508 * 509 * @param h twister instance to control 510 * @param path object-like notation path to the object to modify 511 * @param value value to use for @a modify_path 512 * @param cb callback to call once twister gets this instruction. 513 * @param cb_cls closure for @a cb_callback 514 * 515 * @return operation handle. 516 */ 517 struct TALER_TWISTER_Operation * 518 TALER_TWISTER_modify_path_ul (struct TALER_TWISTER_Handle *h, 519 const char *path, 520 const char *value, 521 GNUNET_SCHEDULER_TaskCallback cb, 522 void *cb_cls) 523 { 524 struct TALER_TWISTER_Operation *op; 525 struct GNUNET_MQ_Envelope *env; 526 struct TWISTER_ModifyPath *src; 527 uint16_t stralloc; 528 529 stralloc = strlen (path) + strlen (value) + 2; 530 if (sizeof (*src) + stralloc > UINT16_MAX) 531 { 532 GNUNET_break (0); 533 return NULL; 534 } 535 op = GNUNET_new (struct TALER_TWISTER_Operation); 536 op->h = h; 537 op->cb = cb; 538 op->cb_cls = cb_cls; 539 GNUNET_CONTAINER_DLL_insert_tail (h->op_head, 540 h->op_tail, 541 op); 542 env = GNUNET_MQ_msg_extra (src, 543 stralloc, 544 TWISTER_MESSAGE_TYPE_MODIFY_PATH_UL); 545 GNUNET_assert (stralloc == 546 GNUNET_STRINGS_buffer_fill ((char *) &src[1], 547 stralloc, 548 2, 549 path, 550 value)); 551 GNUNET_MQ_send (h->mq, env); 552 return op; 553 } 554 555 556 /** 557 * Change the HTTP response header of @a header to @a value. 558 * 559 * @param h twister instance to control 560 * @param header the HTTP response header to modify 561 * @param value value to use for @a header 562 * @param cb callback to call once twister gets this instruction. 563 * @param cb_cls closure for @a cb_callback 564 * 565 * @return operation handle. 566 */ 567 struct TALER_TWISTER_Operation * 568 TALER_TWISTER_modify_header_dl (struct TALER_TWISTER_Handle *h, 569 const char *header, 570 const char *value, 571 GNUNET_SCHEDULER_TaskCallback cb, 572 void *cb_cls) 573 { 574 struct TALER_TWISTER_Operation *op; 575 struct GNUNET_MQ_Envelope *env; 576 struct TWISTER_ModifyPath *src; 577 uint16_t stralloc; 578 579 if ( (0 == strcasecmp (header, 580 MHD_HTTP_HEADER_CONNECTION)) && 581 (0 == strcasecmp (value, 582 "Keep-Alive")) ) 583 { 584 GNUNET_break (0); 585 return NULL; 586 } 587 if (0 == strcasecmp (header, 588 MHD_HTTP_HEADER_CONTENT_LENGTH)) 589 { 590 GNUNET_break (0); 591 return NULL; 592 } 593 stralloc = strlen (header) + strlen (value) + 2; 594 if (sizeof (*src) + stralloc > UINT16_MAX) 595 { 596 GNUNET_break (0); 597 return NULL; 598 } 599 op = GNUNET_new (struct TALER_TWISTER_Operation); 600 op->h = h; 601 op->cb = cb; 602 op->cb_cls = cb_cls; 603 GNUNET_CONTAINER_DLL_insert_tail (h->op_head, 604 h->op_tail, 605 op); 606 env = GNUNET_MQ_msg_extra (src, 607 stralloc, 608 TWISTER_MESSAGE_TYPE_MODIFY_HEADER_DL); 609 610 GNUNET_assert (stralloc == 611 GNUNET_STRINGS_buffer_fill ((char *) &src[1], 612 stralloc, 613 2, 614 header, 615 value)); 616 GNUNET_MQ_send (h->mq, env); 617 return op; 618 } 619 620 621 /** 622 * Change the next response code to @a new_rc. 623 * 624 * @param h twister instance to control 625 * @param new_rc response code to return from the next response 626 * @param cb function to call once twister is ready 627 * @param cb_cls closure for @a cb 628 * @return operation handle (to possibly abort) 629 */ 630 struct TALER_TWISTER_Operation * 631 TALER_TWISTER_change_response_code (struct TALER_TWISTER_Handle *h, 632 unsigned int new_rc, 633 GNUNET_SCHEDULER_TaskCallback cb, 634 void *cb_cls) 635 { 636 struct TALER_TWISTER_Operation *op; 637 struct GNUNET_MQ_Envelope *env; 638 struct TWISTER_SetResponseCode *src; 639 640 op = GNUNET_new (struct TALER_TWISTER_Operation); 641 op->h = h; 642 op->cb = cb; 643 op->cb_cls = cb_cls; 644 GNUNET_CONTAINER_DLL_insert_tail (h->op_head, 645 h->op_tail, 646 op); 647 /* Prepare *env*elope. */ 648 env = GNUNET_MQ_msg 649 (src, TWISTER_MESSAGE_TYPE_SET_RESPONSE_CODE); 650 /* Put data into the envelope. */ 651 src->response_code = htonl ((uint32_t) new_rc); 652 /* Send message. */ 653 GNUNET_MQ_send (h->mq, env); 654 return op; 655 } 656 657 658 /* end of twister_api.c */