Advertisement
Snusmumriken

Simple http server

Jul 20th, 2018
282
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 10.32 KB | None | 0 0
  1. local socket = require 'socket'
  2.  
  3. local config = {
  4.     port       = 8080,
  5.     timeout    = 30,
  6.     chunklimit = 10,
  7.     chunksize  = 1024,
  8.     body_limit = 1024 * 1024 * 5,
  9. }
  10.  
  11. -- ######## Utility block
  12. local function appendfile(path, text)
  13.     local file = assert(io.open(path), 'a+')
  14.     file:write(tostring(text))
  15.     file:close()
  16. end
  17.  
  18.  
  19. -- ######## Client block
  20. local client = {}
  21. client.__index = client
  22.  
  23. function client:new(sock, server)
  24.     self = setmetatable({}, self)
  25.     self.sock      = sock
  26.     self.server    = server
  27.     self.close_t   = server.config.timeout
  28.     self.timeout   = server.config.timeout
  29.     self.onMessage = server.onMessage
  30.     self.header    = {}
  31.     self.__body    = {}
  32.     self.__receive = 0
  33.  
  34.     self.loggingLayout = 'Client ' .. self.sock:getpeername() .. ' '
  35.     self.sock:settimeout(0)
  36.     return self
  37. end
  38.  
  39. -- Пользуемся серверным логом.
  40. function client:log(msg)
  41.     self.server:log(self.loggingLayout .. tostring(msg))
  42. end
  43.  
  44. function client:debug(msg)
  45.     self.server:debug(self.loggingLayout .. tostring(msg))
  46. end
  47.  
  48. local function trace(msg, layer)
  49.     return debug.traceback("Error: " .. tostring(msg), 1 + (layer or 1) )
  50. end
  51.  
  52. function client:callback()
  53.     -- эта штука генерирует функцию do-end на каждый запрос, по хорошему - выделить её отдельно.
  54.     -- Трейсбек <3
  55.     local succ, err = xpcall(function() self:onMessage() end, trace)
  56.     if not succ then
  57.         err = tostring(err)
  58.         self:send(err, '500')
  59.         self:log('Client: error: ' .. err)
  60.     end
  61. end
  62.  
  63. -- Тут есть самоубийство на случай дисконнекта!
  64. function client:atomReceive(mode)
  65.     local data, status, partial = self.sock:receive(mode)
  66.     if status == 'closed' then
  67.         self:destroy()
  68.         return
  69.     end
  70.     return data, status
  71. end
  72.  
  73. function client:receive()
  74.     if self.destroyed then return end
  75.    
  76.     -- Этот финт позволяет не напрягаясь параллельно принимать заголовки входящих запросов
  77.     local data, status = self:atomReceive('*l')
  78.    
  79.     while data do
  80.         table.insert(self.header, data)
  81.         if data == '' then
  82.             self:parseHeaders()
  83.             if (self.verb == 'POST' or self.verb == 'PUT') and self.contentLength then
  84.                 if self.contentLength > config.body_limit then
  85.                     self:send(
  86.                         'Body is too big: limit is '
  87.                             .. config.body_limit .. ', got: '
  88.                             .. self.contentLength,
  89.                         '400'
  90.                     )
  91.                     return self:destroy()
  92.                 end
  93.                
  94.                 self.__receive = self.contentLength
  95.                 self.receive = self.receiveBody
  96.                 return
  97.             end
  98.             self:callback()
  99.             self:reset()
  100.         end
  101.         data, status = self:atomReceive('*l')
  102.     end
  103. end
  104.  
  105. function client:receiveBody()
  106.     if self.destroyed then return end
  107.     local chunksize = config.chunksize
  108.    
  109.     local chunks = 0
  110.     local data, status = self:atomReceive(math.min(chunksize, self.__receive))
  111.  
  112.     while data do
  113.         chunks = chunks + 1
  114.         self.__receive = self.__receive - #data
  115.         table.insert(self.__body, data)
  116.        
  117.         if self.__receive == 0 then
  118.             self.data = table.concat(self.__body)
  119.             self:callback()
  120.             self:reset()
  121.         end
  122.         data, status = self:atomReceive(math.min(chunksize, self.__receive))
  123.     end
  124.     if self.connectionClose then
  125.         self:destroy()
  126.     end
  127. end
  128.  
  129. function client:reset()
  130.     self.receive = nil
  131.     self.__receive = 0
  132.     self.__body = {}
  133.     self.header = {}
  134. end
  135.  
  136. function client:parseHeaders()
  137.     local head = self.header[1] or 'UNKNOWN'
  138.     self.verb = head:match('%w+')
  139.  
  140.     -- Строчные запросы
  141.     if head:find('?') then
  142.         local querry = head:match('?(.-) ') .. '&'
  143.         local data = {}
  144.         for key, value in querry:gmatch('(.-)=(.-)&') do
  145.             data[key] = value
  146.         end
  147.         self.querry = data
  148.     end
  149.    
  150.     -- Остальные заголовки
  151.     for i = 2, #self.header do
  152.         local header, data = self.header[i]:match('(.-): (.*)')
  153.         if header then
  154.             self.header[header:lower()] = data
  155.         end
  156.     end
  157.    
  158.     -- Числа стоит преобразовать в числа
  159.     if self.header['content-length'] then
  160.         self.contentLength   = tonumber(self.header['content-length'])
  161.         self.connectionClose = (self.header['connection'] or ''):lower() == 'close'
  162.     end
  163. end
  164.  
  165. -- Клиент сам убивает себя и удаляет у сервера
  166. function client:destroy()
  167.     if self.destroyed then return end
  168.     self.destroyed = true
  169.     self:close()
  170.    
  171.     -- если где-то ошибка с удалением - лучше перестраховаться
  172.     return self.server:removeClient(self.sock) or self.server:removeAllClients()
  173. end
  174.  
  175. function client:update(dt)
  176.     self.timeout = self.timeout - dt
  177.     if self.timeout < 0 then return self:destroy() end
  178.     if self:receive() then return self end
  179. end
  180.  
  181. function client:send(msg, status)
  182.     status = status or self.header['status'] or '200'
  183.    
  184.     local headers = {}
  185.     headers['content-length'] = #msg
  186.    
  187.     -- Ответ как бы в того же типа что и запрос, но можно поменять в процессе
  188.     headers['content-type']   = self.header['content-type'] or 'text/html'
  189.     headers['server']         = 'luasocket-server'
  190.     -- Достаточно написать '200' или '500' или '404' чтобы был достаточно валидный статус, лишних строк не нужно.
  191.     -- Его напишет и в первую строку ответа и в хедер статуса, норм
  192.     headers['status']         = status:match('%d+')
  193.     headers['connection']     = 'close'
  194.    
  195.     local output = {}
  196.     output[1] = 'HTTP/1.1 ' .. status
  197.  
  198.     for k, v in pairs(headers) do
  199.         table.insert(output, k .. ': ' .. v)
  200.     end
  201.    
  202.     table.insert(output, '')
  203.     table.insert(output, msg)
  204.    
  205.     output = table.concat(output, '\r\n')
  206.     self:debug('Client send: \n['..output..']')
  207.     self.sock:send(output)
  208. end
  209.  
  210. -- закрытие без уничтожения, хм
  211. function client:close()
  212.     self.sock:close()
  213. end
  214.  
  215. -- ######## Server block
  216. server = {}
  217. server.__index = server
  218.  
  219. -- Имя нужно для логирования и некоторых специальных вещей (типа автозабора свойств из конфига).
  220. -- Колбек и порт можно указать позже, почти в любой момент.
  221. function server:new(name, plog, port, on_message)
  222.     name = assert(type(name) == 'string' and name, 'Arg#1 error: string server-name expected, got '..type(name))
  223.     plog = assert(type(plog) == 'string' and plog, 'Arg#2 error: string logging path expected, got '..type(plog))
  224.  
  225.     self = setmetatable({}, self)
  226.     self.name           = name
  227.     self.config         = {}
  228.     self.clients        = {}
  229.     self.clientCount    = 0
  230.     self.time           = socket.gettime()
  231.     self.config.port    = port or config.port
  232.     self.config.timeout = config.timeout
  233.     self.onMessage      = on_message
  234.     self.textblock      = {}
  235.  
  236.     self.loggingPath    = plog .. '/http_server_'..self.name..'.log'
  237.     self:log('Init')
  238.  
  239.     self.dbg            = true
  240.    
  241.     return self
  242. end
  243.  
  244. function server:debug(mode)
  245.     self.dbg = not not mode
  246. end
  247.  
  248. function server:log(msg)
  249.     appendfile(self.loggingPath, os.date('%c', os.time()) .. ' LOG   ' ..tostring(msg)..'\r\n')
  250. end
  251.  
  252. function server:debug(msg)
  253.     if not self.dbg then return end
  254.     appendfile(self.loggingPath, os.date('%c', os.time()) .. ' DEBUG ' ..tostring(msg)..'\r\n')
  255. end
  256.  
  257. setmetatable(server, {__call = server.new})
  258.  
  259. -- Обновление таймеров клиентов, чтобы отрубались когда нужно.
  260. function server:getDeltaTime()
  261.     local tm  = socket.gettime()
  262.     local dt  = tm - self.time
  263.     self.time = tm
  264.     return dt
  265. end
  266.  
  267. function server:setTimeout(n)
  268.     self.config.timeout = assert(type(n) == 'number' and n > 0 and n, 'arg#1 error: positive number expected, got ' .. type(n) .. ': ' .. tostring(n))
  269.     self:log('Set timeout: ' .. n)
  270.     return self
  271. end
  272.  
  273. -- Ну не будет же клиент сам ломиться в сервак и удалять себя.
  274. function server:removeClient(sock)
  275.     self:debug('Server: remove client: '..tostring(sock:getpeername()))
  276.     local succ = self.clients[sock] ~= nil
  277.     self.clients[sock] = nil
  278.     return succ
  279. end
  280.  
  281. -- Экстренная очистка
  282. function server:removeAllClients()
  283.     self:log('Server: remove all clients')
  284.     for k, v in pairs(self.clients) do
  285.         v:close()
  286.         k:close()
  287.     end
  288.     self.clients = {}
  289. end
  290.  
  291. function server:setCallback(cb)
  292.     self.onMessage = assert(type(cb) == 'function' and cb or 'arg#1 error: callback function expected, got '..type(cb))
  293.     self:log('Server: set callback '..tostring(cb))
  294.     return self
  295. end
  296.  
  297. function server:start(port, cb)
  298.     self.onMessage   = cb or self.onMessage
  299.     self.onMessage   = assert(type(self.onMessage) == 'function' and self.onMessage, 'Set callback function first!')
  300.     self.config.port = port or self.config.port
  301.  
  302.     local channel, err = socket.bind('*', self.config.port)
  303.    
  304.     if not channel then
  305.         return nil, err
  306.     end
  307.    
  308.     self:log('Server: start: '..tostring(self.config.port) .. ': ' .. tostring(channel) .. ' error?:' ..tostring(err))
  309.     self.channel = assert(channel, err)
  310.     self.channel:settimeout(0)
  311.    
  312.     return self
  313. end
  314.  
  315. function server:shutdown()
  316.     self:removeAllClients()
  317.     self.channel:close()
  318.     self:log('Server: shutdown')
  319. end
  320.  
  321. function server:accept()
  322.     local sock = self.channel:accept()
  323.     while sock do
  324.         self.clients[sock] = client:new(sock, self)
  325.         self:debug('Server: new client '..tostring(sock:getpeername()) )
  326.         sock = self.channel:accept()
  327.     end
  328. end
  329.  
  330. function server:update(dt)
  331.     self.clientCount = 0
  332.     for sock, client in pairs(self.clients) do
  333.         self.clientCount = self.clientCount + 1
  334.         client:update(dt)
  335.     end
  336.    
  337.     self.textblock['Listen'] = self.config.port
  338.    
  339.     local cliCount, cliReceive = 0, 0
  340.     for k, v in pairs(self.clients) do
  341.         cliCount = cliCount + 1
  342.         cliReceive = cliReceive + v.__receive
  343.     end
  344.    
  345.     self.textblock['Clients'] = cliCount
  346.     self.textblock['Receive'] = cliReceive
  347.     self.textblock['Time']    = math.floor(self.time)
  348. end
  349.  
  350. function server:loop()
  351.     while true do
  352.         local dt = self:getDeltaTime()
  353.         self:accept()
  354.         self:update(dt)
  355.         socket.sleep(.02)
  356.     end
  357. end
  358.  
  359. if not ... then
  360.     local http = server:new('Simple_test', '*')
  361.     local function onMessage(cli)
  362.         if cli.header['content-length'] then
  363.             cli:send(cli.data, '200')
  364.             return
  365.         end
  366.         cli:send('Yo', '200')
  367.     end
  368.  
  369.     http:start(8080, onMessage)
  370.     http:loop()
  371. end
  372.  
  373. return server
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement