From f28f2ca6626e2f58ee9dfaadb388172c673c5e17 Mon Sep 17 00:00:00 2001 From: Luca Toscano Date: Sun, 1 Jul 2018 07:36:34 +0000 Subject: [PATCH] documentation rebuild git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1834771 13f79535-47bb-0310-9956-ffa450edef68 --- docs/manual/developer/output-filters.html.en | 73 ++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/docs/manual/developer/output-filters.html.en b/docs/manual/developer/output-filters.html.en index 9527e57f95..86eac20209 100644 --- a/docs/manual/developer/output-filters.html.en +++ b/docs/manual/developer/output-filters.html.en @@ -44,6 +44,7 @@
  • Buffering buckets
  • Non-blocking bucket reads
  • Ten rules for output filters
  • +
  • Use case: buffering in mod_ratelimit
  • See also

    top
    @@ -482,6 +483,78 @@ while ((e = APR_BRIGADE_FIRST(bb)) != APR_BRIGADE_SENTINEL(bb)) { +
    top
    +
    +

    Use case: buffering in mod_ratelimit

    + +

    The r1833875 change is a good + example to show what buffering and keeping state means in the context of an + output filter. In this use case, a user asked on the users' mailing list a + interesting question about why mod_ratelimit seemed not to + honor its setting with proxied content (either rate limiting at a different + speed or simply not doing it at all). Before diving deep into the solution, + it is better to explain on a high level how mod_ratelimit works. + The trick is really simple: take the rate limit settings and calculate a + chunk size of data to flush every 200ms to the client. For example, let's imagine + that to set rate-limit 60 in our config, these are the high level + steps to find the chunk size:

    +
    /* milliseconds to wait between each flush of data */
    +RATE_INTERVAL_MS = 200;
    +/* rate limit speed in b/s */
    +speed = 60 * 1024;
    +/* final chunk size is 12228 bytes */
    +chunk_size = (speed / (1000 / RATE_INTERVAL_MS));
    + +

    If we apply this calculation to a bucket brigade carrying 38400 bytes, it means + that the filter will try to do the following:

    +
      +
    1. Split the 38400 bytes in chunks of maximum 12228 bytes each.
    2. +
    3. Flush the first 12228 chunk of bytes and sleep 200ms.
    4. +
    5. Flush the second 12228 chunk of bytes and sleep 200ms.
    6. +
    7. Flush the third 12228 chunk of bytes and sleep 200ms.
    8. +
    9. Flush the remaining 1716 bytes.
    10. +
    +

    The above pseudo code works fine if the output filter handles only one brigade + for each response, but it might happen that it needs to be called multiple times + with different brigade sizes as well. The former use case is for example when + httpd directly serves some content, like a static file: the bucket brigade + abstraction takes care of handling the whole content, and rate limiting + works nicely. But if the same static content is served via mod_proxy_http (for + example a backend is serving it rather than httpd) then the content generator + (in this case mod_proxy_http) may use a maximum buffer size and then send data + as bucket brigades to the output filters chain regularly, triggering of course + multiple calls to mod_ratelimit. If the reader tries to execute the pseudo code + assuming multiple calls to the output filter, each one requiring to process + a bucket brigade of 38400 bytes, then it is easy to spot some + anomalies:

    +
      +
    1. Between the last flush of a brigade and the first one of the next, + there is no sleep.
    2. +
    3. Even if the sleep was forced after the last flush, then that chunk size + would not be the ideal size (1716 bytes instead of 12228) and the final client's speed + would quickly become different than what set in the httpd's config.
    4. +
    +

    In this case, two things might help:

    +
      +
    1. Use the ctx internal data structure, initialized by mod_ratelimit + for each response handling cycle, to "remember" when the last sleep was + performed across multiple invocations, and act accordingly.
    2. +
    3. If a bucket brigade is not splittable into a finite number of chunk_size + blocks, store the remaining bytes (located in the tail of the bucket brigade) + in a temporary holding area (namely another bucket brigade) and then use + ap_save_brigade to set them aside. + These bytes will be preprended to the next bucket brigade that will be handled + in the subsequent invocation.
    4. +
    5. Avoid the previous logic if the bucket brigade that is currently being + processed contains the end of stream bucket (EOS). There is no need to sleep + or buffering data if the end of stream is reached.
    6. +
    +

    The commit linked in the beginning of the section contains also a bit of code + refactoring so it is not trivial to read during the first pass, but the overall + idea is basically what written up to now. The goal of this section is not to + cause an headache to the reader trying to read C code, but to put him/her into + the right mindset needed to use efficiently the tools offered by the httpd's + filter chain toolset.

    Available Languages:  en 

    -- 2.40.0