Hypersonic

 view release on metacpan or  search on metacpan

lib/Hypersonic/Socket.pm  view on Meta::CPAN

      ->line('addr.sin_port = htons((uint16_t)port);')
      ->line('addr.sin_addr.s_addr = INADDR_ANY;')
      ->blank
      ->if('bind(fd, (struct sockaddr*)&addr, sizeof(addr)) < 0')
        ->line('int saved_errno = errno;')
        ->line('close(fd);')
        ->line('croak("bind(port=%d) failed: %s", (int)port, strerror(saved_errno));')
      ->endif
      ->blank
      ->if('listen(fd, SOMAXCONN) < 0')
        ->line('int saved_errno = errno;')
        ->line('close(fd);')
        ->line('croak("listen() failed: %s", strerror(saved_errno));')
      ->endif
      ->blank
      ->line('ST(0) = sv_2mortal(newSViv(fd));')
      ->xs_return('1')
      ->xs_end;

    # Event loop functions (create_event_loop, event_add, event_del, ev_poll)
    # have been moved to Hypersonic::Event::* backend modules

    # Generate http_accept
    $builder->xs_function('jit_http_accept')
      ->xs_preamble
      ->line('if (items != 1) croak("Usage: http_accept(listen_fd)");')
      ->line('IV listen_fd = SvIV(ST(0));')
      ->blank
      ->line('struct sockaddr_in client_addr;')
      ->line('socklen_t client_len = sizeof(client_addr);')
      ->blank
      ->line('int client_fd = accept((int)listen_fd, (struct sockaddr*)&client_addr, &client_len);')
      ->blank
      ->if('client_fd < 0')
        ->line('ST(0) = sv_2mortal(newSViv(-1));')
        ->line('XSRETURN(1);')
      ->endif
      ->blank
      ->line('int flags = fcntl(client_fd, F_GETFL, 0);')
      ->line('fcntl(client_fd, F_SETFL, flags | O_NONBLOCK);')
      ->blank
      ->line('ST(0) = sv_2mortal(newSViv(client_fd));')
      ->xs_return('1')
      ->xs_end;

    # Generate http_recv - zero-copy HTTP parsing
    $builder->xs_function('jit_http_recv')
      ->xs_preamble
      ->line('if (items != 1) croak("Usage: http_recv(fd)");')
      ->line('IV fd = SvIV(ST(0));')
      ->blank
      ->line('ssize_t len = recv((int)fd, recv_buf, RECV_BUF_SIZE - 1, 0);')
      ->blank
      ->if('len <= 0')
        ->line('ST(0) = &PL_sv_undef;')
        ->line('XSRETURN(1);')
      ->endif
      ->blank
      ->line('recv_buf[len] = \'\\0\';')
      ->blank
      ->comment('Quick parse - extract method, path, detect keep-alive')
      ->line('const char* p = recv_buf;')
      ->line('const char* end = recv_buf + len;')
      ->blank
      ->comment('Method')
      ->line('const char* method = p;')
      ->line('while (p < end && *p != \' \') p++;')
      ->line('int method_len = p - method;')
      ->if('p >= end')
        ->line('ST(0) = &PL_sv_undef;')
        ->line('XSRETURN(1);')
      ->endif
      ->line('p++;')
      ->blank
      ->comment('Path')
      ->line('const char* path = p;')
      ->line('while (p < end && *p != \' \' && *p != \'?\') p++;')
      ->line('int path_len = p - path;')
      ->if('p >= end')
        ->line('ST(0) = &PL_sv_undef;')
        ->line('XSRETURN(1);')
      ->endif
      ->blank
      ->comment('Skip to end of request line')
      ->line('while (p < end && *p != \'\\n\') p++;')
      ->if('p >= end')
        ->line('ST(0) = &PL_sv_undef;')
        ->line('XSRETURN(1);')
      ->endif
      ->line('p++;')
      ->blank
      ->comment('Check for Connection: close')
      ->line('int keep_alive = 1;')
      ->line('while (p < end) {')
      ->line('    if (*p == \'\\r\' || *p == \'\\n\') break;')
      ->line('    if (end - p > 17 && strncasecmp(p, "Connection: close", 17) == 0) {')
      ->line('        keep_alive = 0;')
      ->line('    }')
      ->line('    while (p < end && *p != \'\\n\') p++;')
      ->line('    if (p < end) p++;')
      ->line('}')
      ->blank
      ->comment('Skip blank line')
      ->line('if (p < end && *p == \'\\r\') p++;')
      ->line('if (p < end && *p == \'\\n\') p++;')
      ->blank
      ->comment('Body')
      ->line('const char* body = p;')
      ->line('int body_len = end - p;')
      ->blank
      ->comment('Build request array: [method, path, body, keep_alive, fd]')
      ->line('AV* req = newAV();')
      ->line('av_push(req, newSVpvn(method, method_len));')
      ->line('av_push(req, newSVpvn(path, path_len));')
      ->line('av_push(req, newSVpvn(body, body_len));')
      ->line('av_push(req, newSViv(keep_alive));')
      ->line('av_push(req, newSViv(fd));')
      ->blank
      ->line('ST(0) = sv_2mortal(newRV_noinc((SV*)req));')
      ->xs_return('1')
      ->xs_end;

    # Generate http_send - writev for zero-copy
    $builder->xs_function('jit_http_send')
      ->xs_preamble
      ->line('if (items < 2 || items > 3) croak("Usage: http_send(fd, body, [content_type])");')
      ->line('IV fd = SvIV(ST(0));')
      ->blank
      ->line('STRLEN body_len;')
      ->line('const char* body = SvPV(ST(1), body_len);')
      ->blank
      ->line('const char* content_type = "text/plain";')
      ->if('items == 3 && SvOK(ST(2))')
        ->line('STRLEN ct_len;')
        ->line('content_type = SvPV(ST(2), ct_len);')
      ->endif
      ->blank
      ->line('static __thread char header[512];')
      ->line('int hdr_len = snprintf(header, sizeof(header),')
      ->line('    "HTTP/1.1 200 OK\\r\\n"')
      ->line('    "Content-Type: %s\\r\\n"')
      ->line('    "Content-Length: %zu\\r\\n"')
      ->line('    "Connection: keep-alive\\r\\n\\r\\n",')
      ->line('    content_type, body_len);')
      ->blank
      ->line('struct iovec iov[2];')
      ->line('iov[0].iov_base = header;')
      ->line('iov[0].iov_len = (size_t)hdr_len;')
      ->line('iov[1].iov_base = (void*)body;')
      ->line('iov[1].iov_len = body_len;')
      ->blank
      ->line('ssize_t sent = writev((int)fd, iov, 2);')
      ->line('ST(0) = sv_2mortal(newSViv((IV)sent));')
      ->xs_return('1')
      ->xs_end;

    # Generate http_send_404
    $builder->xs_function('jit_http_send_404')
      ->xs_preamble
      ->line('if (items != 1) croak("Usage: http_send_404(fd)");')
      ->line('IV fd = SvIV(ST(0));')
      ->blank
      ->line('static const char resp[] =')
      ->line('    "HTTP/1.1 404 Not Found\\r\\n"')
      ->line('    "Content-Type: text/plain\\r\\n"')
      ->line('    "Content-Length: 9\\r\\n"')
      ->line('    "Connection: close\\r\\n\\r\\n"')
      ->line('    "Not Found";')
      ->blank
      ->line('ssize_t sent = send((int)fd, resp, sizeof(resp) - 1, 0);')
      ->line('ST(0) = sv_2mortal(newSViv((IV)sent));')
      ->xs_return('1')
      ->xs_end;

    # Generate close_fd
    $builder->xs_function('jit_close_fd')
      ->xs_preamble
      ->line('if (items != 1) croak("Usage: close_fd(fd)");')
      ->line('IV fd = SvIV(ST(0));')
      ->line('int result = close((int)fd);')
      ->line('ST(0) = sv_2mortal(newSViv(result));')
      ->xs_return('1')
      ->xs_end;

    # Compile via XS::JIT (socket-only functions - event loop is in backends)
    XS::JIT->compile(
        code      => $builder->code,
        name      => $module_name,
        cache_dir => $cache_dir,
        # Windows needs to link against Winsock for the JIT-compiled .so
        # to resolve socket()/recv()/send()/etc.
        ($^O eq 'MSWin32' ? (extra_ldflags => '-lws2_32') : ()),
        functions => {
            'Hypersonic::Socket::create_listen_socket' => { source => 'jit_create_listen_socket', is_xs_native => 1 },
            'Hypersonic::Socket::http_accept'          => { source => 'jit_http_accept', is_xs_native => 1 },
            'Hypersonic::Socket::http_recv'            => { source => 'jit_http_recv', is_xs_native => 1 },
            'Hypersonic::Socket::http_send'            => { source => 'jit_http_send', is_xs_native => 1 },
            'Hypersonic::Socket::http_send_404'        => { source => 'jit_http_send_404', is_xs_native => 1 },
            'Hypersonic::Socket::close_fd'             => { source => 'jit_close_fd', is_xs_native => 1 },
        },
    );

    $COMPILED = 1;

lib/Hypersonic/Socket.pm  view on Meta::CPAN

=head1 CLASS METHODS

=head2 compile_socket_ops

    Hypersonic::Socket->compile_socket_ops(
        cache_dir => '_socket_cache',
    );

Compile the JIT socket operations. Called automatically on module import.

Options:

=over 4

=item cache_dir

Directory for caching compiled XS code. Default: C<_hypersonic_socket_cache>

=back

=head1 SOCKET FUNCTIONS

All functions are JIT-compiled to XS for maximum performance.

=head2 create_listen_socket

    my $fd = Hypersonic::Socket::create_listen_socket($port);

Create a non-blocking TCP listening socket.

Features:

=over 4

=item * SO_REUSEADDR - Allow quick restart

=item * SO_REUSEPORT - Enable kernel load balancing (Linux)

=item * O_NONBLOCK - Non-blocking I/O

=item * Bound to all interfaces (INADDR_ANY)

=back

Returns the file descriptor, or -1 on error.

=head2 http_accept

    my $client_fd = Hypersonic::Socket::http_accept($listen_fd);

Accept a new connection on the listen socket.

Returns the client file descriptor, or -1 on error.

=head2 http_recv

    my $request = Hypersonic::Socket::http_recv($fd);

Receive and quick-parse an HTTP request.

Returns an arrayref: C<[method, path, body, keep_alive, fd]>

=over 4

=item * C<method> - HTTP method (GET, POST, etc.)

=item * C<path> - Request path

=item * C<body> - Request body (for POST, PUT, etc.)

=item * C<keep_alive> - 1 if Connection: keep-alive, 0 otherwise

=item * C<fd> - Client file descriptor

=back

Uses zero-copy parsing for maximum speed.

=head2 http_send

    Hypersonic::Socket::http_send($fd, $body, $content_type);

Send an HTTP 200 response with the given body.

Uses C<writev()> for zero-copy I/O when possible.

=head2 http_send_404

    Hypersonic::Socket::http_send_404($fd);

Send a pre-computed 404 Not Found response.

=head2 close_fd

    Hypersonic::Socket::close_fd($fd);

Close a file descriptor.

=head1 PERFORMANCE

All functions achieve approximately 7M operations/second in benchmarks
due to:

=over 4

=item * JIT compilation to native XS

=item * Zero-copy parsing

=item * Minimal memory allocations

=back

=head1 INTERNAL USE

These functions are typically called by the main C<Hypersonic> module's
JIT-compiled event loop. The generated C code calls these functions
directly for socket operations.

You only need to use this module directly if:

=over 4

=item * Extending Hypersonic with custom protocols

=item * Testing or debugging socket operations

=back

=head1 SEE ALSO



( run in 1.531 second using v1.01-cache-2.11-cpan-f56aa216473 )