On 6/6/23 18:38, Richard W.M. Jones wrote:
> On Tue, Jun 06, 2023 at 06:09:09PM +0200, Laszlo Ersek wrote:
>> On 6/6/23 13:22, Richard W.M. Jones wrote:

>>> @@ -250,6 +255,11 @@ handle_requests (int s)
>>>        }
>>>        memcpy (path, &request[5], n);
>>>        path[n] = '\0';
>>> +      if (head_fails_with_403) {
>>> +        send_403_forbidden (s);
>>> +        eof = true;
>>> +        break;
>>> +      }
>>
>> I'm not a fan of the pre-existent pattern where we both set "eof = true"
>> and break out of the loop. It would have to be cleaned up first, in a
>> different patch, but I'm not sure if we care enough.
>
> It's a test :-) However what do you suggest?  Both statements are
> necessary because we have to break out of the inner for(;;) loop, run
> a tiny bit of common cleanup code (just a fprintf), and then break
> from the while(!eof) loop.

That argument is valid in case we are inside the inner ("for") loop, but
here we are not (handle_requests() [tests/web-server.c]), as far as I
can tell. The context with

      memcpy (path, &request[5], n);
      path[n] = '\0';

is outside (after) the "for" loop.

The pre-patch code is misleading in this regard (it uses the same
pattern of both eof=true plus break in three places).

One option would be to use "eof=true" plus *continue*, rather than
"break". That would:

- prevent the rest of the *outer* (while) loop's body from running, just
as the break does,

- set "eof=true" consistently with the outer loop's design; IOW, rely on
"eof" solely for exiting the outer loop, plus, after the "while" loop,
"eof" would be true.

Consider the following (pre-patch) cleanup (git diff
--function-context):

> diff --git a/tests/web-server.c b/tests/web-server.c
> index 9b37326c0540..7f9a15760a22 100644
> --- a/tests/web-server.c
> +++ b/tests/web-server.c
> @@ -190,107 +190,107 @@ static void
>  handle_requests (int s)
>  {
>    bool eof = false;
>
>    fprintf (stderr, "web server: accepted connection\n");
>
>    while (!eof) {
>      size_t r, n, sz;
>      enum method method;
>      char path[128];
>
>      /* Read request until we see "\r\n\r\n" (end of headers) or EOF. */
>      n = 0;
>      for (;;) {
>        if (n >= sizeof request - 1 /* allow one byte for \0 */) {
>          fprintf (stderr, "web server: request too long\n");
>          exit (EXIT_FAILURE);
>        }
>        sz = sizeof request - n - 1;
>        r = read (s, &request[n], sz);
>        if (r == -1) {
>          perror ("read");
>          exit (EXIT_FAILURE);
>        }
>        if (r == 0) {
>          eof = true;
>          break;
>        }
>        n += r;
>        request[n] = '\0';
>        if (strstr (request, "\r\n\r\n"))
>          break;
>      }
>
>      if (n == 0)
>        continue;
>
>      fprintf (stderr, "web server: request:\n%s", request);
>
>      /* Call the optional user function to check the request. */
>      if (check_request) check_request (request);
>
>      /* Get the method and path fields from the first line. */
>      if (strncmp (request, "HEAD ", 5) == 0) {
>        method = HEAD;
>        n = strcspn (&request[5], " \n\t");
>        if (n >= sizeof path) {
>          send_500_internal_server_error (s);
>          eof = true;
> -        break;
> +        continue;
>        }
>        memcpy (path, &request[5], n);
>        path[n] = '\0';
>      }
>      else if (strncmp (request, "GET ", 4) == 0) {
>        method = GET;
>        n = strcspn (&request[4], " \n\t");
>        if (n >= sizeof path) {
>          send_500_internal_server_error (s);
>          eof = true;
> -        break;
> +        continue;
>        }
>        memcpy (path, &request[4], n);
>        path[n] = '\0';
>      }
>      else {
>        send_405_method_not_allowed (s);
>        eof = true;
> -      break;
> +      continue;
>      }
>
>      fprintf (stderr, "web server: requested path: %s\n", path);
>
>      /* For testing retry-request + curl:
>       *   /mirror redirects round-robin to /mirror1, /mirror2, /mirror3
>       *   /mirror1 returns a file of \x01 bytes
>       *   /mirror2 returns a file of \x02 bytes
>       *   /mirror3 returns 404 errors
>       * Anything else returns a 500 error
>       */
>      if (strcmp (path, "/mirror") == 0)
>        handle_mirror_redirect_request (s);
>      else if (strcmp (path, "/mirror1") == 0)
>        handle_mirror_data_request (s, method, 1);
>      else if (strcmp (path, "/mirror2") == 0)
>        handle_mirror_data_request (s, method, 2);
>      else if (strcmp (path, "/mirror3") == 0) {
>        send_404_not_found (s);
>        eof = true;
>      }
>      else if (strncmp (path, "/mirror", 7) == 0) {
>        send_500_internal_server_error (s);
>        eof = true;
>      }
>
>      /* Otherwise it's a regular file request.  'path' is ignored, we
>       * only serve a single file passed to web_server().
>       */
>      else
>        handle_file_request (s, method);
>
>      fprintf (stderr, "web server: completed request\n");
>    }
>
>    fprintf (stderr, "web server: closing socket\n");
>    close (s);
>  }

This would prevent the "web server: completed request" message from
being logged, but that's already the case with:

- the one continue statement we have in place anyway,

- all the break statements the apove patch replaces.

Those too prevent the "web server: completed request" message, so the
new "continue" statements make no difference in that regard.

If we wanted to print "web server: completed request" in any case, then
we have at least two options.

The ugly one (git diff --function-context --ignore-space-change):

> diff --git a/tests/web-server.c b/tests/web-server.c
> index 9b37326c0540..7f7bd4424cd8 100644
> --- a/tests/web-server.c
> +++ b/tests/web-server.c
> @@ -190,107 +190,109 @@ static void
>  handle_requests (int s)
>  {
>    bool eof = false;
>
>    fprintf (stderr, "web server: accepted connection\n");
>
>    while (!eof) {
>      size_t r, n, sz;
>      enum method method;
>      char path[128];
>
>      /* Read request until we see "\r\n\r\n" (end of headers) or EOF. */
>      n = 0;
>      for (;;) {
>        if (n >= sizeof request - 1 /* allow one byte for \0 */) {
>          fprintf (stderr, "web server: request too long\n");
>          exit (EXIT_FAILURE);
>        }
>        sz = sizeof request - n - 1;
>        r = read (s, &request[n], sz);
>        if (r == -1) {
>          perror ("read");
>          exit (EXIT_FAILURE);
>        }
>        if (r == 0) {
>          eof = true;
>          break;
>        }
>        n += r;
>        request[n] = '\0';
>        if (strstr (request, "\r\n\r\n"))
>          break;
>      }
>
>      if (n == 0)
>        continue;
>
>      fprintf (stderr, "web server: request:\n%s", request);
>
>      /* Call the optional user function to check the request. */
>      if (check_request) check_request (request);
>
>      /* Get the method and path fields from the first line. */
>      if (strncmp (request, "HEAD ", 5) == 0) {
>        method = HEAD;
>        n = strcspn (&request[5], " \n\t");
>        if (n >= sizeof path) {
>          send_500_internal_server_error (s);
>          eof = true;
> -        break;
>        }
> +      else {
>          memcpy (path, &request[5], n);
>          path[n] = '\0';
>        }
> +    }
>      else if (strncmp (request, "GET ", 4) == 0) {
>        method = GET;
>        n = strcspn (&request[4], " \n\t");
>        if (n >= sizeof path) {
>          send_500_internal_server_error (s);
>          eof = true;
> -        break;
>        }
> +      else {
>          memcpy (path, &request[4], n);
>          path[n] = '\0';
>        }
> +    }
>      else {
>        send_405_method_not_allowed (s);
>        eof = true;
> -      break;
>      }
>
> +    if (!eof) {
>        fprintf (stderr, "web server: requested path: %s\n", path);
>
>        /* For testing retry-request + curl:
>         *   /mirror redirects round-robin to /mirror1, /mirror2, /mirror3
>         *   /mirror1 returns a file of \x01 bytes
>         *   /mirror2 returns a file of \x02 bytes
>         *   /mirror3 returns 404 errors
>         * Anything else returns a 500 error
>         */
>        if (strcmp (path, "/mirror") == 0)
>          handle_mirror_redirect_request (s);
>        else if (strcmp (path, "/mirror1") == 0)
>          handle_mirror_data_request (s, method, 1);
>        else if (strcmp (path, "/mirror2") == 0)
>          handle_mirror_data_request (s, method, 2);
>        else if (strcmp (path, "/mirror3") == 0) {
>          send_404_not_found (s);
>          eof = true;
>        }
>        else if (strncmp (path, "/mirror", 7) == 0) {
>          send_500_internal_server_error (s);
>          eof = true;
>        }
>
>        /* Otherwise it's a regular file request.  'path' is ignored, we
>         * only serve a single file passed to web_server().
>         */
>        else
>          handle_file_request (s, method);
> -
> +    }
>      fprintf (stderr, "web server: completed request\n");
>    }
>
>    fprintf (stderr, "web server: closing socket\n");
>    close (s);
>  }

And the nice one (git diff --function-context --ignore-space-change):

> diff --git a/tests/web-server.c b/tests/web-server.c
> index 9b37326c0540..3ea785f749af 100644
> --- a/tests/web-server.c
> +++ b/tests/web-server.c
> @@ -186,111 +186,121 @@ start_web_server (void *arg)
>    }
>  }
>
> -static void
> -handle_requests (int s)
> +/* Returns "true" iff we should continue serving requests. */
> +static bool
> +handle_request (int s)
>  {
> -  bool eof = false;
> -
> -  fprintf (stderr, "web server: accepted connection\n");
> -
> -  while (!eof) {
> -    size_t r, n, sz;
>    enum method method;
> +  size_t n;
>    char path[128];
>
> -    /* Read request until we see "\r\n\r\n" (end of headers) or EOF. */
> -    n = 0;
> -    for (;;) {
> -      if (n >= sizeof request - 1 /* allow one byte for \0 */) {
> -        fprintf (stderr, "web server: request too long\n");
> -        exit (EXIT_FAILURE);
> -      }
> -      sz = sizeof request - n - 1;
> -      r = read (s, &request[n], sz);
> -      if (r == -1) {
> -        perror ("read");
> -        exit (EXIT_FAILURE);
> -      }
> -      if (r == 0) {
> -        eof = true;
> -        break;
> -      }
> -      n += r;
> -      request[n] = '\0';
> -      if (strstr (request, "\r\n\r\n"))
> -        break;
> -    }
> -
> -    if (n == 0)
> -      continue;
> -
> -    fprintf (stderr, "web server: request:\n%s", request);
> -
>    /* Call the optional user function to check the request. */
>    if (check_request) check_request (request);
>
>    /* Get the method and path fields from the first line. */
>    if (strncmp (request, "HEAD ", 5) == 0) {
>      method = HEAD;
>      n = strcspn (&request[5], " \n\t");
>      if (n >= sizeof path) {
>        send_500_internal_server_error (s);
> -        eof = true;
> -        break;
> +      return false;
>      }
>      memcpy (path, &request[5], n);
>      path[n] = '\0';
>    }
>    else if (strncmp (request, "GET ", 4) == 0) {
>      method = GET;
>      n = strcspn (&request[4], " \n\t");
>      if (n >= sizeof path) {
>        send_500_internal_server_error (s);
> -        eof = true;
> -        break;
> +      return false;
>      }
>      memcpy (path, &request[4], n);
>      path[n] = '\0';
>    }
>    else {
>      send_405_method_not_allowed (s);
> -      eof = true;
> -      break;
> +    return false;
>    }
>
>    fprintf (stderr, "web server: requested path: %s\n", path);
>
>    /* For testing retry-request + curl:
>     *   /mirror redirects round-robin to /mirror1, /mirror2, /mirror3
>     *   /mirror1 returns a file of \x01 bytes
>     *   /mirror2 returns a file of \x02 bytes
>     *   /mirror3 returns 404 errors
>     * Anything else returns a 500 error
>     */
> -    if (strcmp (path, "/mirror") == 0)
> +  if (strcmp (path, "/mirror") == 0) {
>      handle_mirror_redirect_request (s);
> -    else if (strcmp (path, "/mirror1") == 0)
> +    return true;
> +  }
> +  if (strcmp (path, "/mirror1") == 0) {
>      handle_mirror_data_request (s, method, 1);
> -    else if (strcmp (path, "/mirror2") == 0)
> +    return true;
> +  }
> +  if (strcmp (path, "/mirror2") == 0) {
>      handle_mirror_data_request (s, method, 2);
> -    else if (strcmp (path, "/mirror3") == 0) {
> +    return true;
> +  }
> +  if (strcmp (path, "/mirror3") == 0) {
>      send_404_not_found (s);
> -      eof = true;
> +    return false;
>    }
> -    else if (strncmp (path, "/mirror", 7) == 0) {
> +  if (strncmp (path, "/mirror", 7) == 0) {
>      send_500_internal_server_error (s);
> -      eof = true;
> +    return false;
>    }
>
>    /* Otherwise it's a regular file request.  'path' is ignored, we
>     * only serve a single file passed to web_server().
>     */
> -    else
>    handle_file_request (s, method);
> +  return true;
> +}
>
> +static void
> +handle_requests (int s)
> +{
> +  bool eof = false;
> +
> +  fprintf (stderr, "web server: accepted connection\n");
> +
> +  while (!eof) {
> +    size_t r, n, sz;
> +
> +    /* Read request until we see "\r\n\r\n" (end of headers) or EOF. */
> +    n = 0;
> +    for (;;) {
> +      if (n >= sizeof request - 1 /* allow one byte for \0 */) {
> +        fprintf (stderr, "web server: request too long\n");
> +        exit (EXIT_FAILURE);
> +      }
> +      sz = sizeof request - n - 1;
> +      r = read (s, &request[n], sz);
> +      if (r == -1) {
> +        perror ("read");
> +        exit (EXIT_FAILURE);
> +      }
> +      if (r == 0) {
> +        eof = true;
> +        break;
> +      }
> +      n += r;
> +      request[n] = '\0';
> +      if (strstr (request, "\r\n\r\n"))
> +        break;
> +    }
> +
> +    if (n == 0)
> +      continue;
> +
> +    fprintf (stderr, "web server: request:\n%s", request);
> +    eof = !handle_request (s);
>      fprintf (stderr, "web server: completed request\n");
>    }
>
>    fprintf (stderr, "web server: closing socket\n");
>    close (s);
>  }

(I've built and tested the last one.)

But this is probably too much churn for a test.

Laszlo
_______________________________________________
Libguestfs mailing list
Libguestfs@redhat.com
https://listman.redhat.com/mailman/listinfo/libguestfs

Reply via email to