Hi Thierry,

Op 18-10-2015 om 21:37 schreef thierry.fourn...@arpalert.org:
On Sun, 18 Oct 2015 00:07:13 +0200
PiBa-NL <piba.nl....@gmail.com> wrote:

Hi haproxy list,

For testing purposes i am trying to 'modify' a response of a webserver
but only having limited success. Is this supposed to work?
As a more usefull goal than the current LAL to TST replacement i imagine
rewriting absolute links on a webpage could be possible which is
sometimes problematic with 'dumb' webapplications..

Or is it outside of the current scope of implemented functionality? If
so, it on the 'lua todo list' ?

I tried for example a configuration like below. And get several
different results in the browser.
-Sometimes i get 4 times TSTA
-Sometimes i see after the 8th TSTA- Connection: keep-alive << this
happens most of the time..
-Sometimes i get 9 times TSTA + STOP << this would be the desired
outcome (only seen very few times..)

Probably due to the response-buffer being filled differently due to
'timing'..

The "connection: keep-alive" text is probably from the actual server
reply which is 'appended' behind the response generated by my lua
script.?. However shouldn't the .done() prevent that from being send to
the client?

Ive tried putting a loop into the lua script to call res:get() multiple
times but that didnt seem to work..

Also to properly modify a page i would need to know all changes before
sending the headers with changed content-length back to the client..

Can someone confirm this is or isn't (reliably) possible? Or how this
can be scripted in lua differently?

Hello,

Your script replace 3 bytes by 3 bytes, this must run with HTTP, but if
your replacement change the length of the response, you can have some
difficulties with clients, or with keepalive.
Yes i started with replacing with the same number of bytes to avoid some of the possible troubles caused by changing the length.. And as seen in the haproxy.cfg it is configured with 'mode http'.

The res:get(), returns the current content of the response buffer.
Maybe it not contains the full response. You must execute a loop with
regular "core.yield()" to get back the hand to HAProxy and wait for new
Calling yield does allow to 'wait' for more data to come in.. No guarantee that it only takes 1 yield for data to 'grow'..

[info] 278/055943 (77431) : luahttpresponse Content-Length XYZ: 14115
[info] 278/055943 (77431) : luahttpresponse SIZE: 2477
[info] 278/055943 (77431) : luahttpresponse LOOP
[info] 278/055943 (77431) : luahttpresponse SIZE: 6221
[info] 278/055943 (77431) : luahttpresponse LOOP
[info] 278/055943 (77431) : luahttpresponse SIZE: 7469
[info] 278/055943 (77431) : luahttpresponse LOOP
[info] 278/055943 (77431) : luahttpresponse SIZE: 7469
[info] 278/055943 (77431) : luahttpresponse LOOP
[info] 278/055943 (77431) : luahttpresponse SIZE: 7469
[info] 278/055943 (77431) : luahttpresponse LOOP
[info] 278/055943 (77431) : luahttpresponse SIZE: 7469
[info] 278/055943 (77431) : luahttpresponse LOOP
[info] 278/055943 (77431) : luahttpresponse SIZE: 7469
[info] 278/055943 (77431) : luahttpresponse LOOP
[info] 278/055943 (77431) : luahttpresponse SIZE: 7469
[info] 278/055943 (77431) : luahttpresponse LOOP
[info] 278/055943 (77431) : luahttpresponse SIZE: 7469
[info] 278/055943 (77431) : luahttpresponse LOOP
[info] 278/055943 (77431) : luahttpresponse SIZE: 8717
[info] 278/055943 (77431) : luahttpresponse LOOP
[info] 278/055943 (77431) : luahttpresponse SIZE: 14337
[info] 278/055943 (77431) : luahttpresponse DONE?: 14337

data. When all the data are read, res:get() returns an error.
Not sure when/how this error would happen.? The result of res:get only seems to get bigger while the webserver is sending the response..

The res:send() is dangerous because it send data directly to the client
without the end of haproxy analysis. Maybe it is the cause o your
problem.

Try to use res:set().
Ok tried that, new try with function below.

The difficulty is that another "res:get()" returns the same data that
these you put.

I don't known if you can modify an http response greater than one
buffer.
Would be nice if that was somehow possible. But my current lua script cannot..

The function res:close() closes the connection even if HAProxy want to
keep the connection alive. I suggest that you don't use this function.
It seems txn.res:close() does not exist? txn:done()

I reproduce the error message using curl. By default curl tries
to transfer data with keepalive, and it is not happy if the all the
announced data are not transfered.

    Connection: keep-alive curl: (18) transfer closed with outstanding
    read data remaining

It seems that i reproduce a bug. I'm looking for.
Ok if you can create a patch, let me know. Happy to test if it solves some of the issues i see.

Thierry

This function seems to work for responses up to +-15KB.
Sometimes the number of loops it runs is different, and it seems kinda in-efficient to just run loops until the response is 'complete', another strange observation is that the res:set inside the loop is required, even though it doesn't set a modified response, eventually the complete response is modified in the browser result. Second request over a keep-alive connection also fails. Adding http-server-close also closes the client connection, but does avoid the problem with the second request.. In the lua script I dont account for headers size yet when checking if the response is completely read, but i dont think thats affected the test..

Is there anything i can do to improve this function? (Besides removing the txn:Info() lines.)

  function luahttpresponse(txn)
    local resheaders = txn.http:res_get_headers()
    local contentlength = tonumber(resheaders["content-length"][0])
    local response2 = txn.res:get()
    txn:Info("luahttpresponse Content-Length XYZ: " .. contentlength)
    txn:Info("luahttpresponse SIZE: " .. string.len(response2))
    while string.len(response2) < contentlength  do
        txn:Info("luahttpresponse LOOP")
        txn.res:set(response2)
        core.yield()
        response2 = txn.res:get()
        txn:Info("luahttpresponse SIZE: " .. string.len(response2))
    end
    response2 = string.gsub(response2,"LAL","TST")
    txn.res:set(response2)
    txn:Info("luahttpresponse DONE?: " .. string.len(response2))
  end

Regards,
PiBa-NL

Thanks in advance,
PiBa-NL

## haproxy.cfg
listen proxyresponse
      bind :10006
      mode http
      http-response lua.luahttpresponse
      server x 192.168.0.40:302

## script.lua
    function luahttpresponse(txn)
      local response2 = txn.res:get()
      response2 = string.gsub(response2,"LAL","TST")
      txn.res:send(response2)
      txn.done()
    end
core.register_action("luahttpresponse" , { "http-res" }, luahttpresponse);

## webpage.aspx , with 2.7KB output.
------------------
<%@ Page Language="C#" %>
<html><body>START
<% var x = new String('x', 250);
x = "<input type=\"hidden\" value=\""+x+"\" />"; %>
1 -LALA- <% Response.Write(x); %>
2 -LALA- <% Response.Write(x); %>
3 -LALA- <% Response.Write(x); %>
4 -LALA- <% Response.Write(x); %>
5 -LALA- <% Response.Write(x); %>
6 -LALA- <% Response.Write(x); %>
7 -LALA- <% Response.Write(x); %>
8 -LALA- <% Response.Write(x); %>
9 -LALA- <% Response.Write(x); %>
STOP
--------------------




Reply via email to