Hypersonic
view release on metacpan or search on metacpan
lib/Hypersonic/Protocol/HTTP2.pm view on Meta::CPAN
->line(' { (uint8_t*)"content-type", (uint8_t*)"text/plain", 12, 10, NGHTTP2_NV_FLAG_NONE },')
->line(' { (uint8_t*)"content-length", (uint8_t*)"9", 14, 1, NGHTTP2_NV_FLAG_NONE },')
->line(' };')
->line(' ')
->line(' nghttp2_data_provider data_prd;')
->line(' data_prd.source.ptr = (void*)body;')
->line(' data_prd.read_callback = h2_data_source_read_cb;')
->line(' ')
->line(' nghttp2_submit_response(session, stream_id, hdrs, 3, &data_prd);')
->line(' nghttp2_session_send(session);')
->line('}')
->blank;
return $builder;
}
# Generate HTTP/2 input processing
sub gen_input_processor {
my ($class, $builder) = @_;
$builder->comment('HTTP/2: Process incoming data from socket')
->line('static int h2_process_input(H2Connection* conn, const uint8_t* data, size_t len) {')
->line(' ssize_t rv = nghttp2_session_mem_recv(conn->session, data, len);')
->line(' if (rv < 0) {')
->line(' return -1; /* Protocol error */')
->line(' }')
->line(' ')
->line(' /* Send any pending frames */')
->line(' rv = nghttp2_session_send(conn->session);')
->line(' if (rv != 0) {')
->line(' return -1;')
->line(' }')
->line(' ')
->line(' return 0;')
->line('}')
->blank;
return $builder;
}
# Generate HTTP/2 connection detection (for h2c upgrade or ALPN)
sub gen_connection_preface_check {
my ($class, $builder) = @_;
$builder->comment('HTTP/2: Check for connection preface (h2c)')
->line('static const char H2_PREFACE[] = "PRI * HTTP/2.0\\r\\n\\r\\nSM\\r\\n\\r\\n";')
->line('#define H2_PREFACE_LEN 24')
->blank
->line('static int is_h2_preface(const char* data, size_t len) {')
->line(' return len >= H2_PREFACE_LEN && memcmp(data, H2_PREFACE, H2_PREFACE_LEN) == 0;')
->line('}')
->blank;
return $builder;
}
# ============================================================
# HTTP/2 Streaming Support (Phase 3)
# ============================================================
# Generate streaming headers without END_STREAM
sub gen_stream_headers {
my ($class, $builder) = @_;
$builder->comment('HTTP/2 Streaming: Send HEADERS without END_STREAM (allows more DATA)')
->line('static int h2_stream_headers(nghttp2_session* session, int32_t stream_id,')
->line(' int status, const char* content_type) {')
->line(' char status_str[4];')
->line(' snprintf(status_str, sizeof(status_str), "%d", status);')
->line(' ')
->line(' nghttp2_nv hdrs[] = {')
->line(' { (uint8_t*)":status", (uint8_t*)status_str, 7, strlen(status_str), NGHTTP2_NV_FLAG_NONE },')
->line(' { (uint8_t*)"content-type", (uint8_t*)content_type, 12, strlen(content_type), NGHTTP2_NV_FLAG_NONE },')
->line(' };')
->line(' ')
->line(' /* Submit headers WITHOUT END_STREAM - more DATA frames to come */')
->line(' int rv = nghttp2_submit_headers(session, NGHTTP2_FLAG_END_HEADERS,')
->line(' stream_id, NULL, hdrs, 2, NULL);')
->line(' if (rv < 0) return rv;')
->line(' ')
->line(' return nghttp2_session_send(session);')
->line('}')
->blank;
return $builder;
}
# Generate streaming data chunk sender
sub gen_stream_data {
my ($class, $builder) = @_;
$builder->comment('HTTP/2 Streaming: Chunk provider for streaming DATA frames')
->line('typedef struct {')
->line(' const uint8_t* data;')
->line(' size_t length;')
->line(' size_t pos;')
->line('} H2ChunkProvider;')
->blank
->line('static ssize_t h2_chunk_read_cb(nghttp2_session* session,')
->line(' int32_t stream_id,')
->line(' uint8_t* buf, size_t length,')
->line(' uint32_t* data_flags,')
->line(' nghttp2_data_source* source,')
->line(' void* user_data) {')
->line(' (void)session; (void)stream_id; (void)user_data;')
->line(' H2ChunkProvider* provider = (H2ChunkProvider*)source->ptr;')
->line(' size_t remaining = provider->length - provider->pos;')
->line(' size_t to_copy = remaining < length ? remaining : length;')
->line(' ')
->line(' memcpy(buf, provider->data + provider->pos, to_copy);')
->line(' provider->pos += to_copy;')
->line(' ')
->line(' if (provider->pos >= provider->length) {')
->line(' /* This DATA frame is complete (but not end of stream) */')
->line(' *data_flags |= NGHTTP2_DATA_FLAG_EOF;')
->line(' }')
->line(' ')
->line(' return (ssize_t)to_copy;')
->line('}')
->blank
->comment('HTTP/2 Streaming: Send a single DATA frame (not final)')
->line('static int h2_stream_data(nghttp2_session* session, int32_t stream_id,')
->line(' const uint8_t* data, size_t len) {')
->line(' /* Allocate provider on stack - nghttp2 copies data synchronously */')
->line(' H2ChunkProvider provider = { data, len, 0 };')
->line(' ')
->line(' nghttp2_data_provider data_prd;')
->line(' data_prd.source.ptr = &provider;')
->line(' data_prd.read_callback = h2_chunk_read_cb;')
->line(' ')
->line(' /* Submit DATA without END_STREAM */')
->line(' int rv = nghttp2_submit_data(session, NGHTTP2_FLAG_NONE,')
->line(' stream_id, &data_prd);')
->line(' if (rv < 0) return rv;')
->line(' ')
->line(' return nghttp2_session_send(session);')
->line('}')
->blank;
return $builder;
}
# Generate stream end (empty DATA with END_STREAM)
sub gen_stream_end {
my ($class, $builder) = @_;
$builder->comment('HTTP/2 Streaming: Send empty DATA with END_STREAM flag')
->line('static int h2_stream_end(nghttp2_session* session, int32_t stream_id) {')
->line(' /* Submit empty data with END_STREAM flag */')
->line(' nghttp2_data_provider data_prd;')
->line(' data_prd.source.ptr = NULL;')
->line(' data_prd.read_callback = NULL;')
->line(' ')
->line(' int rv = nghttp2_submit_data(session, NGHTTP2_FLAG_END_STREAM,')
->line(' stream_id, NULL);')
->line(' if (rv < 0) return rv;')
->line(' ')
->line(' return nghttp2_session_send(session);')
->line('}')
->blank;
return $builder;
}
# Generate flow control helpers
sub gen_flow_control {
my ($class, $builder) = @_;
$builder->comment('HTTP/2 Streaming: Check flow control window')
->line('static int h2_can_send(nghttp2_session* session, int32_t stream_id, size_t len) {')
->line(' /* Check connection-level window */')
->line(' int32_t conn_window = nghttp2_session_get_remote_window_size(session);')
->line(' if (conn_window < (int32_t)len) return 0;')
->line(' ')
->line(' /* Check stream-level window */')
->line(' int32_t stream_window = nghttp2_session_get_stream_remote_window_size(')
->line(' session, stream_id);')
->line(' if (stream_window < (int32_t)len) return 0;')
->line(' ')
->line(' return 1;')
->line('}')
->blank
->comment('HTTP/2 Streaming: Get available window size')
->line('static int32_t h2_window_size(nghttp2_session* session, int32_t stream_id) {')
->line(' int32_t conn_window = nghttp2_session_get_remote_window_size(session);')
->line(' int32_t stream_window = nghttp2_session_get_stream_remote_window_size(')
->line(' session, stream_id);')
->line(' return conn_window < stream_window ? conn_window : stream_window;')
->line('}')
->blank;
return $builder;
}
# Generate XS wrappers for HTTP/2 streaming from Perl
sub gen_stream_xs_wrappers {
my ($class, $builder) = @_;
$builder->comment('XS wrappers for HTTP/2 streaming from Perl');
# h2_stream_start(session_ptr, stream_id, status, content_type)
$builder->xs_function('hypersonic_h2_stream_start')
->xs_preamble
->check_items(4, 4, 'session_ptr, stream_id, status, content_type')
->line('nghttp2_session* session = (nghttp2_session*)SvUV(ST(0));')
->line('int32_t stream_id = (int32_t)SvIV(ST(1));')
->line('int status = (int)SvIV(ST(2));')
->line('STRLEN ct_len;')
->line('const char* content_type = SvPV(ST(3), ct_len);')
->line('int rv = h2_stream_headers(session, stream_id, status, content_type);')
->line('XSRETURN_IV(rv);')
->xs_end
->blank;
# h2_stream_write(session_ptr, stream_id, data)
$builder->xs_function('hypersonic_h2_stream_write')
->xs_preamble
->check_items(3, 3, 'session_ptr, stream_id, data')
->line('nghttp2_session* session = (nghttp2_session*)SvUV(ST(0));')
->line('int32_t stream_id = (int32_t)SvIV(ST(1));')
->line('STRLEN data_len;')
->line('const char* data = SvPV(ST(2), data_len);')
->line('int rv = h2_stream_data(session, stream_id, (const uint8_t*)data, data_len);')
->line('XSRETURN_IV(rv);')
->xs_end
->blank;
# h2_stream_end(session_ptr, stream_id)
$builder->xs_function('hypersonic_h2_stream_end')
->xs_preamble
->check_items(2, 2, 'session_ptr, stream_id')
->line('nghttp2_session* session = (nghttp2_session*)SvUV(ST(0));')
->line('int32_t stream_id = (int32_t)SvIV(ST(1));')
->line('int rv = h2_stream_end(session, stream_id);')
->line('XSRETURN_IV(rv);')
->xs_end
->blank;
return $builder;
}
# Generate all HTTP/2 streaming code
sub generate_streaming {
my ($class, $builder, $opts) = @_;
$class->gen_stream_headers($builder);
$class->gen_stream_data($builder);
$class->gen_stream_end($builder);
$class->gen_flow_control($builder);
$class->gen_stream_xs_wrappers($builder);
return $builder;
}
1;
__END__
=head1 NAME
Hypersonic::Protocol::HTTP2 - JIT code generation for HTTP/2 protocol
=head1 SYNOPSIS
use Hypersonic::Protocol::HTTP2;
# Check if nghttp2 is available
if (Hypersonic::Protocol::HTTP2->check_nghttp2()) {
my $cflags = Hypersonic::Protocol::HTTP2->get_extra_cflags();
my $ldflags = Hypersonic::Protocol::HTTP2->get_extra_ldflags();
}
# Generate HTTP/2 C code
Hypersonic::Protocol::HTTP2->gen_includes($builder);
Hypersonic::Protocol::HTTP2->gen_callbacks($builder);
=head1 DESCRIPTION
This module provides JIT compile-time code generation for HTTP/2 protocol
support using the nghttp2 library. All methods generate C code using
XS::JIT::Builder - there is zero runtime overhead.
=head2 HTTP/2 Features
=over 4
=item * Binary framing via nghttp2
=item * HPACK header compression
=item * Stream multiplexing
=item * Server push (future)
=back
=head1 AUTHOR
Hypersonic Contributors
=cut
( run in 0.700 second using v1.01-cache-2.11-cpan-140bd7fdf52 )