Houdini 20.0 Python scripting hou hou.webServer

hou.webServer.urlHandler HOM 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 behaviour 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 /hom/hou/webServer/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 url handler should specifically bind to. If no port is specified the url handler is bound to the main port of the web server. The port number can be an actual port value, 0 to indicate binding to the main port or -1 which indicates binding to all ports.

Note

When specifying a port number if the handler needs to bind to the main port it is recommended to specify 0 and not the actual port number. The lookup for the handler is more performant and it has the added benefit that if you change the main port the handler will still be bound to the main port.

Note

The name of the decorated function does not matter.

import hou

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

To download a file to the client, return the result of hou.webServer.fileResponse():

import os.path
import hou

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

To build remote procedure call APIs, use the hou.webServer.apiFunction() decorator.

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, @hou.webServer.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


@hou.webServer.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.OpNode object at the given path.  If it doesn't exist, respond
    # with 404 Not Found.
    node = hou.node(node_path)
    if not node:
        return hou.webServer.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 hou.webServer.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


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


@hou.webServer.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 hou.webServer.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 = hou.webServer.Response(
        sop.geometryAtFrame(frame).data(),
        content_type="application/octet-stream"
    )
    response.setHeader(
        "Content-Disposition",
        'attachment; filename="crag_%s.bgeo' % frame
    )
    return response
See also

hou.webServer

Classes

Starting and Stopping

Handling Web Requests and Returning Responses

API Calls

  • hou.webServer.apiFunction()

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

  • hou.webServer.APIError

    Raise this exception in apiFunction handlers to indicate an error.