Houdini 20.0 hwebserver

hwebserver.urlHandler function

Decorator for functions that handle requests to Houdini’s web server.

On this page

urlHandler(path, is_prefix=False, ports=[])

When you decorate a function with this decorator, it registers the function as the handler for requests at the given server path.

path

A string starting with "/", but which may or may not end with a slash. When registering paths, trailing slashes do not affect the behavior of the server; if path does not have a slash it is implicitly added, and when the server matches paths to request handlers, any paths sent in server requests have a path added to them if it is missing. Note that /hwebserver/Request.html#path contains the unaltered path that was requested, though.

is_prefix

If False, the handler function will only be invoked when the requested path matches path. If True, however, the handler will be called when the requested path starts with path, followed by a slash, followed by anything else.

ports

Any ports the handler function can be invoked on. If no ports are specified the handler function is bound to the main port of the webserver. The port name can be any name that is not empty and is not main (reserved for the main port).

Note

The name of the decorated function does not matter.

import hwebserver

@hwebserver.urlHandler("/hello")
def my_handler(request):
    return hwebserver.Response("Hello world")

To download a file to the client, return the result of hwebserver.fileResponse:

import os.path
import hwebserver

@hwebserver.urlHandler("/hello")
def my_handler(request):
    file_path = os.path.expandvars("$HIP/hello.txt")
    return hwebserver.fileResponse(file_path)

To build remote procedure call APIs, use the hwebserver.apiFunction decorator.

NEW

URL handlers support asyncio (Python 3 builds only). Since there is a performance hit to using asyncio as the web server is C++ first. It is recommended to use coroutine API functions only when an API function is likely to wait for IO while handling the request. The decorator will automatically detect the function type and handle the function based on its type.

Prefix paths

You can use the is_prefix=True argument in the decorator to specify that your function will handle all requests starting with the given path.

For example, @hwebserver.urlHandler("/node", is_prefix=True) will handle all requests that start with /node, such as /node/obj, /node/stage/light1, /node/obj/my_hda/sphere1, and so on.

In the handler, you can get the full request path using request.path() and it is up to the handler to strip off the prefix path to get the “suffix” path.

import hou
import hwebserver


@hwebserver.urlHandler("/node", is_prefix=True)
def node_handler(request):
    path = request.path()
    assert path.startswith("/node")

    # Strip off the "/node" at the start and any trailing slash.
    node_path = path[len("/node"):]
    if not node_path:
        node_path = "/"
    if node_path != "/" and node_path.endswith("/"):
        node_path = node_path[:-1]

    # Get the hou.Node object at the given path.  If it doesn't exist, respond
    # with 404 Not Found.
    node = hou.node(node_path)
    if not node:
        return hwebserver.notFoundResponse(request)

    html = "<h1>{}</h1>".format(node.name())
    html += "<p>{}</p>".format(node.type().name())
    html += "<ul>\n"
    if node != hou.root():
        html += '<li><a href="/node{}">..</a></li>\n'.format(
            node.parent().path())
    for child_node in node.children():
        html += '<li><a href="/node{}">{}</a></li>\n'.format(
            child_node.path(), child_node.name())
    html += "</ul>\n"

    return hwebserver.Response(html)

Path matching

  • If a path has an exact match except for a trailing slash, the trailing slash is removed.

  • If the suffix of a “prefixed” match has a trailing slash, the trailing slash is removed.

For example, suppose you've registered / and /exact as exact path handlers, and /pre and /pre/usd as prefix handlers.

Requested Path

Handler

request.path()

/

/

/

/exact

/exact

/exact

/exact/

/exact

/exact/

/exact/geo

404

N/A

/pre/

/pre

/pre/

/pre

/pre

/pre

/pre/geo

/pre

/pre/geo

/pre/geo/

/pre

/pre/geo/

/pre/usd

/pre/usd

/pre/usd

/pre/usd/scene

/pre/usd

/pre/usd/scene

/pregeo

404

N/A

/missing

404

N/A

Examples

import hou
import hwebserver


@hwebserver.urlHandler("/")
def index(request):
    return hwebserver.Response(
        '''
        <form method="GET" action="/crag.bgeo">
            Download geometry at frame:
            <input name="frame" value="1">
            <input type="submit" value="Download">
        </form>
        ''')


@hwebserver.urlHandler("/crag.bgeo")
def crag(request):
    '''Return a bgeo file of the Crag test geometry at the requested frame.'''
    url_args = request.GET()
    frame_str = url_args.get("frame", "1")
    try:
        frame = float(frame_str)
    except ValueError:
        return hwebserver.errorResponse(
            request, "Frame is not a number", 422)

    sop = hou.node("/obj/geo1/testgeometry_crag1")
    if sop is None:
        sop = hou.node("/obj").createNode("geo").createNode("testgeometry_crag")

    response = hwebserver.Response(
        sop.geometryAtFrame(frame).data(),
        content_type="application/octet-stream"
    )
    response.setHeader(
        "Content-Disposition",
        'attachment; filename="crag_%s.bgeo' % frame
    )
    return response
See also

hwebserver

Classes

Starting and Stopping

Handling Web Requests and Returning Responses

WebSocket

  • WebSocket

    Base class for WebSocket support with the embedded server.

  • hwebserver.webSocket

    Decorator for registering WebSocket classes with Houdini’s web server.

API Calls

  • hwebserver.apiFunction

    Decorator for functions that can be called through an API endpoint on Houdini’s web server, returning JSON or binary responses.

  • hwebserver.APIError

    Raise this exception in apiFunction handlers to indicate an error.