Skip to content

Latest commit

 

History

History
256 lines (181 loc) · 7.11 KB

File metadata and controls

256 lines (181 loc) · 7.11 KB

Plack::Handler::Netty - PSGI Server Example

A complete working example demonstrating how to run PSGI applications on PerlOnJava using Netty as the HTTP server backend.

Overview

Plack::Handler::Netty is a PSGI server handler that bridges PSGI apps to Java's Netty HTTP server. It provides:

  • General PSGI support - Any PSGI-compatible app can be adapted
  • High-performance async I/O - Netty handles 10k+ concurrent connections on a single thread
  • Single-runtime PSGI model - The handler deliberately confines one app runtime even though PerlOnJava applications can create explicit ithreads
  • Standard PSGI 1.1 - Full compliance with streaming and delayed response support

Architecture

Client → Netty (async I/O, single thread) → PlackHandlerNetty.java
    ↓
    Plack::Handler::Netty.pm (Perl facade)
    ↓
    PSGI Application ($app->(\%env))
    ↓
Response ← [status, headers, body] ← Netty

Implementation Location:

  • Java Backend: src/main/java/org/perlonjava/runtime/perlmodule/PlackHandlerNetty.java
  • Perl Module: src/main/perl/lib/Plack/Handler/Netty.pm
  • Bundled in: PerlOnJava JAR (target/perlonjava-*.jar)

Quick Start

1. Build PerlOnJava

From the project root:

make

2. Run the Example

./jperl examples/http_server_plack/test.pl

The server will start on http://localhost:5000.

3. Test the Server

In another terminal:

# Homepage
curl http://localhost:5000/

# Route with parameter
curl http://localhost:5000/hello/World

# JSON API
curl http://localhost:5000/json

# View PSGI environment
curl http://localhost:5000/env

# POST request (echo)
curl -X POST http://localhost:5000/echo -d 'test data'

# 404 error
curl http://localhost:5000/notfound

Example Application

The test.pl script contains a complete PSGI application with:

  • / - Homepage with HTML response
  • /hello/{name} - Route with path parameter extraction
  • /json - JSON API endpoint
  • /env - PSGI environment hash dump (debugging)
  • /echo (POST) - Echoes back POST body
  • 404 handling - Returns 404 for unknown routes

Usage in Your Own Apps

use Plack::Handler::Netty;

my $app = sub {
    my ($env) = @_;
    return [
        200,
        ['Content-Type' => 'text/plain'],
        ['Hello from Netty!']
    ];
};

my $handler = Plack::Handler::Netty->new(
    host => '0.0.0.0',
    port => 5000,
);

$handler->run($app);

PSGI Environment

The handler provides all standard PSGI v1.1 environment keys:

  • REQUEST_METHOD, PATH_INFO, QUERY_STRING
  • SERVER_NAME, SERVER_PORT, SERVER_PROTOCOL
  • CONTENT_LENGTH, CONTENT_TYPE
  • HTTP_* headers (normalized to uppercase with underscores)
  • psgi.version, psgi.url_scheme, psgi.input, psgi.errors
  • psgi.multithread (false by default; true with a runtime pool), psgi.multiprocess (false)
  • psgi.run_once (false), psgi.nonblocking (true), psgi.streaming (true)

Configuration Options

Plack::Handler::Netty->new(%options) accepts:

Option Default Description
host 0.0.0.0 Bind address
port 5000 Listen port
backlog 128 TCP backlog queue size
keepalive 30 HTTP keep-alive timeout (seconds)
max_request_size 10485760 Max request body size (bytes, ~10MB)
ssl 0 Enable HTTPS/TLS
ssl_cert - SSL certificate path (PEM format)
ssl_key - SSL private key path (PEM format)
ssl_ca - CA certificate for client verification (optional)
ssl_protocols TLSv1.2, TLSv1.3 Allowed TLS versions (arrayref, optional)
ssl_ciphers - Cipher suites (colon-separated, optional)

HTTPS Support

The handler supports SSL/TLS via Netty's SslHandler:

my $handler = Plack::Handler::Netty->new(
    port     => 443,
    ssl      => 1,
    ssl_cert => '/path/to/cert.pem',
    ssl_key  => '/path/to/key.pem',
);
$handler->run($app);

Generating Test Certificates

For testing (self-signed certificate):

cd examples/http_server_plack/certs
./generate_test_cert.sh

Use the generated server-cert.pem and server-key.pem paths as the ssl_cert and ssl_key options in your own PSGI application. When testing a self-signed endpoint with curl, pass -k to skip certificate verification.

Production Certificates

For production, use Let's Encrypt:

certbot certonly --standalone -d yourdomain.com

# Certificates will be at:
# /etc/letsencrypt/live/yourdomain.com/fullchain.pem
# /etc/letsencrypt/live/yourdomain.com/privkey.pem

Do NOT use self-signed certificates in production.

Using with Your Own PSGI Apps

Use your own PSGI app coderef with Plack::Handler::Netty->run($app) as shown in the generic example above.

Framework-specific experiments are kept in dev/sandbox/http_server/ until they are stable.

Concurrency Model

Single-runtime async I/O - Uses a Netty event loop to handle concurrent connections without concurrent callbacks into the captured PSGI application:

Good for: I/O-bound apps (databases, APIs, file serving)
Handles: Thousands of concurrent connections efficiently
⚠️ Limitation: CPU-bound request handlers may block other requests

PerlOnJava supports isolated Perl ithreads, but that capability alone does not make a single captured PSGI runtime concurrently callable. The default handler therefore advertises psgi.multithread => \0. Set JPERL_RUNTIME_POOL_SIZE=N to prebuild N independent application snapshots; pooled requests then advertise psgi.multithread => \1.

Limitations

  • Pooling is explicit - The correctness-first default is one application runtime. Size the opt-in pool for the application's memory and latency needs.

Performance

Expected performance for "Hello World" apps: 30,000+ requests/sec

Benchmark results (Apple Silicon):

  • Hello World: 32,980 req/sec @ 100 concurrent connections
  • JSON API: 22,461 req/sec with dynamic content
  • Streaming: 16,312 req/sec for streaming responses

See PERFORMANCE.md for detailed benchmarks.

Files in This Directory

Test Scripts

  • test.pl - Complete working PSGI example with multiple test endpoints
  • test_streaming.pl - PSGI streaming response examples

Documentation

  • README.md - This file (main documentation)
  • PERFORMANCE.md - Detailed performance benchmarks

Supporting Files

  • certs/ - Test SSL certificates and generation scripts

Real implementation is bundled in the JAR:

  • Java: src/main/java/org/perlonjava/runtime/perlmodule/PlackHandlerNetty.java
  • Perl: src/main/perl/lib/Plack/Handler/Netty.pm

Troubleshooting

"Can't locate Plack/Handler/Netty.pm"

Make sure you're running with the built JAR:

./jperl your_app.pl       # Good - uses bundled version
java -cp ... your_app.pl   # May not find module

Port already in use

Change the port in your code:

my $handler = Plack::Handler::Netty->new(port => 9000);
$handler->run($app);

Contributing

Found a bug or want to improve this? Please submit an issue or PR to PerlOnJava.

License

Same as PerlOnJava.