Compare commits
19 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2afef5d0fe | |||
| 0efb553d7c | |||
| eb6c7ba651 | |||
| 50a6cf88f4 | |||
|
|
b7636e977d | ||
|
|
fbd63a9aea | ||
| e2503cf103 | |||
| 808c73cf93 | |||
| 36ae77e98a | |||
| a7ce660e3b | |||
| 4a5b9433f5 | |||
| 9d5a7b559c | |||
| b98058f84e | |||
| 97578e5073 | |||
| b4a8ec0360 | |||
| 1760935658 | |||
| c61ee0537d | |||
| 8f8fd31e64 | |||
| 7633e87ab9 |
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,3 +1,6 @@
|
||||
# lua-env
|
||||
luajit/*
|
||||
|
||||
# Compiled Lua sources
|
||||
luac.out
|
||||
|
||||
|
||||
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
[submodule "multi"]
|
||||
path = multi
|
||||
url = https://github.com/rayaman/multi.git
|
||||
184
README.html
184
README.html
File diff suppressed because one or more lines are too long
16
README.md
16
README.md
@ -1,10 +1,8 @@
|
||||
# net (2.0.1)
|
||||
Updated from 2.0.0 to 2.0.1
|
||||
Added:
|
||||
- Examples
|
||||
- Support for latest multi version
|
||||
- Updated readme
|
||||
# net (5.0.0) Total Rewrite
|
||||
|
||||
This readme needs to be rewritten, that will happen when all planned features are added to v5
|
||||
|
||||
I've returned to this project. Mainly to create a way to test my multi-tasking library. Notable features, create server/client connections that work async. Added wrapper for http and https request (multi, luasocket and luasec required) to work in coroutines without needing a lua-core modifacation.
|
||||
|
||||
# Discord
|
||||
For real-time assistance with my libraries! A place where you can ask questions and get help with any of my libraries</br>
|
||||
@ -26,12 +24,12 @@ A simple and powerful way to make servers and clients
|
||||
- [ ] Improve 'stable' modules
|
||||
- [ ] AUDP - advance udp. Ensures packets arrive and handles late packets.
|
||||
- [ ] P2P - peer to peer (Server to set up initial connection)
|
||||
- [ ] Relay - offput server load (locally)
|
||||
- [ ] Threading - Simple threading ~~(UDP/AUDP Only)~~ Thanks to an updated multi library we can thread with ease
|
||||
- [ ] Threading (requires lanes which is installed when multi is installed)
|
||||
- [ ] Priority handling
|
||||
|
||||
# Note
|
||||
You will see a bunch of files inside of the net folder. All that is stable is the init.lua and sft.lua file. Everything else is a work in progress. Plus I am planning on rewritting all of the modules to take advantage of the new threading features that are found in the new multi updates. PRogress on this will be made soon. I have just been away from my PC for a while.
|
||||
|
||||
# Usage
|
||||
server.lua
|
||||
```lua
|
||||
@ -39,7 +37,7 @@ require("bin") -- this library needs a lot of work it has a bunch of old useless
|
||||
require("multi") -- you need this to handle multiple connections and such
|
||||
require("net") -- That requires the main library
|
||||
server=net:newTCPServer(12345) -- create a server that listens on port 12345
|
||||
server.OnDataRecieved(function(self,data,CID_OR_HANDLE,IP_OR_HANDLE,PORT_OR_IP,UPDATER_OR_NIL) -- a bit confusing, but dont worry you will hardly ever need more then the first 5 arguments, unless you are writing modules!
|
||||
server.OnDataRecieved(function(self, data, CID, IP, PORT) -- a bit confusing, but dont worry you will hardly ever need more then the first 5 arguments, unless you are writing modules!
|
||||
if data=="Hello!" then
|
||||
print("Got response from client sending back data!")
|
||||
self:send(IP_OR_HANDLE,"Hello Client!",PORT_OR_IP) -- doing it like this makes this code work for both udp and tcp
|
||||
|
||||
46
client.lua
46
client.lua
@ -1,17 +1,29 @@
|
||||
package.path="?/init.lua;"..package.path
|
||||
local multi = require("multi")
|
||||
local net = require("net")
|
||||
client = net:newTCPClient("localhost",12345)
|
||||
client:enableBinaryMode()
|
||||
local file = bin.new()
|
||||
client.OnDataRecieved(function(self,data)
|
||||
if data == "END" then
|
||||
file:tofile("test2.mp3")
|
||||
print("File transfered!")
|
||||
else
|
||||
file:tackE(data)
|
||||
end
|
||||
end)
|
||||
client.OnClientReady:holdUT() -- waits until the client is ready... You can also connect to this event as well and have code do stuff too
|
||||
client:send("Hello Server!")
|
||||
multi:mainloop()
|
||||
package.path = "./?/init.lua;./?.lua;"..package.path
|
||||
local net = require("lnet.tcp")
|
||||
local multi, thread = require("multi"):init()
|
||||
--
|
||||
-- local http = require("lnet.http")
|
||||
-- local http_ = require("socket.http")
|
||||
|
||||
local https = require("lnet.https")
|
||||
|
||||
-- multi:newThread("Timer",function()
|
||||
-- while true do
|
||||
-- thread.sleep(8)
|
||||
-- print("...")
|
||||
-- end
|
||||
-- end)
|
||||
-- download("http://212.183.159.230/5MB.zip","test1.bin")
|
||||
-- download("http://212.183.159.230/50MB.zip","test2.bin").OnError(print)
|
||||
|
||||
print(https.request("https://erowall.com/wallpapers/large/32757.jpg"))
|
||||
|
||||
-- local client = net.newCastedClient("Test")--net:newTCPClient("localhost",12345)
|
||||
|
||||
-- client:send("Test!")
|
||||
|
||||
-- client.OnDataRecieved(function(c,data)
|
||||
-- print("Response: ",data)
|
||||
-- end)
|
||||
|
||||
--multi:mainloop()
|
||||
|
||||
35
lnet/base/client.lua
Normal file
35
lnet/base/client.lua
Normal file
@ -0,0 +1,35 @@
|
||||
local multi, thread = require("multi"):init()
|
||||
local client = {}
|
||||
client.__index = client
|
||||
client.updaterRate = 1
|
||||
client.sMode = "*l"
|
||||
client.rMode = "*l"
|
||||
function client:init(type)
|
||||
self.Type = type
|
||||
self.OnDataRecieved = multi:newConnection()
|
||||
self.OnServerNotAvailable = multi:newConnection()
|
||||
self.OnClientReady = multi:newConnection()
|
||||
self.OnClientDisconnected = multi:newConnection()
|
||||
self.OnConnectionRegained = multi:newConnection()
|
||||
self.OnPreSend = multi:newConnection()
|
||||
self.OnPreRecieved = multi:newConnection()
|
||||
self.OnError = multi:newConnection()
|
||||
self.process = multi:newProcessor()
|
||||
self.process.Start()
|
||||
end
|
||||
function client:send(data)
|
||||
-- Override this function
|
||||
end
|
||||
function client:close()
|
||||
-- Override this function
|
||||
end
|
||||
function client:setUpdateRate(n)
|
||||
self.updaterRate = n
|
||||
end
|
||||
function client:setReceiveMode(mode)
|
||||
self.rMode = mode
|
||||
end
|
||||
function client:setSendMode(mode)
|
||||
self.sMode = mode
|
||||
end
|
||||
return client
|
||||
87
lnet/base/server.lua
Normal file
87
lnet/base/server.lua
Normal file
@ -0,0 +1,87 @@
|
||||
local multi, thread = require("multi"):init()
|
||||
local socket = require("socket")
|
||||
local net = require("lnet")
|
||||
local server = {}
|
||||
local bCaster = 0
|
||||
server.__index = server
|
||||
server.updaterRate = 1
|
||||
server.rMode = "*l"
|
||||
server.sMode = "*l"
|
||||
function server:init(type)
|
||||
self.Type = type
|
||||
self.OnClientsModulesList = multi:newConnection()
|
||||
self.OnPreRecieved = multi:newConnection()
|
||||
self.OnDataRecieved = multi:newConnection()
|
||||
self.OnClientClosed = multi:newConnection()
|
||||
self.OnClientConnected = multi:newConnection()
|
||||
self.OnPreSend = multi:newConnection()
|
||||
self.idleRate = 5
|
||||
self.clientHandlers = {}
|
||||
self.bannedCIDs = {}
|
||||
self.bannedIPs = {}
|
||||
self.broad = socket.udp()
|
||||
self.localIP = net.getLocalIP()
|
||||
self.ips = {}
|
||||
self.links = {}
|
||||
self.cids = {}
|
||||
self.process = multi:newProcessor()
|
||||
self.process.Start()
|
||||
end
|
||||
function server:setIdleRate(minutes)
|
||||
self.idleRate = minutes
|
||||
end
|
||||
function server:setUpdateRate(n)
|
||||
self.updaterRate = n
|
||||
end
|
||||
function server:banCID(cid)
|
||||
table.insert(self.bannedCIDs,cid)
|
||||
end
|
||||
function server:banIP(ip)
|
||||
table.insert(self.bannedIPs)
|
||||
end
|
||||
function server:setRecieveMode(mode)
|
||||
self.rMode = mode
|
||||
end
|
||||
function server:setSendMode(mode)
|
||||
self.sMode = mode
|
||||
end
|
||||
function server:broadcast(name)
|
||||
if not self.isBroadcasting then
|
||||
bCaster = bCaster + 1
|
||||
self.isBroadcasting = true
|
||||
self.process:newThread("Broadcast Handler<"..bCaster..">",function()
|
||||
while true do
|
||||
thread.yield()
|
||||
self.broad:setoption("broadcast",true)
|
||||
self.broad:sendto(table.concat({name,self.Type,self.localIP},"|")..":"..self.port, "255.255.255.255", 11111)
|
||||
-- Send to localhost as well
|
||||
self.broad:sendto(table.concat({name,self.Type,self.localIP},"|")..":"..self.port, self.localIP, 11111)
|
||||
self.broad:setoption("broadcast", false)
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
function server:send(cid,data)
|
||||
-- Override this
|
||||
end
|
||||
function server:getCid(ip,port)
|
||||
if self.cids[ip .. port] then
|
||||
return self.cids[ip .. port]
|
||||
end
|
||||
end
|
||||
function server:sendAll(data)
|
||||
for i,v in pairs(self.cids) do
|
||||
self:send(data,cid)
|
||||
end
|
||||
end
|
||||
function server:sendAllBut(data,cid)
|
||||
for i,v in pairs(self.cids) do
|
||||
if v~=cid then
|
||||
self:send(data,cid)
|
||||
end
|
||||
end
|
||||
end
|
||||
function server:clientRegistered(cid)
|
||||
return self.cids[cid]
|
||||
end
|
||||
return server
|
||||
372
lnet/http.lua
Normal file
372
lnet/http.lua
Normal file
@ -0,0 +1,372 @@
|
||||
-----------------------------------------------------------------------------
|
||||
-- HTTP/1.1 client support for the Lua language.
|
||||
-- LuaSocket toolkit.
|
||||
-- Author: Diego Nehab
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- Declare module and import dependencies
|
||||
-------------------------------------------------------------------------------
|
||||
local net = require("lnet")
|
||||
local multi, thread = require("multi"):init()
|
||||
local socket = require("socket")
|
||||
local url = require("socket.url")
|
||||
local ltn12 = require("ltn12")
|
||||
local mime = require("mime")
|
||||
local string = require("string")
|
||||
local headers = require("socket.headers")
|
||||
local base = _G
|
||||
local table = require("table")
|
||||
net.http = {}
|
||||
local _M = net.http
|
||||
local default_block_size = 1024
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- Program constants
|
||||
-----------------------------------------------------------------------------
|
||||
-- connection timeout in seconds
|
||||
TIMEOUT = 60
|
||||
-- default port for document retrieval
|
||||
_M.PORT = 80
|
||||
-- user agent field sent in request
|
||||
_M.USERAGENT = socket._VERSION
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- Reads MIME headers from a connection, unfolding where needed
|
||||
-----------------------------------------------------------------------------
|
||||
local function receiveheaders(sock, headers)
|
||||
local line, name, value, err
|
||||
headers = headers or {}
|
||||
-- get first line
|
||||
line, err = sock:receive()
|
||||
if err then return nil, err end
|
||||
-- headers go until a blank line is found
|
||||
while line ~= "" do
|
||||
-- get field-name and value
|
||||
name, value = socket.skip(2, string.find(line, "^(.-):%s*(.*)"))
|
||||
if not (name and value) then return nil, "malformed reponse headers" end
|
||||
name = string.lower(name)
|
||||
-- get next line (value might be folded)
|
||||
line, err = sock:receive()
|
||||
if err then return nil, err end
|
||||
-- unfold any folded values
|
||||
while string.find(line, "^%s") do
|
||||
value = value .. line
|
||||
line = sock:receive()
|
||||
if err then return nil, err end
|
||||
end
|
||||
-- save pair in table
|
||||
if headers[name] then headers[name] = headers[name] .. ", " .. value
|
||||
else headers[name] = value end
|
||||
end
|
||||
return headers
|
||||
end
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- Extra sources and sinks
|
||||
-----------------------------------------------------------------------------
|
||||
socket.sourcet["http-chunked"] = function(sock, headers)
|
||||
return base.setmetatable({
|
||||
getfd = function() return sock:getfd() end,
|
||||
dirty = function() return sock:dirty() end
|
||||
}, {
|
||||
__call = function()
|
||||
-- get chunk size, skip extention
|
||||
local line, err = sock:receive()
|
||||
if err then return nil, err end
|
||||
local size = base.tonumber(string.gsub(line, ";.*", ""), 16)
|
||||
if not size then return nil, "invalid chunk size" end
|
||||
-- was it the last chunk?
|
||||
if size > 0 then
|
||||
-- if not, get chunk and skip terminating CRLF
|
||||
local chunk, err, part = sock:receive(size)
|
||||
if chunk then sock:receive() end
|
||||
return chunk, err
|
||||
else
|
||||
-- if it was, read trailers into headers table
|
||||
headers, err = receiveheaders(sock, headers)
|
||||
if not headers then return nil, err end
|
||||
end
|
||||
end
|
||||
})
|
||||
end
|
||||
|
||||
socket.sinkt["http-chunked"] = function(sock)
|
||||
return base.setmetatable({
|
||||
getfd = function() return sock:getfd() end,
|
||||
dirty = function() return sock:dirty() end
|
||||
}, {
|
||||
__call = function(self, chunk, err)
|
||||
if not chunk then return sock:send("0\r\n\r\n") end
|
||||
local size = string.format("%X\r\n", string.len(chunk))
|
||||
return sock:send(size .. chunk .. "\r\n")
|
||||
end
|
||||
})
|
||||
end
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- Low level HTTP API
|
||||
-----------------------------------------------------------------------------
|
||||
local metat = { __index = {} }
|
||||
|
||||
function _M.open(host, port, create)
|
||||
-- create socket with user connect function, or with default
|
||||
local c = socket.try((create or socket.tcp)())
|
||||
local h = base.setmetatable({ c = c }, metat)
|
||||
-- create finalized try
|
||||
h.try = socket.newtry(function() h:close() end)
|
||||
-- set timeout before connecting
|
||||
h.try(c:settimeout(_M.TIMEOUT))
|
||||
h.try(c:connect(host, port or _M.PORT))
|
||||
-- here everything worked
|
||||
return h
|
||||
end
|
||||
|
||||
function metat.__index:sendrequestline(method, uri)
|
||||
local reqline = string.format("%s %s HTTP/1.1\r\n", method or "GET", uri)
|
||||
return self.try(self.c:send(reqline))
|
||||
end
|
||||
|
||||
function metat.__index:sendheaders(tosend)
|
||||
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
|
||||
self.try(self.c:send(h))
|
||||
return 1
|
||||
end
|
||||
|
||||
function metat.__index:sendbody(headers, source, step)
|
||||
source = source or ltn12.source.empty()
|
||||
step = step or ltn12.pump.step
|
||||
-- if we don't know the size in advance, send chunked and hope for the best
|
||||
local mode = "http-chunked"
|
||||
if headers["content-length"] then mode = "keep-open" end
|
||||
return self.try(ltn12.pump.all(source, socket.sink(mode, self.c), step))
|
||||
end
|
||||
|
||||
function metat.__index:receivestatusline()
|
||||
local status = self.try(self.c:receive(5))
|
||||
-- identify HTTP/0.9 responses, which do not contain a status line
|
||||
-- this is just a heuristic, but is what the RFC recommends
|
||||
if status ~= "HTTP/" then return nil, status end
|
||||
-- otherwise proceed reading a status line
|
||||
status = self.try(self.c:receive("*l", status))
|
||||
local code = socket.skip(2, string.find(status, "HTTP/%d*%.%d* (%d%d%d)"))
|
||||
return self.try(base.tonumber(code), status)
|
||||
end
|
||||
|
||||
function metat.__index:receiveheaders()
|
||||
return self.try(receiveheaders(self.c))
|
||||
end
|
||||
|
||||
function metat.__index:receivebody(headers, sink, step)
|
||||
sink = sink or ltn12.sink.null()
|
||||
step = step or ltn12.pump.step
|
||||
local length = base.tonumber(headers["content-length"])
|
||||
local t = headers["transfer-encoding"] -- shortcut
|
||||
local mode = "default" -- connection close
|
||||
if t and t ~= "identity" then mode = "http-chunked"
|
||||
elseif base.tonumber(headers["content-length"]) then mode = "by-length" end
|
||||
local ret,lp
|
||||
lp = length%default_block_size
|
||||
for i=1,math.floor(length/default_block_size) do
|
||||
thread.yield()
|
||||
ret = self.try(ltn12.pump.step(socket.source(mode, self.c, default_block_size), sink, step))
|
||||
thread.pushStatus(i,math.floor(length/default_block_size))
|
||||
end
|
||||
if lp~=0 then
|
||||
ret = self.try(ltn12.pump.step(socket.source(mode, self.c, lp), sink, step))
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
function metat.__index:receive09body(status, sink, step)
|
||||
local source = ltn12.source.rewind(socket.source("until-closed", self.c))
|
||||
source(status)
|
||||
return self.try(ltn12.pump.all(source, sink, step))
|
||||
end
|
||||
|
||||
function metat.__index:close()
|
||||
return self.c:close()
|
||||
end
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- High level HTTP API
|
||||
-----------------------------------------------------------------------------
|
||||
local function adjusturi(reqt)
|
||||
local u = reqt
|
||||
-- if there is a proxy, we need the full url. otherwise, just a part.
|
||||
if not reqt.proxy and not PROXY then
|
||||
u = {
|
||||
path = socket.try(reqt.path, "invalid path 'nil'"),
|
||||
params = reqt.params,
|
||||
query = reqt.query,
|
||||
fragment = reqt.fragment
|
||||
}
|
||||
end
|
||||
return url.build(u)
|
||||
end
|
||||
|
||||
local function adjustproxy(reqt)
|
||||
local proxy = reqt.proxy or PROXY
|
||||
if proxy then
|
||||
proxy = url.parse(proxy)
|
||||
return proxy.host, proxy.port or 3128
|
||||
else
|
||||
return reqt.host, reqt.port
|
||||
end
|
||||
end
|
||||
|
||||
local function adjustheaders(reqt)
|
||||
-- default headers
|
||||
local lower = {
|
||||
["user-agent"] = _M.USERAGENT,
|
||||
["host"] = reqt.host,
|
||||
["connection"] = "close, TE",
|
||||
["te"] = "trailers"
|
||||
}
|
||||
-- if we have authentication information, pass it along
|
||||
if reqt.user and reqt.password then
|
||||
lower["authorization"] =
|
||||
"Basic " .. (mime.b64(reqt.user .. ":" .. reqt.password))
|
||||
end
|
||||
-- override with user headers
|
||||
for i,v in base.pairs(reqt.headers or lower) do
|
||||
lower[string.lower(i)] = v
|
||||
end
|
||||
return lower
|
||||
end
|
||||
|
||||
-- default url parts
|
||||
local default = {
|
||||
host = "",
|
||||
port = _M.PORT,
|
||||
path ="/",
|
||||
scheme = "http"
|
||||
}
|
||||
|
||||
local function adjustrequest(reqt)
|
||||
-- parse url if provided
|
||||
local nreqt = reqt.url and url.parse(reqt.url, default) or {}
|
||||
-- explicit components override url
|
||||
for i,v in base.pairs(reqt) do nreqt[i] = v end
|
||||
if nreqt.port == "" then nreqt.port = 80 end
|
||||
socket.try(nreqt.host and nreqt.host ~= "",
|
||||
"invalid host '" .. base.tostring(nreqt.host) .. "'")
|
||||
-- compute uri if user hasn't overriden
|
||||
nreqt.uri = reqt.uri or adjusturi(nreqt)
|
||||
-- ajust host and port if there is a proxy
|
||||
nreqt.host, nreqt.port = adjustproxy(nreqt)
|
||||
-- adjust headers in request
|
||||
nreqt.headers = adjustheaders(nreqt)
|
||||
return nreqt
|
||||
end
|
||||
|
||||
local function shouldredirect(reqt, code, headers)
|
||||
return headers.location and
|
||||
string.gsub(headers.location, "%s", "") ~= "" and
|
||||
(reqt.redirect ~= false) and
|
||||
(code == 301 or code == 302 or code == 303 or code == 307) and
|
||||
(not reqt.method or reqt.method == "GET" or reqt.method == "HEAD")
|
||||
and (not reqt.nredirects or reqt.nredirects < 5)
|
||||
end
|
||||
|
||||
local function shouldreceivebody(reqt, code)
|
||||
if reqt.method == "HEAD" then return nil end
|
||||
if code == 204 or code == 304 then return nil end
|
||||
if code >= 100 and code < 200 then return nil end
|
||||
return 1
|
||||
end
|
||||
|
||||
-- forward declarations
|
||||
local trequest, tredirect
|
||||
|
||||
--[[local]] function tredirect(reqt, location)
|
||||
local result, code, headers, status = trequest {
|
||||
-- the RFC says the redirect URL has to be absolute, but some
|
||||
-- servers do not respect that
|
||||
url = url.absolute(reqt.url, location),
|
||||
source = reqt.source,
|
||||
sink = reqt.sink,
|
||||
headers = reqt.headers,
|
||||
proxy = reqt.proxy,
|
||||
nredirects = (reqt.nredirects or 0) + 1,
|
||||
create = reqt.create
|
||||
}
|
||||
-- pass location header back as a hint we redirected
|
||||
headers = headers or {}
|
||||
headers.location = headers.location or location
|
||||
return result, code, headers, status
|
||||
end
|
||||
|
||||
-- We don't need to protect this function the thread:newFunction wrapper handles errors for us
|
||||
function trequest(reqt)
|
||||
-- we loop until we get what we want, or
|
||||
-- until we are sure there is no way to get it
|
||||
local nreqt = adjustrequest(reqt)
|
||||
local h = _M.open(nreqt.host, nreqt.port, nreqt.create)
|
||||
-- send request line and headers
|
||||
h:sendrequestline(nreqt.method, nreqt.uri)
|
||||
h:sendheaders(nreqt.headers)
|
||||
-- if there is a body, send it
|
||||
if nreqt.source then
|
||||
h:sendbody(nreqt.headers, nreqt.source, nreqt.step)
|
||||
end
|
||||
local code, status = h:receivestatusline()
|
||||
-- if it is an HTTP/0.9 server, simply get the body and we are done
|
||||
if not code then
|
||||
h:receive09body(status, nreqt.sink, nreqt.step)
|
||||
return 1, 200
|
||||
end
|
||||
local headers
|
||||
-- ignore any 100-continue messages
|
||||
while code == 100 do
|
||||
headers = h:receiveheaders()
|
||||
code, status = h:receivestatusline()
|
||||
end
|
||||
headers = h:receiveheaders()
|
||||
-- at this point we should have a honest reply from the server
|
||||
-- we can't redirect if we already used the source, so we report the error
|
||||
if shouldredirect(nreqt, code, headers) and not nreqt.source then
|
||||
h:close()
|
||||
return tredirect(reqt, headers.location)
|
||||
end
|
||||
-- here we are finally done
|
||||
if shouldreceivebody(nreqt, code) then
|
||||
h:receivebody(headers, nreqt.sink, nreqt.step)
|
||||
end
|
||||
h:close()
|
||||
return 1, code, headers, status
|
||||
end
|
||||
|
||||
local function srequest(u, b)
|
||||
local t = {}
|
||||
local reqt = {
|
||||
url = u,
|
||||
sink = ltn12.sink.table(t)
|
||||
}
|
||||
if b then
|
||||
reqt.source = ltn12.source.string(b)
|
||||
reqt.headers = {
|
||||
["content-length"] = string.len(b),
|
||||
["content-type"] = "application/x-www-form-urlencoded"
|
||||
}
|
||||
reqt.method = "POST"
|
||||
end
|
||||
local code, headers, status = socket.skip(1, trequest(reqt))
|
||||
local data = table.concat(t)
|
||||
if #data>0 then
|
||||
return data, code, headers, status
|
||||
else
|
||||
return nil, code, headers, status
|
||||
end
|
||||
end
|
||||
|
||||
_M.request = thread:newFunction(function(reqt, body)
|
||||
if base.type(reqt) == "string" then return srequest(reqt, body)
|
||||
else return trequest(reqt) end
|
||||
end,true)
|
||||
|
||||
return _M
|
||||
146
lnet/https.lua
Normal file
146
lnet/https.lua
Normal file
@ -0,0 +1,146 @@
|
||||
----------------------------------------------------------------------------
|
||||
-- LuaSec 1.0.1
|
||||
-- Copyright (C) 2009-2021 PUC-Rio
|
||||
--
|
||||
-- Author: Pablo Musa
|
||||
-- Author: Tomas Guisasola
|
||||
---------------------------------------------------------------------------
|
||||
|
||||
local socket = require("socket")
|
||||
local ssl = require("ssl")
|
||||
local ltn12 = require("ltn12")
|
||||
local http = require("lnet.http")
|
||||
local url = require("socket.url")
|
||||
|
||||
local try = socket.try
|
||||
|
||||
--
|
||||
-- Module
|
||||
--
|
||||
local _M = {
|
||||
_VERSION = "1.0.1",
|
||||
_COPYRIGHT = "LuaSec 1.0.1 - Copyright (C) 2009-2021 PUC-Rio",
|
||||
PORT = 443,
|
||||
TIMEOUT = 60
|
||||
}
|
||||
|
||||
-- TLS configuration
|
||||
local cfg = {
|
||||
protocol = "any",
|
||||
options = {"all", "no_sslv2", "no_sslv3", "no_tlsv1"},
|
||||
verify = "none",
|
||||
}
|
||||
|
||||
--------------------------------------------------------------------
|
||||
-- Auxiliar Functions
|
||||
--------------------------------------------------------------------
|
||||
|
||||
-- Insert default HTTPS port.
|
||||
local function default_https_port(u)
|
||||
return url.build(url.parse(u, {port = _M.PORT}))
|
||||
end
|
||||
|
||||
-- Convert an URL to a table according to Luasocket needs.
|
||||
local function urlstring_totable(url, body, result_table)
|
||||
url = {
|
||||
url = default_https_port(url),
|
||||
method = body and "POST" or "GET",
|
||||
sink = ltn12.sink.table(result_table)
|
||||
}
|
||||
if body then
|
||||
url.source = ltn12.source.string(body)
|
||||
url.headers = {
|
||||
["content-length"] = #body,
|
||||
["content-type"] = "application/x-www-form-urlencoded",
|
||||
}
|
||||
end
|
||||
return url
|
||||
end
|
||||
|
||||
-- Forward calls to the real connection object.
|
||||
local function reg(conn)
|
||||
local mt = getmetatable(conn.sock).__index
|
||||
for name, method in pairs(mt) do
|
||||
if type(method) == "function" then
|
||||
conn[name] = function (self, ...)
|
||||
return method(self.sock, ...)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Return a function which performs the SSL/TLS connection.
|
||||
local function tcp(params)
|
||||
params = params or {}
|
||||
-- Default settings
|
||||
for k, v in pairs(cfg) do
|
||||
params[k] = params[k] or v
|
||||
end
|
||||
-- Force client mode
|
||||
params.mode = "client"
|
||||
-- 'create' function for LuaSocket
|
||||
return function ()
|
||||
local conn = {}
|
||||
conn.sock = try(socket.tcp())
|
||||
local st = getmetatable(conn.sock).__index.settimeout
|
||||
function conn:settimeout(...)
|
||||
return st(self.sock, _M.TIMEOUT)
|
||||
end
|
||||
-- Replace TCP's connection function
|
||||
function conn:connect(host, port)
|
||||
try(self.sock:connect(host, port))
|
||||
self.sock = try(ssl.wrap(self.sock, params))
|
||||
self.sock:sni(host)
|
||||
self.sock:settimeout(_M.TIMEOUT)
|
||||
try(self.sock:dohandshake())
|
||||
reg(self, getmetatable(self.sock))
|
||||
return 1
|
||||
end
|
||||
return conn
|
||||
end
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------
|
||||
-- Main Function
|
||||
--------------------------------------------------------------------
|
||||
|
||||
-- Make a HTTP request over secure connection. This function receives
|
||||
-- the same parameters of LuaSocket's HTTP module (except 'proxy' and
|
||||
-- 'redirect') plus LuaSec parameters.
|
||||
--
|
||||
-- @param url mandatory (string or table)
|
||||
-- @param body optional (string)
|
||||
-- @return (string if url == string or 1), code, headers, status
|
||||
--
|
||||
local function request(url, body)
|
||||
local result_table = {}
|
||||
local stringrequest = type(url) == "string"
|
||||
if stringrequest then
|
||||
url = urlstring_totable(url, body, result_table)
|
||||
else
|
||||
url.url = default_https_port(url.url)
|
||||
end
|
||||
if http.PROXY or url.proxy then
|
||||
return nil, "proxy not supported"
|
||||
elseif url.redirect then
|
||||
return nil, "redirect not supported"
|
||||
elseif url.create then
|
||||
return nil, "create function not permitted"
|
||||
end
|
||||
-- New 'create' function to establish a secure connection
|
||||
url.create = tcp(url)
|
||||
local res, code, headers, status = http.request(url)
|
||||
if res and stringrequest then
|
||||
return table.concat(result_table), code, headers, status
|
||||
end
|
||||
return res, code, headers, status
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Export module
|
||||
--
|
||||
|
||||
_M.request = request
|
||||
_M.tcp = tcp
|
||||
|
||||
return _M
|
||||
195
lnet/init.lua
Normal file
195
lnet/init.lua
Normal file
@ -0,0 +1,195 @@
|
||||
--[[
|
||||
UPCOMMING ADDITIONS
|
||||
AUDP - advance udp. Ensures packets arrive and handles late packets.
|
||||
P2P - peer to peer (Server to set up initial connection)
|
||||
Relay - offput server load (locally)
|
||||
Threading - Simple threading ~~(UDP/AUDP Only)~~ Thanks to an updated multi library we can thread with ease
|
||||
Priority handling
|
||||
]]
|
||||
--[[
|
||||
TODO: Finish stuff for Priority handling
|
||||
]]
|
||||
function table.merge(t1, t2)
|
||||
for k, v in pairs(t2) do
|
||||
if type(v) == "table" then
|
||||
if type(t1[k] or false) == "table" then
|
||||
table.merge(t1[k] or {}, t2[k] or {})
|
||||
else
|
||||
t1[k] = v
|
||||
end
|
||||
else
|
||||
t1[k] = v
|
||||
end
|
||||
end
|
||||
return t1
|
||||
end
|
||||
function string.trim(s)
|
||||
local from = s:match "^%s*()"
|
||||
return from > #s and "" or s:match(".*%S", from)
|
||||
end
|
||||
local guid = {}
|
||||
local char = {}
|
||||
for i = 48, 57 do
|
||||
char[#char + 1] = string.char(i)
|
||||
end
|
||||
for i = 65, 90 do
|
||||
char[#char + 1] = string.char(i)
|
||||
end
|
||||
for i = 97, 122 do
|
||||
char[#char + 1] = string.char(i)
|
||||
end
|
||||
|
||||
math.random()
|
||||
math.random()
|
||||
math.random()
|
||||
local multi, thread = require("multi").init()
|
||||
local socket = require("socket")
|
||||
local http = require("socket.http")
|
||||
--ssl=require("ssl")
|
||||
--https=require("ssl.https")
|
||||
local net = {}
|
||||
net.Version = {5, 0, 0} -- This will probably stay this version for quite a while... The modules on the otherhand will change more often
|
||||
net._VERSION = "5.0.0"
|
||||
net.ClientCache = {}
|
||||
net.OnServerCreated = multi:newConnection()
|
||||
net.OnClientCreated = multi:newConnection()
|
||||
net.loadedModules = {}
|
||||
net.OnCastedClientInfo = multi:newConnection()
|
||||
net.autoInit = true
|
||||
net.ConnectionDriver = {}
|
||||
net.BroadcastDriver = {}
|
||||
math.randomseed(math.ceil(os.time()+(os.clock()*1000)))
|
||||
local isHyphen = {[9] = 1, [14] = 1, [19] = 1, [24] = 1}
|
||||
net.generateGUID = function(t)
|
||||
local pass = {}
|
||||
local a = 0
|
||||
local x = ""
|
||||
for i in string.format("%x",os.time()+math.ceil(os.time()+os.clock()*1000)):gmatch(".") do
|
||||
table.insert(pass,i)
|
||||
end
|
||||
for z = 9, 36 do
|
||||
if isHyphen[z] then
|
||||
x = "-"
|
||||
else
|
||||
a = math.random(1, #char)
|
||||
x = char[a]
|
||||
end
|
||||
table.insert(pass, x)
|
||||
if t == z then
|
||||
break
|
||||
end
|
||||
end
|
||||
z = nil
|
||||
return tostring(table.concat(pass))
|
||||
end
|
||||
function net.normalize(input)
|
||||
local enc = mime.b64(input)
|
||||
return enc
|
||||
end
|
||||
function net.denormalize(input)
|
||||
local unenc = mime.unb64(input)
|
||||
return unenc
|
||||
end
|
||||
function net.getLocalIP()
|
||||
local someRandomIP = "192.168.1.122"
|
||||
local someRandomPort = "3102"
|
||||
local mySocket = socket.udp()
|
||||
mySocket:setpeername(someRandomIP, someRandomPort)
|
||||
local dat = (mySocket:getsockname())
|
||||
mySocket:close()
|
||||
return dat
|
||||
end
|
||||
function net.getExternalIP()
|
||||
local data = http.request("http://www.myipnumber.com/my-ip-address.asp")
|
||||
return data:match("(%d+%.%d+%.%d+%.%d+)")
|
||||
end
|
||||
function net.registerModule(mod, version)
|
||||
if net[mod] then
|
||||
error("Module by the name: " .. mod .. " has already been registered! Remember some modules are internal and use certain names!")
|
||||
end
|
||||
table.insert(net.loadedModules, mod)
|
||||
net[mod] = {}
|
||||
if version then
|
||||
net[mod].Version = version
|
||||
net[mod]._VERSION = table.concat(version,".")
|
||||
else
|
||||
net[mod].Version = {1, 0, 0}
|
||||
net[mod]._VERSION = {1, 0, 0}
|
||||
end
|
||||
return {Version = version, _VERSION = table.concat(version,".")}
|
||||
end
|
||||
function net.getModuleVersion(ext)
|
||||
if not ext then
|
||||
return string.format("%d.%d.%d", net.Version[1], net.Version[2], net.Version[3])
|
||||
end
|
||||
return string.format("%d.%d.%d", net[ext].Version[1], net[ext].Version[2], net[ext].Version[3])
|
||||
end
|
||||
function net.resolveID(obj)
|
||||
local num = math.random(10000000, 99999999)
|
||||
if obj[tostring(num)] then
|
||||
return net.resolveID(obj)
|
||||
end
|
||||
obj.ids[tostring(num)] = true
|
||||
return tostring(num)
|
||||
end
|
||||
function net.inList(list, dat)
|
||||
for i, v in pairs(list) do
|
||||
if v == dat then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
function net.setTrigger(funcW, funcE)
|
||||
multi:newTrigger(func)
|
||||
end
|
||||
net.registerModule("net", net.Version)
|
||||
-- Client broadcast
|
||||
function net.newCastedClient(name) -- connects to the broadcasted server
|
||||
local listen = socket.udp() -- make a new socket
|
||||
listen:setsockname(net.getLocalIP(), 11111)
|
||||
listen:settimeout(0)
|
||||
local timer = multi:newTimer()
|
||||
while true do
|
||||
local data, ip, port = listen:receivefrom()
|
||||
if timer:Get() > 3 then
|
||||
error("Timeout! Server by the name: " .. name .. " has not been found!")
|
||||
end
|
||||
if data then
|
||||
local n, tp, ip, port = data:match("(%S-)|(%S-)|(%S-):(%d+)")
|
||||
if n:match(name) then
|
||||
if tp == "tcp" then
|
||||
return net.newTCPClient(ip, tonumber(port))
|
||||
else
|
||||
return net.newClient(ip, tonumber(port))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
function net.newCastedClients(name) -- connects to the broadcasted server
|
||||
local listen = socket.udp() -- make a new socket
|
||||
listen:setsockname(net.getLocalIP(), 11111)
|
||||
listen:settimeout(0)
|
||||
multi:newThread("net.castedTask",function()
|
||||
while true do
|
||||
thread.skip(24)
|
||||
local data, ip, port = listen:receivefrom()
|
||||
if data then
|
||||
local n, tp, ip, port = data:match("(%S-)|(%S-)|(%S-):(%d+)")
|
||||
if n:match(name) and not net.ClientCache[n] then
|
||||
local capture = n:match(name)
|
||||
local client = {}
|
||||
if tp == "tcp" then
|
||||
client = net:newTCPClient(ip, tonumber(port))
|
||||
else
|
||||
client = net:newUDPClient(ip, tonumber(port))
|
||||
end
|
||||
net.ClientCache[n] = client
|
||||
net.OnCastedClientInfo:Fire(client, n, ip, port)
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
return net
|
||||
135
lnet/tcp/init.lua
Normal file
135
lnet/tcp/init.lua
Normal file
@ -0,0 +1,135 @@
|
||||
local net = require("lnet")
|
||||
local clientbase = require("lnet.base.client")
|
||||
local serverbase = require("lnet.base.server")
|
||||
local multi, thread = require("multi"):init()
|
||||
local socket = require("socket")
|
||||
local tcpcount = 0
|
||||
net.newTCPServer = thread:newFunction(function(port)
|
||||
local c = {}
|
||||
setmetatable(c,serverbase)
|
||||
c:init("tcp")
|
||||
c.tcp = assert(socket.bind("*", port or 0))
|
||||
c.tcp:settimeout(0)
|
||||
c.ip, c.port = c.tcp:getsockname()
|
||||
if port and port == 0 then
|
||||
_, c.port = c.tcp:getsockname()
|
||||
end
|
||||
function c:send(cid,data)
|
||||
local dat = {data = data, cid = cid}
|
||||
self.OnPreSend:Fire(dat)
|
||||
if self.sMode == "*l" then
|
||||
cid:send(data .. "\n")
|
||||
else
|
||||
cid:send(data)
|
||||
end
|
||||
end
|
||||
tcpcount = tcpcount + 1
|
||||
c.updateThread = c.process:newThread("TCPServer Thread<"..tcpcount..">",function()
|
||||
while true do
|
||||
thread.skip(c.updaterRate)
|
||||
local client = c.tcp:accept(c.rMode)
|
||||
if client then
|
||||
print("Got Client!")
|
||||
table.insert(c.ips, client)
|
||||
client:settimeout(0)
|
||||
client:setoption("keepalive", true)
|
||||
ip, port = client:getpeername()
|
||||
if ip and port then
|
||||
c.OnClientConnected:Fire(c, client, ip, port)
|
||||
-- We need to cache the client handler so we can work with it
|
||||
c.clientHandlers[client] = multi:newThread("ServerClientHandler<".. tostring(client):match(": (.+)") ..">",function()
|
||||
local cli = client
|
||||
while true do
|
||||
thread.yield()
|
||||
local data, err, dat, len
|
||||
data, err = thread.hold(function()
|
||||
data, err = cli:receive(c.rMode)
|
||||
if data then print(data) end
|
||||
if data~=nil and err then
|
||||
print(err)
|
||||
return multi.NIL, err
|
||||
end
|
||||
return data
|
||||
end)
|
||||
if err == "closed" then
|
||||
for i = 1, #c.ips do
|
||||
if c.ips[i] == cli then
|
||||
table.remove(c.ips, i)
|
||||
end
|
||||
end
|
||||
c.OnClientClosed:Fire(c, "Client Closed Connection!", cli, ip)
|
||||
c.links[cli] = nil -- lets clean up
|
||||
thread.kill()
|
||||
end
|
||||
if data then
|
||||
if net.inList(c.bannedIPs, ip) then
|
||||
return
|
||||
end
|
||||
c.OnDataRecieved:Fire(c, data, cli, ip, port)
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
end
|
||||
end).OnError(function(...)
|
||||
print(...)
|
||||
end)
|
||||
net.OnServerCreated:Fire(c)
|
||||
return c
|
||||
end,true)
|
||||
|
||||
net.newTCPClient = thread:newFunction(function(host, port)
|
||||
local c = {}
|
||||
setmetatable(c,clientbase)
|
||||
c:init("tcp")
|
||||
c.ip = assert(socket.dns.toip(host))
|
||||
c.tcp = socket.connect(c.ip,port)
|
||||
c.tcp:settimeout(0)
|
||||
c.tcp:setoption("keepalive",true)
|
||||
function c:send(data)
|
||||
if self.sMode == "*l" then
|
||||
data = data .. "\n"
|
||||
end
|
||||
print("Sending:",data)
|
||||
local dat = {data = data}
|
||||
self.OnPreSend:Fire(dat)
|
||||
local ind, err = self.tcp:send(dat.data)
|
||||
print(ind,err)
|
||||
print("Data Sent!")
|
||||
if err == "closed" then
|
||||
self.OnClientDisconnected:Fire(self,err)
|
||||
elseif err == "timeout" then
|
||||
self.OnClientDisconnected:Fire(self,err)
|
||||
end
|
||||
end
|
||||
c.updateThread = c.process:newThread("TCPClient Thread<"..tcpcount..">",function()
|
||||
while true do
|
||||
thread.skip(c.updaterRate)
|
||||
local data = thread.hold(function()
|
||||
local d,err = c.tcp:receive(c.rMode)
|
||||
if not(d) then
|
||||
if err == "closed" then
|
||||
c.OnClientDisconnected:Fire(c,err)
|
||||
elseif err == "timeout" then
|
||||
c.OnClientDisconnected:Fire(c,err)
|
||||
else
|
||||
print(err)
|
||||
end
|
||||
else
|
||||
return d
|
||||
end
|
||||
end)
|
||||
if data then
|
||||
local dat = {data = data}
|
||||
c.OnPreRecieved:Fire(dat)
|
||||
c.OnDataRecieved:Fire(c,dat.data)
|
||||
end
|
||||
end
|
||||
end).OnError(function(...)
|
||||
print(...)
|
||||
end)
|
||||
net.OnClientCreated:Fire(c)
|
||||
return c
|
||||
end,true)
|
||||
return net
|
||||
102
lnet/udp/init.lua
Normal file
102
lnet/udp/init.lua
Normal file
@ -0,0 +1,102 @@
|
||||
local net = require("lnet")
|
||||
local clientbase = require("lnet.base.client")
|
||||
local serverbase = require("lnet.base.server")
|
||||
local multi, thread = require("multi"):init()
|
||||
local CID = {}
|
||||
CID.__index = cid
|
||||
local udpcount = 0
|
||||
CID.ip = "0.0.0.0"
|
||||
CID.port = 0
|
||||
net.newUDPServer = thread:newFunction(function(port)
|
||||
local c = {}
|
||||
setmetatable(c,serverbase)
|
||||
c:init("udp")
|
||||
c.udp = assert(socket.udp())
|
||||
c.udp:settimeout(0)
|
||||
c.udp:setsockname("*",port)
|
||||
local inactivity = {}
|
||||
if port == 0 then
|
||||
_,c.port = c.udp:getsockname()
|
||||
else
|
||||
c.port = port
|
||||
end
|
||||
udpcount = udpcount + 1
|
||||
function c:send(cid,data)
|
||||
local dat = {data = data, cid = cid}
|
||||
self.OnPreSend:Fire(dat)
|
||||
self.udp:sendto(dat.data,dat.cid.ip,dat.cid.port)
|
||||
end
|
||||
c.updateThread = c.process:newThread("UDPServer Thread<"..udpcount..">",function()
|
||||
-- Every now and then we are going to check to see if a client has been inactive
|
||||
local sideJob = thread:newFunction(function()
|
||||
thread.sleep(60*c.idleRate)
|
||||
for i,v in pairs(c.cids) do
|
||||
thread.skip(1)
|
||||
if os.clock() - v.activity >= 60*c.idleRate then
|
||||
c.OnClientClosed:Fire(v)
|
||||
c.cids[i] = nil
|
||||
end
|
||||
end
|
||||
return true
|
||||
end)
|
||||
while true do
|
||||
thread.skip(c.updaterRate)
|
||||
local data, ip, port = c.udp:receivefrom()
|
||||
sideJob().connect(function(yes)
|
||||
if yes then
|
||||
sideJob:Resume()
|
||||
end
|
||||
end)
|
||||
sideJob:Pause()
|
||||
if data then
|
||||
local cid = c:getCid(ip,port)
|
||||
if not cid then
|
||||
local cd = {}
|
||||
setmetatable(cd,CID)
|
||||
cd.ip = ip
|
||||
cd.port = port
|
||||
cd.activity = os.clock()
|
||||
c.cids[ip .. port] = cd
|
||||
cid = cd
|
||||
c.OnClientConnected:Fire(c, cd, ip, port)
|
||||
end
|
||||
cid.activity = os.clock()
|
||||
local dat = {data = data,cid = cid}
|
||||
c.OnPreRecieved:Fire(dat)
|
||||
c.OnDataRecieved:Fire(c,dat.data,dat.cid,cid.ip,cid.port)
|
||||
end
|
||||
end
|
||||
end).OnError(function(...)
|
||||
print(...)
|
||||
end)
|
||||
net.OnServerCreated:Fire(c)
|
||||
return c
|
||||
end,true)
|
||||
net.newUDPClient = thread:newFunction(function(host, port)
|
||||
local c = {}
|
||||
setmetatable(c,clientbase)
|
||||
c:init("udp")
|
||||
c.ip = assert(socket.dns.toip(host))
|
||||
c.udp = assert(socket.udp())
|
||||
c.udp:settimeout(0)
|
||||
c.udp:setpeername(c.ip,port)
|
||||
function c:send(data)
|
||||
local dat = {data = data}
|
||||
self.OnPreSend:Fire(dat)
|
||||
self.udp:send(dat.data)
|
||||
end
|
||||
c.updateThread = c.process:newThread("UDPServer Thread<"..udpcount..">",function()
|
||||
while true do
|
||||
thread.skip(c.updaterRate)
|
||||
local data = thread.hold(function()
|
||||
return c.udp:receive()
|
||||
end)
|
||||
local dat = {data = data}
|
||||
c.OnPreRecieved:Fire(dat)
|
||||
c.OnDataRecieved:Fire(c,dat.data)
|
||||
end
|
||||
end)
|
||||
net.OnClientCreated:Fire(c)
|
||||
return c
|
||||
end,true)
|
||||
return net
|
||||
1
multi
Submodule
1
multi
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit d36204c87f5c63a4e6f7510040e495bbab3de2d1
|
||||
893
net/init.lua
893
net/init.lua
@ -1,893 +0,0 @@
|
||||
--[[
|
||||
UPCOMMING ADDITIONS
|
||||
AUDP - advance udp. Ensures packets arrive and handles late packets.
|
||||
P2P - peer to peer (Server to set up initial connection)
|
||||
Relay - offput server load (locally)
|
||||
Threading - Simple threading ~~(UDP/AUDP Only)~~ Thanks to an updated multi library we can thread with ease
|
||||
Priority handling
|
||||
]]
|
||||
--[[
|
||||
TODO: Finish stuff for Priority handling
|
||||
]]
|
||||
function table.merge(t1, t2)
|
||||
for k,v in pairs(t2) do
|
||||
if type(v) == 'table' then
|
||||
if type(t1[k] or false) == 'table' then
|
||||
table.merge(t1[k] or {}, t2[k] or {})
|
||||
else
|
||||
t1[k] = v
|
||||
end
|
||||
else
|
||||
t1[k] = v
|
||||
end
|
||||
end
|
||||
return t1
|
||||
end
|
||||
function string.trim(s)
|
||||
local from = s:match"^%s*()"
|
||||
return from > #s and "" or s:match(".*%S", from)
|
||||
end
|
||||
local guid = {}
|
||||
local char = {}
|
||||
for i = 48,57 do
|
||||
char[#char+1]=string.char(i)
|
||||
end
|
||||
for i = 65,90 do
|
||||
char[#char+1]=string.char(i)
|
||||
end
|
||||
for i = 97,122 do
|
||||
char[#char+1]=string.char(i)
|
||||
end
|
||||
local isHyphen = {[9]=1,[14]=1,[19]=1,[24]=1}
|
||||
math.randomseed(os.time())
|
||||
local multi = require("multi")
|
||||
local socket=require("socket")
|
||||
local http=require("socket.http")
|
||||
local mime=require("mime")
|
||||
--ssl=require("ssl")
|
||||
--https=require("ssl.https")
|
||||
local net={}
|
||||
net.Version={3,0,0} -- This will probably stay this version for quite a while... The modules on the otherhand will be more inconsistant
|
||||
net._VERSION="3.0.0"
|
||||
net.ClientCache = {}
|
||||
net.OnServerCreated=multi:newConnection()
|
||||
net.OnClientCreated=multi:newConnection()
|
||||
net.loadedModules={}
|
||||
net.OnCastedClientInfo=multi:newConnection()
|
||||
net.autoInit=true
|
||||
net.ConnectionDriver = {}
|
||||
net.BroadcastDriver = {}
|
||||
net.generateGUID = function(t)
|
||||
local pass = {}
|
||||
local a=0
|
||||
local x=""
|
||||
for z = 1,36 do
|
||||
if isHyphen[z] then
|
||||
x='-'
|
||||
else
|
||||
a = math.random(1,#char)
|
||||
x = char[a]
|
||||
end
|
||||
table.insert(pass, x)
|
||||
if t == z then break end
|
||||
end
|
||||
z = nil
|
||||
return tostring(table.concat(pass))
|
||||
end
|
||||
function net.normalize(input)
|
||||
local enc=mime.b64(input)
|
||||
return enc
|
||||
end
|
||||
function net.denormalize(input)
|
||||
local unenc=mime.unb64(input)
|
||||
return unenc
|
||||
end
|
||||
function net.getLocalIP()
|
||||
local someRandomIP = "192.168.1.122"
|
||||
local someRandomPort = "3102"
|
||||
local mySocket = socket.udp()
|
||||
mySocket:setpeername(someRandomIP,someRandomPort)
|
||||
local dat = (mySocket:getsockname())
|
||||
mySocket:close()
|
||||
return dat
|
||||
end
|
||||
function net.getExternalIP()
|
||||
local data=http.request("http://whatismyip.host")
|
||||
return data:match("(%d+.%d+.%d+.%d+)")
|
||||
end
|
||||
function net:registerModule(mod,version)
|
||||
if net[mod] then
|
||||
error("Module by the name: "..mod.." has already been registered! Remember some modules are internal and use certain names!")
|
||||
end
|
||||
table.insert(self.loadedModules,mod)
|
||||
net[mod]={}
|
||||
if version then
|
||||
net[mod].Version=version
|
||||
net[mod]._VERSION=version[1].."."..version[2].."."..version[3]
|
||||
else
|
||||
net[mod].Version={1,0,0}
|
||||
net[mod]._VERSION={1,0,0}
|
||||
end
|
||||
return {Version=version,_VERSION=version[1].."."..version[2].."."..version[3]}
|
||||
end
|
||||
function net.getModuleVersion(ext)
|
||||
if not ext then
|
||||
return string.format("%d.%d.%d",net.Version[1],net.Version[2],net.Version[3])
|
||||
end
|
||||
return string.format("%d.%d.%d",net[ext].Version[1],net[ext].Version[2],net[ext].Version[3])
|
||||
end
|
||||
function net.resolveID(obj)
|
||||
local num=math.random(10000000,99999999)
|
||||
if obj[tostring(num)] then
|
||||
return net.resolveID(obj)
|
||||
end
|
||||
obj.ids[tostring(num)]=true
|
||||
return tostring(num)
|
||||
end
|
||||
function net.inList(list,dat)
|
||||
for i,v in pairs(list) do
|
||||
if v==dat then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
function net.setTrigger(funcW,funcE)
|
||||
multi:newTrigger(func)
|
||||
end
|
||||
net:registerModule("net",net.Version)
|
||||
-- Client broadcast
|
||||
function net:newCastedClient(name) -- connects to the broadcasted server
|
||||
local listen = socket.udp() -- make a new socket
|
||||
listen:setsockname(net.getLocalIP(), 11111)
|
||||
listen:settimeout(0)
|
||||
local timer=multi:newTimer()
|
||||
while true do
|
||||
local data, ip, port = listen:receivefrom()
|
||||
if timer:Get()>3 then
|
||||
error("Timeout! Server by the name: "..name.." has not been found!")
|
||||
end
|
||||
if data then
|
||||
print("found!",data)
|
||||
local n,tp,ip,port=data:match("(%S-)|(%S-)|(%S-):(%d+)")
|
||||
if n:match(name) then
|
||||
print("Found Server!",n,tp,ip,port)
|
||||
if tp=="tcp" then
|
||||
return net:newTCPClient(ip,tonumber(port))
|
||||
else
|
||||
return net:newClient(ip,tonumber(port))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
function net:newCastedClients(name) -- connects to the broadcasted server
|
||||
local listen = socket.udp() -- make a new socket
|
||||
listen:setsockname(net.getLocalIP(), 11111)
|
||||
listen:settimeout(0)
|
||||
multi:newTLoop(function(self)
|
||||
local data, ip, port = listen:receivefrom()
|
||||
if data then
|
||||
local n,tp,ip,port=data:match("(%S-)|(%S-)|(%S-):(%d+)")
|
||||
if n:match(name) and not net.ClientCache[n] then
|
||||
local capture = n:match(name)
|
||||
local client = {}
|
||||
if tp=="tcp" then
|
||||
client=net:newTCPClient(ip,tonumber(port))
|
||||
else
|
||||
client=net:newUDPClient(ip,tonumber(port))
|
||||
end
|
||||
net.ClientCache[n]=client
|
||||
net.OnCastedClientInfo:Fire(client,n,ip,port)
|
||||
end
|
||||
end
|
||||
end,.1):setName("net.castedTask")
|
||||
end
|
||||
-- UDP Stuff
|
||||
function net:newUDPServer(port,servercode,nonluaServer)
|
||||
local c={}
|
||||
c.udp=assert(socket.udp())
|
||||
c.udp:settimeout(0)
|
||||
c.udp:setsockname("*", port)
|
||||
c.ips={}
|
||||
c.Type="udp"
|
||||
if port == 0 then
|
||||
_, c.port = c.udp:getsockname()
|
||||
end
|
||||
c.ids={}
|
||||
c.servercode=servercode
|
||||
c.bannedIPs={}
|
||||
c.bannedCIDs={}
|
||||
c.autoNormalization=false
|
||||
function c:setUpdateRate(n)
|
||||
print("Not needed in a udp server!")
|
||||
end
|
||||
function c:banCID(cid)
|
||||
table.insert(self.bannedCIDs,cid)
|
||||
end
|
||||
function c:banIP(ip)
|
||||
table.insert(self.bannedIPs,cid)
|
||||
end
|
||||
c.broad=socket.udp()
|
||||
c.hostip=net.getLocalIP()
|
||||
function c:broadcast(name)
|
||||
table.insert(net.BroadcastDriver,function(loop,dt)
|
||||
self.broad:setoption('broadcast',true)
|
||||
self.broad:sendto(name.."|"..self.Type.."|"..self.hostip..":"..self.port, "255.255.255.255", 11111)
|
||||
self.broad:setoption('broadcast',false)
|
||||
end)
|
||||
end
|
||||
function c:send(ip,data,port,cid)
|
||||
if self.autoNormalization then
|
||||
data=net.normalize(data)
|
||||
end
|
||||
if self.servercode then
|
||||
cid=cid or self:CIDFrom(ip,port)
|
||||
if not self.ips[cid] then
|
||||
print("Can't determine cid from client... sending the client a new one!")
|
||||
local cid=net.resolveID(self)
|
||||
print("Sending unique cid to client: "..cid)
|
||||
self.ips[cid]={ip,port,0,self.servercode==nil}
|
||||
print(ip)
|
||||
self.udp:sendto("I!"..cid,ip,port)
|
||||
if self.servercode then
|
||||
self.udp:sendto("S!",ip,port)
|
||||
end
|
||||
return
|
||||
end
|
||||
if net.inList(self.bannedIPs,ip) or net.inList(self.bannedCIDs,cid) then
|
||||
self.udp:sendto("BANNED CLIENT", ip, port or self.port)
|
||||
elseif self.ips[cid][4] then
|
||||
self.udp:sendto(data, ip, port or self.port)
|
||||
elseif self.ips[cid][4]==false then
|
||||
self.udp:sendto("Make sure your server code is correct!", ip, port)
|
||||
end
|
||||
else
|
||||
self.udp:sendto(data, ip, port or self.port)
|
||||
end
|
||||
end
|
||||
function c:pollClientModules(ip,port)
|
||||
self:send(ip,"L!",port)
|
||||
end
|
||||
function c:CIDFrom(ip,port)
|
||||
for i,v in pairs(self.ips) do
|
||||
if(ip==v[1] and v[2]==port) then
|
||||
return i
|
||||
end
|
||||
end
|
||||
end
|
||||
function c:sendAll(data)
|
||||
for i,v in pairs(self.ips) do
|
||||
self:send(v[1],data,v[2],i)
|
||||
end
|
||||
end
|
||||
function c:sendAllBut(data,cid)
|
||||
for i,v in pairs(self.ips) do
|
||||
if i~=cid then
|
||||
self:send(v[1],data,v[2],i)
|
||||
end
|
||||
end
|
||||
end
|
||||
function c:clientRegistered(cid)
|
||||
return self.ips[cid]
|
||||
end
|
||||
function c:clientLoggedIn(cid)
|
||||
if not self.clientRegistered(cid) then
|
||||
return nil
|
||||
end
|
||||
return self.ips[cid][4]
|
||||
end
|
||||
function c:update()
|
||||
local data,ip,port=self.udp:receivefrom()
|
||||
if net.inList(self.bannedIPs,ip) or net.inList(self.bannedCIDs,cid) then
|
||||
print("We will ignore data from a banned client!")
|
||||
return
|
||||
end
|
||||
if data then
|
||||
if self.autoNormalization then
|
||||
data=net.denormalize(data)
|
||||
end
|
||||
if data:sub(1,4)=="pong" then
|
||||
--print("Recieved pong from: "..data:sub(5,-1))
|
||||
self.ips[data:sub(5,-1)][3]=os.clock()
|
||||
elseif data:sub(1,2)=="S!" then
|
||||
local cid=self:CIDFrom(ip,port)
|
||||
if data:sub(3,-1)==self.servercode then
|
||||
print("Servercode Accepted: "..self.servercode)
|
||||
if self.ips[cid] then
|
||||
self.ips[cid][4]=true
|
||||
else
|
||||
print("Server can't keep up! CID: "..cid.." has been skipped! Sending new CID to the client!")
|
||||
local cid=net.resolveID(self)
|
||||
print("Sending unique cid to client: "..cid)
|
||||
self.ips[cid]={ip,port,0,self.servercode==nil}
|
||||
print(ip)
|
||||
self.udp:sendto("I!"..cid,ip,port)
|
||||
if self.servercode then
|
||||
self.udp:sendto("S!",ip,port)
|
||||
end
|
||||
end
|
||||
else
|
||||
self.udp:sendto("Make sure your server code is correct!", ip, port)
|
||||
end
|
||||
elseif data:sub(1,2)=="C!" then
|
||||
local hook=(data:sub(11,-1)):match("!(.-)!")
|
||||
self.OnDataRecieved:getConnection(hook):Fire(self,data:sub(11,-1),data:sub(3,10),ip,port)
|
||||
elseif data:sub(1,2)=="E!" then
|
||||
self.ips[data:sub(3,10)]=nil
|
||||
obj.ids[data:sub(3,10)]=false
|
||||
self.OnClientClosed:Fire(self,"Client Closed Connection!",data:sub(3,10),ip,port)
|
||||
elseif data=="I!" then
|
||||
local cid=net.resolveID(self)
|
||||
print("Sending unique cid to client: "..cid)
|
||||
self.ips[cid]={ip,port,os.clock(),self.servercode==nil}
|
||||
print(ip)
|
||||
self.udp:sendto("I!"..cid,ip,port)
|
||||
if self.servercode then
|
||||
self.udp:sendto("S!",ip,port)
|
||||
end
|
||||
self.OnClientConnected:Fire(self,cid,ip,port)
|
||||
elseif data:sub(1,2)=="L!" then
|
||||
cid,cList=data:sub(3,10),data:sub(11,-1)
|
||||
local list={}
|
||||
for m,v in cList:gmatch("(%S-):(%S-)|") do
|
||||
list[m]=v
|
||||
end
|
||||
self.OnClientsModulesList:Fire(list,cid,ip,port)
|
||||
end
|
||||
end
|
||||
for cid,dat in pairs(self.ips) do
|
||||
if not((os.clock()-dat[3])<65) then
|
||||
self.ips[cid]=nil
|
||||
self.OnClientClosed:Fire(self,"Client lost Connection: ping timeout",cid,ip,port)
|
||||
end
|
||||
end
|
||||
end
|
||||
c.OnClientsModulesList=multi:newConnection()
|
||||
c.OnDataRecieved=multi:newConnection()
|
||||
c.OnClientClosed=multi:newConnection()
|
||||
c.OnClientConnected=multi:newConnection()
|
||||
c.connectiontest=multi:newAlarm(30):setName("net.pingOutTask")
|
||||
c.connectiontest.link=c
|
||||
c.connectiontest:OnRing(function(alarm)
|
||||
alarm.link:sendAll("ping")
|
||||
alarm:Reset()
|
||||
end)
|
||||
table.insert(net.ConnectionDriver,c)
|
||||
net.OnServerCreated:Fire(c)
|
||||
return c
|
||||
end
|
||||
local pingManager = {}
|
||||
function net:newUDPClient(host,port,servercode,nonluaServer)
|
||||
local c={}
|
||||
c.ip=assert(socket.dns.toip(host))
|
||||
c.udp=assert(socket.udp())
|
||||
c.udp:settimeout(0)
|
||||
c.udp:setpeername(c.ip, port)
|
||||
c.cid="NIL"
|
||||
c.lastPing=0
|
||||
c.Type="udp"
|
||||
c.servercode=servercode
|
||||
c.autoReconnect=true
|
||||
c.autoNormalization=false
|
||||
function c:pollPing(n)
|
||||
return not((os.clock()-self.lastPing)<(n or 60))
|
||||
end
|
||||
function c:send(data)
|
||||
if self.autoNormalization then
|
||||
data=net.normalize(data)
|
||||
end
|
||||
self.udp:send("C!"..self.cid..data)
|
||||
end
|
||||
function c:sendRaw(data)
|
||||
if self.autoNormalization then
|
||||
data=net.normalize(data)
|
||||
end
|
||||
self.udp:send(data)
|
||||
end
|
||||
function c:getCID()
|
||||
if self:IDAssigned() then
|
||||
return self.cid
|
||||
end
|
||||
end
|
||||
function c:close()
|
||||
self:send("E!")
|
||||
end
|
||||
function c:IDAssigned()
|
||||
return self.cid~="NIL"
|
||||
end
|
||||
function c:update()
|
||||
local data=self.udp:receive()
|
||||
if data then
|
||||
if self.autoNormalization then
|
||||
data=net.denormalize(data)
|
||||
end
|
||||
if data:sub(1,2)=="I!" then
|
||||
self.cid=data:sub(3,-1)
|
||||
self.OnClientReady:Fire(self)
|
||||
elseif data=="S!" then
|
||||
self.udp:send("S!"..(self.servercode or ""))
|
||||
elseif data=="L!" then
|
||||
local mods=""
|
||||
local m=""
|
||||
for i=1,#net.loadedModules do
|
||||
m=net.loadedModules[i]
|
||||
mods=mods..m..":"..net.getModuleVersion(m).."|"
|
||||
end
|
||||
self.udp:send("L!"..self.cid..mods)
|
||||
elseif data=="ping" then
|
||||
self.lastPing=os.clock()
|
||||
self.OnPingRecieved:Fire(self)
|
||||
self.udp:send("pong"..self.cid)
|
||||
else
|
||||
local hook=data:match("!(.-)!")
|
||||
self.OnDataRecieved:getConnection(hook):Fire(self,data)
|
||||
end
|
||||
end
|
||||
end
|
||||
function c:reconnect()
|
||||
if not nonluaServer then
|
||||
self.cid="NIL"
|
||||
c.udp:send("I!")
|
||||
end
|
||||
self.pingEvent:Resume()
|
||||
self.OnConnectionRegained:Fire(self)
|
||||
end
|
||||
c.pingEvent=multi:newEvent(function(self) return self.link:pollPing() end)
|
||||
c.pingEvent:OnEvent(function(self)
|
||||
if self.link.autoReconnect then
|
||||
self.link.OnServerNotAvailable:Fire("Connection to server lost: ping timeout! Attempting to reconnect...")
|
||||
self.link.OnClientDisconnected:Fire(self,"closed")
|
||||
self.link:reconnect()
|
||||
else
|
||||
self.link.OnServerNotAvailable:Fire("Connection to server lost: ping timeout!")
|
||||
self.link.OnClientDisconnected:Fire(self,"closed")
|
||||
end
|
||||
self:Pause()
|
||||
end):setName("net.pingInTask")
|
||||
c.pingEvent.link=c
|
||||
c.OnPingRecieved=multi:newConnection()
|
||||
c.OnDataRecieved=multi:newConnection()
|
||||
c.OnServerNotAvailable=multi:newConnection()
|
||||
c.OnClientReady=multi:newConnection()
|
||||
c.OnClientDisconnected=multi:newConnection()
|
||||
c.OnConnectionRegained=multi:newConnection()
|
||||
c.notConnected=multi:newFunction(function(self)
|
||||
multi:newAlarm(3):OnRing(function(alarm)
|
||||
if self.link:IDAssigned()==false then
|
||||
self.link.OnServerNotAvailable:Fire("Can't connect to the server: no response from server")
|
||||
end
|
||||
alarm:Destroy()
|
||||
end):setName("net.clientTimeout")
|
||||
end)
|
||||
c.notConnected.link=c
|
||||
if not nonluaServer then
|
||||
c.udp:send("I!")
|
||||
end
|
||||
table.insert(net.ConnectionDriver,c)
|
||||
multi.nextStep(function() c.notConnected() end)
|
||||
net.OnClientCreated:Fire(c)
|
||||
return c
|
||||
end
|
||||
multi:newThread(function()
|
||||
|
||||
end)
|
||||
--TCP Stuff
|
||||
function net:newTCPClientObject(fd)
|
||||
local c = {}
|
||||
local client
|
||||
c.Type="tcp-ClientObj"
|
||||
c.rMode="*l"
|
||||
c.sMode="*l"
|
||||
function c:packMsg(data)
|
||||
local temp = bin.new()
|
||||
temp:addBlock(#data,self.numspace,"n")
|
||||
temp:addBlock(data)
|
||||
return temp:getData()
|
||||
end
|
||||
function c:enableBinaryMode()
|
||||
self.rMode = "b"
|
||||
self.sMode = "b"
|
||||
end
|
||||
if fd then
|
||||
client=socket.tcp()
|
||||
client:setfd(fd)
|
||||
_,port = client:getsockname()
|
||||
c.handle = client
|
||||
else
|
||||
error("You need to enter a fd in order to be able to create a tcp client object like this!")
|
||||
end
|
||||
function c:setUpdateRate(n)
|
||||
self.updaterRate=n
|
||||
end
|
||||
function c:setReceiveMode(mode)
|
||||
self.rMode=mode
|
||||
end
|
||||
function c:setSendMode(mode)
|
||||
self.rMode=mode
|
||||
end
|
||||
function c:send(data)
|
||||
if self.autoNormalization then
|
||||
data=net.normalize(data)
|
||||
end
|
||||
if self.sMode=="*l" then
|
||||
self.handle:send(data.."\n")
|
||||
elseif self.sMode=="b" then
|
||||
self.handle:send(self:packMsg(data))
|
||||
else
|
||||
self.handle:send(data)
|
||||
end
|
||||
end
|
||||
multi:newThread("ServerClientHandler",function()
|
||||
while true do
|
||||
thread.skip(1)
|
||||
local data, err, dat, len
|
||||
if self.rMode == "b" then
|
||||
thread.hold(function()
|
||||
dat = client:receive(self.numspace)
|
||||
return dat
|
||||
end)
|
||||
len = bin.new(dat):getBlock("n",self.numspace)
|
||||
data, err = client:receive(len)
|
||||
else
|
||||
data, err = client:receive(self.rMode)
|
||||
end
|
||||
if err=="closed" then
|
||||
for i=1,#self.ips do
|
||||
if self.ips[i]==client then
|
||||
table.remove(self.ips,i)
|
||||
end
|
||||
end
|
||||
self.OnClientClosed:Fire(self,"Client Closed Connection!",client,client,ip)
|
||||
self.links[client]=nil -- lets clean up
|
||||
self:Destroy()
|
||||
end
|
||||
if data then
|
||||
if self.autoNormalization then
|
||||
data=net.denormalize(data)
|
||||
end
|
||||
if net.inList(self.bannedIPs,ip) then
|
||||
print("We will ingore data from a banned client!")
|
||||
return
|
||||
end
|
||||
local hook=data:match("!(.-)!")
|
||||
self.OnDataRecieved:getConnection(hook):Fire(self,data,client,client,ip,self)
|
||||
if data:sub(1,2)=="L!" then
|
||||
cList=data
|
||||
local list={}
|
||||
for m,v in cList:gmatch("(%S-):(%S-)|") do
|
||||
list[m]=v
|
||||
end
|
||||
self.OnClientsModulesList:Fire(list,client,client,ip)
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
c.OnClientsModulesList=multi:newConnection()
|
||||
c.OnDataRecieved=multi:newConnection()
|
||||
c.OnClientClosed=multi:newConnection()
|
||||
c.OnClientConnected=multi:newConnection()
|
||||
return c
|
||||
end
|
||||
function net:newTCPServer(port)
|
||||
local c={}
|
||||
local port = port or 0
|
||||
c.tcp=assert(socket.bind("*", port))
|
||||
c.tcp:settimeout(0)
|
||||
c.ip,c.port=c.tcp:getsockname()
|
||||
c.ips={}
|
||||
if port == 0 then
|
||||
_, c.port = c.tcp:getsockname()
|
||||
end
|
||||
c.ids={}
|
||||
c.bannedIPs={}
|
||||
c.Type="tcp"
|
||||
c.rMode="*l"
|
||||
c.sMode="*l"
|
||||
c.updaterRate=1
|
||||
c.autoNormalization=false
|
||||
c.updates={}
|
||||
c.links={}
|
||||
c.numspace = 4
|
||||
c.broad=socket.udp()
|
||||
c.hostip=net.getLocalIP()
|
||||
function c:packMsg(data)
|
||||
local temp = bin.new()
|
||||
temp:addBlock(#data,self.numspace,"n")
|
||||
temp:addBlock(data)
|
||||
return temp:getData()
|
||||
end
|
||||
function c:enableBinaryMode()
|
||||
self.rMode = "b"
|
||||
self.sMode = "b"
|
||||
end
|
||||
function c:broadcast(name)
|
||||
table.insert(net.BroadcastDriver,function(loop,dt)
|
||||
self.broad:setoption('broadcast',true)
|
||||
self.broad:sendto(name.."|"..self.Type.."|"..self.hostip..":"..self.port, "255.255.255.255", 11111)
|
||||
self.broad:setoption('broadcast',false)
|
||||
end)
|
||||
end
|
||||
function c:setUpdateRate(n)
|
||||
self.updaterRate=n
|
||||
end
|
||||
function c:setReceiveMode(mode)
|
||||
self.rMode=mode
|
||||
end
|
||||
function c:setSendMode(mode)
|
||||
self.rMode=mode
|
||||
end
|
||||
function c:banCID(cid)
|
||||
print("Function not supported on a tcp server!")
|
||||
end
|
||||
function c:banIP(ip)
|
||||
table.insert(self.bannedIPs,cid)
|
||||
end
|
||||
function c:send(handle,data)
|
||||
if self.autoNormalization then
|
||||
data=net.normalize(data)
|
||||
end
|
||||
if self.sMode=="*l" then
|
||||
handle:send(data.."\n")
|
||||
elseif self.sMode=="b" then
|
||||
handle:send(self:packMsg(data))
|
||||
else
|
||||
handle:send(data)
|
||||
end
|
||||
end
|
||||
function c:sendAllData(handle,data)
|
||||
if self.autoNormalization then
|
||||
data=net.normalize(data)
|
||||
end
|
||||
handle:send(data)
|
||||
end
|
||||
function c:pollClientModules(ip,port)
|
||||
self:send(ip,"L!",port)
|
||||
end
|
||||
function c:CIDFrom(ip,port)
|
||||
print("Method not supported when using a TCP Server!")
|
||||
return "CIDs in TCP work differently!"
|
||||
end
|
||||
function c:sendAll(data)
|
||||
for i,v in pairs(self.ips) do
|
||||
self:send(v,data)
|
||||
end
|
||||
end
|
||||
function c:sendAllBut(data,cid)
|
||||
for i,v in pairs(self.ips) do
|
||||
if not(cid==i) then
|
||||
self:send(v,data)
|
||||
end
|
||||
end
|
||||
end
|
||||
function c:clientRegistered(cid)
|
||||
return self.ips[cid]
|
||||
end
|
||||
function c:clientLoggedIn(cid)
|
||||
return self.ips[cid]
|
||||
end
|
||||
function c:getUpdater(cid)
|
||||
return self.updates[cid]
|
||||
end
|
||||
function c:update()
|
||||
local client = self.tcp:accept(self.rMode)
|
||||
if not client then return end
|
||||
table.insert(self.ips,client)
|
||||
client:settimeout(0)
|
||||
--client:setoption('tcp-nodelay', true)
|
||||
client:setoption('keepalive', true)
|
||||
ip,port=client:getpeername()
|
||||
if ip and port then
|
||||
print("Got connection from: ",ip,port)
|
||||
-- local updater=multi:newUpdater(skip):setName("net.tcpClientObj")
|
||||
-- self.updates[client]=updater
|
||||
self.OnClientConnected:Fire(self,client,client,ip)
|
||||
--updater:OnUpdate(function(self)
|
||||
multi:newThread("ServerClientHandler",function()
|
||||
while true do
|
||||
thread.skip(1)
|
||||
local data, err, dat, len
|
||||
if self.rMode == "b" then
|
||||
thread.hold(function()
|
||||
dat = client:receive(self.numspace)
|
||||
return dat
|
||||
end)
|
||||
len = bin.new(dat):getBlock("n",self.numspace)
|
||||
data, err = client:receive(len)
|
||||
else
|
||||
data, err = client:receive(self.rMode)
|
||||
end
|
||||
if err=="closed" then
|
||||
for i=1,#self.ips do
|
||||
if self.ips[i]==client then
|
||||
table.remove(self.ips,i)
|
||||
end
|
||||
end
|
||||
self.OnClientClosed:Fire(self,"Client Closed Connection!",client,client,ip)
|
||||
self.links[client]=nil -- lets clean up
|
||||
self:Destroy()
|
||||
thread.kill()
|
||||
end
|
||||
if data then
|
||||
if self.autoNormalization then
|
||||
data=net.denormalize(data)
|
||||
end
|
||||
if net.inList(self.bannedIPs,ip) then
|
||||
print("We will ingore data from a banned client!")
|
||||
return
|
||||
end
|
||||
local hook=data:match("!(.-)!")
|
||||
self.OnDataRecieved:getConnection(hook):Fire(self,data,client,client,ip,self)
|
||||
if data:sub(1,2)=="L!" then
|
||||
cList=data
|
||||
local list={}
|
||||
for m,v in cList:gmatch("(%S-):(%S-)|") do
|
||||
list[m]=v
|
||||
end
|
||||
self.OnClientsModulesList:Fire(list,client,client,ip)
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
-- updater:SetSkip(self.updaterRate)
|
||||
-- updater.client=client
|
||||
-- updater.Link=self
|
||||
-- function updater:setReceiveMode(mode)
|
||||
-- self.rMode=mode
|
||||
-- end
|
||||
-- self.links[client]=updater
|
||||
end
|
||||
end
|
||||
c.OnClientsModulesList=multi:newConnection()
|
||||
c.OnDataRecieved=multi:newConnection()
|
||||
c.OnClientClosed=multi:newConnection()
|
||||
c.OnClientConnected=multi:newConnection()
|
||||
table.insert(net.ConnectionDriver,c)
|
||||
net.OnServerCreated:Fire(c)
|
||||
return c
|
||||
end
|
||||
function net:newTCPClient(host,port)
|
||||
local c={}
|
||||
c.ip=assert(socket.dns.toip(host))
|
||||
c.tcp=socket.connect(c.ip,port)
|
||||
if not c.tcp then
|
||||
print("Can't connect to the server: no response from server")
|
||||
return false
|
||||
end
|
||||
c.tcp:settimeout(0)
|
||||
--c.tcp:setoption('tcp-nodelay', true)
|
||||
c.tcp:setoption('keepalive', true)
|
||||
c.Type="tcp"
|
||||
c.autoReconnect=true
|
||||
c.rMode="*l"
|
||||
c.sMode="*l"
|
||||
c.autoNormalization=false
|
||||
c.numspace = 4
|
||||
function c:enableBinaryMode()
|
||||
self.rMode = "b"
|
||||
self.sMode = "b"
|
||||
end
|
||||
function c:setReceiveMode(mode)
|
||||
self.rMode=mode
|
||||
end
|
||||
function c:setSendMode(mode)
|
||||
self.sMode=mode
|
||||
end
|
||||
function c:packMsg(data)
|
||||
local temp = bin.new()
|
||||
temp:addBlock(#data,self.numspace)
|
||||
temp:addBlock(data)
|
||||
return temp:getData()
|
||||
end
|
||||
function c:send(data)
|
||||
if self.autoNormalization then
|
||||
data=net.normalize(data)
|
||||
end
|
||||
if self.sMode=="*l" then
|
||||
ind,err=self.tcp:send(data.."\n")
|
||||
elseif self.sMode=="b" then
|
||||
ind,err=self.tcp:send(self:packMsg(data))
|
||||
else
|
||||
ind,err=self.tcp:send(data)
|
||||
end
|
||||
if err=="closed" then
|
||||
self.OnClientDisconnected:Fire(self,err)
|
||||
elseif err=="timeout" then
|
||||
self.OnClientDisconnected:Fire(self,err)
|
||||
elseif err then
|
||||
print(err)
|
||||
end
|
||||
end
|
||||
function c:sendRaw(data)
|
||||
if self.autoNormalization then
|
||||
data=net.normalize(data)
|
||||
end
|
||||
self.tcp:send(data)
|
||||
end
|
||||
function c:getCID()
|
||||
return "No Cid on a tcp client!"
|
||||
end
|
||||
function c:close()
|
||||
self.tcp:close()
|
||||
end
|
||||
function c:IDAssigned()
|
||||
return true
|
||||
end
|
||||
function c:update()
|
||||
if not self.tcp then return end
|
||||
local data,err,dat
|
||||
if self.rMode == "b" then
|
||||
thread.hold(function()
|
||||
dat = self.tcp:receive(self.numspace)
|
||||
return dat
|
||||
end)
|
||||
len = bin.new(dat):getBlock("n",self.numspace)
|
||||
data, err = self.tcp:receive(len)
|
||||
else
|
||||
data,err = self.tcp:receive()
|
||||
end
|
||||
if err=="closed" then
|
||||
self.OnClientDisconnected:Fire(self,err)
|
||||
elseif err=="timeout" then
|
||||
self.OnClientDisconnected:Fire(self,err)
|
||||
elseif err then
|
||||
print(err)
|
||||
end
|
||||
if data then
|
||||
if self.autoNormalization then
|
||||
data=net.denormalize(data)
|
||||
end
|
||||
local hook=data:match("!(.-)!")
|
||||
self.OnDataRecieved:getConnection(hook):Fire(self,data)
|
||||
end
|
||||
end
|
||||
function c:reconnect()
|
||||
multi:newFunction(function(func)
|
||||
self.tcp=socket.connect(self.ip,self.port)
|
||||
if self.tcp==nil then
|
||||
print("Can't connect to the server: No response from server!")
|
||||
multi:newAlarm(3):OnRing(function(alarm)
|
||||
self:reconnect()
|
||||
alarm:Destroy()
|
||||
return
|
||||
end):setName("net.timeoutTask")
|
||||
end
|
||||
self.OnConnectionRegained:Fire(self)
|
||||
self.tcp:settimeout(0)
|
||||
--self.tcp:setoption('tcp-nodelay', true)
|
||||
self.tcp:setoption('keepalive', true)
|
||||
end)
|
||||
end
|
||||
c.event=multi:newEvent(function(event)
|
||||
return event.link:IDAssigned()
|
||||
end):OnEvent(function(event)
|
||||
event.link.OnClientReady:Fire(event.link)
|
||||
event:Destroy()
|
||||
end)
|
||||
c.event:setName("net.handshakeTask")
|
||||
c.event.link=c
|
||||
c.OnClientReady=multi:newConnection()
|
||||
c.OnClientDisconnected=multi:newConnection()
|
||||
c.OnDataRecieved=multi:newConnection()
|
||||
c.OnConnectionRegained=multi:newConnection()
|
||||
table.insert(net.ConnectionDriver,c)
|
||||
net.OnClientCreated:Fire(c)
|
||||
return c
|
||||
end
|
||||
net.timer = multi:newTimer():Start()
|
||||
multi:newThread("ClientServerHandler",function()
|
||||
while true do
|
||||
thread.skip()
|
||||
for i=1,#net.ConnectionDriver do
|
||||
thread.skip()
|
||||
net.ConnectionDriver[i]:update()
|
||||
end
|
||||
if net.timer:Get()>=1 then
|
||||
for i=1,#net.BroadcastDriver do
|
||||
net.BroadcastDriver[i]()
|
||||
end
|
||||
net.timer:Reset()
|
||||
end
|
||||
end
|
||||
end)
|
||||
return net
|
||||
@ -18,7 +18,7 @@ net.OnServerCreated:connect(function(s)
|
||||
local cmd,arg1,arg2=data:match("!version! ")
|
||||
end,"version")
|
||||
s.OnClientConnected(function(self,CID_OR_HANDLE,IP_OR_HANDLE,PORT_OR_IP)
|
||||
multi:newFunction(function(func) -- anom func, allows for fancy multitasking
|
||||
multi:newFunction(function(func) -- anon func, allows for fancy multitasking
|
||||
multi:newFunction(function(self)
|
||||
local range=self:newRange()
|
||||
for i in range(1,#self.loadedModules) do
|
||||
|
||||
32
rockspec/lnet-5.0-0.rockspec
Normal file
32
rockspec/lnet-5.0-0.rockspec
Normal file
@ -0,0 +1,32 @@
|
||||
package = "llnet"
|
||||
version = "5.0-0"
|
||||
source = {
|
||||
url = "git://github.com/rayaman/lnet.git",
|
||||
tag = "lnet-v5",
|
||||
}
|
||||
description = {
|
||||
summary = "Lua lnetworking library that wraps around lua-socket to make lnetworking easy.",
|
||||
detailed = [[
|
||||
This library uses the multi library. The new multitasking library and this one are now co-Dependant if using the lnetworkManager integration for lnetwork parallelism. This has an event driven approach for lnetworking which allows one to easily work async with the data.
|
||||
]],
|
||||
homepage = "https://github.com/rayaman/lnet",
|
||||
license = "MIT"
|
||||
}
|
||||
dependencies = {
|
||||
"lua >= 5.1",
|
||||
"luasocket",
|
||||
"luasec",
|
||||
"multi",
|
||||
}
|
||||
build = {
|
||||
type = "builtin",
|
||||
modules = {
|
||||
["lnet.init"] = "lnet/init.lua",
|
||||
["lnet.tcp.init"] = "lnet/tcp/init.lua",
|
||||
["lnet.udp.init"] = "lnet/udp/init.lua",
|
||||
["lnet.base.client"] = "lnet/base/client.lua",
|
||||
["lnet.base.server"] = "lnet/base/server.lua",
|
||||
["lnet.http"] = "lnet/http.lua",
|
||||
["lnet.https"] = "lnet/https.lua"
|
||||
}
|
||||
}
|
||||
27
server.lua
27
server.lua
@ -1,20 +1,11 @@
|
||||
package.path="?/init.lua;"..package.path
|
||||
local multi = require("multi")
|
||||
local net = require("net")
|
||||
local GLOBAL, THREAD = require("multi.integration.lanesManager").init()
|
||||
server = net:newTCPServer(12345)
|
||||
server:enableBinaryMode()
|
||||
print("Server hosted on "..net.getExternalIP().." listening on port: 12345")
|
||||
server.OnDataRecieved(function(self,data,cid,ip,port)
|
||||
print(data)
|
||||
local file = bin.load("test.mp3")
|
||||
local dat = file:read(1024)
|
||||
while dat do
|
||||
thread.sleep(.002)
|
||||
self:send(ip,dat,port,cid)
|
||||
dat = file:read(1024)
|
||||
end
|
||||
self:send(ip,dat or "",port,cid)
|
||||
self:send(ip,"END",port,cid)
|
||||
package.path = "./?/init.lua;./?.lua;"..package.path
|
||||
local net = require("lnet.tcp")
|
||||
local multi, thread = require("multi"):init()
|
||||
server = net.newTCPServer(12345)
|
||||
server:broadcast("Test")
|
||||
print("Server has been broadcasted!")
|
||||
server.OnDataRecieved(function(serv, data,cid)
|
||||
print("Response: ",data)
|
||||
server:send(cid,"Hello!")
|
||||
end)
|
||||
multi:mainloop()
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user