😸
Neovim + LuaでWEBサーバーを実装する
NeovimとLuaの勉強がてら、簡単なHTTPサーバーを実装してみました。
「WEBアプリケーションのコードをNeovimで編集する」ではありません。「俺(Neovim)自身がWEBサーバーになることだ」ってやつです。
NVIM v0.9.4
TCP echo
TCPで送られてきたメッセージをそのまま返すシンプルな例です。:help tcp-server
にあるのと同じものです。
ここではLuvrefモジュールを使用しています。これは、LibUVという非同期I/Oをマルチプラットフォームで実現するためのライブラリのLuaラッパーです。LibUVは他にもNode.jsやJuliaでも使用されています。
バージョンによってvim.loop
やvim.uv
だったりするので:help luvref.txt
で確認してください。
tcp_echo_server.lua
local uv = vim.loop
-- local uv = vim.uv
local function create_server(host, port, on_connect)
local server = uv.new_tcp()
server:bind(host, port)
server:listen(128, function(err)
assert(not err, err) -- Check for errors.
local sock = uv.new_tcp()
server:accept(sock) -- Accept client connection.
on_connect(sock) -- Start reading messages.
end)
return server
end
local server = create_server('0.0.0.0', 0, function(sock)
sock:read_start(function(err, chunk)
assert(not err, err) -- Check for errors.
sock:write(chunk) -- Echo received messages to the channel.
sock:close() -- Always close handles to avoid leaks.
end)
end)
print('TCP echo-server listening on port: ' .. server:getsockname().port)
実行
Neovim中で:luafile
で実行して、出力されたポート番号にncなどで接続します。ポートはランダムです。
:luafile tcp_echo_server.lua
TCP echo-server listening on port: 36795
$ nc 0.0.0.0 36795
hello
hello
REST API
REST APIでやりとりできるように後半のserver
変数を少しいじりました。
リクエストの先頭(例:GET /hello HTTP/1.1
)を解析し、メソッドやURIを元にルーティングしています。今回は簡単な例なのでIF文でゴリ押ししました。
rest_api_server.lua
local uv = vim.loop
-- local uv = vim.uv
local function create_server(host, port, on_connect)
local server = uv.new_tcp()
server:bind(host, port)
server:listen(128, function(err)
assert(not err, err) -- Check for errors.
local sock = uv.new_tcp()
server:accept(sock) -- Accept client connection.
on_connect(sock) -- Start reading messages.
end)
return server
end
local server = create_server('0.0.0.0', 0, function(sock)
sock:read_start(function(err, chunk)
assert(not err, err) -- Check for errors.
local method, path = string.match(chunk, '(%a+)%s+(/%S+)')
local response = ""
if method and path then
if method == "GET" and path == "/hello" then
response = "HTTP/1.1 200 OK\r\nContent-Type: application/json\r\n\r\n" .. [[ { "origin": "175.177.49.33" } ]]
else
response = "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\n\r\n" .. chunk
end
else
response = "HTTP/1.1 200 Ok\r\nContent-Type: text/plain\r\n\r\n" .. chunk
end
sock:write(response)
sock:close()
end)
end)
print('REST API server listening on port: ' .. server:getsockname().port)
実行
同じように:luafile
で実行し、curlやブラウザなどでアクセスしてください。
:luafile rest_api_server.lua
REST API echo-server listening on port: 38625
$ curl 0.0.0.0:38625
GET / HTTP/1.1
Host: 0.0.0.0:38625
User-Agent: curl/8.5.0
Accept: */*
$ curl 0.0.0.0:38625/hello
{ "origin": "175.177.49.33" }
ここまでできたら本格的なWEBサーバーも構築できるような気がしてきませんか?
なお、「denops使えばいいじゃん」は禁句です。
参考
Discussion