Server API¶
The server API provides tools for running Gemini servers programmatically. Use these APIs to create custom server implementations, add middleware, and configure server behavior.
Overview¶
The server API consists of:
ServerConfig- Configuration management and TOML loadingstart_server()- Server startup and lifecycle management- Middleware - Request processing and security (rate limiting, access control, certificate auth)
- Handlers - Request handling and response generation
Quick Start¶
Minimal Server¶
Start a basic Gemini server with default settings:
import asyncio
from pathlib import Path
from nauyaca.server.config import ServerConfig
from nauyaca.server.server import start_server
async def main():
config = ServerConfig(
host="localhost",
port=1965,
document_root=Path("./capsule"),
certfile=Path("cert.pem"),
keyfile=Path("key.pem")
)
await start_server(config)
if __name__ == "__main__":
asyncio.run(main())
Server with Configuration File¶
Load configuration from a TOML file:
import asyncio
from pathlib import Path
from nauyaca.server.config import ServerConfig
from nauyaca.server.server import start_server
async def main():
# Load from TOML
config = ServerConfig.from_toml(Path("config.toml"))
# Start server with configuration
await start_server(
config,
enable_directory_listing=True,
log_level="INFO"
)
if __name__ == "__main__":
asyncio.run(main())
Server with Custom Middleware¶
Add custom middleware to the server:
import asyncio
from pathlib import Path
from nauyaca.server.config import ServerConfig
from nauyaca.server.server import start_server
from nauyaca.server.middleware import RateLimitConfig, AccessControlConfig
async def main():
config = ServerConfig(
host="0.0.0.0", # Listen on all interfaces
port=1965,
document_root=Path("/var/gemini/capsule"),
certfile=Path("/etc/gemini/cert.pem"),
keyfile=Path("/etc/gemini/key.pem")
)
# Configure rate limiting
rate_limit_config = RateLimitConfig(
capacity=20, # Allow burst of 20 requests
refill_rate=2.0, # Refill 2 tokens per second
retry_after=60 # Ask clients to wait 60s if limited
)
# Configure access control
access_control_config = AccessControlConfig(
allow_list=["192.168.1.0/24", "10.0.0.0/8"],
deny_list=["192.168.1.100"],
default_allow=False # Deny by default
)
await start_server(
config,
enable_rate_limiting=True,
rate_limit_config=rate_limit_config,
access_control_config=access_control_config,
log_level="DEBUG"
)
if __name__ == "__main__":
asyncio.run(main())
ServerConfig¶
ServerConfig
dataclass
¶
ServerConfig(
host: str = "localhost",
port: int = DEFAULT_PORT,
document_root: Path | str = ".",
certfile: Path | str | None = None,
keyfile: Path | str | None = None,
enable_rate_limiting: bool = True,
rate_limit_capacity: int = 10,
rate_limit_refill_rate: float = 1.0,
rate_limit_retry_after: int = 30,
enable_access_control: bool = True,
access_control_allow_list: list[str] | None = None,
access_control_deny_list: list[str] | None = None,
access_control_default_allow: bool = True,
max_file_size: int = DEFAULT_MAX_FILE_SIZE,
certificate_auth_paths: list[dict[str, Any]]
| None = None,
require_client_cert: bool = False,
hash_client_ips: bool = True,
enable_titan: bool = False,
titan_upload_dir: Path | str | None = None,
titan_max_upload_size: int = 10 * 1024 * 1024,
titan_allowed_mime_types: list[str] | None = None,
titan_auth_tokens: list[str] | None = None,
titan_enable_delete: bool = False,
locations: list[LocationConfig] | None = None,
)
Configuration for Gemini server.
Attributes:
| Name | Type | Description |
|---|---|---|
host |
str
|
Server host address (default: "localhost"). |
port |
int
|
Server port (default: 1965). |
document_root |
Path | str
|
Path to directory containing files to serve. |
certfile |
Path | str | None
|
Path to TLS certificate file. |
keyfile |
Path | str | None
|
Path to TLS private key file. |
Examples:
>>> config = ServerConfig(
... host="localhost",
... port=1965,
... document_root=Path("/var/gemini/capsule"),
... certfile=Path("/etc/gemini/cert.pem"),
... keyfile=Path("/etc/gemini/key.pem")
... )
__post_init__
¶
Validate and normalize configuration after initialization.
Source code in src/nauyaca/server/config.py
from_env
classmethod
¶
Extract configuration from environment variables.
Reads NAUYACA_* environment variables and returns a dict suitable for overriding ServerConfig fields. Only returns values that are actually set in the environment.
Supported variables
NAUYACA_HOST: Server host address NAUYACA_PORT: Server port (integer) NAUYACA_DOCUMENT_ROOT: Path to document root NAUYACA_CERTFILE: Path to TLS certificate NAUYACA_KEYFILE: Path to TLS private key
Returns:
| Type | Description |
|---|---|
dict[str, Any]
|
Dict with keys matching ServerConfig fields. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If NAUYACA_PORT is not a valid integer. |
Examples:
>>> import os
>>> os.environ['NAUYACA_HOST'] = '0.0.0.0'
>>> env_config = ServerConfig.from_env()
>>> env_config.get('host')
'0.0.0.0'
Source code in src/nauyaca/server/config.py
from_toml
classmethod
¶
Load configuration from TOML file.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
Path
|
Path to TOML configuration file. |
required |
Returns:
| Type | Description |
|---|---|
ServerConfig
|
ServerConfig instance loaded from TOML. |
Raises:
| Type | Description |
|---|---|
FileNotFoundError
|
If config file doesn't exist. |
ValueError
|
If config is invalid or cannot be parsed. |
Examples:
>>> config = ServerConfig.from_toml(Path("config.toml"))
>>> print(config.host, config.port)
localhost 1965
Source code in src/nauyaca/server/config.py
345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 | |
get_access_control_config
¶
Get access control configuration.
Returns:
| Type | Description |
|---|---|
AccessControlConfig | None
|
AccessControlConfig instance if enabled and lists are configured, |
AccessControlConfig | None
|
None otherwise. |
Source code in src/nauyaca/server/config.py
get_certificate_auth_config
¶
Get certificate authentication configuration.
Returns:
| Type | Description |
|---|---|
CertificateAuthConfig | None
|
CertificateAuthConfig instance if path rules are configured, None otherwise. |
Source code in src/nauyaca/server/config.py
get_location_router
¶
Build a router from location configurations.
Creates handlers for each location and registers them with a Router. If no locations are configured, returns None.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
enable_directory_listing
|
bool
|
Default directory listing setting for static handlers that don't specify it. |
False
|
Returns:
| Type | Description |
|---|---|
Router | None
|
Router instance configured with location handlers, or None if |
Router | None
|
no locations are configured. |
Examples:
>>> config = ServerConfig.from_toml(Path("config.toml"))
>>> router = config.get_location_router()
>>> if router:
... response = router.route(request)
Source code in src/nauyaca/server/config.py
get_rate_limit_config
¶
Get rate limit configuration.
Returns:
| Type | Description |
|---|---|
RateLimitConfig
|
RateLimitConfig instance with current settings. |
Source code in src/nauyaca/server/config.py
get_upload_handler
¶
Get the Titan upload handler if Titan is enabled.
Returns:
| Type | Description |
|---|---|
FileUploadHandler | None
|
FileUploadHandler instance if Titan is enabled, None otherwise. |
Source code in src/nauyaca/server/config.py
validate
¶
Validate the server configuration.
Raises:
| Type | Description |
|---|---|
ValueError
|
If configuration is invalid. |
Source code in src/nauyaca/server/config.py
Loading from TOML¶
The ServerConfig.from_toml() method loads configuration from a TOML file:
from pathlib import Path
from nauyaca.server.config import ServerConfig
# Load from file
config = ServerConfig.from_toml(Path("config.toml"))
# Access configuration values
print(f"Server will run on {config.host}:{config.port}")
print(f"Serving files from {config.document_root}")
Example TOML configuration:
[server]
host = "0.0.0.0"
port = 1965
document_root = "/var/gemini/capsule"
certfile = "/etc/gemini/cert.pem"
keyfile = "/etc/gemini/key.pem"
max_file_size = 104857600 # 100 MiB
[rate_limit]
enabled = true
capacity = 10
refill_rate = 1.0
retry_after = 30
[access_control]
allow_list = ["192.168.1.0/24"]
default_allow = false
[logging]
hash_ips = true
Programmatic Configuration¶
Create configuration entirely in code:
from pathlib import Path
from nauyaca.server.config import ServerConfig
config = ServerConfig(
host="localhost",
port=1965,
document_root=Path("/var/gemini/capsule"),
certfile=Path("/etc/gemini/cert.pem"),
keyfile=Path("/etc/gemini/key.pem"),
# Rate limiting
enable_rate_limiting=True,
rate_limit_capacity=10,
rate_limit_refill_rate=1.0,
rate_limit_retry_after=30,
# Access control
access_control_allow_list=["192.168.1.0/24"],
access_control_default_allow=False,
# Security
hash_client_ips=True,
max_file_size=100 * 1024 * 1024 # 100 MiB
)
# Validate configuration
config.validate()
# Get middleware configurations
rate_limit_config = config.get_rate_limit_config()
access_control_config = config.get_access_control_config()
Server Functions¶
start_server
async
¶
start_server(
config: ServerConfig,
enable_directory_listing: bool = False,
log_level: str = "INFO",
log_file: Path | None = None,
json_logs: bool = False,
enable_rate_limiting: bool = True,
rate_limit_config: RateLimitConfig | None = None,
access_control_config: AccessControlConfig
| None = None,
certificate_auth_config: CertificateAuthConfig
| None = None,
hash_ips: bool | None = None,
max_file_size: int | None = None,
) -> None
Start a Gemini server with the given configuration.
This function sets up a Gemini server with static file serving, routing, TLS configuration, and middleware. It runs until interrupted.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
ServerConfig
|
Server configuration. |
required |
enable_directory_listing
|
bool
|
Enable automatic directory listings. |
False
|
log_level
|
str
|
Logging level (DEBUG, INFO, WARNING, ERROR). |
'INFO'
|
log_file
|
Path | None
|
Optional path to log file. If None, logs to stdout. |
None
|
json_logs
|
bool
|
If True, output logs in JSON format. |
False
|
enable_rate_limiting
|
bool
|
Enable rate limiting middleware. |
True
|
rate_limit_config
|
RateLimitConfig | None
|
Rate limiting configuration. Uses defaults if None. |
None
|
access_control_config
|
AccessControlConfig | None
|
Access control configuration. None to disable. |
None
|
certificate_auth_config
|
CertificateAuthConfig | None
|
Certificate auth configuration. None to disable. |
None
|
hash_ips
|
bool | None
|
Hash client IPs in logs. If None, uses config.hash_client_ips. |
None
|
max_file_size
|
int | None
|
Maximum file size to serve. If None, uses config.max_file_size. |
None
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If configuration is invalid. |
OSError
|
If unable to bind to the specified host/port. |
Examples:
>>> import asyncio
>>> from pathlib import Path
>>> config = ServerConfig(
... host="localhost",
... port=1965,
... document_root=Path("./capsule"),
... certfile=Path("cert.pem"),
... keyfile=Path("key.pem")
... )
>>> asyncio.run(start_server(config))
Source code in src/nauyaca/server/server.py
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 | |
Server Lifecycle¶
The start_server() function runs indefinitely until interrupted:
import asyncio
import signal
from nauyaca.server.config import ServerConfig
from nauyaca.server.server import start_server
async def main():
config = ServerConfig.from_toml(Path("config.toml"))
# This runs until Ctrl+C or SIGTERM
await start_server(config)
if __name__ == "__main__":
try:
asyncio.run(main())
except KeyboardInterrupt:
print("Server stopped by user")
Graceful Shutdown¶
Handle graceful shutdown with signal handlers:
import asyncio
import signal
from nauyaca.server.config import ServerConfig
from nauyaca.server.server import start_server
shutdown_event = asyncio.Event()
def handle_shutdown(signum, frame):
shutdown_event.set()
async def main():
# Set up signal handlers
signal.signal(signal.SIGINT, handle_shutdown)
signal.signal(signal.SIGTERM, handle_shutdown)
config = ServerConfig.from_toml(Path("config.toml"))
# Run server with shutdown handling
server_task = asyncio.create_task(start_server(config))
shutdown_task = asyncio.create_task(shutdown_event.wait())
# Wait for either server to finish or shutdown signal
done, pending = await asyncio.wait(
[server_task, shutdown_task],
return_when=asyncio.FIRST_COMPLETED
)
# Cancel remaining tasks
for task in pending:
task.cancel()
print("Server shutdown complete")
if __name__ == "__main__":
asyncio.run(main())
Middleware¶
Middleware components process requests before they reach handlers. They can:
- Block requests (rate limiting, access control)
- Require authentication (client certificates)
- Log requests
- Modify request context
Middleware Protocol¶
All middleware must implement the Middleware protocol:
Middleware
¶
Bases: Protocol
Protocol for middleware components.
process_request
async
¶
process_request(
request_url: str,
client_ip: str,
client_cert_fingerprint: str | None = None,
) -> tuple[bool, str | None]
Process a request.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
request_url
|
str
|
The requested URL. |
required |
client_ip
|
str
|
The client's IP address. |
required |
client_cert_fingerprint
|
str | None
|
SHA-256 fingerprint of client certificate, or None if client didn't present a certificate. |
None
|
Returns:
| Type | Description |
|---|---|
bool
|
Tuple of (allow, error_response): |
str | None
|
|
tuple[bool, str | None]
|
|
Source code in src/nauyaca/server/middleware.py
Rate Limiting¶
RateLimitConfig
dataclass
¶
Configuration for rate limiting.
RateLimiter
¶
Rate limiting middleware using token bucket algorithm.
Tracks per-IP request rates and returns status 44 (SLOW DOWN) when limits are exceeded.
Initialize rate limiter.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
RateLimitConfig | None
|
Rate limit configuration. Uses defaults if None. |
None
|
Source code in src/nauyaca/server/middleware.py
start
¶
stop
async
¶
process_request
async
¶
process_request(
request_url: str,
client_ip: str,
client_cert_fingerprint: str | None = None,
) -> tuple[bool, str | None]
Process request with rate limiting.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
request_url
|
str
|
The requested URL. |
required |
client_ip
|
str
|
The client's IP address. |
required |
client_cert_fingerprint
|
str | None
|
Client certificate fingerprint (unused). |
None
|
Returns:
| Type | Description |
|---|---|
tuple[bool, str | None]
|
Tuple of (allow, error_response). |
Source code in src/nauyaca/server/middleware.py
Example usage:
from nauyaca.server.middleware import RateLimiter, RateLimitConfig
# Create rate limiter
config = RateLimitConfig(
capacity=10, # Allow 10 requests in burst
refill_rate=1.0, # Add 1 token per second
retry_after=30 # Tell clients to wait 30s
)
rate_limiter = RateLimiter(config)
rate_limiter.start() # Start background cleanup
# Use with server
await start_server(
server_config,
enable_rate_limiting=True,
rate_limit_config=config
)
Access Control¶
AccessControlConfig
dataclass
¶
AccessControlConfig(
allow_list: list[str] | None = None,
deny_list: list[str] | None = None,
default_allow: bool = True,
)
Configuration for IP-based access control.
AccessControl
¶
IP-based access control middleware.
Supports allow/deny lists with CIDR notation. Returns status 53 (PROXY REQUEST REFUSED) for blocked IPs.
Initialize access control.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
AccessControlConfig | None
|
Access control configuration. Uses defaults if None. |
None
|
Source code in src/nauyaca/server/middleware.py
process_request
async
¶
process_request(
request_url: str,
client_ip: str,
client_cert_fingerprint: str | None = None,
) -> tuple[bool, str | None]
Process request with access control.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
request_url
|
str
|
The requested URL. |
required |
client_ip
|
str
|
The client's IP address. |
required |
client_cert_fingerprint
|
str | None
|
Client certificate fingerprint (unused). |
None
|
Returns:
| Type | Description |
|---|---|
tuple[bool, str | None]
|
Tuple of (allow, error_response). |
Source code in src/nauyaca/server/middleware.py
Example usage:
from nauyaca.server.middleware import AccessControl, AccessControlConfig
# Allow specific networks, deny specific IPs
config = AccessControlConfig(
allow_list=["192.168.1.0/24", "10.0.0.0/8"],
deny_list=["192.168.1.100", "10.0.0.50"],
default_allow=False # Deny everything not in allow list
)
access_control = AccessControl(config)
# Use with server
await start_server(
server_config,
access_control_config=config
)
CIDR Notation
Access control lists support CIDR notation for network ranges:
192.168.1.0/24matches 192.168.1.0-192.168.1.25510.0.0.0/8matches 10.0.0.0-10.255.255.255192.168.1.100matches a single IP (equivalent to /32)
Certificate Authentication¶
CertificateAuthPathRule
dataclass
¶
CertificateAuthPathRule(
prefix: str,
require_cert: bool = False,
allowed_fingerprints: set[str] | None = None,
)
Certificate auth rule for a specific path prefix.
Per Gemini application best practices, certificate requirements are typically activated for specific URL prefixes (e.g., /app/, /admin/) rather than globally, allowing public and authenticated content to coexist.
CertificateAuthConfig
dataclass
¶
Configuration for path-based certificate authentication.
Path rules are checked in order - the first matching rule applies. If no rule matches a request path, the request is allowed without certificate requirements.
CertificateAuth
¶
Certificate-based authentication middleware.
Can require client certificates (status 60) and/or validate against a whitelist of allowed certificate fingerprints (status 61) on a per-path basis.
Per Gemini application best practices, this enables: - Account registration flows using client certificates - Certificate-based access control for specific paths - User identity verification via certificate fingerprints - Mixed public/authenticated content serving
Initialize certificate authentication middleware.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
CertificateAuthConfig | None
|
Certificate auth configuration. Uses defaults if None. |
None
|
Source code in src/nauyaca/server/middleware.py
process_request
async
¶
process_request(
request_url: str,
client_ip: str,
client_cert_fingerprint: str | None = None,
) -> tuple[bool, str | None]
Process request with path-based certificate authentication.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
request_url
|
str
|
The requested URL. |
required |
client_ip
|
str
|
The client's IP address. |
required |
client_cert_fingerprint
|
str | None
|
SHA-256 fingerprint of client certificate. |
None
|
Returns:
| Type | Description |
|---|---|
tuple[bool, str | None]
|
Tuple of (allow, error_response). |
Source code in src/nauyaca/server/middleware.py
Example usage:
from nauyaca.server.middleware import (
CertificateAuth,
CertificateAuthConfig,
CertificateAuthPathRule
)
# Require certificates for specific paths
config = CertificateAuthConfig(
path_rules=[
# Public path - no cert required
CertificateAuthPathRule(
prefix="/public/",
require_cert=False
),
# Admin area - cert required
CertificateAuthPathRule(
prefix="/admin/",
require_cert=True
),
# App area - specific certs only
CertificateAuthPathRule(
prefix="/app/",
require_cert=True,
allowed_fingerprints={
"a1b2c3...", # SHA-256 fingerprints
"d4e5f6..."
}
)
]
)
# Use with server
await start_server(
server_config,
certificate_auth_config=config
)
Certificate Fingerprints
Certificate fingerprints are SHA-256 hashes of the DER-encoded certificate. Use the nauyaca cert fingerprint command to calculate fingerprints for client certificates.
PyOpenSSL Required
When using certificate authentication, Nauyaca automatically uses PyOpenSSL instead of the standard library's ssl module. This is required because OpenSSL 3.x silently rejects self-signed client certificates, which are common in Geminispace.
Middleware Chain¶
MiddlewareChain
¶
Chain multiple middleware components together.
Initialize middleware chain.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
middlewares
|
list[Middleware]
|
List of middleware instances. |
required |
Source code in src/nauyaca/server/middleware.py
process_request
async
¶
process_request(
request_url: str,
client_ip: str,
client_cert_fingerprint: str | None = None,
) -> tuple[bool, str | None]
Process request through all middleware.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
request_url
|
str
|
The requested URL. |
required |
client_ip
|
str
|
The client's IP address. |
required |
client_cert_fingerprint
|
str | None
|
SHA-256 fingerprint of client certificate. |
None
|
Returns:
| Type | Description |
|---|---|
tuple[bool, str | None]
|
Tuple of (allow, error_response). Returns first rejection. |
Source code in src/nauyaca/server/middleware.py
Middleware are executed in order:
- CertificateAuth - Check client certificates first
- AccessControl - Then check IP-based access
- RateLimiter - Finally check rate limits
The first middleware that rejects a request stops the chain.
Handlers¶
Handlers generate responses for requests. Nauyaca provides built-in handlers for common use cases.
RequestHandler Base Class¶
RequestHandler
¶
Bases: ABC
Abstract base class for request handlers.
All request handlers should inherit from this class and implement the handle() method.
handle
abstractmethod
¶
Handle a Gemini request and return a response.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
request
|
GeminiRequest
|
The incoming request to handle. |
required |
Returns:
| Type | Description |
|---|---|
GeminiResponse
|
A GeminiResponse object. |
StaticFileHandler¶
StaticFileHandler
¶
StaticFileHandler(
document_root: Path | str,
default_indices: list[str] | None = None,
enable_directory_listing: bool = False,
max_file_size: int | None = None,
)
Bases: RequestHandler
Handler for serving static files from a document root.
This handler serves files from a specified directory with path traversal protection and automatic MIME type detection.
Attributes:
| Name | Type | Description |
|---|---|---|
document_root |
Path to the directory containing files to serve. |
|
default_indices |
List of index filenames to try for directory requests. |
|
max_file_size |
Maximum file size to serve (in bytes). |
Examples:
>>> handler = StaticFileHandler(Path("/var/gemini/capsule"))
>>> request = GeminiRequest.from_line("gemini://example.com/file.gmi")
>>> response = handler.handle(request)
Initialize the static file handler.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
document_root
|
Path | str
|
Path to the directory containing files to serve. |
required |
default_indices
|
list[str] | None
|
List of index filenames to try for directory requests (default: ["index.gmi", "index.gemini"]). |
None
|
enable_directory_listing
|
bool
|
If True, generate directory listings for directories without an index file (default: False). |
False
|
max_file_size
|
int | None
|
Maximum file size to serve in bytes (default: 100 MiB per Gemini best practices). |
None
|
Source code in src/nauyaca/server/handler.py
handle
¶
Handle a request for a static file.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
request
|
GeminiRequest
|
The incoming request. |
required |
Returns:
| Type | Description |
|---|---|
GeminiResponse
|
A GeminiResponse with the file contents or an error. |
Source code in src/nauyaca/server/handler.py
90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 | |
Example usage:
from pathlib import Path
from nauyaca.server.handler import StaticFileHandler
from nauyaca.protocol.request import GeminiRequest
# Create handler
handler = StaticFileHandler(
document_root=Path("/var/gemini/capsule"),
default_indices=["index.gmi", "index.gemini"],
enable_directory_listing=True,
max_file_size=100 * 1024 * 1024 # 100 MiB
)
# Handle a request
request = GeminiRequest.from_line("gemini://example.com/page.gmi\r\n")
response = handler.handle(request)
print(f"Status: {response.status}")
print(f"Meta: {response.meta}")
print(f"Body: {response.body[:100]}...")
Default Handler
The start_server() function automatically configures a StaticFileHandler for the document root. You don't need to create one manually unless you need custom behavior.
ErrorHandler¶
ErrorHandler
¶
Bases: RequestHandler
Handler that returns error responses.
Useful for handling 404 Not Found and other error cases.
Examples:
>>> handler = ErrorHandler(StatusCode.NOT_FOUND, "Page not found")
>>> response = handler.handle(request)
>>> response.status
51
Initialize the error handler.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
status
|
StatusCode
|
The error status code to return. |
required |
message
|
str
|
The error message (becomes the meta field). |
required |
Source code in src/nauyaca/server/handler.py
handle
¶
Return an error response.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
request
|
GeminiRequest
|
The incoming request (ignored). |
required |
Returns:
| Type | Description |
|---|---|
GeminiResponse
|
A GeminiResponse with the configured error. |
Source code in src/nauyaca/server/handler.py
Example usage:
from nauyaca.server.handler import ErrorHandler
from nauyaca.protocol.status import StatusCode
# Create 404 handler
not_found_handler = ErrorHandler(
status=StatusCode.NOT_FOUND,
message="Page not found"
)
# Create maintenance handler
maintenance_handler = ErrorHandler(
status=StatusCode.TEMPORARY_FAILURE,
message="Server maintenance - try again later"
)
Custom Handlers¶
Create custom handlers by subclassing RequestHandler:
from nauyaca.server.handler import RequestHandler
from nauyaca.protocol.request import GeminiRequest
from nauyaca.protocol.response import GeminiResponse
from nauyaca.protocol.status import StatusCode
class EchoHandler(RequestHandler):
"""Handler that echoes the request URL."""
def handle(self, request: GeminiRequest) -> GeminiResponse:
body = f"# Echo\n\nYou requested: {request.url}\n"
return GeminiResponse(
status=StatusCode.SUCCESS.value,
meta="text/gemini",
body=body
)
# Use with router
from nauyaca.server.router import Router
router = Router()
router.add_route("/echo", EchoHandler().handle)
See Also¶
- Configuration Reference - Complete TOML configuration guide
- CLI Reference - Command-line server management
- How-to: Configure Server - Server configuration guide
- How-to: Rate Limiting - Rate limiting setup
- How-to: Client Certificates - Certificate authentication
- Tutorial: Securing Your Server - Security best practices