soupsvr.lua 2.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  1. #! /usr/bin/env lua
  2. --
  3. -- Sample server using libsoup library. Listens on 1080 port and serves
  4. -- local files from current directory. Allows to be terminated by query
  5. -- for /quit file (i.e. curl http://localhost:1080/quit)
  6. --
  7. local coroutine = require 'coroutine'
  8. local lgi = require 'lgi'
  9. local bytes = require 'bytes'
  10. local GLib = lgi.GLib
  11. local Gio = lgi.Gio
  12. local Soup = lgi.Soup
  13. local app = Gio.Application { application_id = 'org.lgi.soupsvr' }
  14. function app:on_activate()
  15. app:hold()
  16. local server = Soup.Server { port = 1080 }
  17. -- Set up quit handler.
  18. server:add_handler('/quit', function(server, msg, path, query, ctx)
  19. msg.status_code = 200
  20. msg.response_body:complete()
  21. server:quit()
  22. app:release()
  23. end)
  24. -- Set up file retriever handler.
  25. server:add_handler('/', function(server, msg, path, query, ctx)
  26. local stream = Gio.File.new_for_path(path:sub(2)):read()
  27. if stream then
  28. -- The whole is send by function running in coroutine.
  29. -- Coroutine yields when it waits either for data from the
  30. -- disk or signal that chunk was successfully sent.
  31. local next_chunk = coroutine.wrap(function()
  32. local buffer = bytes.new(4096)
  33. while true do
  34. -- Read another chunk of data from the source to the
  35. -- buffer.
  36. stream:read_async(buffer, GLib.PRIORITY_DEFAULT, nil,
  37. coroutine.running())
  38. local size = stream.read_finish(coroutine.yield())
  39. if size < 0 then
  40. -- IO error reading disk, this simply shuts our toy
  41. -- server down.
  42. server:quit()
  43. app:release()
  44. return
  45. end
  46. -- Send the chunk to the message body.
  47. msg.response_body:append(tostring(buffer):sub(1, size))
  48. server:unpause_message(msg)
  49. -- Wait until soup signalizes that chunk was written.
  50. coroutine.yield()
  51. if size < #buffer then break end
  52. end
  53. msg.response_body:complete()
  54. end)
  55. -- Prepare sending using chunked method.
  56. msg.status_code = 200
  57. msg.response_headers:set_encoding('CHUNKED')
  58. -- When headers are written, start writing body by initial
  59. -- resuming of sending coroutine.
  60. msg.on_wrote_headers = next_chunk
  61. -- When the chunk is sent, resume coroutine so that it starts
  62. -- reading and sending another chunk.
  63. msg.on_wrote_chunk = next_chunk
  64. else
  65. -- File was not found, send proper code.
  66. msg.status_code = 404
  67. msg.response_body:complete()
  68. end
  69. end)
  70. -- Start the server running asynchronously.
  71. server:run_async()
  72. end
  73. app:run { arg[0], ... }