]> git.ipfire.org Git - thirdparty/knot-resolver.git/commitdiff
modules/http: added safe stream handler, doc
authorMarek Vavrusa <marek@vavrusa.com>
Mon, 13 Jun 2016 17:13:08 +0000 (10:13 -0700)
committerMarek Vavrusa <marek@vavrusa.com>
Wed, 6 Jul 2016 06:33:38 +0000 (23:33 -0700)
modules/http/README.rst
modules/http/http.lua

index 9bf2c6b74800f1d08009e697edf076a4c1fc00ea..2d9cd8d05e8549f0b353ed3ffc366203ff784ca3 100644 (file)
@@ -157,6 +157,64 @@ exported restful APIs and subscribe to WebSockets.
 
        http.snippets['/health'] = {'Health service', '<p>UP!</p>'}
 
+How to expose RESTful services
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+A RESTful service is likely to respond differently to different type of methods and requests,
+there are three things that you can do in a service handler to send back results.
+First is to just send whatever you want to send back, it has to respect MIME type that the service
+declared in the endpoint definition. The response code would then be ``200 OK``, any non-string
+responses will be packed to JSON. Alternatively, you can respond with a number corresponding to
+the HTTP response code or send headers and body yourself.
+
+.. code-block:: lua
+
+       -- Our upvalue
+       local value = 42
+
+       -- Expose the service
+       http.endpoints['/service'] = {'application/json',
+       function (h, stream)
+               -- Get request method and deal with it properly
+               local m = h:get(':method')
+               local path = h:get(':path')
+               log('[service] method %s path %s', m, path)
+               -- Return table, response code will be '200 OK'
+               if m == 'GET' then
+                       return {key = path, value = value}
+               -- Save body, perform check and either respond with 505 or 200 OK
+               elseif m == 'POST' then
+                       local data = stream:get_body_as_string()
+                       if not tonumber(data) then
+                               return 505
+                       end
+                       value = tonumber(data)
+               -- Unsupported method, return 405 Method not allowed
+               else
+                       return 405
+               end
+       end}
+
+In some cases you might need to send back your own headers instead of default provided by HTTP handler,
+you can do this, but then you have to return ``false`` to notify handler that it shouldn't try to generate
+a response.
+
+.. code-block:: lua
+
+       local headers = require('http.headers')
+       function (h, stream)
+               -- Send back headers
+               local hsend = headers.new()
+               hsend:append(':status', '200')
+               hsend:append('content-type', 'binary/octet-stream')
+               assert(stream:write_headers(hsend, false))
+               -- Send back data
+               local data = 'binary-data'
+               assert(stream:write_chunk(data, true))
+               -- Disable default handler action
+               return false
+       end
+
 How to expose more interfaces
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
index c17bb8eb3a6381dd78258a686b932ad1fc0bab90..fb81e91cdc0d04efdcdbb6e2704926571b3e21ab 100644 (file)
@@ -86,28 +86,30 @@ end
 -- Export HTTP service page snippets
 M.snippets = {}
 
--- Serve GET requests, we only support a fixed
--- number of endpoints that are actually preloaded
--- in memory or constructed on request
-local function serve_get(h, stream)
+-- Serve known requests, for methods other than GET
+-- the endpoint must be a closure and not a preloaded string
+local function serve(h, stream)
        local hsend = headers.new()
        local path = h:get(':path')
        local entry = M.endpoints[path]
        -- Unpack MIME and data
-       local mime, data
+       local mime, data, err
        if entry then
                mime, data = unpack(entry)
        end
        -- Get string data out of service endpoint
        if type(data) == 'function' then
-               data = data(h, stream)
+               data, err = data(h, stream)
                -- Handler doesn't provide any data
                if data == false then return end
+               if type(data) == 'number' then return tostring(data) end
+       -- Methods other than GET require handler to be closure
+       elseif h:get(':method') ~= 'GET' then
+               return '501'
        end
        if type(data) == 'table' then data = tojson(data) end
        if not mime or type(data) ~= 'string' then
-               hsend:append(':status', '404')
-               assert(stream:write_headers(hsend, true))
+               return '404'
        else
                -- Serve content type appropriately
                hsend:append(':status', '200')
@@ -147,14 +149,15 @@ local function route(endpoints)
                        end
                        ws:close()
                        return
-               -- Handle HTTP method appropriately
-               elseif m == 'GET' then
-                       serve_get(h, stream)
                else
-                       -- Method is not supported
-                       local hsend = headers.new()
-                       hsend:append(':status', '500')
-                       assert(stream:write_headers(hsend, true))
+                       local ok, err = pcall(serve, h, stream)
+                       if not ok or err then
+                               log('[http] %s %s: %s', m, path, err or '500')
+                               -- Method is not supported
+                               local hsend = headers.new()
+                               hsend:append(':status', err or '500')
+                               assert(stream:write_headers(hsend, true))
+                       end
                end
                stream:shutdown()
        end