Jump to content
C4 Forums | Control4

How to parse data from OnServerDataIn


emaxedon

Recommended Posts

I've created a server in my driver using C4.CreateServer. The OnServerDataIn function is invoked when the director receives data on the port.

Right now I'm sending it a HTTP POST request, but don't know how to extract only the body from the "strData".

--[[
	Invoked by Director when data comes in on an open Server Socket.
]]
function OnServerDataIn(nHandle, strData, strClientAddress, strPort)
	-- TODO: Parse body of POST from strData
end

Right now when I send it a POST request that looks like:

POST / HTTP/1.1
Content-type:text/plain
host:example.com
content-length:5

test

strData always looks like:

POST / HTTP/1.1Content-type:text/plainhost:example.comcontent-length:5test 

Does Control4 strip out the \r\n from the POST request? 

Link to comment
Share on other sites


No, but printing it might.

You can likely see the entire query if you do a hexdump with the data...

function OnServerDataIn(nHandle, strData, strClientAddress, strPort)
  hexdump(strData)
end

You end up seeing your header, then 0d 0a 0d 0a, then your data:

00000000  50 4F 53 54 20 2F 20 48  54 54 50 2F 31 2E 30 0D  POST / HTTP/1.0.
00000010  0A 55 73 65 72 2D 41 67  65 6E 74 3A 20 63 75 72  .User-Agent: cur
00000020  6C 2F 37 2E 33 35 2E 30  0D 0A 48 6F 73 74 3A 20  l/7.35.0..Host: 
00000030  31 39 32 2E 31 36 38 2E  32 39 2E 31 30 3A 38 37  192.168.29.10:87
00000040  36 35 0D 0A 41 63 63 65  70 74 3A 20 2A 2F 2A 0D  65..Accept: */*.
00000050  0A 43 6F 6E 74 65 6E 74  2D 4C 65 6E 67 74 68 3A  .Content-Length:
00000060  20 34 0D 0A 43 6F 6E 74  65 6E 74 2D 54 79 70 65   4..Content-Type
00000070  3A 20 61 70 70 6C 69 63  61 74 69 6F 6E 2F 78 2D  : application/x-
00000080  77 77 77 2D 66 6F 72 6D  2D 75 72 6C 65 6E 63 6F  www-form-urlenco
00000090  64 65 64 0D 0A 56 69 61  3A 20 31 2E 31 20 70 66  ded..Via: 1.1 pf
000000A0  73 65 6E 73 65 3A 33 31  32 38 20 28 73 71 75 69  sense:3128 (squi
000000B0  64 2F 32 2E 37 2E 53 54  41 42 4C 45 39 29 0D 0A  d/2.7.STABLE9)..
000000C0  58 2D 46 6F 72 77 61 72  64 65 64 2D 46 6F 72 3A  X-Forwarded-For:
000000D0  20 31 39 32 2E 31 36 38  2E 32 39 2E 31 38 0D 0A   192.168.29.18..
000000E0  43 61 63 68 65 2D 43 6F  6E 74 72 6F 6C 3A 20 6D  Cache-Control: m
000000F0  61 78 2D 61 67 65 3D 32  35 39 32 30 30 0D 0A 43  ax-age=259200..C
00000100  6F 6E 6E 65 63 74 69 6F  6E 3A 20 6B 65 65 70 2D  onnection: keep-
00000110  61 6C 69 76 65 0D 0A 0D  0A                       alive....
00000000  62 6C 61 68                                       blah

If you'll notice, in this case, you're getting *2* calls to OnServerDataIn (notice the address where 'blah' starts is 00000000)...

That means you need to buffer all the data you've gotten until you get it all, then the easiest way is to use a pattern match to get the data out.

Something like this:

function OnServerDataIn(nHandle, strData, strClientAddress, strPort)
  -- Make a buffer for every handle that comes in...
  gBuf = gBuf or {}
  gBuf[nHandle] = gBuf[nHandle] or ""
  gBuf[nHandle] = gBuf[nHandle] .. strData

  -- You really should check the header to make sure you have the entire Content-Length, but I am going to just assume
  -- that my data will all be there within a half a second after the data starts coming...
  -- This will probably break if you have large data packets...
  
  C4:SetTimer(500,
    function()
      if (gBuf[nHandle]) then
        local _, _, data = gBuf[nHandle]:find("\r\n\r\n(.+)")
        gBuf[nHandle] = nil -- clear buffer, otherwise memory leak...
        UseMyData(data)
      end
    end
  )
end

function UseMyData(postData)
  -- Here's where you'll use that posted data...
  print("Post Data: " .. postData)
end

Good luck with it.

RyanE

 

Link to comment
Share on other sites

OK, here's a better implementation that uses Content-Length if it exists:

function OnServerDataIn(nHandle, strData, strClientAddress, strPort)
  gBuf = gBuf or {}     -- Make a buffer for every handle that comes in...
  gBuf[nHandle] = (gBuf[nHandle] or "") .. strData

  HEADER_WHITESPACE = "\r\n\r\n"
  CONTENT_LENGTH_HEADER = "Content%-Length: "

  if (gBuf[nHandle]:find(CONTENT_LENGTH_HEADER)) then
    local _, _, content_len = gBuf[nHandle]:find(CONTENT_LENGTH_HEADER .. "(%d+)%s")
    local _, header_end = gBuf[nHandle]:find(HEADER_WHITESPACE)
    if (string.len(gBuf[nHandle]) >= header_end + content_len) then     -- Found full Content...
      local _, _, data = gBuf[nHandle]:find(HEADER_WHITESPACE .. "(.+)")
      gBuf[nHandle] = nil -- clear buffer, otherwise memory leak...
      UseMyData(data)
      C4:ServerCloseClient(nHandle)
    end
  else
    -- No Content-Length header found, grab data after a couple of seconds...
    C4:SetTimer(2000,
      function()
        -- Check if the buffer still exists...
        if (gBuf[nHandle]) then
          local _, _, data = gBuf[nHandle]:find(HEADER_WHITESPACE .. "(.+)")
          gBuf[nHandle] = nil -- clear buffer, otherwise memory leak...
          UseMyData(data or "")
          C4:ServerCloseClient(nHandle)
        end
      end
    )
  end
end


function UseMyData(postData)
  print("Post Data: " .. postData)
end

BTW, I'm not checking to see if I have a valid header_end.  That's likely a bug that may bite you if you have a lot of headers.

I'll leave that as an exercise to the reader to fix.

:)

I'm also not sending an HTTP reply.  Baaaad Laaama!

RyanE

 

Link to comment
Share on other sites

Archived

This topic is now archived and is closed to further replies.

×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.