Hypersonic

 view release on metacpan or  search on metacpan

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

    $class->gen_xs_headers($builder);
    $class->gen_xs_content_type($builder);
    $class->gen_xs_write($builder);
    $class->gen_xs_end($builder);
    $class->gen_xs_abort($builder);
    
    return $builder;
}

sub gen_stream_registry {
    my ($class, $builder, $max) = @_;
    
    $builder->comment('Stream registry - O(1) lookup by fd')
      ->line('#define STREAM_MAX ' . $max)
      ->line('#define STREAM_STATE_INIT     0')
      ->line('#define STREAM_STATE_STARTED  1')
      ->line('#define STREAM_STATE_FINISHED 2')
      ->line('#define STREAM_STATE_ABORTED  3')
      ->blank
      ->line('typedef struct {')
      ->line('    int state;')
      ->line('    int chunks_sent;')
      ->line('    int http2;')
      ->line('    int status;')
      ->line('    char content_type[128];')
      ->line('    char extra_headers[512];')  # For Cache-Control, X-Accel-Buffering, etc.
      ->line('} StreamState;')
      ->blank
      ->line('static StreamState stream_registry[STREAM_MAX];')
      ->blank;
}

sub gen_status_text {
    my ($class, $builder) = @_;
    
    $builder->line('static const char* stream_status_text(int status) {')
      ->line('    switch(status) {')
      ->line('        case 200: return "OK";')
      ->line('        case 201: return "Created";')
      ->line('        case 204: return "No Content";')
      ->line('        case 400: return "Bad Request";')
      ->line('        case 404: return "Not Found";')
      ->line('        case 500: return "Internal Server Error";')
      ->line('        default: return "OK";')
      ->line('    }')
      ->line('}')
      ->blank;
}

sub gen_stream_start_c {
    my ($class, $builder) = @_;
    
    $builder->line('static void stream_start_http1(int fd) {')
      ->line('    StreamState* s = &stream_registry[fd];')
      ->line('    char headers[2048];')
      ->line('    int len = snprintf(headers, sizeof(headers),')
      ->line('        "HTTP/1.1 %d %s\\r\\n"')
      ->line('        "Content-Type: %s\\r\\n"')
      ->line('        "%s"')  # Extra headers (Cache-Control, etc.)
      ->line('        "Transfer-Encoding: chunked\\r\\n"')
      ->line('        "Connection: keep-alive\\r\\n\\r\\n",')
      ->line('        s->status, stream_status_text(s->status), s->content_type, s->extra_headers);')
      ->line('    send(fd, headers, len, 0);')
      ->line('    s->state = STREAM_STATE_STARTED;')
      ->line('    s->chunks_sent = 0;')
      ->line('}')
      ->blank;
}

sub gen_stream_write_chunk_c {
    my ($class, $builder) = @_;
    
    $builder->line('static void stream_write_chunk_http1(int fd, const char* data, size_t len) {')
      ->line('    if (len == 0) return;')
      ->line('    char size_line[32];')
      ->line('    int header_len = snprintf(size_line, sizeof(size_line), "%zx\\r\\n", len);')
      ->raw(<<'C')
#ifdef _WIN32
    /* No writev on Windows; three send() calls instead. The chunk
     * size line is tiny and the trailing CRLF is 2 bytes so the
     * extra syscalls are negligible. */
    send(fd, size_line, header_len, 0);
    send(fd, data, len, 0);
    send(fd, "\r\n", 2, 0);
#else
    struct iovec iov[3];
    iov[0].iov_base = size_line;
    iov[0].iov_len = header_len;
    iov[1].iov_base = (void*)data;
    iov[1].iov_len = len;
    iov[2].iov_base = "\r\n";
    iov[2].iov_len = 2;
    writev(fd, iov, 3);
#endif
C
      ->line('    stream_registry[fd].chunks_sent++;')
      ->line('}')
      ->blank;
}

sub gen_stream_end_c {
    my ($class, $builder) = @_;
    
    $builder->line('static void stream_end_http1(int fd) {')
      ->line('    send(fd, "0\\r\\n\\r\\n", 5, 0);')
      ->line('    stream_registry[fd].state = STREAM_STATE_FINISHED;')
      ->line('}')
      ->blank;
}

sub gen_stream_reset_c {
    my ($class, $builder) = @_;
    
    $builder->line('static void stream_reset(int fd) {')
      ->line('    memset(&stream_registry[fd], 0, sizeof(StreamState));')
      ->line('    stream_registry[fd].status = 200;')
      ->line('    strcpy(stream_registry[fd].content_type, "text/plain");')
      ->line('}')
      ->blank;
}



( run in 0.604 second using v1.01-cache-2.11-cpan-5a3173703d6 )