Hypersonic

 view release on metacpan or  search on metacpan

lib/Hypersonic.pm  view on Meta::CPAN

            push @ws_handlers, $route->{handler};
            push @ws_paths, $route->{path};
            # Check for Room usage in route options
            if ($route->{opts}{rooms}) {
                $self->{route_analysis}{needs_websocket_rooms} = 1;
            }
        }
        $self->{_websocket_handlers} = \@ws_handlers;
        $self->{_websocket_paths} = \@ws_paths;
        $self->{route_analysis}{needs_websocket} = 1;
        # Handler is needed if we have websocket routes
        $self->{route_analysis}{needs_websocket_handler} = 1;
        # WebSocket uses Stream for connection handling
        $self->{route_analysis}{needs_streaming} = 1;
    }

    # JIT: Explicit opt-in for Room support (can also be set in new())
    if ($self->{websocket_rooms}) {
        $self->{route_analysis}{needs_websocket_rooms} = 1;
    }

    # JIT: Build per-route middleware arrays (only if route middleware is present)
    my $analysis = $self->{route_analysis};
    if ($analysis->{has_route_middleware}) {
        my @route_before_mw;
        my @route_after_mw;
        for my $route (@{$self->{routes}}) {
            next unless $route->{dynamic};
            # Store arrays of middleware handlers per route (by handler_idx)
            push @route_before_mw, $route->{before};
            push @route_after_mw, $route->{after};
        }
        $self->{_route_before_mw} = \@route_before_mw;
        $self->{_route_after_mw} = \@route_after_mw;
    }

    # JIT: Store Perl-only middleware for runtime call_sv dispatch
    # Builder middleware is handled at compile time (inline C), not runtime
    if ($analysis->{has_global_before} || $analysis->{has_global_after}) {
        $self->{_perl_before_mw} = $analysis->{perl_before};
        $self->{_perl_after_mw}  = $analysis->{perl_after};
    }

    # Generate C code with pure C event loop
    my $c_code = $self->_generate_server_code(\@full_responses);

    # Compile via XS::JIT
    #
    # Derive a STABLE module id from a hash of the generated C source
    # (plus this dist's $VERSION and the target perl's archname/version
    # so a cache built for one perl is never loaded into another).
    #
    # Pre-0.18 we used 'Hypersonic::_Server_' . int(rand(100000)) which
    # gave a different module name on every fresh perl process; because
    # XS::JIT's on-disk cache lives at
    #   <cache_dir>/lib/auto/<safe_name>/<safe_name>.<dlext>
    # (see XS-JIT/lib/XS/JIT/xs_jit.c xs_jit_cache_path()), a different
    # $name on every run forced a full gcc/cc re-invocation EVERY time
    # the test suite or a user re-ran their server. On slow CPAN smoker
    # boxes that gcc invocation takes 30-60+ seconds per server, which
    # is what caused the SIGKILL cascade in CPAN tester reports for
    # 0.17 (t/0035-e2e-streaming.t, t/2012..t/2017, t/2102).
    #
    # Using a content hash means: identical route+option configurations
    # produce the same module name -> warm cache hit -> dlopen() of a
    # 100ms .so instead of a 30s gcc rebuild. The random fallback id is
    # retained for the degenerate case where Digest::MD5 isn't available.
    my $module_id;
    {
        my $hash_input = join("\0",
            $c_code,
            $VERSION,
            (defined $Config::Config{archname}
                ? $Config::Config{archname} : ''),
            $] || '',
        );
        if (eval { require Digest::MD5; 1 }) {
            # 16 hex chars (64 bits) is plenty of namespace for one
            # process and short enough to keep the on-disk file names
            # readable. md5_hex is core since perl 5.7.3.
            $module_id = substr(Digest::MD5::md5_hex($hash_input), 0, 16);
        } else {
            # No Digest::MD5? Fall back to the legacy random id. Cache
            # won't be reused across runs but at least nothing breaks.
            $module_id = $self->{id};
        }
    }
    my $module_name = 'Hypersonic::_Server_' . $module_id;
    
    # Build compile options - add TLS flags if enabled
    my %functions = (
        "${module_name}::run_event_loop" => {
            source       => 'hypersonic_run_event_loop',
            is_xs_native => 1,
        },
        "${module_name}::dispatch" => {
            source       => 'hypersonic_dispatch',
            is_xs_native => 1,
        },
    );

    # Add Stream and SSE XS functions if streaming is enabled
    if ($self->{route_analysis}{needs_streaming}) {
        %functions = (%functions, %{Hypersonic::Stream->get_xs_functions()});
        %functions = (%functions, %{Hypersonic::SSE->get_xs_functions()});
    }

    # Add WebSocket XS functions if WebSocket routes are registered
    if ($self->{route_analysis}{needs_websocket}) {
        require Hypersonic::WebSocket;
        %functions = (%functions, %{Hypersonic::WebSocket->get_xs_functions()});
    }

    # Add WebSocket Handler XS functions
    if ($self->{route_analysis}{needs_websocket_handler}) {
        require Hypersonic::WebSocket::Handler;
        %functions = (%functions, %{Hypersonic::WebSocket::Handler->get_xs_functions()});
    }

    # Add WebSocket Room XS functions
    if ($self->{route_analysis}{needs_websocket_rooms}) {



( run in 1.123 second using v1.01-cache-2.11-cpan-9581c071862 )