HTTPD Response Handler Hook

As another instance of "I want to augment this block as a function body, with some stuff" there's this pattern in HTTPD. Here is a usage:

The idea is that SERVER.SPEC.ACTIONS is a plain BLOCK! of what to do. Here's a sample usage:

    import %httpd.reb
    trap [
        str: {Test} n: 0
        wait srv: open [scheme: 'httpd 8000 [
            n: n + 1
            expected: copy str
            repeat n [append expected expected]
            lib.print [{SERVER} n {:} (length of as binary! expected) {bytes}]
            render expected
        ]]
    ] then (func [e] [print mold/limit e 2000])

If you do something like fetch %index.html, that BLOCK! after the 8000 is executed. Here what's happening is it's just incrementing a number and doubling the length of the response sent back each time.

The implementation was like this:

    server.locals.handler: func [
        return: [~]
        request [object!]
        response [object!]
    ] compose [
        render: :response.render
        redirect: :response.redirect
        print: :response.print

        (spread (
            match block! server.spec.actions else [default-response]
        ))
    ]

It wants to override PRINT so that what you print becomes part of the response. So you're supposed to get these three service functions RENDER / REDIRECT / PRINT as well as access to the REQUEST and RESPONSE objects.

Now that SPREAD is spreading unbound material, this does not work.

PUNCH would be one way of doing this:

    server.locals.handler: func [
        return: [~]
        request [object!]
        response [object!]
    ] compose [
        render: :response.render
        redirect: :response.redirect
        print: :response.print

        (punch [request response render redirect print] as group! (
            match block! server.spec.actions else [default-response]
        ))
    ]

Though if you had something like WITH you could imagine it like:

    server.locals.handler: func [
        return: [~]
        request [object!]
        response [object!]
    ] compose [
        do with [request response {  ; idea of FENCE! representing object
            render: :response.render
            redirect: :response.redirect
            print: :response.print
        }] (
            match block! server.spec.actions else [default-response]
        )
    ]

The COMPOSE is not strictly necessary there, but it lets you do at least a little work once vs. every time the function is called.

Which points to something useful about the PUNCH-based concept... beyond being usable in dialects where things like DO WITH aren't available... it has the potential to perform better (assuming punch-merges are faster than usermode function calls, which I believe they would be).

2 Likes