static ngx_int_t
ngx_http_brotli_filter_compress(ngx_http_request_t *r, ngx_http_brotli_ctx_t *ctx)
{
    BROTLI_BOOL              rc;
    ngx_buf_t               *b;
    ngx_chain_t             *cl;

    rc = BrotliEncoderCompressStream(ctx->bro, ctx->flush, &ctx->available_in, (const uint8_t **)&ctx->next_in, &ctx->available_out, &ctx->next_out, NULL);

    if (rc != BROTLI_TRUE) {
        ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
                      "compress() failed: %d, %d", ctx->flush, rc);
        return NGX_ERROR;
    }

    if (ctx->next_in) {
        ctx->in_buf->pos = ctx->next_in;

        if (ctx->available_in == 0) {
            ctx->next_in = NULL;
        }
    }

    ctx->out_buf->last = ctx->next_out;

    if (ctx->available_out == 0) {

        /* brotli wants to output some more compressed data */

        cl = ngx_alloc_chain_link(r->pool);
        if (cl == NULL) {
            return NGX_ERROR;
        }

        cl->buf = ctx->out_buf;
        cl->next = NULL;
        *ctx->last_out = cl;
        ctx->last_out = &cl->next;

        ctx->redo = 1;

        return NGX_AGAIN;
    }

    ctx->redo = 0;

    if (ctx->flush == BROTLI_OPERATION_FLUSH) {

        ctx->flush = BROTLI_OPERATION_PROCESS;

        cl = ngx_alloc_chain_link(r->pool);
        if (cl == NULL) {
            return NGX_ERROR;
        }

        b = ctx->out_buf;

        if (ngx_buf_size(b) == 0) {

            b = ngx_calloc_buf(ctx->request->pool);
            if (b == NULL) {
                return NGX_ERROR;
            }

        } else {
            ctx->available_out = 0;
        }

        b->flush = 1;

        cl->buf = b;
        cl->next = NULL;
        *ctx->last_out = cl;
        ctx->last_out = &cl->next;

        r->connection->buffered &= ~NGX_HTTP_GZIP_BUFFERED;

        return NGX_OK;
    }

    if (BrotliEncoderIsFinished(ctx->bro)) {
        if (ngx_http_brotli_filter_end(r, ctx) != NGX_OK) {
            return NGX_ERROR;
        }
        return NGX_OK;
    }

    return NGX_AGAIN;
}
static ngx_int_t
ngx_http_brotli_filter_compress(ngx_http_request_t *r, ngx_http_brotli_ctx_t *ctx)
{
    int                      rc;
    ngx_buf_t               *b;
    ngx_chain_t             *cl;

    ngx_log_debug6(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                 "brotli in: ni:%p no:%p ai:%ud ao:%ud fl:%d redo:%d",
                 ctx->bstream.next_in, ctx->bstream.next_out,
                 ctx->bstream.avail_in, ctx->bstream.avail_out,
                 ctx->flush, ctx->redo);

    rc = brotli_compress(&ctx->bstream, ctx->flush);

    if (rc != B_OK && rc != B_STREAM_END && rc != B_BUF_ERROR) {
        ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
                      "compress() failed: %d, %d", ctx->flush, rc);
        return NGX_ERROR;
    }

    ngx_log_debug5(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "brotli out: ni:%p no:%p ai:%ud ao:%ud rc:%d",
                   ctx->bstream.next_in, ctx->bstream.next_out,
                   ctx->bstream.avail_in, ctx->bstream.avail_out,
                   rc);

    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "brotli in_buf:%p pos:%p",
                   ctx->in_buf, ctx->in_buf->pos);

    if (ctx->bstream.next_in) {
        ctx->in_buf->pos = ctx->bstream.next_in;

        if (ctx->bstream.avail_in == 0) {
            ctx->bstream.next_in = NULL;
        }
    }

    ctx->out_buf->last = ctx->bstream.next_out;

    if (ctx->bstream.avail_out == 0) {

        /* brotli wants to output some more compressed data */

        cl = ngx_alloc_chain_link(r->pool);
        if (cl == NULL) {
            return NGX_ERROR;
        }

        cl->buf = ctx->out_buf;
        cl->next = NULL;
        *ctx->last_out = cl;
        ctx->last_out = &cl->next;

        ctx->redo = 1;

        return NGX_AGAIN;
    }

    ctx->redo = 0;

    if (ctx->flush == B_SYNC_FLUSH) {

        ctx->flush = B_NO_FLUSH;

        cl = ngx_alloc_chain_link(r->pool);
        if (cl == NULL) {
            return NGX_ERROR;
        }

        b = ctx->out_buf;

        if (ngx_buf_size(b) == 0) {

            b = ngx_calloc_buf(ctx->request->pool);
            if (b == NULL) {
                return NGX_ERROR;
            }

        } else {
            ctx->bstream.avail_out = 0;
        }

        b->flush = 1;

        cl->buf = b;
        cl->next = NULL;
        *ctx->last_out = cl;
        ctx->last_out = &cl->next;

        r->connection->buffered &= ~NGX_HTTP_GZIP_BUFFERED;

        return NGX_OK;
    }

    if (rc == B_STREAM_END) {
        if (ngx_http_brotli_filter_end(r, ctx) != NGX_OK) {
            return NGX_ERROR;
        }
        return NGX_OK;
    }
    return NGX_AGAIN;
}