]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: cli: handle payload in CLI proxy
authorWilliam Lallemand <wlallemand@haproxy.com>
Tue, 11 Dec 2018 15:10:57 +0000 (16:10 +0100)
committerWilly Tarreau <w@1wt.eu>
Tue, 11 Dec 2018 16:05:36 +0000 (17:05 +0100)
The CLI proxy was not handling payload. To do that, we needed to keep a
connection active on a server and to transfer each new line over that
connection until we receive a empty line.

The CLI proxy handles the payload in the same way that the CLI do it.

Examples:

   $ echo -e "@1;add map #-1 <<\n$(cat data)\n" | socat /tmp/master-socket -

   $ socat /tmp/master-socket readline
   prompt
   master> @1
   25130> add map #-1 <<
   + test test
   + test2 test2
   + test3 test3
   +

   25130>

include/types/stream.h
src/cli.c
src/stream.c

index 52c0b97c0aecdafe50f53513472b38d010e2a1a4..8eb51158f9126a4e9ee35b3e921d6dd4cf4171c5 100644 (file)
@@ -164,6 +164,7 @@ struct stream {
 
        int pcli_next_pid;                      /* next target PID to use for the CLI proxy */
        int pcli_prompt;                        /* is there a prompt ?! */
+       int pcli_flags;                         /* flags for CLI proxy */
 
        char *unique_id;                        /* custom unique ID */
 
index 3af5c6ac862efbfedc74c19d83713dca849b7ee6..9d210dd360d0a04aab45804f37fd4039415db67e 100644 (file)
--- a/src/cli.c
+++ b/src/cli.c
@@ -1661,10 +1661,14 @@ void pcli_write_prompt(struct stream *s)
        if (!s->pcli_prompt)
                return;
 
-       if (s->pcli_next_pid == 0)
-               chunk_appendf(msg, "master> ");
-       else
-               chunk_appendf(msg, "%d> ", s->pcli_next_pid);
+       if (s->pcli_flags & APPCTX_CLI_ST1_PAYLOAD) {
+               chunk_appendf(msg, "+ ");
+       } else {
+               if (s->pcli_next_pid == 0)
+                       chunk_appendf(msg, "master> ");
+               else
+                       chunk_appendf(msg, "%d> ", s->pcli_next_pid);
+       }
        co_inject(oc, msg->area, msg->data);
 }
 
@@ -1815,26 +1819,39 @@ int pcli_parse_request(struct stream *s, struct channel *req, char **errmsg, int
        int argl; /* number of args */
        char *p;
        char *trim = NULL;
+       char *payload = NULL;
        int wtrim = 0; /* number of words to trim */
        int reql = 0;
        int i = 0;
 
        p = str;
 
-       /* Looks for the end of one command */
-       while (p+reql < end) {
-               /* handle escaping */
-               if (str[reql] == '\\') {
+       if (!(s->pcli_flags & APPCTX_CLI_ST1_PAYLOAD)) {
+
+               /* Looks for the end of one command */
+               while (p+reql < end) {
+                       /* handle escaping */
+                       if (p[reql] == '\\') {
+                               reql++;
+                               continue;
+                       }
+                       if (p[reql] == ';' || p[reql] == '\n') {
+                               /* found the end of the command */
+                               p[reql] = '\n';
+                               reql++;
+                               break;
+                       }
                        reql++;
-                       continue;
                }
-               if (str[reql] == ';' || str[reql] == '\n') {
-                       /* found the end of the command */
-                       str[reql] = '\n';
+       } else {
+               while (p+reql < end) {
+                       if (p[reql] == '\n') {
+                               /* found the end of the line */
+                               reql++;
+                               break;
+                       }
                        reql++;
-                       break;
                }
-               reql++;
        }
 
        /* set end to first byte after the end of the command */
@@ -1845,6 +1862,19 @@ int pcli_parse_request(struct stream *s, struct channel *req, char **errmsg, int
                return -1;
        }
 
+       /* last line of the payload */
+       if ((s->pcli_flags & APPCTX_CLI_ST1_PAYLOAD) && (reql == 1)) {
+               s->pcli_flags &= ~APPCTX_CLI_ST1_PAYLOAD;
+               return reql;
+       }
+
+       payload = strstr(p, PAYLOAD_PATTERN);
+       if ((end - 1) == (payload + strlen(PAYLOAD_PATTERN))) {
+               /* if the payload pattern is at the end */
+               s->pcli_flags |= APPCTX_CLI_ST1_PAYLOAD;
+               return reql;
+       }
+
        *(end-1) = '\0';
 
        /* splits the command in words */
@@ -1952,8 +1982,15 @@ read_again:
 
                /* forward only 1 command */
                channel_forward(req, to_forward);
-               /* we send only 1 command per request, and we write close after it */
-               channel_shutw_now(req);
+
+               if (!(s->pcli_flags & APPCTX_CLI_ST1_PAYLOAD)) {
+                       /* we send only 1 command per request, and we write close after it */
+                       channel_shutw_now(req);
+               } else {
+                       pcli_write_prompt(s);
+               }
+
+               s->res.flags |= CF_WAKE_ONCE; /* need to be called again */
 
                /* remove the XFER_DATA analysers, which forwards all
                 * the data, we don't want to forward the next requests
@@ -1963,15 +2000,17 @@ read_again:
                req->analysers |= AN_REQ_FLT_END|CF_FLT_ANALYZE;
                s->res.analysers |= AN_RES_WAIT_CLI;
 
-               if (next_pid > -1)
-                       target_pid = next_pid;
-               else
-                       target_pid = s->pcli_next_pid;
-               /* we can connect now */
-               s->target = pcli_pid_to_server(target_pid);
+               if (!(s->flags & SF_ASSIGNED)) {
+                       if (next_pid > -1)
+                               target_pid = next_pid;
+                       else
+                               target_pid = s->pcli_next_pid;
+                       /* we can connect now */
+                       s->target = pcli_pid_to_server(target_pid);
 
-               s->flags |= (SF_DIRECT | SF_ASSIGNED);
-               channel_auto_connect(req);
+                       s->flags |= (SF_DIRECT | SF_ASSIGNED);
+                       channel_auto_connect(req);
+               }
 
        } else if (to_forward == 0) {
                /* we trimmed things but we might have other commands to consume */
@@ -2011,6 +2050,13 @@ int pcli_wait_for_response(struct stream *s, struct channel *rep, int an_bit)
        channel_dont_close(&s->res);
        channel_dont_close(&s->req);
 
+       if (s->pcli_flags & APPCTX_CLI_ST1_PAYLOAD) {
+               s->req.analysers |= AN_REQ_WAIT_CLI;
+               s->res.analysers &= ~AN_RES_WAIT_CLI;
+               s->req.flags |= CF_WAKE_ONCE; /* need to be called again if there is some command left in the request */
+               return 0;
+       }
+
        /* forward the data */
        if (ci_data(rep)) {
                c_adv(rep, ci_data(rep));
index 67b0c8d710c46579a8f1c075ca5e1d28580709a9..24df70af35c992c6604e64b0329e8a2c596fedc7 100644 (file)
@@ -202,6 +202,7 @@ struct stream *stream_new(struct session *sess, enum obj_type *origin)
        s->flags |= SF_INITIALIZED;
        s->pcli_next_pid = 0;
        s->pcli_prompt = 0;
+       s->pcli_flags = 0;
        s->unique_id = NULL;
 
        if ((t = task_new(tid_bit)) == NULL)