brennim
17:38
  • 📁
    blog
  • 📁
    games
  • 📁
    photos
  • 📄
    guestbook.txt
×

Diversão com iteradores em Lua

2024-04-10

Você provavelmente conhece o for em Lua. Se quiser iterar sobre um vetor, você pode usá-lo em sua forma numérica e sem graça, ou usando um iterador:

for i = 1, #tbl do
  print(tbl[i])
end

for i, e in ipairs(tbl) do
  print(e)
end

ipairs é uma iterador da biblioteca padrão de Lua, que pode ser usada no lado direito do for. Mas não é a única. Também temos a função io.lines, que itera sobre todas as linhas de um arquivo:

for line in io.lines("poem.txt") do
  print(line)
end

Nosso iterador

No post passado, aprendemos como iniciar um servidor TCP para agir como um servidor HTTP. Seria útil ler o que o cliente manda pra gente nos headers.

Os headers de uma requisição HTTP aparecem 1 por linha, então vamos buscar uma solução semelhante ao io.lines. Um iterador em Lua precisa retornar uma função, que, ao ser chamada, retona o próximo valor, ou nil se chegou ao fim.

Vamos ler uma linha do cliente usando o método :receive() sem nenhum parâmetro. Se a linha for vazia, retornamos nil, senão, retornamos a linha. Se o cliente chegar ao fim da transmissão, a linha já será nil.

local function lines(client)
  return function()
    local line = client:receive()

    if line == "" then 
      return nil 
    end

    return line
  end
end

Resultado final

Com o nosso iterador, podemos incrementar o servidor que criamos no último post para ler os headers da requisição:

local socket = require "socket"

local server = socket.bind("*", 3000)

while true do
  local client = server:accept()
  local headers = {}

  local request_line = client:receive()

  for line in lines(client) do
    local name, value = line:match "^(.-):%s*(.*)$"
    headers[name] = value
  end

  client:send("HTTP/1.1 200 OK\r\n\r\n")
  client:close()
end