Using bind() to create a server with Lua Socket - lua

Working with LuaSocket, this code works:
local socket = require'socket'
local server = socket.bind('*',51423)
local client = server:accept()
but this code fails:
local socket = require 'socket'
local server = socket.tcp()
server:bind('*',51423)
local client = server:accept()
--> lua: /tmp/server.lua:4: calling 'accept' on bad self (tcp{server} expected, got userdata)
Yet the documentation for TCP bind implies that the latter should work, stating:
"Note: The function socket.bind is available and is a shortcut for the creation of server sockets."
How can I convert a generic "master" object into a server?
The motivation for this is the desire to add a timeout on the bind operation itself:
local socket = require'socket'
local server = socket.tcp()
server:settimeout(2/1000) -- Only wait 2ms when attempting to bind
server:bind('*',51423)

The answer is at the top of the same documentation page (oops):
"A master object can be transformed into a server … with the method listen (after a call to bind)"
It would seem that s = socket.bind(…) is actually equivalent to:
s = socket.tcp()
s:bind(…)
s:listen(32)
I'm not sure why they are split into two functions, but modifying the code to add listen() causes it to work:
local socket = require 'socket'
local server = socket.tcp()
server:bind('*',51423)
server:listen(32)
local client = server:accept()

Related

Finagle connect to a service and not the host

I have a server that hosts many services.
In a scala application, I need to query one of its services : service1/api/endpoint1
The problem I'm facing, is that Http.client.newService expects a host with a port, so in my case
val client: Service[Request, Response] = Http.client
.withRetryBudget(budget)
.withMaxResponseSize(StorageUnit.fromMegabytes(1900))
.withRetryBackoff(Backoff.const(10.seconds))
.newService("myHost:9999")
When posting a request
val req = RequestBuilder
.post("/service/api/endpoint1")
.body(jsonReq)
.request
val request: Future[Response] = client(req)
val response = Await.result[Response](request, Duration.fromMinutes(4))
I got the error
An existing connection was forcibly closed by the remote host at remote address myhost:9999
because myhost:9999 doesn't allow direct connections, one can only connect to one of it's services, myhost:9999/service1 or so..
Is there a way to achieve this ? i.e to create the httpclient with the whole url myHost:9999/service1 ?

how to send https request from lua in haproxy before routing request?

I used haproxy Socket class as outlined here https://www.haproxy.com/blog/5-ways-to-extend-haproxy-with-lua/#actions to make http request to external service from lua code (See code below).
How can I make an https request to the service?
Is it possible to specify a domain name instead of IP address of the service to connect to?
Any help is appreciated.
local function http_request(txn, data)
local addr = <external-IP>
local port = 80
-- Set up a request to the service
local hdrs = {
[1] = string.format('host: %s:%s', addr, port),
[2] = 'accept: */*',
[3] = 'connection: close'
}
local req = {
[1] = string.format('GET %s HTTP/1.1', data.path),
[2] = table.concat(hdrs, '\r\n'),
[3] = '\r\n'
}
req = table.concat(req, '\r\n')
-- Use core.tcp to get an instance of the Socket class
local socket = core.tcp()
socket:settimeout(data.timeout)
-- Connect to the service and send the request
if socket:connect(addr, port) then
if socket:send(req) then
-- Skip response headers
while true do
local line, _ = socket:receive('*l')
if not line then break end
if line == '' then break end
end
-- Get response body, if any
local content = socket:receive('*a')
return content
else
core.Alert('Could not connect to server (send)')
end
socket:close()
else
core.Alert('Could not connect to server (connect)')
end
end
Recently while working on a problem I figured out that we cannot pass domain name. I was using http.lua lib. This http.lua lib uses Socket class as you are doing in your code.
Also after searching a lot I was unable to find a dns resolver lib. One was there something related to nginx lua, but it requires installing lots of different lua libs, so I skipped it.
The work around I did is, created my own dns resolver service http://127.0.0.1:53535 in HAProxy as below
listen lua_dns
bind 127.0.0.1:53535
http-request do-resolve(txn.dstip,mydns,ipv4) hdr(ResolveHost),lower
http-request return status 200 content-type text/plain lf-string OK hdr ResolvedIp "%[var(txn.dstip)]"
To this service I pass the domain name in request header ResolveHost and get the IP in response header ResolvedIp.
Now the lua functions to parse the domain from URL and call dns resolver service is as below
local function parse_domain(url)
local schema, host, _ = url:match("^(.*)://(.-)[?/](.*)$")
if not schema then
-- maybe path (request uri) is missing
schema, host = url:match("^(.*)://(.-)$")
if not schema then
core.Info("ERROR :: Could not parse URL: "..url)
return nil
end
end
return host
end
local function resolve_domain(domain)
local d = parse_domain(domain)
local r, msg = http.get{ url = "http://127.0.0.1:53535", headers={ResolveHost=d} }
if r == nil then
core.Info("ERROR: "..msg..". While resolving doamin: "..d)
return msg
end
return r.headers['resolvedip']
end
Now replace the resolved IP with the domain name in the URL using gsub()
url = string:gsub(domain_name, resolved_ip)
and then call your API using http.lua
local res, msg = http.get{ url=url, headers=headers }
Here the http.lua lib will handle HTTP and HTTPS urls.

lua udp not working between computers

my udp code doesn't work when sending from computer 1 to computer 2, but it works sending from comp 2 to comp 1. I have tried both on the same computer, works fine as expected. The computers are connected through ethernet on a lan, and windows picks up the computers and lets me log in and read/write to/from the computers.
Server:
local socket = require "socket"
-- begin
local udp = socket.udp()
udp:settimeout(1)
udp:setsockname('*', 12345)
local data, msg_or_ip, port_or_nil
local cmd, parms
local running = true
print "Beginning server loop."
while running do
data, msg_or_ip, port_or_nil = udp:receivefrom()
if data then
print(data,msg_or_ip,port_or_nil)
udp:sendto(data,msg_or_ip,port_or_nil)
elseif msg_or_ip ~= 'timeout' then
print("Unknown network error: "..tostring(msg))
end
socket.sleep(0.01)
end
Client:
local socket = require "socket"
local address, port = "192.168.137.161", 12345
udp = socket.udp()
udp:settimeout(1)
while true do
udp:setpeername(address, port)
udp:send(io.read())
udp:close()
--udp:setsockname("*",12345)
data, msg_or_ip, port_or_nil = udp:receivefrom()
print(data, msg_or_ip, port_or_nil)
udp:close()
end
The client sends the message, server picks it up, and should echo it right back, but the client spits out
nil refused nil, with the tab between nil and refused.
I think this is mostly just something with sending from the server computer to the client computer, since it should just echo the data.
I could see how this problem would be hard to answer, due to not everyone having full access to two computers at once. For the record lua 5.1 is used on both, socket is v2.0.2 on both as well.
for all who were wondering, it was because I closed the socket. nothing major.

LuaSocket (UDP) not receiving datagrams

I'm experimenting with LuaSocket for a project I'm working on. I've chosen UDP as my protocol.
Looking for docs and tutorials online, I've attempted to create a client-server pair for testing and learning.
According to what I've read, the following code should work. However, only the server seems to be working properly. The client sends a message, but it will not receive the reply from the server.
Thank you for any help anyone can provide.
Server:
-- Server
#!/usr/bin/env lua5.1
local socket = require("socket")
udp = socket.udp()
udp:setsockname("*", 53474)
udp:settimeout(0)
while true do
data, ip, port = udp:receivefrom()
if data then
print("Received: ", data, ip, port)
udp:sendto(data, ip, port)
end
socket.sleep(0.01)
end
Client:
-- Client
#!/usr/bin/env lua5.1
local socket = require("socket")
udp = socket.udp()
udp:setpeername("127.0.0.1", 53474)
udp:settimeout(0)
udp:send("Data!")
data = udp:receive()
if data then
print("Received: ", data)
end
The timeout value you set is 0, which causes the client timeout every time.
To fix it, give it a positive timeout value:
udp:settimeout(1)
Or set it to nil or a negative value, so it blocks indefinitely:
udp:settimeout()
or
udp:settimeout(-1)

Lua http socket evaluation

I use lua 5.1 and the luaSocket 2.0.2-4 to retrieve a page from a web server. I first check if the server is responding and then assign the web server response to lua variables.
local mysocket = require("socket.http")
if mysocket.request(URL) == nil then
print('The server is unreachable on:\n'..URL)
return
end
local response, httpCode, header = mysocket.request(URL)
Everything works as expected but the request is executed two times. I wonder if I could do Something like (which doesn't work obviously):
local mysocket = require("socket.http")
if (local response, httpCode, header = mysocket.request(URL)) == nil then
print('The server is unreachable on:\n'..URL)
return
end
Yes, something like this :
local mysocket = require("socket.http")
local response, httpCode, header = mysocket.request(URL)
if response == nil then
print('The server is unreachable on:\n'..URL)
return
end
-- here you do your stuff that's supposed to happen when request worked
Request will be sent only once, and function will exit if it failed.
Even better, when request fails, the second return is the reason:
In case of failure, the function returns nil followed by an error message.
(From the documentation for http.request)
So you can print the problem straight from the socket's mouth:
local http = require("socket.http")
local response, httpCode, header = http.request(URL)
if response == nil then
-- the httpCode variable contains the error message instead
print(httpCode)
return
end
-- here you do your stuff that's supposed to happen when request worked

Resources