gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[GNUnet-SVN] [taler-twister] branch master updated (3513119 -> f717a7a)


From: gnunet
Subject: [GNUnet-SVN] [taler-twister] branch master updated (3513119 -> f717a7a)
Date: Sat, 17 Mar 2018 01:57:51 +0100

This is an automated email from the git hooks/post-receive script.

marcello pushed a change to branch master
in repository twister.

    from 3513119  need process-trait.
     new c4368a1  fix cli arg
     new 0fd827b  parse proxied response.
     new 8a3bf1c  get path parsing to compile.
     new fd60998  parsing proxied responses.
     new be48cb5  fix iteration over object path's parts.
     new 8fe0585  helper function.
     new 324eff4  fix actual object emptying.
     new bcb47c0  lazy check object/array path element.
     new 2d22025  help string
     new 8414403  fix object walking.
     new 3952ac7  fix double destruction of response.
     new 7bf67d2  test command to empty objects.
     new 06921d9  conditional delete.
     new dcad2ca  logging
     new b32820a  testing empty-object feature.
     new b6b7f46  fix proxied response headers.
     new 7aed390  moving object-killer into helper function.
     new 63c64f0  avoid duplicating tokenized string.
     new 540ac8a  renaming operation.
     new 67b9551  option name.
     new ef7127b  dup'ing path string.
     new c5156b4  object walker as separate function.
     new 1f2e546  add command to modify responses.
     new ed4f74e  add testing-lib command to modify responses.
     new 07b6e87  make mods cumulative
     new 8b914fd  remove number conversion, for now.
     new 8d7076f  fix last path token detection.
     new cd9a77b  new-line
     new 1dd44af  adding command for randomly truncating responses.
     new 9cb8e4a  body malformation test CMD.
     new 0f30e5a  implement upload data malformation.
     new c815c8c  fix headers preparation for malformed uploads.
     new 9dc568d  rely again on RNG.
     new f717a7a  leading slash when constructing twister url.

The 34 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 src/include/taler_twister_service.h       |  65 ++++
 src/include/taler_twister_testing_lib.h   |  60 ++++
 src/test/test_twister.sh                  |  77 +++-
 src/test/test_twister_webserver.c         |   6 +
 src/twister/taler-twister-service.c       | 560 ++++++++++++++++++++++++++----
 src/twister/taler-twister.c               | 145 +++++++-
 src/twister/testing_api_cmd_exec_client.c | 519 ++++++++++++++++++++++++++-
 src/twister/testing_api_helpers.c         |   2 +-
 src/twister/twister.h                     | 106 +++++-
 src/twister/twister_api.c                 | 153 ++++++++
 10 files changed, 1590 insertions(+), 103 deletions(-)

diff --git a/src/include/taler_twister_service.h 
b/src/include/taler_twister_service.h
index c9b5fb8..486baf2 100644
--- a/src/include/taler_twister_service.h
+++ b/src/include/taler_twister_service.h
@@ -76,6 +76,26 @@ struct TALER_TWISTER_Operation;
 
 
 /**
+ * Change the response field pointed by @a modify_path with
+ * @a modify_value.
+ *
+ * @param h twister instance to control
+ * @param modify_path object-like notation path to the object to
+ *        modify
+ * @param modify_value value to use for @a modify_path
+ * @param cb callback to call once twister gets this instruction.
+ * @param cb_cls closure for @a cb_callback
+ *
+ * @return operation handle.
+ */
+struct TALER_TWISTER_Operation *
+TALER_TWISTER_modify_path (struct TALER_TWISTER_Handle *h,
+                           const char *path,
+                           const char *value,
+                           GNUNET_SCHEDULER_TaskCallback cb,
+                           void *cb_cls);
+
+/**
  * Change the next response code to @a new_rc.
  *
  * @param h twister instance to control
@@ -91,6 +111,51 @@ TALER_TWISTER_change_response_code
    GNUNET_SCHEDULER_TaskCallback cb,
    void *cb_cls);
 
+/**
+ * Randomly truncate the response.
+ *
+ * @param h twister instance to control
+ * @param cb function to call once twister is ready
+ * @param cb_cls closure for @a cb
+ * @return operation handle (to possibly abort)
+ */
+struct TALER_TWISTER_Operation *
+TALER_TWISTER_malform
+  (struct TALER_TWISTER_Handle *h,
+   GNUNET_SCHEDULER_TaskCallback cb,
+   void *cb_cls);
+
+/**
+ * Randomly truncate the request.
+ *
+ * @param h twister instance to control
+ * @param cb function to call once twister is ready
+ * @param cb_cls closure for @a cb
+ * @return operation handle (to possibly abort)
+ */
+struct TALER_TWISTER_Operation *
+TALER_TWISTER_malform_upload
+  (struct TALER_TWISTER_Handle *h,
+   GNUNET_SCHEDULER_TaskCallback cb,
+   void *cb_cls);
+
+/**
+ * Delete the object pointed to by @a path.
+ *
+ * @param h twister instance to control
+ * @param path object-like notation to point the object to be
+          deleted.  E.g., the path "f1.f2.0" will delete the object
+          {"f1": {"f2": [{"to be": "deleted"}]}}.
+ * @param cb function to call once twister is ready
+ * @param cb_cls closure for @a cb
+ * @return operation handle (to possibly abort)
+ */
+struct TALER_TWISTER_Operation *
+TALER_TWISTER_delete_path
+  (struct TALER_TWISTER_Handle *h,
+   const char *path,
+   GNUNET_SCHEDULER_TaskCallback cb,
+   void *cb_cls);
 
 /**
  * Abort operation.  Twister behavior may then include the
diff --git a/src/include/taler_twister_testing_lib.h 
b/src/include/taler_twister_testing_lib.h
index 597dc3a..2dde7f5 100644
--- a/src/include/taler_twister_testing_lib.h
+++ b/src/include/taler_twister_testing_lib.h
@@ -47,5 +47,65 @@ TALER_TESTING_cmd_hack_response_code (const char *label,
                                       unsigned int http_status);
 
 
+/**
+ * This command deletes the JSON object contained in the
+ * response and pointed by @a path.
+ *
+ * @param label command label
+ * @param config_filename configuration filename.
+ * @param path object-like path notation to point the object
+ *        to be made empty.  "f1.0.f2" will empty the object
+ *        {"f1": [{"f2": "I'll be deleted"}]}.
+ *
+ * @return the command
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_delete_object (const char *label,
+                                 const char *config_filename,
+                                 const char *path);
+
+
+/**
+ * This command deletes the JSON object pointed by @a path.
+ *
+ * @param label command label
+ * @param config_filename configuration filename.
+ * @param path object-like path notation to point the object
+ *        to delete.
+ *
+ * @return the command
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_modify_object (const char *label,
+                                 const char *config_filename,
+                                 const char *path,
+                                 const char *value);
+
+
+/**
+ * This command makes the next response randomly malformed
+ * (by truncating it).
+ *
+ * @param label command label
+ * @param config_filename configuration filename.
+ *
+ * @return the command
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_malform_response (const char *label,
+                                    const char *config_filename);
+
+/**
+ * This command makes the next request randomly malformed
+ * (by truncating it).
+ *
+ * @param label command label
+ * @param config_filename configuration filename.
+ *
+ * @return the command
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_malform_request (const char *label,
+                                   const char *config_filename);
 
 #endif
diff --git a/src/test/test_twister.sh b/src/test/test_twister.sh
index f7d278f..1413a0d 100755
--- a/src/test/test_twister.sh
+++ b/src/test/test_twister.sh
@@ -1,6 +1,6 @@
 #!/bin/sh
 
-# Steps:
+TWISTER_URL="http://localhost:8888/";
 
 # Launch the Web server.
 ./test_twister_webserver &
@@ -10,21 +10,80 @@ web_server_pid=$!
 taler-twister-service -c ./test_twister.conf &
 twister_service_pid=$!
 
-# Use the Twister-APIs to hack the response code.
+# hack the response code.
 taler-twister -c ./test_twister.conf --responsecode 202
 
-# 4) Use curl to GET /something
-status_code=$(curl -s http://localhost:8888/ -o /dev/null \
+status_code=$(curl -s ${TWISTER_URL} -o /dev/null \
   -w "%{http_code}")
 
-# shutdown twister and webserver
-kill $web_server_pid
-kill $twister_service_pid
-
-# check status code has been hacked
+# check status code was hacked
 if ! test 202 = $status_code; then
   echo "Response code has not been hacked."
+  kill $web_server_pid
+  kill $twister_service_pid
+  exit 1
+fi
+
+# malform response.
+taler-twister -c ./test_twister.conf --malform
+malformed_body=$(curl -s ${TWISTER_URL})
+
+# check response body has been emptied
+if test '{"hello":[{"this":"browser!"}],"when":"today"}' = \
+    "$malformed_body"; then
+  printf "Response body (%s) has not been emptied as expected\n" \
+    "$malformed_body"
+  kill $web_server_pid
+  kill $twister_service_pid
+  exit 1
+fi
+
+
+# delete object.
+taler-twister -c ./test_twister.conf -d "hello.0"
+emptied_body=$(curl -s ${TWISTER_URL})
+
+# check response body has been emptied
+if ! test '{"hello":[],"when":"today"}' = "$emptied_body"; then
+  printf "Response body (%s) has not been emptied as expected\n" \
+    "$emptied_body"
+  kill $web_server_pid
+  kill $twister_service_pid
   exit 1
 fi
 
+# set field
+taler-twister -c ./test_twister.conf \
+  --modobject "hello" \
+  --value "fake"
+modobject_body=$(curl -s ${TWISTER_URL})
+
+if ! test \
+    '{"hello":"fake","when":"today"}' = "$modobject_body"; then
+  printf "Response body (%s) has not been modified as expected\n" \
+    "$modobject_body"
+  kill $web_server_pid
+  kill $twister_service_pid
+  exit 1
+fi
+
+# check if cumulative mods work.
+taler-twister -c ./test_twister.conf \
+  --modobject "hello" -V "world" \
+  --deleteobject "when"
+cum_mods=$(curl -s ${TWISTER_URL})
+
+if ! test '{"hello":"world"}' = $cum_mods; then
+  printf "Response body (%s) has not been" \
+         " cumulative-modified as expected\n" \
+    "$cum_mods"
+  kill $web_server_pid
+  kill $twister_service_pid
+  exit 1
+fi
+
+# shutdown twister and webserver
+kill $web_server_pid
+kill $twister_service_pid
+
 exit 0
diff --git a/src/test/test_twister_webserver.c 
b/src/test/test_twister_webserver.c
index 238155a..7616e1e 100644
--- a/src/test/test_twister_webserver.c
+++ b/src/test/test_twister_webserver.c
@@ -43,7 +43,13 @@ answer_to_connection
    const char *version, const char *upload_data,
    size_t *upload_data_size, void **con_cls)
 {
+  #if 0
   const char *page = "<html><body>Hello, browser!</body></html>";
+  #endif
+
+  const char *page = "{\"hello\": [{\"this\": \"browser!\"}],"\
+                     "\"when\": \"today\"}";
+
   struct MHD_Response *response;
   int ret;
   (void)cls;               /* Unused. Silent compiler warning. */
diff --git a/src/twister/taler-twister-service.c 
b/src/twister/taler-twister-service.c
index eaf77a4..6104fda 100644
--- a/src/twister/taler-twister-service.c
+++ b/src/twister/taler-twister-service.c
@@ -1,23 +1,24 @@
 /*
-     This file is part of GNUnet.
-     Copyright (C) 2012-2014 GNUnet e.V.
-     Copyright (C) 2018 Taler Systems SA
-
-     GNUnet is free software; you can redistribute it and/or
-     modify it under the terms of the GNU General Public License
-     as published by the Free Software Foundation; either version
-     3, or (at your option) any later version.
-
-     GNUnet is distributed in the hope that it will be useful, but
-     WITHOUT ANY WARRANTY; without even the implied warranty of
-     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-     GNU General Public License for more details.
-
-     You should have received a copy of the GNU General Public
-     License along with GNUnet; see the file COPYING.  If not,
-     write to the Free Software Foundation, Inc., 51 Franklin
-     Street, Fifth Floor, Boston, MA 02110-1301, USA.
+  This file is part of GNUnet.
+  Copyright (C) 2012-2014 GNUnet e.V.
+  Copyright (C) 2018 Taler Systems SA
+
+  GNUnet is free software; you can redistribute it and/or
+  modify it under the terms of the GNU General Public License
+  as published by the Free Software Foundation; either version
+  3, or (at your option) any later version.
+
+  GNUnet is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public
+  License along with GNUnet; see the file COPYING.  If not,
+  write to the Free Software Foundation, Inc., 51 Franklin
+  Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */
+
 /**
  * @author Martin Schanzenbach
  * @author Christian Grothoff
@@ -35,6 +36,9 @@
 #endif
 #include <gnunet/gnunet_util_lib.h>
 #include "twister.h"
+#include <jansson.h>
+#include <microhttpd.h>
+#include <taler/taler_util.h>
 
 
 /**
@@ -47,6 +51,11 @@
 #define LOG_CURL_EASY(level,fun,rc) \
   GNUNET_log(level, _("%s failed at %s:%d: `%s'\n"), fun, __FILE__, __LINE__, 
curl_easy_strerror (rc))
 
+/**
+ * Constant value to make sure the
+ * final } from JSONs gets dropped, when truncating.
+ */
+#define TRUNC_SIZE 4
 
 /* ******** Datastructures for HTTP handling ********** */
 
@@ -129,6 +138,11 @@ struct HttpRequest
   char *io_buf;
 
   /**
+   * Hold the response obtained by modifying the original one.
+   */
+  struct MHD_Response *mod_response;
+
+  /**
    * MHD response object for this request.
    */
   struct MHD_Response *response;
@@ -139,6 +153,11 @@ struct HttpRequest
   char *url;
 
   /**
+   * Response's JSON.
+   */
+  json_t *json;
+
+  /**
    * Handle to cURL
    */
   CURL *curl;
@@ -184,7 +203,6 @@ struct HttpRequest
 
 /* *********************** Globals **************************** */
 
-
 /**
  * The cURL download task (curl multi API).
  */
@@ -229,9 +247,44 @@ static char *target_server_base_url;
  */
 static unsigned int hack_response_code;
 
+/**
+ * Will point to a JSON object to delete 
+ */
+static char delete_path[TWISTER_PATH_LENGTH] = {'\0'};
 
+/**
+ * Will point to a JSON object to modify 
+ */
+static char modify_path[TWISTER_PATH_LENGTH] = {'\0'};
 
-/* ************************* Global helpers ********************* */
+/**
+ * If true, will randomly truncate the request body
+ * to upload to the proxied service.
+ */
+static unsigned int malform_upload = GNUNET_NO;
+
+/**
+ * If true, will randomly truncate the response before
+ * returning to the client.
+ */
+static unsigned int malform = GNUNET_NO;
+
+/**
+ * New value.
+ */
+static char modify_value[TWISTER_VALUE_LENGTH];
+
+/**
+ * Size of the malformed body to be uploaded to the
+ * proxied service.
+ */
+static size_t malformed_size;
+
+/**
+ * Will point to the path if it has to be deleted
+ */
+
+/* ********************* Global helpers ****************** */
 
 
 /**
@@ -245,11 +298,13 @@ run_mhd_now (void);
 
 
 /**
- * We're getting an HTTP response header from cURL.  Convert it to the
- * MHD response headers.  Mostly copies the headers, but makes special
- * adjustments based on control requests.
+ * We're getting an HTTP response header from cURL.
+ * Convert it to the MHD response headers.  Mostly copies
+ * the headers, but makes special adjustments based on
+ * control requests.
  *
- * @param buffer curl buffer with a single line of header data; not 
0-terminated!
+ * @param buffer curl buffer with a single
+ *        line of header data; not 0-terminated!
  * @param size curl blocksize
  * @param nmemb curl blocknumber
  * @param cls our `struct HttpRequest *`
@@ -413,7 +468,7 @@ static int
 create_mhd_response_from_hr (struct HttpRequest *hr)
 {
   long resp_code;
-  struct HttpResponseHeader *header;
+  json_error_t error;
 
   if (NULL != hr->response)
   {
@@ -429,21 +484,12 @@ create_mhd_response_from_hr (struct HttpRequest *hr)
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
               "Creating MHD response with code %u\n",
               (unsigned int) resp_code);
+
   hr->response_code = resp_code;
-  hr->response = MHD_create_response_from_buffer (hr->io_len,
-                                                 hr->io_buf,
-                                                 MHD_RESPMEM_MUST_COPY);
-  for (header = hr->header_head; NULL != header; header = header->next)
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-               "Adding MHD response header %s->%s\n",
-               header->type,
-               header->value);
-    GNUNET_break (MHD_YES ==
-                  MHD_add_response_header (hr->response,
-                                           header->type,
-                                           header->value));
-  }
+  hr->json = json_loadb (hr->io_buf,
+                         hr->io_len,
+                         JSON_DECODE_ANY,
+                         &error);
 #if 0
   if (NULL != hr->leho)
   {
@@ -467,8 +513,8 @@ create_mhd_response_from_hr (struct HttpRequest *hr)
 
 
 /**
- * Handle response payload data from cURL.  Copies it into our `io_buf` to make
- * it available to MHD.
+ * Handle response payload data from cURL.
+ * Copies it into our `io_buf` to make it available to MHD.
  *
  * @param ptr pointer to the data
  * @param size number of blocks of data
@@ -506,8 +552,8 @@ curl_download_cb (void *ptr,
 
 
 /**
- * cURL callback for uploaded (PUT/POST) data.  Copies from our `io_buf`
- * to make it available to curl.
+ * cURL callback for uploaded (PUT/POST) data.
+ * Copies from our `io_buf` to make it available to curl.
  *
  * @param buf where to write the data
  * @param size number of bytes per member
@@ -542,6 +588,7 @@ curl_upload_cb (void *buf,
   GNUNET_memcpy (buf,
                 hr->io_buf,
                 to_copy);
+  /* shift remaining data back to the beginning of the buffer.  */
   memmove (hr->io_buf,
            &hr->io_buf[to_copy],
            hr->io_len - to_copy);
@@ -556,7 +603,7 @@ curl_upload_cb (void *buf,
 }
 
 
-/* ************************** main loop of cURL interaction ****************** 
*/
+/* ************** main loop of cURL interaction ************* */
 
 
 /**
@@ -615,7 +662,8 @@ curl_download_prepare ()
   if (-1 == to)
     rtime = GNUNET_TIME_UNIT_FOREVER_REL;
   else
-    rtime = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, to);
+    rtime = GNUNET_TIME_relative_multiply
+      (GNUNET_TIME_UNIT_MILLISECONDS, to);
   if (-1 != max)
   {
     grs = GNUNET_NETWORK_fdset_create ();
@@ -751,9 +799,9 @@ curl_task_download (void *cls)
 
 
 /**
- * Read HTTP request header field from the request.  Copies the fields
- * over to the 'headers' that will be given to curl.  However,
- * substitutions may be applied.
+ * Read HTTP request header field from the request.
+ * Copies the fields over to the 'headers' that will be
+ * given to curl.  However, substitutions may be applied.
  *
  * @param cls our `struct HttpRequest`
  * @param kind value kind
@@ -769,6 +817,7 @@ con_val_iter (void *cls,
 {
   struct HttpRequest *hr = cls;
   char *hdr;
+  char *new_value = NULL;
 
   (void) kind;
 #if 0
@@ -776,6 +825,19 @@ con_val_iter (void *cls,
        (NULL != hr->leho) )
     value = hr->leho;
 #endif
+
+  if (GNUNET_YES == malform_upload)
+  {
+    if (0 == strcmp ("Content-Length", key)) 
+    {
+      GNUNET_asprintf (&new_value,
+                       "%lu",
+                       malformed_size);
+      value = new_value;
+      malform_upload = GNUNET_NO;
+    }
+  }
+
   GNUNET_asprintf (&hdr,
                    "%s: %s",
                    key,
@@ -786,9 +848,210 @@ con_val_iter (void *cls,
   hr->headers = curl_slist_append (hr->headers,
                                    hdr);
   GNUNET_free (hdr);
+  GNUNET_free_non_null (new_value);
   return MHD_YES;
 }
 
+/**
+ * Walk a JSON object (tipically a response we got
+ * from a proxied service), and return the last token
+ * from the path, plus the parent object of the object
+ * targeted by path.
+ *
+ * @param path the path pointing to a object in the response
+ * @param[out] parent will point to the parent of the targeted
+ *             object
+ * @param[out] response parsed original response, to be decref'd
+ *             by the caller.
+ * @param[out] target last token of the path.  E.g. given a x.y.z,
+ *             will point to 'z'.  To be freed by the caller.
+ * @param hr contains original response.
+ *
+ * @return GNUNET_OK if @a path was valid.
+ */
+static unsigned int
+walk_response_object (const char *path,
+                      json_t **parent,
+                      char **target,
+                      struct HttpRequest *hr)
+{
+
+  json_t *element;
+  json_t *cur;
+  char *token;
+  char *last_token;
+  char *path_dup;
+
+  GNUNET_asprintf (&path_dup,
+                   ".%s", /* Make sure path starts with dot. */
+                   path);
+
+  last_token = strrchr (path_dup, '.') + 1;
+  /* Give first nondelim char. */
+  token = strtok (path_dup, ".");
+  element = hr->json;
+
+  while (last_token != token)
+  {
+    TALER_LOG_DEBUG ("token/last_token: address@hidden / address@hidden",
+                     token, token,
+                     last_token, last_token);
+    if (NULL == token)
+      return GNUNET_SYSERR; // path was ".", refuse to process it.
+    
+    if (NULL != (cur = json_object_get (element,
+                                        token)))
+    {
+      element = cur;
+      token = strtok (NULL, ".");
+      continue;
+    }
+
+    if (NULL != (cur = json_array_get (element,
+                                       (unsigned int) strtoul
+                                         (token, NULL, 10))))
+    {
+      element = cur;
+      token = strtok (NULL, ".");
+      continue;
+    }
+    TALER_LOG_WARNING ("Path token '%s' not found\n",
+                       token);
+    GNUNET_free (path_dup);
+
+    return GNUNET_NO;
+  }
+
+  if ( (NULL == json_object_get (element,
+                                 last_token) ) &&
+       /* NOTE: if token is bad but converts to either 0, or
+        * ULONG max AND the array has such a index, then this
+        * test won't detect any error. Likewise, the method for
+        * deleting/modifying the response will operate on that
+        * same random array element. */
+       (NULL == json_array_get (element, (unsigned int) strtoul
+         (token, NULL, 10))) )
+  {
+    TALER_LOG_WARNING ("(Last) path token '%s' not found\n",
+                       last_token); 
+    GNUNET_free (path_dup);
+    return GNUNET_NO;
+  }
+
+  *target = GNUNET_strdup (last_token);
+  *parent = element;
+  GNUNET_free (path_dup);
+
+  return GNUNET_OK;
+}
+
+/**
+ * TODO.
+ */
+static void
+modify_object (struct MHD_Connection *con,
+               struct HttpRequest *hr)
+{
+
+  char *target;
+  unsigned int ret_modify;
+  json_t *parent;
+  json_t *new_value;
+  json_error_t error;
+
+  if (GNUNET_OK != walk_response_object (modify_path,
+                                         &parent,
+                                         &target,
+                                         hr))
+    return;
+
+  /* At this point, the parent and the target are pointed to. */
+  
+  if (NULL != (new_value = json_loads (modify_value,
+                                       JSON_REJECT_DUPLICATES
+                                       | JSON_DISABLE_EOF_CHECK,
+                                       &error)))
+  {
+    TALER_LOG_DEBUG ("New value parsed as object/array\n");
+    goto perform_modbody;
+  }
+
+  if (NULL != (new_value = json_string (modify_value)))
+  {
+    TALER_LOG_DEBUG ("New value parsed as string\n");
+    goto perform_modbody;
+  }
+
+  TALER_LOG_ERROR ("Unvalid new value given: %s\n", modify_value);
+  modify_path[0] = '\0'; 
+  GNUNET_free (target);
+  json_decref (new_value);
+  return;
+
+  perform_modbody:
+    ret_modify = -1;
+    if (json_is_object (parent))
+      ret_modify = json_object_set (parent,
+                                    target,
+                                    new_value);
+    if (json_is_array (parent))
+      ret_modify = json_array_set_new
+        (parent,
+         (unsigned int) strtoul (target, NULL, 10),
+         new_value);
+    if (-1 == ret_modify)
+      TALER_LOG_WARNING ("Could not replace '%s'\n", target);
+    
+    modify_path[0] = '\0';
+    GNUNET_free (target);
+    json_decref (new_value);
+    return;
+}
+
+/**
+ * Delete object within the proxied response.
+ * Always queues a response; only deletes the object if it is
+ * found within the response, otherwise return it verbatim (but
+ * will look for it into the next response).  Will flush the
+ * operation once the wanted object has been found.
+ *
+ * @return MHD_YES / MHD_NO depending on successful / failing
+ * response queueing.
+ */
+static void
+delete_object (struct MHD_Connection *con,
+               struct HttpRequest *hr)
+{
+  char *target;
+  json_t *parent;
+
+  if (GNUNET_OK != walk_response_object (delete_path,
+                                         &parent,
+                                         &target,
+                                         hr))
+    return;
+
+  /* here, element is the parent of the element to be deleted. */
+  int ret_deletion = -1;
+
+  if (json_is_object (parent))
+    ret_deletion = json_object_del (parent, target);
+
+  if (json_is_array (parent))
+  {
+    ret_deletion = json_array_remove
+      (parent, (unsigned int) strtoul (target,
+                                       NULL,
+                                       10));
+  }
+  if (-1 == ret_deletion)
+    TALER_LOG_WARNING ("Could not delete '%s'\n", target);
+
+  delete_path[0] = '\0';
+  GNUNET_free (target);
+  return;
+}
+
 
 /**
  * Main MHD callback for handling requests.
@@ -825,10 +1088,14 @@ create_response (void *cls,
                  void **con_cls)
 {
   struct HttpRequest *hr = *con_cls;
+  struct HttpResponseHeader *header;
 
   (void) cls;
   (void) url;
 
+  char *body;
+  size_t body_len;
+
   if (NULL == hr)
   {
     GNUNET_break (0);
@@ -849,24 +1116,46 @@ create_response (void *cls,
     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                 "Processing %u bytes UPLOAD\n",
                (unsigned int) *upload_data_size);
+
+    /* Grow the buffer if remaining space isn't enough.  */
     if (hr->io_size - hr->io_len < *upload_data_size)
     {
+      /* How can this assertion be false?  */
       GNUNET_assert (hr->io_size * 2 + 1024 > hr->io_size);
+      /* This asserts that upload_data_size > 0, ?  */
       GNUNET_assert (*upload_data_size + hr->io_len > hr->io_len);
+
       GNUNET_array_grow (hr->io_buf,
                         hr->io_size,
                         GNUNET_MAX
                            (hr->io_size * 2 + 1024,
                            *upload_data_size + hr->io_len));
     }
+
+    /* Finally copy upload data.  */
     GNUNET_memcpy (&hr->io_buf[hr->io_len],
                    upload_data,
                    *upload_data_size);
+
+    /* Note, each request starts with fresh and
+     * equal-to-zero io_len (?).  */
     hr->io_len += *upload_data_size;
     *upload_data_size = 0;
     return MHD_YES;
   }
 
+  /* Malform request body.  Note, this flag will be
+   * cleared only after having set the right malformed
+   * body lenght in the request headers.  */
+  if (GNUNET_YES == malform_upload)
+  {
+    TALER_LOG_DEBUG
+      ("Will (badly) truncate the request.\n");
+    malformed_size = GNUNET_CRYPTO_random_u32
+      (GNUNET_CRYPTO_QUALITY_WEAK, hr->io_len);
+    hr->io_len = malformed_size;
+  }
+
   /* Upload finished, generate curl request */
   if (NULL == hr->curl)
   {
@@ -874,9 +1163,12 @@ create_response (void *cls,
                 "Generating curl request\n");
     hr->curl = curl_easy_init ();
     if (NULL == hr->curl)
+    {
+      TALER_LOG_ERROR ("Could not init the curl handle\n");
       return MHD_queue_response (con,
                                  MHD_HTTP_INTERNAL_SERVER_ERROR,
                                  curl_failure_response);
+    }
     if (0 != hr->io_len)
       curl_easy_setopt (hr->curl,
                        CURLOPT_POSTFIELDSIZE,
@@ -1039,6 +1331,8 @@ create_response (void *cls,
                       CURLOPT_HTTPHEADER,
                       hr->headers);
     curl_download_prepare ();
+
+    /* means (?) upload is over.  */
     if (0 == hr->io_len)
       hr->state = REQUEST_STATE_DOWNLOAD_STARTED;
     return MHD_YES;
@@ -1047,26 +1341,76 @@ create_response (void *cls,
   if (REQUEST_STATE_DOWNLOAD_DONE != hr->state)
     return MHD_YES; /* wait for curl */
   
-  if (NULL == hr->response)
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-               "Still waiting for response to be ready\n");
-    return MHD_YES;
-  }
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Queueing response with MHD\n");
   if (0 != hack_response_code)
   {
     hr->response_code = hack_response_code;
     hack_response_code = 0; /* reset for next request */
   }
+
+  if ('\0' != delete_path[0])
+  {
+    TALER_LOG_DEBUG ("Will delete path: %s\n",
+                     delete_path);
+    delete_object (con, hr);
+  }
+
+  if ('\0' != modify_path[0])
+  {
+    TALER_LOG_DEBUG ("Will modify path: %s to value %s\n",
+                     modify_path,
+                     modify_value);
+    modify_object (con, hr);
+  }
+
+  body_len = hr->io_len;
+  body = hr->io_buf;
+
+  if (NULL != hr->json)
+  {
+    TALER_LOG_DEBUG ("Parsing final JSON.\n");
+    body = json_dumps (hr->json, JSON_COMPACT);
+    body_len = strlen (body);
+    json_decref (hr->json);
+  }
+
+  if (GNUNET_YES == malform)
+  {
+    size_t fake_len;
+
+    TALER_LOG_DEBUG
+      ("Will (badly) truncate the response.\n");
+    fake_len = GNUNET_CRYPTO_random_u32
+      (GNUNET_CRYPTO_QUALITY_WEAK, body_len);
+    body_len = fake_len; 
+    malform = GNUNET_NO;
+  }
+
+  hr->response = MHD_create_response_from_buffer
+    (body_len,
+     body,
+     MHD_RESPMEM_MUST_COPY);
+
+  GNUNET_free (body);
+  for (header = hr->header_head;
+       NULL != header;
+       header = header->next)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+               "Adding MHD response header %s->%s\n",
+               header->type,
+               header->value);
+    GNUNET_break
+      (MHD_YES == MHD_add_response_header (hr->response,
+                                           header->type,
+                                           header->value));
+  }
   return MHD_queue_response (con,
                              hr->response_code,
                              hr->response);
 }
 
 
-/* ******************** MHD HTTP setup and event loop ******************** */
+/* ************ MHD HTTP setup and event loop *************** */
 
 
 /**
@@ -1109,6 +1453,11 @@ mhd_completed_cb (void *cls,
     hr->curl = NULL;
     hr->io_len = 0;
   }
+
+  if ((NULL != hr->mod_response))
+    /* Destroy hacked responses. */
+    MHD_destroy_response (hr->mod_response);
+
   if ( (NULL != hr->response) &&
        (curl_failure_response != hr->response) )
     /* Destroy non-error responses... (?) */
@@ -1201,8 +1550,9 @@ do_httpd (void *cls);
 
 /**
  * Schedule MHD.  This function should be called initially when an
- * MHD is first getting its client socket, and will then automatically
- * always be called later whenever there is work to be done.
+ * MHD is first getting its client socket, and will then
+ * automatically always be called later whenever there is work to
+ * be done.
  */
 static void
 schedule_httpd (void)
@@ -1350,6 +1700,8 @@ run (void *cls,
   (void) cls;
   (void) service;
   cfg = c;
+  delete_path[0] = '\0';
+
   if (0 != curl_global_init (CURL_GLOBAL_WIN32))
   {
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
@@ -1470,6 +1822,75 @@ send_acknowledgement (struct GNUNET_SERVICE_Client *c)
   GNUNET_SERVICE_client_continue (c);
 }
 
+/**
+ * Control handler for malforming responses.
+ *
+ * @param cls message queue for sending replies
+ * @param src received message
+ */
+static void
+handle_malform (void *cls,
+                const struct TWISTER_Malform *src)
+{
+  struct GNUNET_SERVICE_Client *c = cls;
+
+  TALER_LOG_DEBUG ("Flagging response malformation\n");
+  malform = GNUNET_YES;
+  send_acknowledgement (c);
+}
+
+/**
+ * Control handler for malforming requests.
+ *
+ * @param cls message queue for sending replies
+ * @param src received message
+ */
+static void
+handle_malform_upload (void *cls,
+                       const struct TWISTER_Malform *src)
+{
+  struct GNUNET_SERVICE_Client *c = cls;
+
+  TALER_LOG_DEBUG ("Flagging request malformation\n");
+  malform_upload = GNUNET_YES;
+  send_acknowledgement (c);
+}
+
+/**
+ * Control handler for deleting JSON objects
+ *
+ * @param cls message queue for sending replies
+ * @param src received message
+ */
+static void
+handle_modify_path (void *cls,
+                    const struct TWISTER_ModifyPath *src)
+{
+  struct GNUNET_SERVICE_Client *c = cls;
+
+  strcpy (modify_path, src->path);
+  strcpy (modify_value, src->value);
+
+  send_acknowledgement (c);
+}
+
+
+/**
+ * Control handler for deleting JSON objects
+ *
+ * @param cls message queue for sending replies
+ * @param src received message
+ */
+static void
+handle_delete_path (void *cls,
+                    const struct TWISTER_DeletePath *src)
+{
+  struct GNUNET_SERVICE_Client *c = cls;
+
+  strcpy (delete_path, src->path);
+  send_acknowledgement (c);
+}
+
 
 /**
  * Control handler for changing the response code
@@ -1502,6 +1923,27 @@ GNUNET_SERVICE_MAIN
                          TWISTER_MESSAGE_TYPE_SET_RESPONSE_CODE,
                          struct TWISTER_SetResponseCode,
                          NULL),
+
+ GNUNET_MQ_hd_fixed_size (modify_path,
+                         TWISTER_MESSAGE_TYPE_MODIFY_PATH,
+                         struct TWISTER_ModifyPath,
+                         NULL),
+
+ GNUNET_MQ_hd_fixed_size (malform_upload,
+                         TWISTER_MESSAGE_TYPE_MALFORM_UPLOAD,
+                         struct TWISTER_Malform,
+                         NULL),
+
+ GNUNET_MQ_hd_fixed_size (malform,
+                         TWISTER_MESSAGE_TYPE_MALFORM,
+                         struct TWISTER_Malform,
+                         NULL),
+
+ GNUNET_MQ_hd_fixed_size (delete_path,
+                         TWISTER_MESSAGE_TYPE_DELETE_PATH,
+                         struct TWISTER_DeletePath,
+                         NULL),
+
  GNUNET_MQ_handler_end ());
 
 
diff --git a/src/twister/taler-twister.c b/src/twister/taler-twister.c
index 7abef7b..ce39104 100644
--- a/src/twister/taler-twister.c
+++ b/src/twister/taler-twister.c
@@ -1,22 +1,22 @@
 /*
-      This file is part of Taler
-      Copyright (C) 2008--2014, 2016 GNUnet e.V.
-      Copyright (C) 2018 Taler Systems SA
-
-      Taler is free software; you can redistribute it and/or
-      modify it under the terms of the GNU General Public License
-      as published by the Free Software Foundation; either version
-      3, or (at your option) any later version.
-
-      Taler is distributed in the hope that it will be useful, but
-      WITHOUT ANY WARRANTY; without even the implied warranty of
-      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-      See the GNU General Public License for more details.
-
-      You should have received a copy of the GNU General Public
-      License along with Taler; see the file COPYING.  If not,
-      write to the Free Software Foundation, Inc., 51 Franklin
-      Street, Fifth Floor, Boston, MA 02110-1301, USA.
+  This file is part of Taler
+  Copyright (C) 2008--2014, 2016 GNUnet e.V.
+  Copyright (C) 2018 Taler Systems SA
+
+  Taler is free software; you can redistribute it and/or
+  modify it under the terms of the GNU General Public License
+  as published by the Free Software Foundation; either version
+  3, or (at your option) any later version.
+
+  Taler is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+  See the GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public
+  License along with Taler; see the file COPYING.  If not,
+  write to the Free Software Foundation, Inc., 51 Franklin
+  Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */
 
 /**
@@ -45,6 +45,34 @@ static int status;
 static unsigned int hack_response_code;
 
 /**
+ * Path to a object to be deleted.  The format is "object-like":
+ * path "field0.field1.2.field3" will delete the following object:
+ *
+ * {"field1": {"field2": [0, 1, {"field3": "TO DELETE!"}]}}.
+ */
+static char *delete_path;
+
+/**
+ * If true, will randomly truncate the proxied response.
+ */
+static int malform_response;
+
+/**
+ * If true, will randomly truncate the proxied request.
+ */
+static int malform_upload;
+
+/**
+ * Path to the object to modify.
+ */
+static char *modify_path;
+
+/**
+ * New value for the object pointed by `modify_path`.
+ */
+static char *modify_value;
+
+/**
  * This option is used to check whether the twister can accept
  * connections over the unix domain socket interface.  Used when
  * launching it to see if everything (?) is okay.
@@ -116,6 +144,20 @@ run (void *cls,
     return;
   }
 
+  if ( (0 != malform_upload) &&
+       (NULL != TALER_TWISTER_malform_upload
+         (tth,
+          &handle_acknowledgement,
+          NULL)))
+    num_ops++;
+
+  if ( (0 != malform_response) &&
+       (NULL != TALER_TWISTER_malform
+         (tth,
+          &handle_acknowledgement,
+          NULL)))
+    num_ops++;
+   
 
   if (0 != check_alive)
   {
@@ -133,6 +175,32 @@ run (void *cls,
 
   /* TODO: add other operations here */
 
+  if ( (NULL != delete_path) &&
+       (NULL != TALER_TWISTER_delete_path
+         (tth,
+          delete_path,
+          &handle_acknowledgement,
+          NULL)) )
+    num_ops++;
+
+  if (NULL != modify_path)
+  {
+    if (NULL == modify_value)
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                  "New value not given, give -V|--value also\n");
+      return;
+    }
+
+    if (NULL != TALER_TWISTER_modify_path
+        (tth,
+         modify_path,
+         modify_value,
+         &handle_acknowledgement,
+         NULL))
+      num_ops++;
+  }
+
   if (0 == num_ops)
   {
     fprintf (stderr, "No valid hacks specified!\n");
@@ -152,16 +220,55 @@ main (int argc,
       char *const *argv)
 {
   struct GNUNET_GETOPT_CommandLineOption options[] = {
+
+    GNUNET_GETOPT_option_string
+      ('m',
+       "modobject",
+       "PATH",
+       gettext_noop
+         ("Modify object pointed by PATH, require --value.\n"),
+       &modify_path),
+
+    GNUNET_GETOPT_option_string
+      ('V',
+       "value",
+       "VALUE",
+       gettext_noop
+         ("Make VALUE the new value of the field"
+          " pointed by PATH (--modfield).\n"),
+       &modify_value),
+
+    GNUNET_GETOPT_option_string
+      ('d',
+       "deleteobject",
+       "PATH",
+       gettext_noop
+         ("Delete the object pointed by PATH.\n"),
+       &delete_path),
+
     GNUNET_GETOPT_option_flag
       ('a',
        "checkalive",
        gettext_noop ("Check if twister accepts IPC connections\n"),
        &check_alive),
+
+    GNUNET_GETOPT_option_flag
+      ('U',
+       "malformupload",
+       gettext_noop ("Randomly truncate proxied request"),
+       &malform_upload),
+
+    GNUNET_GETOPT_option_flag
+      ('M',
+       "malform",
+       gettext_noop ("Randomly truncate proxied response"),
+       &malform_response),
+
     GNUNET_GETOPT_option_uint
       ('r',
        "responsecode",
        "STATUS",
-       gettext_noop ("set the next response code to STATUS"),
+       gettext_noop ("Set the next response code to STATUS"),
        &hack_response_code),
     GNUNET_GETOPT_OPTION_END
   };
diff --git a/src/twister/testing_api_cmd_exec_client.c 
b/src/twister/testing_api_cmd_exec_client.c
index b3075d3..b277106 100644
--- a/src/twister/testing_api_cmd_exec_client.c
+++ b/src/twister/testing_api_cmd_exec_client.c
@@ -29,6 +29,72 @@
 #include <taler/taler_testing_lib.h>
 #include "taler_twister_testing_lib.h"
 
+struct ModifyObjectState
+{
+  /**
+   * Process handle for the twister CLI client.
+   */
+  struct GNUNET_OS_Process *proc;
+
+  /**
+   * Object-like notation to the object to delete.
+   */
+  const char *path;
+
+  const char *value;
+
+  /**
+   * Config file name to pass to the CLI client.
+   */
+  const char *config_filename;
+
+};
+
+
+struct DeleteObjectState
+{
+  /**
+   * Process handle for the twister CLI client.
+   */
+  struct GNUNET_OS_Process *proc;
+
+  /**
+   * Object-like notation to the object to delete.
+   */
+  const char *path;
+
+  /**
+   * Config file name to pass to the CLI client.
+   */
+  const char *config_filename;
+};
+
+struct MalformRequestState
+{
+  /**
+   * Process handle for the twister CLI client.
+   */
+  struct GNUNET_OS_Process *proc;
+
+  /**
+   * Config file name to pass to the CLI client.
+   */
+  const char *config_filename;
+};
+
+struct MalformResponseState
+{
+  /**
+   * Process handle for the twister CLI client.
+   */
+  struct GNUNET_OS_Process *proc;
+
+  /**
+   * Config file name to pass to the CLI client.
+   */
+  const char *config_filename;
+};
+
 struct HackResponseCodeState
 {
   /**
@@ -122,7 +188,7 @@ hack_response_code_run (void *cls,
      "taler-twister",
      "taler-twister",
      "-c", hrcs->config_filename,
-     "-a", http_status,
+     "--responsecode", http_status,
      NULL);
   if (NULL == hrcs->proc)
   {
@@ -164,3 +230,454 @@ TALER_TESTING_cmd_hack_response_code (const char *label,
 
   return cmd;
 }
+
+/**
+ * Clean up after the command.  Run during forced termination
+ * (CTRL-C) or test failure or test success.
+ *
+ * @param cls closure
+ */
+static void
+delete_object_cleanup
+  (void *cls,
+   const struct TALER_TESTING_Command *cmd)
+{
+  struct DeleteObjectState *dos = cls;
+
+  if (NULL != dos->proc)
+  {
+    GNUNET_break (0 == GNUNET_OS_process_kill (dos->proc,
+                                               SIGKILL));
+    GNUNET_OS_process_wait (dos->proc);
+    GNUNET_OS_process_destroy (dos->proc);
+    dos->proc = NULL;
+  }
+  GNUNET_free (dos);
+}
+
+/**
+ * Extract information from a command that is useful for other
+ * commands.
+ *
+ * @param cls closure
+ * @param ret[out] result (could be anything)
+ * @param trait name of the trait
+ * @param selector more detailed information about which object
+ *                 to return in case there were multiple generated
+ *                 by the command
+ * @return #GNUNET_OK on success
+ */
+static int
+delete_object_traits (void *cls,
+                      void **ret,
+                      const char *trait,
+                      unsigned int index)
+{
+
+  struct DeleteObjectState *dos = cls;
+  struct TALER_TESTING_Trait traits[] = {
+    TALER_TESTING_make_trait_process (0, &dos->proc),
+    TALER_TESTING_trait_end ()
+  };
+
+  return TALER_TESTING_get_trait (traits,
+                                  ret,
+                                  trait,
+                                  index);
+}
+
+/**
+ * FIXME: document.
+ */
+static void
+delete_object_run (void *cls,
+                   const struct TALER_TESTING_Command *cmd,
+                   struct TALER_TESTING_Interpreter *is)
+{
+  struct DeleteObjectState *dos = cls;
+
+  dos->proc = GNUNET_OS_start_process (GNUNET_NO,
+                                       GNUNET_OS_INHERIT_STD_ALL,
+                                       NULL, NULL, NULL,
+                                       "taler-twister",
+                                       "taler-twister",
+                                       "-c", dos->config_filename,
+                                       "--deleteobject", dos->path,
+                                       NULL);
+  if (NULL == dos->proc)
+  {
+    GNUNET_break (0);
+    TALER_TESTING_interpreter_fail (is);
+    return;
+  }
+  TALER_TESTING_wait_for_sigchld (is);
+}
+
+/**
+ * TODO.
+ */
+static void
+modify_object_cleanup
+  (void *cls,
+   const struct TALER_TESTING_Command *cmd)
+{
+  struct ModifyObjectState *mos = cls;
+
+  if (NULL != mos->proc)
+  {
+    GNUNET_break (0 == GNUNET_OS_process_kill (mos->proc,
+                                               SIGKILL));
+    GNUNET_OS_process_wait (mos->proc);
+    GNUNET_OS_process_destroy (mos->proc);
+    mos->proc = NULL;
+  }
+  GNUNET_free (mos);
+}
+
+
+/**
+ * Extract information from a command that is useful for other
+ * commands.
+ *
+ * @param cls closure
+ * @param ret[out] result (could be anything)
+ * @param trait name of the trait
+ * @param selector more detailed information about which object
+ *                 to return in case there were multiple generated
+ *                 by the command
+ * @return #GNUNET_OK on success
+ */
+static int
+modify_object_traits (void *cls,
+                      void **ret,
+                      const char *trait,
+                      unsigned int index)
+{
+
+  struct ModifyObjectState *mos = cls;
+  struct TALER_TESTING_Trait traits[] = {
+    TALER_TESTING_make_trait_process (0, &mos->proc),
+    TALER_TESTING_trait_end ()
+  };
+
+  return TALER_TESTING_get_trait (traits,
+                                  ret,
+                                  trait,
+                                  index);
+}
+
+
+/**
+ * FIXME: document.
+ */
+static void
+modify_object_run (void *cls,
+                   const struct TALER_TESTING_Command *cmd,
+                   struct TALER_TESTING_Interpreter *is)
+{
+  struct ModifyObjectState *mos = cls;
+
+  mos->proc = GNUNET_OS_start_process (GNUNET_NO,
+                                       GNUNET_OS_INHERIT_STD_ALL,
+                                       NULL, NULL, NULL,
+                                       "taler-twister",
+                                       "taler-twister",
+                                       "-c", mos->config_filename,
+                                       "--modobject", mos->path,
+                                       "--value", mos->value,
+                                       NULL);
+  if (NULL == mos->proc)
+  {
+    GNUNET_break (0);
+    TALER_TESTING_interpreter_fail (is);
+    return;
+  }
+  TALER_TESTING_wait_for_sigchld (is);
+}
+
+
+
+/**
+ * This command deletes the JSON object pointed by @a path.
+ *
+ * @param label command label
+ * @param config_filename configuration filename.
+ * @param path object-like path notation to point the object
+ *        to delete.
+ *
+ * @return the command
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_delete_object (const char *label,
+                                 const char *config_filename,
+                                 const char *path)
+{
+  struct DeleteObjectState *dos;
+  struct TALER_TESTING_Command cmd;
+
+  dos = GNUNET_new (struct DeleteObjectState);
+  dos->path = path;
+  dos->config_filename = config_filename;
+
+  cmd.label = label;
+  cmd.run = &delete_object_run;
+  cmd.cleanup = &delete_object_cleanup;
+  cmd.traits = &delete_object_traits;
+  cmd.cls = dos;
+
+  return cmd;
+}
+
+
+/**
+ * TODO.
+ */
+static void
+malform_request_cleanup
+  (void *cls,
+   const struct TALER_TESTING_Command *cmd)
+{
+  struct MalformRequestState *mrs = cls;
+
+  if (NULL != mrs->proc)
+  {
+    GNUNET_break (0 == GNUNET_OS_process_kill (mrs->proc,
+                                               SIGKILL));
+    GNUNET_OS_process_wait (mrs->proc);
+    GNUNET_OS_process_destroy (mrs->proc);
+    mrs->proc = NULL;
+  }
+  GNUNET_free (mrs);
+}
+
+
+/**
+ * Extract information from a command that is useful for other
+ * commands.
+ *
+ * @param cls closure
+ * @param ret[out] result (could be anything)
+ * @param trait name of the trait
+ * @param selector more detailed information about which object
+ *                 to return in case there were multiple generated
+ *                 by the command
+ * @return #GNUNET_OK on success
+ */
+static int
+malform_request_traits (void *cls,
+                        void **ret,
+                        const char *trait,
+                        unsigned int index)
+{
+
+  struct MalformRequestState *mrs = cls;
+  struct TALER_TESTING_Trait traits[] = {
+    TALER_TESTING_make_trait_process (0, &mrs->proc),
+    TALER_TESTING_trait_end ()
+  };
+
+  return TALER_TESTING_get_trait (traits,
+                                  ret,
+                                  trait,
+                                  index);
+}
+
+
+/**
+ * FIXME: document.
+ */
+static void
+malform_request_run (void *cls,
+                     const struct TALER_TESTING_Command *cmd,
+                     struct TALER_TESTING_Interpreter *is)
+{
+  struct MalformRequestState *mrs = cls;
+
+  mrs->proc = GNUNET_OS_start_process (GNUNET_NO,
+                                       GNUNET_OS_INHERIT_STD_ALL,
+                                       NULL, NULL, NULL,
+                                       "taler-twister",
+                                       "taler-twister",
+                                       "-c", mrs->config_filename,
+                                       "--malformupload",
+                                       NULL);
+  if (NULL == mrs->proc)
+  {
+    GNUNET_break (0);
+    TALER_TESTING_interpreter_fail (is);
+    return;
+  }
+  TALER_TESTING_wait_for_sigchld (is);
+}
+
+
+/**
+ * TODO.
+ */
+static void
+malform_response_cleanup
+  (void *cls,
+   const struct TALER_TESTING_Command *cmd)
+{
+  struct MalformResponseState *mrs = cls;
+
+  if (NULL != mrs->proc)
+  {
+    GNUNET_break (0 == GNUNET_OS_process_kill (mrs->proc,
+                                               SIGKILL));
+    GNUNET_OS_process_wait (mrs->proc);
+    GNUNET_OS_process_destroy (mrs->proc);
+    mrs->proc = NULL;
+  }
+  GNUNET_free (mrs);
+}
+
+
+/**
+ * Extract information from a command that is useful for other
+ * commands.
+ *
+ * @param cls closure
+ * @param ret[out] result (could be anything)
+ * @param trait name of the trait
+ * @param selector more detailed information about which object
+ *                 to return in case there were multiple generated
+ *                 by the command
+ * @return #GNUNET_OK on success
+ */
+static int
+malform_response_traits (void *cls,
+                         void **ret,
+                         const char *trait,
+                         unsigned int index)
+{
+
+  struct MalformResponseState *mrs = cls;
+  struct TALER_TESTING_Trait traits[] = {
+    TALER_TESTING_make_trait_process (0, &mrs->proc),
+    TALER_TESTING_trait_end ()
+  };
+
+  return TALER_TESTING_get_trait (traits,
+                                  ret,
+                                  trait,
+                                  index);
+}
+
+
+/**
+ * FIXME: document.
+ */
+static void
+malform_response_run (void *cls,
+                      const struct TALER_TESTING_Command *cmd,
+                      struct TALER_TESTING_Interpreter *is)
+{
+  struct MalformResponseState *mrs = cls;
+
+  mrs->proc = GNUNET_OS_start_process (GNUNET_NO,
+                                       GNUNET_OS_INHERIT_STD_ALL,
+                                       NULL, NULL, NULL,
+                                       "taler-twister",
+                                       "taler-twister",
+                                       "-c", mrs->config_filename,
+                                       "--malform",
+                                       NULL);
+  if (NULL == mrs->proc)
+  {
+    GNUNET_break (0);
+    TALER_TESTING_interpreter_fail (is);
+    return;
+  }
+  TALER_TESTING_wait_for_sigchld (is);
+}
+
+/**
+ * This command makes the next request randomly malformed
+ * (by truncating it).
+ *
+ * @param label command label
+ * @param config_filename configuration filename.
+ *
+ * @return the command
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_malform_request (const char *label,
+                                   const char *config_filename)
+{
+  struct MalformRequestState *mrs;
+  struct TALER_TESTING_Command cmd;
+
+  mrs = GNUNET_new (struct MalformRequestState);
+  mrs->config_filename = config_filename;
+
+  cmd.label = label;
+  cmd.run = &malform_request_run;
+  cmd.cleanup = &malform_request_cleanup;
+  cmd.traits = &malform_request_traits;
+  cmd.cls = mrs;
+
+  return cmd;
+}
+
+/**
+ * This command makes the next response randomly malformed
+ * (by truncating it).
+ *
+ * @param label command label
+ * @param config_filename configuration filename.
+ *
+ * @return the command
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_malform_response (const char *label,
+                                    const char *config_filename)
+{
+  struct MalformResponseState *mrs;
+  struct TALER_TESTING_Command cmd;
+
+  mrs = GNUNET_new (struct MalformResponseState);
+  mrs->config_filename = config_filename;
+
+  cmd.label = label;
+  cmd.run = &malform_response_run;
+  cmd.cleanup = &malform_response_cleanup;
+  cmd.traits = &malform_response_traits;
+  cmd.cls = mrs;
+
+  return cmd;
+
+}
+
+/**
+ * This command deletes the JSON object pointed by @a path.
+ *
+ * @param label command label
+ * @param config_filename configuration filename.
+ * @param path object-like path notation to point the object
+ *        to delete.
+ *
+ * @return the command
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_modify_object (const char *label,
+                                 const char *config_filename,
+                                 const char *path,
+                                 const char *value)
+{
+  struct ModifyObjectState *mos;
+  struct TALER_TESTING_Command cmd;
+
+  mos = GNUNET_new (struct ModifyObjectState);
+  mos->path = path;
+  mos->value = value;
+  mos->config_filename = config_filename;
+
+  cmd.label = label;
+  cmd.run = &modify_object_run;
+  cmd.cleanup = &modify_object_cleanup;
+  cmd.traits = &modify_object_traits;
+  cmd.cls = mos;
+
+  return cmd;
+}
diff --git a/src/twister/testing_api_helpers.c 
b/src/twister/testing_api_helpers.c
index 5b70ec3..aa5976f 100644
--- a/src/twister/testing_api_helpers.c
+++ b/src/twister/testing_api_helpers.c
@@ -70,7 +70,7 @@ TALER_TESTING_prepare_twister (const char *config_filename)
 
   GNUNET_assert (0 < GNUNET_asprintf
     (&base_url,
-     "http://localhost:%llu";,
+     "http://localhost:%llu/";,
      port));
 
   return base_url;
diff --git a/src/twister/twister.h b/src/twister/twister.h
index 6f07f81..c1ebeef 100644
--- a/src/twister/twister.h
+++ b/src/twister/twister.h
@@ -2,20 +2,20 @@
      This file is part of GNUnet.
      Copyright (C) 2018 Taler Systems SA
 
-     Twister is free software; you can redistribute it and/or modify
-     it under the terms of the GNU General Public License as published
-     by the Free Software Foundation; either version 3, or (at your
-     option) any later version.
-
-     Twister is distributed in the hope that it will be useful, but
-     WITHOUT ANY WARRANTY; without even the implied warranty of
-     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-     General Public License for more details.
-
-     You should have received a copy of the GNU General Public License
-     along with Twister; see the file COPYING.  If not, write to the
-     Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
-     Boston, MA 02110-1301, USA.
+     Twister is free software; you can redistribute it and/or
+     modify it under the terms of the GNU General Public License
+     as published by the Free Software Foundation; either version
+     3, or (at your option) any later version.
+
+     Twister is distributed in the hope that it will be useful,
+     but WITHOUT ANY WARRANTY; without even the implied warranty
+     of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
+     the GNU General Public License for more details.
+
+     You should have received a copy of the GNU General Public
+     License along with Twister; see the file COPYING.  If not,
+     write to the Free Software Foundation, Inc., 51 Franklin
+     Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */
 
 /**
@@ -29,11 +29,89 @@
 
 #include <gnunet/gnunet_common.h>
 
+/**
+ * Max length for a JSON path.
+ */
+#define TWISTER_PATH_LENGTH 100
+
+/**
+ * Max lenght for JSON values to use in place of the originals.
+ */
+#define TWISTER_VALUE_LENGTH 100
 
 #define TWISTER_MESSAGE_TYPE_ACKNOWLEDGEMENT 1
 
 #define TWISTER_MESSAGE_TYPE_SET_RESPONSE_CODE 2
 
+#define TWISTER_MESSAGE_TYPE_DELETE_PATH 3
+
+#define TWISTER_MESSAGE_TYPE_MODIFY_PATH 4
+
+#define TWISTER_MESSAGE_TYPE_MALFORM 5
+
+#define TWISTER_MESSAGE_TYPE_MALFORM_UPLOAD 6
+
+GNUNET_NETWORK_STRUCT_BEGIN
+struct TWISTER_Malform
+{
+  /**
+   * Type: #TWISTER_MESSAGE_TYPE_MALFORM
+   */
+  struct GNUNET_MessageHeader header;
+
+};
+GNUNET_NETWORK_STRUCT_END
+
+GNUNET_NETWORK_STRUCT_BEGIN
+struct TWISTER_ModifyPath
+{
+  /**
+   * Type: #TWISTER_MESSAGE_TYPE_DELETE_PATH
+   */
+  struct GNUNET_MessageHeader header;
+
+  /**
+   * Path to the object to modify.
+   */
+  char path[TWISTER_PATH_LENGTH];
+
+  /**
+   * New value to use.
+   */
+  char value[TWISTER_VALUE_LENGTH];
+};
+GNUNET_NETWORK_STRUCT_END
+
+
+GNUNET_NETWORK_STRUCT_BEGIN
+
+/**
+ * Network size estimate sent from the service
+ * to clients.  Contains the current size estimate
+ * (or 0 if none has been calculated) and the
+ * standard deviation of known estimates.
+ *
+ */
+struct TWISTER_DeletePath
+{
+  /**
+   * Type: #TWISTER_MESSAGE_TYPE_DELETE_PATH
+   */
+  struct GNUNET_MessageHeader header;
+
+  /**
+   * The new response code, in big endian.
+   */
+  char path[TWISTER_PATH_LENGTH];
+
+  /**
+   * New value to use in place of the original.
+   */
+  char value[TWISTER_VALUE_LENGTH];
+
+};
+GNUNET_NETWORK_STRUCT_END
+
 
 GNUNET_NETWORK_STRUCT_BEGIN
 
diff --git a/src/twister/twister_api.c b/src/twister/twister_api.c
index 07583f8..477b774 100644
--- a/src/twister/twister_api.c
+++ b/src/twister/twister_api.c
@@ -204,6 +204,159 @@ TALER_TWISTER_cancel (struct TALER_TWISTER_Operation *op)
 
 
 /**
+ * Randomly truncate the request.
+ *
+ * @param h twister instance to control
+ * @param cb function to call once twister is ready
+ * @param cb_cls closure for @a cb
+ * @return operation handle (to possibly abort)
+ */
+struct TALER_TWISTER_Operation *
+TALER_TWISTER_malform_upload
+  (struct TALER_TWISTER_Handle *h,
+   GNUNET_SCHEDULER_TaskCallback cb,
+   void *cb_cls)
+{
+  struct TALER_TWISTER_Operation *op;
+  struct GNUNET_MQ_Envelope *env;
+  struct TWISTER_Malform *src;
+
+  op = GNUNET_new (struct TALER_TWISTER_Operation);
+  op->h = h;
+  op->cb = cb;
+  op->cb_cls = cb_cls;
+  GNUNET_CONTAINER_DLL_insert_tail (h->op_head,
+                                    h->op_tail,
+                                    op);
+  /* Prepare *env*elope. */
+  env = GNUNET_MQ_msg
+    (src, TWISTER_MESSAGE_TYPE_MALFORM_UPLOAD);
+  /* Send message. */
+  GNUNET_MQ_send (h->mq, env);
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Batching a (upload) body malformation\n");
+  return op;
+}
+
+
+/**
+ * Randomly truncate the response.
+ *
+ * @param h twister instance to control
+ * @param cb function to call once twister is ready
+ * @param cb_cls closure for @a cb
+ * @return operation handle (to possibly abort)
+ */
+struct TALER_TWISTER_Operation *
+TALER_TWISTER_malform
+  (struct TALER_TWISTER_Handle *h,
+   GNUNET_SCHEDULER_TaskCallback cb,
+   void *cb_cls)
+{
+  struct TALER_TWISTER_Operation *op;
+  struct GNUNET_MQ_Envelope *env;
+  struct TWISTER_Malform *src;
+
+  op = GNUNET_new (struct TALER_TWISTER_Operation);
+  op->h = h;
+  op->cb = cb;
+  op->cb_cls = cb_cls;
+  GNUNET_CONTAINER_DLL_insert_tail (h->op_head,
+                                    h->op_tail,
+                                    op);
+  /* Prepare *env*elope. */
+  env = GNUNET_MQ_msg
+    (src, TWISTER_MESSAGE_TYPE_MALFORM);
+  /* Send message. */
+  GNUNET_MQ_send (h->mq, env);
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Batching a body malformation\n");
+  return op;
+}
+
+
+/**
+ * Delete the object pointed to by @a path.
+ *
+ * @param h twister instance to control
+ * @param path object-like notation to point the object to be
+          deleted.  E.g., the path "f1.f2.0" will delete the object
+          {"f1": {"f2": [{"to be": "deleted"}]}}.
+ * @param cb function to call once twister is ready
+ * @param cb_cls closure for @a cb
+ * @return operation handle (to possibly abort)
+ */
+struct TALER_TWISTER_Operation *
+TALER_TWISTER_delete_path
+  (struct TALER_TWISTER_Handle *h,
+   const char *path,
+   GNUNET_SCHEDULER_TaskCallback cb,
+   void *cb_cls)
+{
+  struct TALER_TWISTER_Operation *op;
+  struct GNUNET_MQ_Envelope *env;
+  struct TWISTER_DeletePath *src; //FIXME 'src' right name?
+
+  op = GNUNET_new (struct TALER_TWISTER_Operation);
+  op->h = h;
+  op->cb = cb;
+  op->cb_cls = cb_cls;
+  GNUNET_CONTAINER_DLL_insert_tail (h->op_head,
+                                    h->op_tail,
+                                    op);
+  /* Prepare *env*elope. */
+  env = GNUNET_MQ_msg
+    (src, TWISTER_MESSAGE_TYPE_DELETE_PATH);
+  /* Put data into the envelope. */
+  strcpy (src->path, path);
+  /* Send message. */
+  GNUNET_MQ_send (h->mq, env);
+  return op;
+}
+
+/**
+ * Change the response field pointed by @a modify_path with
+ * @a modify_value.
+ *
+ * @param h twister instance to control
+ * @param modify_path object-like notation path to the object to
+ *        modify
+ * @param modify_value value to use for @a modify_path
+ * @param cb callback to call once twister gets this instruction.
+ * @param cb_cls closure for @a cb_callback
+ *
+ * @return operation handle.
+ */
+struct TALER_TWISTER_Operation *
+TALER_TWISTER_modify_path (struct TALER_TWISTER_Handle *h,
+                           const char *path,
+                           const char *value,
+                           GNUNET_SCHEDULER_TaskCallback cb,
+                           void *cb_cls)
+{
+  struct TALER_TWISTER_Operation *op;
+  struct GNUNET_MQ_Envelope *env;
+  struct TWISTER_ModifyPath *src;
+
+  op = GNUNET_new (struct TALER_TWISTER_Operation);
+  op->h = h;
+  op->cb = cb;
+  op->cb_cls = cb_cls;
+  GNUNET_CONTAINER_DLL_insert_tail (h->op_head,
+                                    h->op_tail,
+                                    op);
+  /* Prepare *env*elope. */
+  env = GNUNET_MQ_msg
+    (src, TWISTER_MESSAGE_TYPE_MODIFY_PATH);
+  /* Put data into the envelope. */
+  strcpy (src->path, path);
+  strcpy (src->value, value);
+  /* Send message. */
+  GNUNET_MQ_send (h->mq, env);
+  return op;
+}
+
+/**
  * Change the next response code to @a new_rc.
  *
  * @param h twister instance to control

-- 
To stop receiving notification emails like this one, please contact
address@hidden



reply via email to

[Prev in Thread] Current Thread [Next in Thread]