Skip to content

gimlet2/kottpd

Repository files navigation

Kottpd

Maven Central License: MIT Kotlin Build Coverage Branches

A lightweight REST framework written in pure Kotlin with zero external dependencies. Perfect for building microservices, REST APIs, and simple web servers.

Features

✨ Pure Kotlin - No external framework dependencies
πŸš€ Lightweight - Minimal footprint (~316 LOC)
πŸ”’ HTTPS Support - Built-in SSL/TLS support
πŸ›£οΈ Flexible Routing - Path-based and regex routing
🎯 Filters - Before/after request filters
⚑ Simple API - Easy to learn and use
πŸ“ Static Files - Built-in static file serving
🎭 Exception Handling - Custom exception handlers

Quick Start

Installation

Add Kottpd to your project using Maven:

<dependency>
    <groupId>com.github.gimlet2</groupId>
    <artifactId>kottpd</artifactId>
    <version>0.2.2</version>
</dependency>

Or Gradle:

implementation("com.github.gimlet2:kottpd:0.2.2")

Hello World

import com.github.gimlet2.kottpd.Server

fun main() {
    val server = Server() // Default port is 9000
    
    server.get("/hello") { _, _ -> 
        "Hello, World!" 
    }
    
    server.start()
    println("Server running at http://localhost:9000")
}

Visit http://localhost:9000/hello to see your server in action!

Usage Examples

Basic Routing

val server = Server(port = 8080)

// GET request
server.get("/users") { req, res ->
    res.send("[{\"id\": 1, \"name\": \"John\"}]")
}

// POST request
server.post("/users") { req, res ->
    val userData = req.content
    res.send("User created: $userData", Status.Created)
}

// PUT request
server.put("/users/:id") { req, res ->
    res.send("User ${req.url} updated")
}

// DELETE request
server.delete("/users/:id") { req, res ->
    res.send("User deleted", Status.NoContent)
}

server.start()

Regex-based Routing

server.get("/api/v.*/users") { req, res ->
    res.send("Matches /api/v1/users, /api/v2/users, etc.")
}

server.get("/files/.*\\.pdf") { req, res ->
    res.send("PDF file requested")
}

Request Filters (Middleware)

// Global before filter (runs before all requests)
server.before { req, res ->
    println("Incoming request: ${req.method} ${req.url}")
    res.send("Log: Request received\n")
}

// Path-specific before filter
server.before("/api/.*") { req, res ->
    // Authentication check
    val token = req.headers["Authorization"]
    if (token == null) {
        res.send("Unauthorized", Status.Unauthorized)
    }
}

// After filter
server.after("/.*") { req, res ->
    res.send("\n--- Request completed ---")
}

Exception Handling

server.get("/error") { req, res ->
    throw IllegalStateException("Something went wrong!")
}

server.exception(IllegalStateException::class) { req, res ->
    res.send("Error handled gracefully", Status.InternalServerError)
}

Static File Serving

// Serve static files from resources/public
server.staticFiles("/public")

// Now files are accessible:
// resources/public/index.html -> http://localhost:9000/index.html
// resources/public/css/style.css -> http://localhost:9000/css/style.css

HTTPS/TLS Support

server.start(
    port = 9443,
    secure = true,
    keyStoreFile = "./keystore.jks",
    password = "keystorePassword"
)

Working with Headers

server.get("/headers") { req, res ->
    val userAgent = req.headers["User-Agent"]
    val contentType = req.headers["Content-Type"]
    
    res.send("User-Agent: $userAgent")
}

Reading Request Body

server.post("/data") { req, res ->
    val body = req.content
    val contentLength = req.headers["Content-Length"]
    
    res.send("Received ${contentLength} bytes: $body", Status.Created)
}

Complete Example

fun main() {
    Server(9000).apply {
        // Static files
        staticFiles("/public")
        
        // Routes
        get("/") { _, _ -> "Welcome to Kottpd!" }
        get("/hello") { _, res -> res.send("Hello") }
        get("/user/.*") { req, res -> res.send("User path: ${req.url}") }
        
        // POST with body
        post("/data") { req, res -> 
            res.send(req.content, Status.Created) 
        }
        
        // Filters
        before("/hello") { _, res -> res.send("before\n") }
        after("/hello") { _, res -> res.send("\nafter") }
        
        // Global filter
        before { _, res -> res.send("[LOG] ") }
        
        // Exception handling
        get("/error") { _, _ -> throw IllegalStateException("Test error") }
        exception(IllegalStateException::class) { _, _ -> "Error handled" }
        
        start()
    }.also {
        println("Server started on http://localhost:9000")
        println("Try: http://localhost:9000/hello")
    }
}

Architecture

Kottpd uses a simple architecture:

  1. Server - Main class that handles routing and server lifecycle
  2. ClientThread - Processes individual HTTP requests in separate threads
  3. HttpRequest - Represents incoming HTTP requests
  4. HttpResponse - Represents outgoing HTTP responses
  5. Status - HTTP status codes
  6. HttpMethod - Supported HTTP methods

Configuration

Custom Port

val server = Server(port = 8080)
// or via system property
// -Dserver.port=8080

Thread Pool

The server uses a cached thread pool by default, which creates new threads as needed and reuses previously constructed threads when available.

Documentation

Comparison with Other Frameworks

Feature Kottpd Ktor Javalin Spring Boot
Size Tiny Medium Small Large
Dependencies None Many Few Many
Learning Curve Easy Medium Easy Steep
Performance Good Excellent Good Good
Best For Simple APIs Production apps REST APIs Enterprise

Limitations

⚠️ Current limitations to be aware of:

  • No built-in JSON serialization (bring your own library)
  • Basic error handling
  • Limited documentation
  • No built-in CORS support
  • Early development stage (v0.2.x)

See ROADMAP.md for planned improvements.

Contributing

We welcome contributions! Please see CONTRIBUTING.md for details.

License

This project is licensed under the MIT License - see the LICENSE file for details.

Author

Andrei Chernyshev - @gimlet2

Support


Made with ❀️ using Kotlin

About

REST framework written in pure Kotlin

Resources

License

Contributing

Security policy

Stars

Watchers

Forks

Packages

 
 
 

Contributors 6