Skip to content

Latest commit

 

History

History
252 lines (176 loc) · 6.67 KB

File metadata and controls

252 lines (176 loc) · 6.67 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-threaded model - Compatible with PerlOnJava's no-threads/no-fork constraints
  • 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:

./gradlew shadowJar  # or: mvn package

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), 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

Then run the HTTPS test server:

./jperl examples/http_server_plack/test_https.pl
curl -k https://localhost:8443/  # -k skips cert 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-threaded async I/O - Uses Netty's event loop (NioEventLoopGroup(1)) to handle concurrent connections without threads/fork:

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

This design avoids PerlOnJava's thread-safety constraints while still providing excellent performance for typical web applications.

Limitations

  • Single-threaded - CPU-intensive handlers block other requests

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.