HTTP Requests

The HTTP extension allows making non-blocking HTTP and (as of Release 15) HTTPS requests.

The recommended way to use this library is to create two functions around your request: one to start it and another to process it. When you want to start an HTTP request you would call your custom startRequest() function, and you would call your custom processRequest() from a function that runs regularly like drawWorld() or frame().

Here is an outline for submitting a high score to a server:

function handleCommand(cmd)
    -- ...

    -- Start the request when the 'submitscore' command is run
    if cmd == "submitscore" then
        startHighScoreSubmission()
    end

    -- ...
end

function makeHighscoreRequestBody()
    return "score=" .. getHighScore() .. "&player=" .. getPlayerName() .. "&time=" .. os.time()
end

function startHighScoreSubmission()
    -- When using HTTPS, it's very important to get your server's certificate
    -- so the HTTP client can verify it's talking to the right server!
    local cert = knLoadAsset("certs/highscore-server.pem")

    highScoreRequest = knHttpRequest("POST", "https://myserver.com/highscore/", makeHighscoreRequestBody(), nil, cert)

    if not highScoreRequest then
        -- handle failing to initialise the request
    end
end

function processHighScoreSubmission()
    if highScoreRequest then
        -- update the request with recieved data and get the status
        local status = knHttpUpdate(highScoreRequest)

        if status == KN_HTTP_PENDING then
            -- we can't really do anything while pending, leave everything as is
        else
            if status == KN_HTTP_ERROR then
                -- handle the error case, maybe show a dialogue to the user
                -- about the error
            else
                local data = knHttpData(highScoreRequest)
                -- handle the successful case with the response data, maybe
                -- show something to confirm the score was submitted. note that
                -- if your server returns something like 200 OK for certian
                -- types of errors this will still technically be an error and
                -- you will want to handle that accordingly
            end

            -- Since the request is finished, don't hang on to the object
            -- anymore and replace it with nil so we know it's no longer needed
            highScoreRequest = nil
        end
    end
end
knHttpRequest(
    method: string,
    url: string,
    [body: string, [headers: table[string: string], [certificate: string]]]
): object

Initiate an HTTP request to the given URL using the given method. The request may optionally contain a body (regardless of the method) by supplying a third parameter with the request body contents. The headers parameter can be used to send custom HTTP headers.

The certificate parameter ought to be specified when using HTTPS so the identity of the server can be verified. The certificate can be in DER or PEM format. If the certificate is not specified, then the server’s identity won’t be verified, which may expose the request to man-in-the-middle attacks.

Returns an HTTP request context (of type userdata) on success or nil on failure.

Note

body is allowed to contain embedded zeros.

Changed in version 14: If you were using the old one (for GET requests) or two (for POST requests) argument versions of this method where the URL was the first argument, you should switch these to the following forms:

knHttpRequest("GET", "http://example.com/mylevel.zip") -- for GET requests
knHttpRequest("POST", "http://example.com/highscore.php", "score=12345") -- for POST requests

when upgrading to the new version.

knHttpUpdate(request): KN_HTTP_PENDING | KN_HTTP_ERROR | KN_HTTP_DONE

Reads any new data and further process the request, possibly finalising it. Returns:

  • KN_HTTP_PENDING if the request is still pending;

  • KN_HTTP_ERROR if the request has finished in error (network errors);

  • KN_HTTP_DONE if the request has succeeded.

This function must be called in a function like tick() or draw() (that is, every so often) until it no longer returns KN_HTTP_PENDING. When it does finish, it is recommended to do any processing, then release the request.

Changed in version 13: Status codes above 299 now return KN_HTTP_DONE instead of KN_HTTP_ERROR.

knHttpData(request): string

Returns the response data for a finished request as a string.

knHttpDataSize(request): integer

Returns the size of the response data in bytes.

knHttpSave(request, path: string)

Efficiently save the contents of the response to the file at path without needing to allocate any extra buffers. This function raises a Lua error on errors and does not return anything; use pcall to catch errors.

Example:

knHttpSave(request, knGetInternalDataPath() .. "/payload.zip")
knHttpGetHeader(request, name: string, [nth: integer]): string

Returns the value associated with the nth response header named name. Returns nil if the header does not exist.

As an example, the following would get the first two Cookie headers form the response:

local cookie1 = knHttpGetHeader(request, "Cookie", 0)
local cookie2 = knHttpGetHeader(request, "Cookie", 1)
knHttpError(request): string

Return a string describing the HTTP error, or nil if there is none. Note that even if there is an error this may return nil, for example due to a lower-level network error.

knHttpErrorCode(request): integer

Return an integer which is the HTTP status code of the response, or 0 if there is not one. This may be 0 even if there was some kind of error.

knHttpRelease(request)

Release the memory associated with an HTTP request. This will normally happen automatically once an HTTP request object has been garbage collected, but calling this will do it immediately instead of waiting on the Lua GC.

Any further functions called on this request may return ``nil`` regardless of their documentation.

Tip: A common pattern for global request objects is to the variable they were stored in to nil in order to release their resources and indicate that the request has finished.

function finishRequest()
   someImportantThing = knHttpData(globalRequestObject)
   globalRequestObject = nil
end
knHttpsCert([certificateData: string])

Set the TLS certificate(s) to verify all future HTTPS requests with. The certificateData can be either a single DER encoded certificate or one or more PEM encoded certificates. Any certificate(s) loaded with a previous call to this function are replaced.

Omitting the certificate data resets to the default state of having no certificates loaded.

It is recommended to keep the certificate data for the server you wish to communicate with in an asset, then load it with knLoadAsset() in something like the HUD’s init() function:

function init()
   knHttpsCert(knLoadAsset("mytlscert.pem"))
   -- ...
end

Deprecated since version 19: Explicitly use the certificate parameter of knHttpRequest()

knHttpsNoCert(magic: string)

Disables verifying the server’s identity when no certificates are loaded instead of causing such requests to raise an error. This is insecure as it could enable Man-in-the-Middle attacks which nullify the security of HTTPS, but useful in certain cases where verifying the identify of the server is impractical.

Because this is insecure, this function only works if a specific string is passed in magic. The string is intentionally not documented here; it can be found in KnShim’s source code.

Deprecated since version 19: KatieLib will now allow unverified hosts by default if a certificate is not supplied. While this is less secure, the main purpose of HTTPS in KatieLib is compatibility with the modern web and not actual security anyway.