Welcome! Log In Create A New Profile

Advanced

How to add functionality to server static files for certain url?

Posted by Anonymous User 
Hi,

We have a very special requirements to implement which is to enable haproxy to server static files.
We know that by introducing Nginx or apache as a backend, we can do that with haproxy, but situation is we cannot do that.
We have looked into the source code for a while, but don't have a clear view of how to do it.

Currently we know that,

1, where to get the http request header,


proto_http.c

```
        /*************************************************************
         * OK, that's finished for the headers. We have done what we *
         * could. Let's switch to the DATA state.                    *
         ************************************************************/
        req->analyse_exp = TICK_ETERNITY;
        req->analysers &= ~an_bit;
```

so we are able to get the specific URL here and return static files (we are ok to hardcode the url->static files map in source code)
for example, here we setting a flag
req->staticFile = 1;


2. stream.c:process_session

```

resync_request:
      while (ana_list && max_loops--) {
                                /* Warning! ensure that analysers are always placed in ascending order! */
                                ANALYZE    (s, req, flt_start_analyze,          ana_list, ana_back, AN_REQ_FLT_START_FE);
                                FLT_ANALYZE(s, req, tcp_inspect_request,        ana_list, ana_back, AN_REQ_INSPECT_FE);
                                FLT_ANALYZE(s, req, http_wait_for_request,      ana_list, ana_back, AN_REQ_WAIT_HTTP);
                                FLT_ANALYZE(s, req, http_wait_for_request_body, ana_list, ana_back, AN_REQ_HTTP_BODY);
                                FLT_ANALYZE(s, req, http_process_req_common,    ana_list, ana_back, AN_REQ_HTTP_PROCESS_FE, sess->fe);
                                FLT_ANALYZE(s, req, process_switching_rules,    ana_list, ana_back, AN_REQ_SWITCHING_RULES);
                                ANALYZE    (s, req, flt_start_analyze,          ana_list, ana_back, AN_REQ_FLT_START_BE);
                                FLT_ANALYZE(s, req, tcp_inspect_request,        ana_list, ana_back, AN_REQ_INSPECT_BE);
                                FLT_ANALYZE(s, req, http_process_req_common,    ana_list, ana_back, AN_REQ_HTTP_PROCESS_BE, s->be);
                                FLT_ANALYZE(s, req, http_process_tarpit,        ana_list, ana_back, AN_REQ_HTTP_TARPIT);
                                FLT_ANALYZE(s, req, process_server_rules,       ana_list, ana_back, AN_REQ_SRV_RULES);
                                FLT_ANALYZE(s, req, http_process_request,       ana_list, ana_back, AN_REQ_HTTP_INNER);
                                FLT_ANALYZE(s, req, tcp_persist_rdp_cookie,     ana_list, ana_back, AN_REQ_PRST_RDP_COOKIE);
                                FLT_ANALYZE(s, req, process_sticking_rules,     ana_list, ana_back, AN_REQ_STICKING_RULES);
                                ANALYZE    (s, req, flt_analyze_http_headers,   ana_list, ana_back, AN_REQ_FLT_HTTP_HDRS);
                                ANALYZE    (s, req, http_request_forward_body,  ana_list, ana_back, AN_REQ_HTTP_XFER_BODY);
                                ANALYZE    (s, req, flt_xfer_data,              ana_list, ana_back, AN_REQ_FLT_XFER_DATA);
                                ANALYZE    (s, req, flt_end_analyze,            ana_list, ana_back, AN_REQ_FLT_END);
                                break;
                        }
```
we know that by using these flags, we can control the function to execute.

so question here, how to abort normal process after we get request header(not to connect to a backend and forward response to client), but read files from harddisk and forward to client?

like
```
req.analysers = AN_REQ_FLT_END;
res.analysers = AN_RES_FLT_END;
?
and how to forward the memory data of this static file to output buffer?


Thanks a lot in advance
Hi,

On Mon, Aug 07, 2017 at 05:02:59PM +0900, flamesea12@yahoo.co.jp wrote:
> Currently we know that,
>
> 1, where to get the http request header,
>
>
> proto_http.c
>
> ```
>         /*************************************************************
>          * OK, that's finished for the headers. We have done what we *
>          * could. Let's switch to the DATA state.                    *
>          ************************************************************/
>         req->analyse_exp = TICK_ETERNITY;
>         req->analysers &= ~an_bit;
> ```
>
> so we are able to get the specific URL here and return static files (we are ok to hardcode the url->static files map in source code)
> for example, here we setting a flag
> req->staticFile = 1;
>
>
> 2. stream.c:process_session
>
> ```
>
> resync_request:
>       while (ana_list && max_loops--) {
>                                 /* Warning! ensure that analysers are always placed in ascending order! */
>                                 ANALYZE    (s, req, flt_start_analyze,          ana_list, ana_back, AN_REQ_FLT_START_FE);
>                                 FLT_ANALYZE(s, req, tcp_inspect_request,        ana_list, ana_back, AN_REQ_INSPECT_FE);
>                                 FLT_ANALYZE(s, req, http_wait_for_request,      ana_list, ana_back, AN_REQ_WAIT_HTTP);
>                                 FLT_ANALYZE(s, req, http_wait_for_request_body, ana_list, ana_back, AN_REQ_HTTP_BODY);
>                                 FLT_ANALYZE(s, req, http_process_req_common,    ana_list, ana_back, AN_REQ_HTTP_PROCESS_FE, sess->fe);
>                                 FLT_ANALYZE(s, req, process_switching_rules,    ana_list, ana_back, AN_REQ_SWITCHING_RULES);
>                                 ANALYZE    (s, req, flt_start_analyze,          ana_list, ana_back, AN_REQ_FLT_START_BE);
>                                 FLT_ANALYZE(s, req, tcp_inspect_request,        ana_list, ana_back, AN_REQ_INSPECT_BE);
>                                 FLT_ANALYZE(s, req, http_process_req_common,    ana_list, ana_back, AN_REQ_HTTP_PROCESS_BE, s->be);
>                                 FLT_ANALYZE(s, req, http_process_tarpit,        ana_list, ana_back, AN_REQ_HTTP_TARPIT);
>                                 FLT_ANALYZE(s, req, process_server_rules,       ana_list, ana_back, AN_REQ_SRV_RULES);
>                                 FLT_ANALYZE(s, req, http_process_request,       ana_list, ana_back, AN_REQ_HTTP_INNER);
>                                 FLT_ANALYZE(s, req, tcp_persist_rdp_cookie,     ana_list, ana_back, AN_REQ_PRST_RDP_COOKIE);
>                                 FLT_ANALYZE(s, req, process_sticking_rules,     ana_list, ana_back, AN_REQ_STICKING_RULES);
>                                 ANALYZE    (s, req, flt_analyze_http_headers,   ana_list, ana_back, AN_REQ_FLT_HTTP_HDRS);
>                                 ANALYZE    (s, req, http_request_forward_body,  ana_list, ana_back, AN_REQ_HTTP_XFER_BODY);
>                                 ANALYZE    (s, req, flt_xfer_data,              ana_list, ana_back, AN_REQ_FLT_XFER_DATA);
>                                 ANALYZE    (s, req, flt_end_analyze,            ana_list, ana_back, AN_REQ_FLT_END);
>                                 break;
>                         }
> ```
> we know that by using these flags, we can control the function to execute.
>
> so question here, how to abort normal process after we get request header(not
> to connect to a backend and forward response to client), but read files from
> harddisk and forward to client?

It's not about "aborting normal process", it's "doing something completely
different instead". Among the rough things you'll have to do, I can count
this :
- stat the file to get the content length. Note that this does result
in blocking I/O which are terrible for the global performance in the
process. Ie during a disk seek, no other session can make progress and
it can take a lot of type (typically 4ms on a fast disk).

- prepare a response message with the appropriate size specified as the
content length, and emit the appropriate HTTP headers into the buffer.

- disable the connection to the server and pretend you're already connected.
In fact we have applets to assure exactly this purpose and I strongly
encourage you to use them for this.

- read the part of the file that can fit into the response buffer and
let this part go to the client

- wake up every time you release some room and read again until you
reach the end of file or the initially planned content-length.

- once the end of file is reached, you need to ensure you've read as
many bytes as anticipated. If not, you'll have to close the connection
to the client otherwise it may remain stuck waiting for more data.

- in parallel to this you'll have to drain any possible contents from
the connection. You may argue that you're not interested in receiving
POSTs nor any body on the request path, but for the connection's safety
you will have to take care of these anyway otherwise you're subject to
a risk of request smuggling.

I tend to think that the easiest way to implement all of this would really
be to implement a dedicated applet. That way you don't touch any of the
analysers and let them continue to do their job, processing requests and
responses over the file's contents. The applet is only responsible for
reading from the file and pushing bytes over the buffer. This way it behaves
like a regular server and you're sure not to break all the internal sequencing.
Take a look at the stats applet or the CLI applet to get an idea how to do
this. If you just need a little bit of static file serving, I think it's
possible to implement entirely in Lua since Lua can also return HTTP
contents.

But please, keep in mind that if you're not serving your files from a
ramdisk, haproxy's performance will be killed every time your disk's head
has to move :-/

Hoping this helps,
Willy
Sorry, only registered users may post in this forum.

Click here to login