HTTP Socket Modification SmartThings?

Hi, I made this post yesterday:

It addressed the problem that I was getting a request closed when using GET http socket from Awair Local API. However, after digging around on a packet level, I realized that the socket.http sent request lines and headers on separate commands and it seemed the Awair did not like that at all, so what was done was basically combining metat.__index:sendrequestline(method, uri) function and metat.__index:sendheaders(tosend) to one function:

function metat.__index:sendrequestlineX(method, uri, tosend)
local reqline = string.format(“%s %s HTTP/1.1\r\n”, method or “GET”, uri)
local canonic = headers.canonic
local h = “\r\n”
for f, v in base.pairs(tosend) do
h = (canonic[f] or f) … ": " … v … “\r\n” … h
end

return self.try(self.c:send(reqline .. h))

end

image

And it works, I can now get the data from my Awair Local API. However, here comes another problem.
I needed to modify the “socket.http” in order to achieve this. I tried referencing it as a local module folder instead of calling from the SmartThings library, and of course it didn’t work as I was missing many dependencies such as mime.lua and .core files. I wonder if there is any way for me to access the SmartThings libraries and modify the socket module. Any suggested workaround will also be welcome and greatly appreciated. Thank you very much in advance.

You can’t access the libraries since they’re shipped on the hub but you can write your own library and package it
@nayelyz

Thanks for tagging me, @RBoy.

He’s right, @sleepdeprived. The ST Lua libraries executed in the Hub cannot be modified. You can find them here to see their current configuration:

I’ll also ask the team to see if they can provide more info about this kind of case.

@sleepdeprived, following up, can you provide more details on the reason why you need to modify socket.http to get the data from the Awair Local API, please?

Hi @nayelyz ,
As I was saying, I used Wireshark to look at the transmission on a packet level and found that the socket.http sent two separate requests, one for the request lines and one for the headers, and apparently Awair Local API did not accept that. Thus, I thought of modifying the http library with the method above. However, here’s a workaround for it that does not require modification on the library itself:

local socket = require “socket”
local tcp = socket.try((create or socket.tcp)())

–Build the request string that will TX to the web server

local reqline = string.format(“%s %s HTTP/1.1\r\n”, “GET”, “”) … “User-Agent:\13\nTE: <>\13\nConnection: <>, TE\13\nHost: \13\n\13\n”

–Setup and connect to the web server

tcp:settimeout(60)

tcp:connect(“”,)

–Transmit the request string

local x = tcp:send(reqline)
local y = “”

–loop through each line of the reply until the JSON data is found.

while string.sub(y, 1, 1) ~= “{” do
y = tcp:receive()
end
–Close the TCP Connection
tcp:close()

– Debug display the data
print (“\n ***** Start Data ***** \n”)
print (y)
print (“\n ***** Stop Data ***** \n”)

In general, luasocket will attempt to send any data passed to :send immediately without buffering so if you need to have this all sent in one chunk you will need to make sure that the whole request header is getting built before passing that off to send. In your first example which I am going to reproduce here with three back tick characters (`) before and after to get code formatting:

function metat.__index:sendrequestlineX(method, uri, tosend)
    local reqline = string.format("%s %s HTTP/1.1\r\n", method or "GET", uri)
    local canonic = headers.canonic
    local h = "\r\n"
    for f, v in base.pairs(tosend) do
      h = (canonic[f] or f) .. ": " .. v .. "\r\n" .. h
  end

    return self.try(self.c:send(reqline .. h))

end

I believe the problem with this is that you don’t seem to be sending the header end new line according to the http spec, the signal that the headers are complete is two \r\n pairs in a row. I personally find it a little easier to use something like

local header = {reqline}
for f, v in base.pairs(tosend) do
    local key = canonic[f] or f
    table.insert(header, string.format("%s: %s", key, v)
end
-- Add one extra entry to create a trailing new line
table.insert(header, "")
local full_header = table.concat(header, "\r\n")

In your second example, you are including the traling new line in reqline but leaving the Host header unpopulated which will lead to problems. In general both examples seem to be missing some context, would you be able to share your full project?

In my experience it is rarely a problem with how many packets are being sent, all http servers I have ever worked with have been able to concatenate a request from any number of packets. I would look somewhere else for why your request is failing, especially if there are debug logs available from the device your are attempting to connect to. Have you tried using the [luncheon library] (Luncheon — Luncheon) to build your request/responses?

The last piece to bring up is that you don’t seem to be using cosock which is the co-routine executor/luasocket wrapper we use in edge drivers, if you are attempting to run this code on a hub it is going to run into some issues if left as is.

1 Like