Skip to content

degubites/UniAPI

Repository files navigation

UniAPI

UniAPI

Dependency-free HTTP API framework for Unity, with main-thread-safe routing.


Status

Pre-alpha (v0.0.1). The public API is unstable and may change between 0.x releases.

Why UniAPI

  • Zero external dependencies — built on the .NET BCL only. Works with or without UniTask.
  • Task<T> based async — modern C# async/await, no coroutines required.
  • Main-thread dispatch built inawait ctx.MainThreadAsync(...) to safely touch Unity APIs from a request handler.
  • Routing with named parameters/api/donors/{type}, with URL-decoded values (Korean / non-ASCII names work).
  • File serving with HTTP Range — videos seekable in browsers, resumable downloads, no memory bloat.
  • 404 / 405 distinction and built-in OPTIONS preflight for CORS.
  • Unity 2022.3+ — verified on Mono; IL2CPP standalone should work (see platform matrix below).

Install

Via UPM Git URL (works today)

In Unity: Window → Package Manager → + → Add package from git URL → paste:

https://codeberg.org/degubites/UniAPI.git

Or pin to a specific release tag:

https://codeberg.org/degubites/UniAPI.git#v0.0.1

Or edit Packages/manifest.json directly:

{
  "dependencies": {
    "com.degubites.uniapi": "https://codeberg.org/degubites/UniAPI.git#v0.0.1"
  }
}

Via OpenUPM

Pending submission review. Once available, you'll be able to install through OpenUPM's scoped registry.

Quick start

using Degubites.UniAPI;
using UnityEngine;

public class Bootstrap : MonoBehaviour
{
    void Start()
    {
        var server = gameObject.AddComponent<UniAPIServer>();
        server.Port = 8080;

        server.MapGet("/api/ping", ctx => ctx.Text("pong"));

        server.MapPost("/api/donors/{type}", async ctx =>
        {
            var type = ctx.RouteValues["type"];
            var body = await ctx.ReadBodyAsTextAsync();

            // Worker thread → main thread → worker thread, transparent.
            await ctx.MainThreadAsync(() => Debug.Log($"Added donor type={type}"));

            await ctx.Text("{\"result\":\"ok\"}",
                contentType: "application/json; charset=utf-8");
        });

        server.StartServer();
    }
}

Verify:

curl http://localhost:8080/api/ping
# pong

Routing

Pattern forms:

  • Static: /api/ping
  • Single parameter: /api/donors/{type}ctx.RouteValues["type"]
  • Multiple parameters: /users/{id}/posts/{postId}

Method handlers: MapGet, MapPost, MapPut, MapDelete, MapPatch. All chainable (each returns the server).

Matching policy in v0.0.1:

  • First registered wins on overlap. Register literal paths before parameterized ones if both could match:
    server.MapGet("/users/me", ...);           // literal — register first
    server.MapGet("/users/{id}", ...);         // parameter
  • Case-sensitive on literal segments.
  • Parameter values are URL-decoded.
  • Wildcards ({*rest}) and constraints ({id:int}) are not in v0.0.1 — see roadmap.

Mismatches:

  • Path doesn't match anything → 404
  • Path matches but for a different method → 405

Serving files (images, videos, downloads)

ServeFileAsync streams from disk with HTTP Range support — videos seekable, downloads resumable, no memory bloat.

using System.IO;
using UnityEngine;

string mediaRoot = Application.streamingAssetsPath;

server.MapGet("/files/{name}", async ctx =>
{
    var name = ctx.RouteValues["name"];
    var safe = SafePath(mediaRoot, name);
    if (safe == null) { await ctx.Status(403); return; }
    await ctx.ServeFileAsync(safe);
});

static string SafePath(string baseDir, string userInput)
{
    var combined = Path.GetFullPath(Path.Combine(baseDir, userInput));
    var baseFull = Path.GetFullPath(baseDir);
    if (!combined.StartsWith(baseFull + Path.DirectorySeparatorChar,
            System.StringComparison.Ordinal)
        && combined != baseFull)
        return null;
    return combined;
}

What ServeFileAsync handles for you:

  • Content-Type inferred from extension (.mp4video/mp4, etc.); override via contentType: argument
  • Content-Length, Accept-Ranges: bytes, Last-Modified set automatically
  • Range: bytes=N-M / bytes=N- / bytes=-N206 Partial Content with Content-Range
  • Unsatisfiable range → 416 with Content-Range: bytes */N
  • Malformed Range syntax → 400
  • Missing file → 404
  • Streams without buffering the whole file in memory — fine for multi-GB videos

Path sanitization is your responsibility — the snippet above shows the typical pattern.

Forced download (with non-ASCII filename)

server.MapGet("/download/{name}", async ctx =>
{
    var name = ctx.RouteValues["name"];
    var safe = SafePath(mediaRoot, name);
    if (safe == null) { await ctx.Status(403); return; }

    // RFC 5987-encoded filename handles Korean / non-ASCII safely.
    var encoded = System.Uri.EscapeDataString(Path.GetFileName(safe));
    ctx.SetHeader("Content-Disposition",
        $"attachment; filename=\"download\"; filename*=UTF-8''{encoded}");

    await ctx.ServeFileAsync(safe);
});

Built-in CORS preflight

OPTIONS requests are auto-answered with 200 and:

Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, PATCH, OPTIONS
Access-Control-Allow-Headers: Content-Type

For more restrictive CORS, add the headers you want from inside your Map* handlers via ctx.SetHeader(...). Tunable preflight behavior is on the v0.1 roadmap.

Platforms

Platform Status Notes
Windows (Mono, Editor) ✅ Verified Used during development
Windows (IL2CPP standalone) ✅ Verified File serving + video Range seeking confirmed in a build
Linux desktop ⚠️ Untested Standard HttpListener — expected to work
macOS ⚠️ Untested Standard HttpListener — expected to work
Android (IL2CPP) ⚠️ Untested Should work; INTERNET permission auto-added by Unity
iOS ⚠️ Untested Should work; may be killed in background
WebGL ❌ Not supported Browser has no socket API
Consoles (Switch / PlayStation / Xbox) ❌ Not supported Platform networking SDKs required

Minimum Unity: 2022.3.

Windows URL ACL

The default binding http://*:{port}/ may require a one-time URL ACL on Windows for non-admin processes:

netsh http add urlacl url=http://*:8080/ user=Everyone

A configurable BindAddress (localhost / specific interface) is planned for v0.1.

Chrome's restricted port list

Chromium-based browsers (Chrome, Edge, Brave, Opera) refuse to connect to certain "well-known service" ports with ERR_UNSAFE_PORT. Notable blocked ports include 25 (SMTP), 139 (NetBIOS), 6000 (X11), 6667 (IRC), 10080 (AMANDA backup), and many others.

The default of 8080 is safe. Other browser-friendly choices: 8000, 8081, 8888, 3000, 5000, 10081.

API surface

Public types: UniAPIServer, UniAPIContext.

UniAPIServer:

  • Map(method, pattern, handler) / MapGet / MapPost / MapPut / MapDelete / MapPatch — chainable
  • StartServer() / StopServer()
  • Port, IsRunning, RouteCount

UniAPIContext:

  • Response: Text(body, statusCode, contentType), Bytes(data, ...), Status(int), NotFound(), SetHeader(name, value), ServeFileAsync(path, contentType=null)
  • Request body: ReadBodyAsTextAsync(), ReadBodyAsBytesAsync()
  • Main thread dispatch: MainThreadAsync(Action), MainThreadAsync<T>(Func<T>), MainThreadAndWait(Action, timeoutMs)
  • Properties: Request, Response, RouteValues, CancellationToken, Method, Path, IsResponseSent

Roadmap (v0.1 candidates)

  • Wildcard routes ({*rest}) and constraint converters ({id:int})
  • Specificity-based matching (literal beats parameter automatically)
  • BindAddress property — localhost-only or specific interface
  • Configurable / opt-out auto-OPTIONS handling
  • Automatic HEAD for registered GET routes
  • JSON helpers (ctx.Json(obj)) with pluggable serializer
  • Content-Disposition shortcut on ServeFileAsync
  • Conditional GET (If-Modified-Since, ETag)
  • Verified IL2CPP standalone + Android builds

Issues & Contributing

This package is developed and tracked on Codeberg. Bug reports, feature requests, and questions go there:

Pull requests are welcome — please open an issue first for anything non-trivial so we can align on the approach before you spend time on a patch.

License

MIT — see LICENSE.md.

About

Dependency-free HTTP API framework for Unity engine, with main-thread-safe routing.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages