/*
 * This file was generated automatically by ExtUtils::ParseXS version 3.51 from the
 * contents of nghttp2.xs. Do not edit this file, edit nghttp2.xs instead.
 *
 *    ANY CHANGES MADE HERE WILL BE LOST!
 *
 */

#line 1 "nghttp2.xs"
#define PERL_NO_GET_CONTEXT
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"

#include <nghttp2/nghttp2.h>
#include <string.h>

/*
 * Net::HTTP2::nghttp2 - Perl XS bindings for nghttp2
 *
 * This module provides server-side HTTP/2 support via nghttp2.
 */

/* Per-stream data provider state for streaming responses */
typedef struct {
    SV *callback;           /* Perl callback to produce data */
    SV *user_data;          /* User data for callback */
    int32_t stream_id;      /* Stream ID */
    int eof;                /* End of data flag */
    int deferred;           /* Currently deferred */
} nghttp2_perl_data_provider;

/* Session wrapper structure */
typedef struct {
    nghttp2_session *session;
    SV *user_data;
    SV *cb_on_begin_headers;
    SV *cb_on_header;
    SV *cb_on_frame_recv;
    SV *cb_on_data_chunk_recv;
    SV *cb_on_stream_close;
    SV *cb_send;
    SV *cb_data_source_read;
    /* Output buffer for mem_send */
    char *send_buf;
    size_t send_buf_len;
    size_t send_buf_cap;
    /* Data providers for active streams (simple linear array) */
    nghttp2_perl_data_provider **data_providers;
    int data_providers_count;
    int data_providers_cap;
} nghttp2_perl_session;

/* Forward declarations */
static ssize_t perl_send_callback(nghttp2_session *session,
                                  const uint8_t *data, size_t length,
                                  int flags, void *user_data);
static int perl_on_begin_headers_callback(nghttp2_session *session,
                                          const nghttp2_frame *frame,
                                          void *user_data);
static int perl_on_header_callback(nghttp2_session *session,
                                   const nghttp2_frame *frame,
                                   const uint8_t *name, size_t namelen,
                                   const uint8_t *value, size_t valuelen,
                                   uint8_t flags, void *user_data);
static int perl_on_frame_recv_callback(nghttp2_session *session,
                                       const nghttp2_frame *frame,
                                       void *user_data);
static int perl_on_data_chunk_recv_callback(nghttp2_session *session,
                                            uint8_t flags, int32_t stream_id,
                                            const uint8_t *data, size_t len,
                                            void *user_data);
static int perl_on_stream_close_callback(nghttp2_session *session,
                                         int32_t stream_id,
                                         uint32_t error_code,
                                         void *user_data);

/* Data provider helper functions */
static nghttp2_perl_data_provider *find_data_provider(nghttp2_perl_session *ps, int32_t stream_id) {
    int i;
    for (i = 0; i < ps->data_providers_count; i++) {
        if (ps->data_providers[i] && ps->data_providers[i]->stream_id == stream_id) {
            return ps->data_providers[i];
        }
    }
    return NULL;
}

static void add_data_provider(nghttp2_perl_session *ps, nghttp2_perl_data_provider *dp) {
    int i;
    /* Find empty slot */
    for (i = 0; i < ps->data_providers_count; i++) {
        if (!ps->data_providers[i]) {
            ps->data_providers[i] = dp;
            return;
        }
    }
    /* Grow array if needed */
    if (ps->data_providers_count >= ps->data_providers_cap) {
        int new_cap = ps->data_providers_cap ? ps->data_providers_cap * 2 : 8;
        ps->data_providers = (nghttp2_perl_data_provider **)realloc(
            ps->data_providers, new_cap * sizeof(nghttp2_perl_data_provider *));
        memset(ps->data_providers + ps->data_providers_cap, 0,
               (new_cap - ps->data_providers_cap) * sizeof(nghttp2_perl_data_provider *));
        ps->data_providers_cap = new_cap;
    }
    ps->data_providers[ps->data_providers_count++] = dp;
}

static void remove_data_provider(nghttp2_perl_session *ps, int32_t stream_id) {
    dTHX;
    int i;
    for (i = 0; i < ps->data_providers_count; i++) {
        if (ps->data_providers[i] && ps->data_providers[i]->stream_id == stream_id) {
            nghttp2_perl_data_provider *dp = ps->data_providers[i];
            if (dp->callback) SvREFCNT_dec(dp->callback);
            if (dp->user_data) SvREFCNT_dec(dp->user_data);
            Safefree(dp);
            ps->data_providers[i] = NULL;
            return;
        }
    }
}

/* Data provider read callback - called by nghttp2 when it wants response body data */
static ssize_t perl_data_source_read_callback(
    nghttp2_session *session,
    int32_t stream_id,
    uint8_t *buf,
    size_t length,
    uint32_t *data_flags,
    nghttp2_data_source *source,
    void *user_data)
{
    dTHX;
    nghttp2_perl_session *ps = (nghttp2_perl_session *)user_data;
    nghttp2_perl_data_provider *dp = (nghttp2_perl_data_provider *)source->ptr;
    dSP;
    int count;
    ssize_t ret = 0;

    if (!dp) {
        *data_flags |= NGHTTP2_DATA_FLAG_EOF;
        return 0;
    }

    /* Special case: if callback is NULL but user_data contains body, use it directly */
    if (!dp->callback && dp->user_data && SvOK(dp->user_data) && !dp->eof) {
        STRLEN body_len;
        const char *body_ptr = SvPVbyte(dp->user_data, body_len);

        if (body_len > length) {
            body_len = length;
        }
        if (body_len > 0) {
            memcpy(buf, body_ptr, body_len);
        }

        /* Mark as EOF - we send the entire body in one go */
        *data_flags |= NGHTTP2_DATA_FLAG_EOF;
        dp->eof = 1;

        return (ssize_t)body_len;
    }

    if (!dp->callback || !SvOK(dp->callback)) {
        *data_flags |= NGHTTP2_DATA_FLAG_EOF;
        return 0;
    }

    ENTER;
    SAVETMPS;
    PUSHMARK(SP);

    /* Call: $callback->($stream_id, $max_length) */
    /* Returns: ($data, $eof) or undef for deferred */
    XPUSHs(sv_2mortal(newSViv(stream_id)));
    XPUSHs(sv_2mortal(newSVuv(length)));
    if (dp->user_data && SvOK(dp->user_data)) {
        XPUSHs(dp->user_data);
    }

    PUTBACK;
    count = call_sv(dp->callback, G_ARRAY | G_EVAL);
    SPAGAIN;

    if (SvTRUE(ERRSV)) {
        /* Callback threw an exception */
        warn("nghttp2 data provider callback error: %s", SvPV_nolen(ERRSV));
        ret = NGHTTP2_ERR_CALLBACK_FAILURE;
    } else if (count == 0) {
        /* No return value = defer */
        dp->deferred = 1;
        ret = NGHTTP2_ERR_DEFERRED;
    } else if (count >= 1) {
        SV *eof_sv = NULL;
        SV *data_sv = NULL;

        if (count >= 2) {
            eof_sv = POPs;
        }
        data_sv = POPs;

        if (!SvOK(data_sv)) {
            /* undef = defer */
            dp->deferred = 1;
            ret = NGHTTP2_ERR_DEFERRED;
        } else {
            STRLEN data_len;
            const char *data_ptr = SvPVbyte(data_sv, data_len);

            /* Copy data to buffer */
            if (data_len > length) {
                data_len = length;  /* Truncate if too much */
            }
            if (data_len > 0) {
                memcpy(buf, data_ptr, data_len);
            }
            ret = (ssize_t)data_len;

            /* Check EOF flag */
            if (eof_sv && SvTRUE(eof_sv)) {
                *data_flags |= NGHTTP2_DATA_FLAG_EOF;
                dp->eof = 1;
            }
            /* If returned empty string with no eof, also defer */
            if (data_len == 0 && !dp->eof) {
                dp->deferred = 1;
                ret = NGHTTP2_ERR_DEFERRED;
            }
        }
    }

    PUTBACK;
    FREETMPS;
    LEAVE;

    return ret;
}

/* Helper to call Perl callbacks */
static int call_perl_callback(pTHX_ SV *callback, AV *args) {
    dSP;
    int count;
    int ret = 0;

    if (!callback || !SvOK(callback)) {
        return 0;
    }

    ENTER;
    SAVETMPS;
    PUSHMARK(SP);

    if (args) {
        int i;
        int len = av_len(args) + 1;
        for (i = 0; i < len; i++) {
            SV **elem = av_fetch(args, i, 0);
            if (elem) {
                XPUSHs(*elem);
            }
        }
    }

    PUTBACK;
    count = call_sv(callback, G_SCALAR | G_EVAL);
    SPAGAIN;

    if (SvTRUE(ERRSV)) {
        /* Callback threw an exception */
        warn("nghttp2 callback error: %s", SvPV_nolen(ERRSV));
        ret = NGHTTP2_ERR_CALLBACK_FAILURE;
    } else if (count > 0) {
        SV *result = POPs;
        if (SvIOK(result)) {
            ret = SvIV(result);
        }
    }

    PUTBACK;
    FREETMPS;
    LEAVE;

    return ret;
}

/* Send callback - buffers data for mem_send */
static ssize_t perl_send_callback(nghttp2_session *session,
                                  const uint8_t *data, size_t length,
                                  int flags, void *user_data) {
    nghttp2_perl_session *ps = (nghttp2_perl_session *)user_data;

    /* Grow buffer if needed */
    if (ps->send_buf_len + length > ps->send_buf_cap) {
        size_t new_cap = ps->send_buf_cap * 2;
        if (new_cap < ps->send_buf_len + length) {
            new_cap = ps->send_buf_len + length + 16384;
        }
        ps->send_buf = (char *)realloc(ps->send_buf, new_cap);
        if (!ps->send_buf) {
            return NGHTTP2_ERR_NOMEM;
        }
        ps->send_buf_cap = new_cap;
    }

    memcpy(ps->send_buf + ps->send_buf_len, data, length);
    ps->send_buf_len += length;

    return (ssize_t)length;
}

/* Begin headers callback */
static int perl_on_begin_headers_callback(nghttp2_session *session,
                                          const nghttp2_frame *frame,
                                          void *user_data) {
    dTHX;
    nghttp2_perl_session *ps = (nghttp2_perl_session *)user_data;
    AV *args;
    int ret;

    if (!ps->cb_on_begin_headers || !SvOK(ps->cb_on_begin_headers)) {
        return 0;
    }

    args = newAV();
    av_push(args, newSViv(frame->hd.stream_id));
    av_push(args, newSViv(frame->hd.type));
    av_push(args, newSViv(frame->hd.flags));

    ret = call_perl_callback(aTHX_ ps->cb_on_begin_headers, args);

    SvREFCNT_dec((SV *)args);
    return ret;
}

/* Header callback */
static int perl_on_header_callback(nghttp2_session *session,
                                   const nghttp2_frame *frame,
                                   const uint8_t *name, size_t namelen,
                                   const uint8_t *value, size_t valuelen,
                                   uint8_t flags, void *user_data) {
    dTHX;
    nghttp2_perl_session *ps = (nghttp2_perl_session *)user_data;
    AV *args;
    int ret;

    if (!ps->cb_on_header || !SvOK(ps->cb_on_header)) {
        return 0;
    }

    args = newAV();
    av_push(args, newSViv(frame->hd.stream_id));
    av_push(args, newSVpvn((const char *)name, namelen));
    av_push(args, newSVpvn((const char *)value, valuelen));
    av_push(args, newSViv(flags));

    ret = call_perl_callback(aTHX_ ps->cb_on_header, args);

    SvREFCNT_dec((SV *)args);
    return ret;
}

/* Frame receive callback */
static int perl_on_frame_recv_callback(nghttp2_session *session,
                                       const nghttp2_frame *frame,
                                       void *user_data) {
    dTHX;
    nghttp2_perl_session *ps = (nghttp2_perl_session *)user_data;
    AV *args;
    HV *frame_hv;
    int ret;

    if (!ps->cb_on_frame_recv || !SvOK(ps->cb_on_frame_recv)) {
        return 0;
    }

    /* Build frame info hash */
    frame_hv = newHV();
    hv_store(frame_hv, "stream_id", 9, newSViv(frame->hd.stream_id), 0);
    hv_store(frame_hv, "type", 4, newSViv(frame->hd.type), 0);
    hv_store(frame_hv, "flags", 5, newSViv(frame->hd.flags), 0);
    hv_store(frame_hv, "length", 6, newSViv(frame->hd.length), 0);

    args = newAV();
    av_push(args, newRV_noinc((SV *)frame_hv));

    ret = call_perl_callback(aTHX_ ps->cb_on_frame_recv, args);

    SvREFCNT_dec((SV *)args);
    return ret;
}

/* Data chunk receive callback */
static int perl_on_data_chunk_recv_callback(nghttp2_session *session,
                                            uint8_t flags, int32_t stream_id,
                                            const uint8_t *data, size_t len,
                                            void *user_data) {
    dTHX;
    nghttp2_perl_session *ps = (nghttp2_perl_session *)user_data;
    AV *args;
    int ret;

    if (!ps->cb_on_data_chunk_recv || !SvOK(ps->cb_on_data_chunk_recv)) {
        return 0;
    }

    args = newAV();
    av_push(args, newSViv(stream_id));
    av_push(args, newSVpvn((const char *)data, len));
    av_push(args, newSViv(flags));

    ret = call_perl_callback(aTHX_ ps->cb_on_data_chunk_recv, args);

    SvREFCNT_dec((SV *)args);
    return ret;
}

/* Stream close callback */
static int perl_on_stream_close_callback(nghttp2_session *session,
                                         int32_t stream_id,
                                         uint32_t error_code,
                                         void *user_data) {
    dTHX;
    nghttp2_perl_session *ps = (nghttp2_perl_session *)user_data;
    AV *args;
    int ret = 0;

    /* Clean up any data provider for this stream */
    remove_data_provider(ps, stream_id);

    if (!ps->cb_on_stream_close || !SvOK(ps->cb_on_stream_close)) {
        return 0;
    }

    args = newAV();
    av_push(args, newSViv(stream_id));
    av_push(args, newSVuv(error_code));

    ret = call_perl_callback(aTHX_ ps->cb_on_stream_close, args);

    SvREFCNT_dec((SV *)args);
    return ret;
}

#line 447 "nghttp2.c"
#ifndef PERL_UNUSED_VAR
#  define PERL_UNUSED_VAR(var) if (0) var = var
#endif

#ifndef dVAR
#  define dVAR		dNOOP
#endif


/* This stuff is not part of the API! You have been warned. */
#ifndef PERL_VERSION_DECIMAL
#  define PERL_VERSION_DECIMAL(r,v,s) (r*1000000 + v*1000 + s)
#endif
#ifndef PERL_DECIMAL_VERSION
#  define PERL_DECIMAL_VERSION \
	  PERL_VERSION_DECIMAL(PERL_REVISION,PERL_VERSION,PERL_SUBVERSION)
#endif
#ifndef PERL_VERSION_GE
#  define PERL_VERSION_GE(r,v,s) \
	  (PERL_DECIMAL_VERSION >= PERL_VERSION_DECIMAL(r,v,s))
#endif
#ifndef PERL_VERSION_LE
#  define PERL_VERSION_LE(r,v,s) \
	  (PERL_DECIMAL_VERSION <= PERL_VERSION_DECIMAL(r,v,s))
#endif

/* XS_INTERNAL is the explicit static-linkage variant of the default
 * XS macro.
 *
 * XS_EXTERNAL is the same as XS_INTERNAL except it does not include
 * "STATIC", ie. it exports XSUB symbols. You probably don't want that
 * for anything but the BOOT XSUB.
 *
 * See XSUB.h in core!
 */


/* TODO: This might be compatible further back than 5.10.0. */
#if PERL_VERSION_GE(5, 10, 0) && PERL_VERSION_LE(5, 15, 1)
#  undef XS_EXTERNAL
#  undef XS_INTERNAL
#  if defined(__CYGWIN__) && defined(USE_DYNAMIC_LOADING)
#    define XS_EXTERNAL(name) __declspec(dllexport) XSPROTO(name)
#    define XS_INTERNAL(name) STATIC XSPROTO(name)
#  endif
#  if defined(__SYMBIAN32__)
#    define XS_EXTERNAL(name) EXPORT_C XSPROTO(name)
#    define XS_INTERNAL(name) EXPORT_C STATIC XSPROTO(name)
#  endif
#  ifndef XS_EXTERNAL
#    if defined(HASATTRIBUTE_UNUSED) && !defined(__cplusplus)
#      define XS_EXTERNAL(name) void name(pTHX_ CV* cv __attribute__unused__)
#      define XS_INTERNAL(name) STATIC void name(pTHX_ CV* cv __attribute__unused__)
#    else
#      ifdef __cplusplus
#        define XS_EXTERNAL(name) extern "C" XSPROTO(name)
#        define XS_INTERNAL(name) static XSPROTO(name)
#      else
#        define XS_EXTERNAL(name) XSPROTO(name)
#        define XS_INTERNAL(name) STATIC XSPROTO(name)
#      endif
#    endif
#  endif
#endif

/* perl >= 5.10.0 && perl <= 5.15.1 */


/* The XS_EXTERNAL macro is used for functions that must not be static
 * like the boot XSUB of a module. If perl didn't have an XS_EXTERNAL
 * macro defined, the best we can do is assume XS is the same.
 * Dito for XS_INTERNAL.
 */
#ifndef XS_EXTERNAL
#  define XS_EXTERNAL(name) XS(name)
#endif
#ifndef XS_INTERNAL
#  define XS_INTERNAL(name) XS(name)
#endif

/* Now, finally, after all this mess, we want an ExtUtils::ParseXS
 * internal macro that we're free to redefine for varying linkage due
 * to the EXPORT_XSUB_SYMBOLS XS keyword. This is internal, use
 * XS_EXTERNAL(name) or XS_INTERNAL(name) in your code if you need to!
 */

#undef XS_EUPXS
#if defined(PERL_EUPXS_ALWAYS_EXPORT)
#  define XS_EUPXS(name) XS_EXTERNAL(name)
#else
   /* default to internal */
#  define XS_EUPXS(name) XS_INTERNAL(name)
#endif

#ifndef PERL_ARGS_ASSERT_CROAK_XS_USAGE
#define PERL_ARGS_ASSERT_CROAK_XS_USAGE assert(cv); assert(params)

/* prototype to pass -Wmissing-prototypes */
STATIC void
S_croak_xs_usage(const CV *const cv, const char *const params);

STATIC void
S_croak_xs_usage(const CV *const cv, const char *const params)
{
    const GV *const gv = CvGV(cv);

    PERL_ARGS_ASSERT_CROAK_XS_USAGE;

    if (gv) {
        const char *const gvname = GvNAME(gv);
        const HV *const stash = GvSTASH(gv);
        const char *const hvname = stash ? HvNAME(stash) : NULL;

        if (hvname)
	    Perl_croak_nocontext("Usage: %s::%s(%s)", hvname, gvname, params);
        else
	    Perl_croak_nocontext("Usage: %s(%s)", gvname, params);
    } else {
        /* Pants. I don't think that it should be possible to get here. */
	Perl_croak_nocontext("Usage: CODE(0x%" UVxf ")(%s)", PTR2UV(cv), params);
    }
}
#undef  PERL_ARGS_ASSERT_CROAK_XS_USAGE

#define croak_xs_usage        S_croak_xs_usage

#endif

/* NOTE: the prototype of newXSproto() is different in versions of perls,
 * so we define a portable version of newXSproto()
 */
#ifdef newXS_flags
#define newXSproto_portable(name, c_impl, file, proto) newXS_flags(name, c_impl, file, proto, 0)
#else
#define newXSproto_portable(name, c_impl, file, proto) (PL_Sv=(SV*)newXS(name, c_impl, file), sv_setpv(PL_Sv, proto), (CV*)PL_Sv)
#endif /* !defined(newXS_flags) */

#if PERL_VERSION_LE(5, 21, 5)
#  define newXS_deffile(a,b) Perl_newXS(aTHX_ a,b,file)
#else
#  define newXS_deffile(a,b) Perl_newXS_deffile(aTHX_ a,b)
#endif

#line 591 "nghttp2.c"

XS_EUPXS(XS_Net__HTTP2__nghttp2__check_nghttp2_available); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_Net__HTTP2__nghttp2__check_nghttp2_available)
{
    dVAR; dXSARGS;
    if (items != 0)
       croak_xs_usage(cv,  "");
    {
	int	RETVAL;
	dXSTARG;
#line 445 "nghttp2.xs"
        nghttp2_info *info = nghttp2_version(0);
        RETVAL = info ? 1 : 0;
#line 605 "nghttp2.c"
	XSprePUSH;
	PUSHi((IV)RETVAL);
    }
    XSRETURN(1);
}


XS_EUPXS(XS_Net__HTTP2__nghttp2_version_string); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_Net__HTTP2__nghttp2_version_string)
{
    dVAR; dXSARGS;
    if (items != 0)
       croak_xs_usage(cv,  "");
    {
	const char *	RETVAL;
	dXSTARG;
#line 454 "nghttp2.xs"
        nghttp2_info *info = nghttp2_version(0);
        RETVAL = info ? info->version_str : "unknown";
#line 625 "nghttp2.c"
	sv_setpv(TARG, RETVAL);
	XSprePUSH;
	PUSHTARG;
    }
    XSRETURN(1);
}


XS_EUPXS(XS_Net__HTTP2__nghttp2_version_num); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_Net__HTTP2__nghttp2_version_num)
{
    dVAR; dXSARGS;
    if (items != 0)
       croak_xs_usage(cv,  "");
    {
	int	RETVAL;
	dXSTARG;
#line 463 "nghttp2.xs"
        nghttp2_info *info = nghttp2_version(0);
        RETVAL = info ? info->version_num : 0;
#line 646 "nghttp2.c"
	XSprePUSH;
	PUSHi((IV)RETVAL);
    }
    XSRETURN(1);
}


XS_EUPXS(XS_Net__HTTP2__nghttp2_NGHTTP2_ERR_WOULDBLOCK); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_Net__HTTP2__nghttp2_NGHTTP2_ERR_WOULDBLOCK)
{
    dVAR; dXSARGS;
    if (items != 0)
       croak_xs_usage(cv,  "");
    {
	int	RETVAL;
	dXSTARG;
#line 472 "nghttp2.xs"
        RETVAL = NGHTTP2_ERR_WOULDBLOCK;
#line 665 "nghttp2.c"
	XSprePUSH;
	PUSHi((IV)RETVAL);
    }
    XSRETURN(1);
}


XS_EUPXS(XS_Net__HTTP2__nghttp2_NGHTTP2_ERR_CALLBACK_FAILURE); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_Net__HTTP2__nghttp2_NGHTTP2_ERR_CALLBACK_FAILURE)
{
    dVAR; dXSARGS;
    if (items != 0)
       croak_xs_usage(cv,  "");
    {
	int	RETVAL;
	dXSTARG;
#line 479 "nghttp2.xs"
        RETVAL = NGHTTP2_ERR_CALLBACK_FAILURE;
#line 684 "nghttp2.c"
	XSprePUSH;
	PUSHi((IV)RETVAL);
    }
    XSRETURN(1);
}


XS_EUPXS(XS_Net__HTTP2__nghttp2_NGHTTP2_ERR_DEFERRED); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_Net__HTTP2__nghttp2_NGHTTP2_ERR_DEFERRED)
{
    dVAR; dXSARGS;
    if (items != 0)
       croak_xs_usage(cv,  "");
    {
	int	RETVAL;
	dXSTARG;
#line 486 "nghttp2.xs"
        RETVAL = NGHTTP2_ERR_DEFERRED;
#line 703 "nghttp2.c"
	XSprePUSH;
	PUSHi((IV)RETVAL);
    }
    XSRETURN(1);
}


XS_EUPXS(XS_Net__HTTP2__nghttp2_NGHTTP2_FLAG_NONE); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_Net__HTTP2__nghttp2_NGHTTP2_FLAG_NONE)
{
    dVAR; dXSARGS;
    if (items != 0)
       croak_xs_usage(cv,  "");
    {
	int	RETVAL;
	dXSTARG;
#line 493 "nghttp2.xs"
        RETVAL = NGHTTP2_FLAG_NONE;
#line 722 "nghttp2.c"
	XSprePUSH;
	PUSHi((IV)RETVAL);
    }
    XSRETURN(1);
}


XS_EUPXS(XS_Net__HTTP2__nghttp2_NGHTTP2_FLAG_END_STREAM); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_Net__HTTP2__nghttp2_NGHTTP2_FLAG_END_STREAM)
{
    dVAR; dXSARGS;
    if (items != 0)
       croak_xs_usage(cv,  "");
    {
	int	RETVAL;
	dXSTARG;
#line 500 "nghttp2.xs"
        RETVAL = NGHTTP2_FLAG_END_STREAM;
#line 741 "nghttp2.c"
	XSprePUSH;
	PUSHi((IV)RETVAL);
    }
    XSRETURN(1);
}


XS_EUPXS(XS_Net__HTTP2__nghttp2_NGHTTP2_FLAG_END_HEADERS); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_Net__HTTP2__nghttp2_NGHTTP2_FLAG_END_HEADERS)
{
    dVAR; dXSARGS;
    if (items != 0)
       croak_xs_usage(cv,  "");
    {
	int	RETVAL;
	dXSTARG;
#line 507 "nghttp2.xs"
        RETVAL = NGHTTP2_FLAG_END_HEADERS;
#line 760 "nghttp2.c"
	XSprePUSH;
	PUSHi((IV)RETVAL);
    }
    XSRETURN(1);
}


XS_EUPXS(XS_Net__HTTP2__nghttp2_NGHTTP2_FLAG_ACK); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_Net__HTTP2__nghttp2_NGHTTP2_FLAG_ACK)
{
    dVAR; dXSARGS;
    if (items != 0)
       croak_xs_usage(cv,  "");
    {
	int	RETVAL;
	dXSTARG;
#line 514 "nghttp2.xs"
        RETVAL = NGHTTP2_FLAG_ACK;
#line 779 "nghttp2.c"
	XSprePUSH;
	PUSHi((IV)RETVAL);
    }
    XSRETURN(1);
}


XS_EUPXS(XS_Net__HTTP2__nghttp2_NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_Net__HTTP2__nghttp2_NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS)
{
    dVAR; dXSARGS;
    if (items != 0)
       croak_xs_usage(cv,  "");
    {
	int	RETVAL;
	dXSTARG;
#line 521 "nghttp2.xs"
        RETVAL = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS;
#line 798 "nghttp2.c"
	XSprePUSH;
	PUSHi((IV)RETVAL);
    }
    XSRETURN(1);
}


XS_EUPXS(XS_Net__HTTP2__nghttp2_NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_Net__HTTP2__nghttp2_NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE)
{
    dVAR; dXSARGS;
    if (items != 0)
       croak_xs_usage(cv,  "");
    {
	int	RETVAL;
	dXSTARG;
#line 528 "nghttp2.xs"
        RETVAL = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE;
#line 817 "nghttp2.c"
	XSprePUSH;
	PUSHi((IV)RETVAL);
    }
    XSRETURN(1);
}


XS_EUPXS(XS_Net__HTTP2__nghttp2_NGHTTP2_SETTINGS_MAX_FRAME_SIZE); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_Net__HTTP2__nghttp2_NGHTTP2_SETTINGS_MAX_FRAME_SIZE)
{
    dVAR; dXSARGS;
    if (items != 0)
       croak_xs_usage(cv,  "");
    {
	int	RETVAL;
	dXSTARG;
#line 535 "nghttp2.xs"
        RETVAL = NGHTTP2_SETTINGS_MAX_FRAME_SIZE;
#line 836 "nghttp2.c"
	XSprePUSH;
	PUSHi((IV)RETVAL);
    }
    XSRETURN(1);
}


XS_EUPXS(XS_Net__HTTP2__nghttp2_NGHTTP2_SETTINGS_ENABLE_PUSH); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_Net__HTTP2__nghttp2_NGHTTP2_SETTINGS_ENABLE_PUSH)
{
    dVAR; dXSARGS;
    if (items != 0)
       croak_xs_usage(cv,  "");
    {
	int	RETVAL;
	dXSTARG;
#line 542 "nghttp2.xs"
        RETVAL = NGHTTP2_SETTINGS_ENABLE_PUSH;
#line 855 "nghttp2.c"
	XSprePUSH;
	PUSHi((IV)RETVAL);
    }
    XSRETURN(1);
}


XS_EUPXS(XS_Net__HTTP2__nghttp2_NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_Net__HTTP2__nghttp2_NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL)
{
    dVAR; dXSARGS;
    if (items != 0)
       croak_xs_usage(cv,  "");
    {
	int	RETVAL;
	dXSTARG;
#line 549 "nghttp2.xs"
        RETVAL = NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL;
#line 874 "nghttp2.c"
	XSprePUSH;
	PUSHi((IV)RETVAL);
    }
    XSRETURN(1);
}


XS_EUPXS(XS_Net__HTTP2__nghttp2_NGHTTP2_DATA_FLAG_NONE); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_Net__HTTP2__nghttp2_NGHTTP2_DATA_FLAG_NONE)
{
    dVAR; dXSARGS;
    if (items != 0)
       croak_xs_usage(cv,  "");
    {
	int	RETVAL;
	dXSTARG;
#line 556 "nghttp2.xs"
        RETVAL = NGHTTP2_DATA_FLAG_NONE;
#line 893 "nghttp2.c"
	XSprePUSH;
	PUSHi((IV)RETVAL);
    }
    XSRETURN(1);
}


XS_EUPXS(XS_Net__HTTP2__nghttp2_NGHTTP2_DATA_FLAG_EOF); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_Net__HTTP2__nghttp2_NGHTTP2_DATA_FLAG_EOF)
{
    dVAR; dXSARGS;
    if (items != 0)
       croak_xs_usage(cv,  "");
    {
	int	RETVAL;
	dXSTARG;
#line 563 "nghttp2.xs"
        RETVAL = NGHTTP2_DATA_FLAG_EOF;
#line 912 "nghttp2.c"
	XSprePUSH;
	PUSHi((IV)RETVAL);
    }
    XSRETURN(1);
}


XS_EUPXS(XS_Net__HTTP2__nghttp2_NGHTTP2_DATA_FLAG_NO_END_STREAM); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_Net__HTTP2__nghttp2_NGHTTP2_DATA_FLAG_NO_END_STREAM)
{
    dVAR; dXSARGS;
    if (items != 0)
       croak_xs_usage(cv,  "");
    {
	int	RETVAL;
	dXSTARG;
#line 570 "nghttp2.xs"
        RETVAL = NGHTTP2_DATA_FLAG_NO_END_STREAM;
#line 931 "nghttp2.c"
	XSprePUSH;
	PUSHi((IV)RETVAL);
    }
    XSRETURN(1);
}


XS_EUPXS(XS_Net__HTTP2__nghttp2_NGHTTP2_DATA_FLAG_NO_COPY); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_Net__HTTP2__nghttp2_NGHTTP2_DATA_FLAG_NO_COPY)
{
    dVAR; dXSARGS;
    if (items != 0)
       croak_xs_usage(cv,  "");
    {
	int	RETVAL;
	dXSTARG;
#line 577 "nghttp2.xs"
        RETVAL = NGHTTP2_DATA_FLAG_NO_COPY;
#line 950 "nghttp2.c"
	XSprePUSH;
	PUSHi((IV)RETVAL);
    }
    XSRETURN(1);
}


XS_EUPXS(XS_Net__HTTP2__nghttp2_NGHTTP2_DATA); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_Net__HTTP2__nghttp2_NGHTTP2_DATA)
{
    dVAR; dXSARGS;
    if (items != 0)
       croak_xs_usage(cv,  "");
    {
	int	RETVAL;
	dXSTARG;
#line 585 "nghttp2.xs"
        RETVAL = NGHTTP2_DATA;
#line 969 "nghttp2.c"
	XSprePUSH;
	PUSHi((IV)RETVAL);
    }
    XSRETURN(1);
}


XS_EUPXS(XS_Net__HTTP2__nghttp2_NGHTTP2_HEADERS); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_Net__HTTP2__nghttp2_NGHTTP2_HEADERS)
{
    dVAR; dXSARGS;
    if (items != 0)
       croak_xs_usage(cv,  "");
    {
	int	RETVAL;
	dXSTARG;
#line 592 "nghttp2.xs"
        RETVAL = NGHTTP2_HEADERS;
#line 988 "nghttp2.c"
	XSprePUSH;
	PUSHi((IV)RETVAL);
    }
    XSRETURN(1);
}


XS_EUPXS(XS_Net__HTTP2__nghttp2_NGHTTP2_SETTINGS); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_Net__HTTP2__nghttp2_NGHTTP2_SETTINGS)
{
    dVAR; dXSARGS;
    if (items != 0)
       croak_xs_usage(cv,  "");
    {
	int	RETVAL;
	dXSTARG;
#line 599 "nghttp2.xs"
        RETVAL = NGHTTP2_SETTINGS;
#line 1007 "nghttp2.c"
	XSprePUSH;
	PUSHi((IV)RETVAL);
    }
    XSRETURN(1);
}


XS_EUPXS(XS_Net__HTTP2__nghttp2_NGHTTP2_PUSH_PROMISE); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_Net__HTTP2__nghttp2_NGHTTP2_PUSH_PROMISE)
{
    dVAR; dXSARGS;
    if (items != 0)
       croak_xs_usage(cv,  "");
    {
	int	RETVAL;
	dXSTARG;
#line 606 "nghttp2.xs"
        RETVAL = NGHTTP2_PUSH_PROMISE;
#line 1026 "nghttp2.c"
	XSprePUSH;
	PUSHi((IV)RETVAL);
    }
    XSRETURN(1);
}


XS_EUPXS(XS_Net__HTTP2__nghttp2_NGHTTP2_GOAWAY); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_Net__HTTP2__nghttp2_NGHTTP2_GOAWAY)
{
    dVAR; dXSARGS;
    if (items != 0)
       croak_xs_usage(cv,  "");
    {
	int	RETVAL;
	dXSTARG;
#line 613 "nghttp2.xs"
        RETVAL = NGHTTP2_GOAWAY;
#line 1045 "nghttp2.c"
	XSprePUSH;
	PUSHi((IV)RETVAL);
    }
    XSRETURN(1);
}


XS_EUPXS(XS_Net__HTTP2__nghttp2__Session__new_server_xs); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_Net__HTTP2__nghttp2__Session__new_server_xs)
{
    dVAR; dXSARGS;
    if (items != 3)
       croak_xs_usage(cv,  "class, callbacks_hv, user_data");
    {
	char *	class = (char *)SvPV_nolen(ST(0))
;
	HV *	callbacks_hv;
	SV *	user_data = ST(2)
;
#line 627 "nghttp2.xs"
        nghttp2_perl_session *ps;
        nghttp2_session_callbacks *callbacks;
        int rv;
        SV **svp;
#line 1070 "nghttp2.c"
	SV *	RETVAL;

	STMT_START {
		SV* const xsub_tmp_sv = ST(1);
		SvGETMAGIC(xsub_tmp_sv);
		if (SvROK(xsub_tmp_sv) && SvTYPE(SvRV(xsub_tmp_sv)) == SVt_PVHV){
		    callbacks_hv = (HV*)SvRV(xsub_tmp_sv);
		}
		else{
		    Perl_croak_nocontext("%s: %s is not a HASH reference",
				"Net::HTTP2::nghttp2::Session::_new_server_xs",
				"callbacks_hv");
		}
	} STMT_END
;
#line 632 "nghttp2.xs"
        /* Allocate our wrapper structure */
        Newxz(ps, 1, nghttp2_perl_session);

        /* Initialize send buffer */
        ps->send_buf_cap = 16384;
        ps->send_buf = (char *)malloc(ps->send_buf_cap);
        ps->send_buf_len = 0;

        /* Store user data */
        if (SvOK(user_data)) {
            ps->user_data = newSVsv(user_data);
        }

        /* Extract callbacks from hash */
        if ((svp = hv_fetch(callbacks_hv, "on_begin_headers", 16, 0))) {
            ps->cb_on_begin_headers = newSVsv(*svp);
        }
        if ((svp = hv_fetch(callbacks_hv, "on_header", 9, 0))) {
            ps->cb_on_header = newSVsv(*svp);
        }
        if ((svp = hv_fetch(callbacks_hv, "on_frame_recv", 13, 0))) {
            ps->cb_on_frame_recv = newSVsv(*svp);
        }
        if ((svp = hv_fetch(callbacks_hv, "on_data_chunk_recv", 18, 0))) {
            ps->cb_on_data_chunk_recv = newSVsv(*svp);
        }
        if ((svp = hv_fetch(callbacks_hv, "on_stream_close", 15, 0))) {
            ps->cb_on_stream_close = newSVsv(*svp);
        }

        /* Create nghttp2 callbacks */
        nghttp2_session_callbacks_new(&callbacks);
        nghttp2_session_callbacks_set_send_callback(callbacks, perl_send_callback);
        nghttp2_session_callbacks_set_on_begin_headers_callback(callbacks, perl_on_begin_headers_callback);
        nghttp2_session_callbacks_set_on_header_callback(callbacks, perl_on_header_callback);
        nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks, perl_on_frame_recv_callback);
        nghttp2_session_callbacks_set_on_data_chunk_recv_callback(callbacks, perl_on_data_chunk_recv_callback);
        nghttp2_session_callbacks_set_on_stream_close_callback(callbacks, perl_on_stream_close_callback);

        /* Create session */
        rv = nghttp2_session_server_new(&ps->session, callbacks, ps);
        nghttp2_session_callbacks_del(callbacks);

        if (rv != 0) {
            if (ps->user_data) SvREFCNT_dec(ps->user_data);
            if (ps->cb_on_begin_headers) SvREFCNT_dec(ps->cb_on_begin_headers);
            if (ps->cb_on_header) SvREFCNT_dec(ps->cb_on_header);
            if (ps->cb_on_frame_recv) SvREFCNT_dec(ps->cb_on_frame_recv);
            if (ps->cb_on_data_chunk_recv) SvREFCNT_dec(ps->cb_on_data_chunk_recv);
            if (ps->cb_on_stream_close) SvREFCNT_dec(ps->cb_on_stream_close);
            free(ps->send_buf);
            Safefree(ps);
            croak("nghttp2_session_server_new failed: %s", nghttp2_strerror(rv));
        }

        /* Bless and return */
        RETVAL = sv_newmortal();
        sv_setref_pv(RETVAL, class, (void *)ps);
        SvREFCNT_inc(RETVAL);
#line 1146 "nghttp2.c"
	RETVAL = sv_2mortal(RETVAL);
	ST(0) = RETVAL;
    }
    XSRETURN(1);
}


XS_EUPXS(XS_Net__HTTP2__nghttp2__Session_DESTROY); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_Net__HTTP2__nghttp2__Session_DESTROY)
{
    dVAR; dXSARGS;
    if (items != 1)
       croak_xs_usage(cv,  "self");
    {
	SV *	self = ST(0)
;
#line 699 "nghttp2.xs"
        nghttp2_perl_session *ps;
#line 1165 "nghttp2.c"
#line 701 "nghttp2.xs"
        ps = (nghttp2_perl_session *)SvIV(SvRV(self));
        if (ps) {
            int i;
            if (ps->session) {
                nghttp2_session_del(ps->session);
            }
            if (ps->user_data) SvREFCNT_dec(ps->user_data);
            if (ps->cb_on_begin_headers) SvREFCNT_dec(ps->cb_on_begin_headers);
            if (ps->cb_on_header) SvREFCNT_dec(ps->cb_on_header);
            if (ps->cb_on_frame_recv) SvREFCNT_dec(ps->cb_on_frame_recv);
            if (ps->cb_on_data_chunk_recv) SvREFCNT_dec(ps->cb_on_data_chunk_recv);
            if (ps->cb_on_stream_close) SvREFCNT_dec(ps->cb_on_stream_close);
            if (ps->send_buf) free(ps->send_buf);
            /* Clean up data providers */
            for (i = 0; i < ps->data_providers_count; i++) {
                if (ps->data_providers[i]) {
                    nghttp2_perl_data_provider *dp = ps->data_providers[i];
                    if (dp->callback) SvREFCNT_dec(dp->callback);
                    if (dp->user_data) SvREFCNT_dec(dp->user_data);
                    Safefree(dp);
                }
            }
            if (ps->data_providers) free(ps->data_providers);
            Safefree(ps);
        }
#line 1192 "nghttp2.c"
    }
    XSRETURN_EMPTY;
}


XS_EUPXS(XS_Net__HTTP2__nghttp2__Session_mem_recv); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_Net__HTTP2__nghttp2__Session_mem_recv)
{
    dVAR; dXSARGS;
    if (items != 2)
       croak_xs_usage(cv,  "self, data");
    {
	SV *	self = ST(0)
;
	SV *	data = ST(1)
;
#line 733 "nghttp2.xs"
        nghttp2_perl_session *ps;
        STRLEN len;
        const char *buf;
        ssize_t rv;
#line 1214 "nghttp2.c"
	int	RETVAL;
	dXSTARG;
#line 738 "nghttp2.xs"
        ps = (nghttp2_perl_session *)SvIV(SvRV(self));
        buf = SvPVbyte(data, len);

        rv = nghttp2_session_mem_recv(ps->session, (const uint8_t *)buf, len);
        if (rv < 0) {
            croak("nghttp2_session_mem_recv failed: %s", nghttp2_strerror((int)rv));
        }
        RETVAL = (int)rv;
#line 1226 "nghttp2.c"
	XSprePUSH;
	PUSHi((IV)RETVAL);
    }
    XSRETURN(1);
}


XS_EUPXS(XS_Net__HTTP2__nghttp2__Session_mem_send); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_Net__HTTP2__nghttp2__Session_mem_send)
{
    dVAR; dXSARGS;
    if (items != 1)
       croak_xs_usage(cv,  "self");
    {
	SV *	self = ST(0)
;
#line 754 "nghttp2.xs"
        nghttp2_perl_session *ps;
        int rv;
#line 1246 "nghttp2.c"
	SV *	RETVAL;
#line 757 "nghttp2.xs"
        ps = (nghttp2_perl_session *)SvIV(SvRV(self));

        /* Clear send buffer */
        ps->send_buf_len = 0;

        /* Trigger send callback to fill buffer */
        rv = nghttp2_session_send(ps->session);
        if (rv != 0) {
            croak("nghttp2_session_send failed: %s", nghttp2_strerror(rv));
        }

        /* Return buffered data */
        if (ps->send_buf_len > 0) {
            RETVAL = newSVpvn(ps->send_buf, ps->send_buf_len);
        } else {
            RETVAL = newSVpvn("", 0);
        }
#line 1266 "nghttp2.c"
	RETVAL = sv_2mortal(RETVAL);
	ST(0) = RETVAL;
    }
    XSRETURN(1);
}


XS_EUPXS(XS_Net__HTTP2__nghttp2__Session_want_read); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_Net__HTTP2__nghttp2__Session_want_read)
{
    dVAR; dXSARGS;
    if (items != 1)
       croak_xs_usage(cv,  "self");
    {
	SV *	self = ST(0)
;
#line 782 "nghttp2.xs"
        nghttp2_perl_session *ps;
#line 1285 "nghttp2.c"
	int	RETVAL;
	dXSTARG;
#line 784 "nghttp2.xs"
        ps = (nghttp2_perl_session *)SvIV(SvRV(self));
        RETVAL = nghttp2_session_want_read(ps->session);
#line 1291 "nghttp2.c"
	XSprePUSH;
	PUSHi((IV)RETVAL);
    }
    XSRETURN(1);
}


XS_EUPXS(XS_Net__HTTP2__nghttp2__Session_want_write); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_Net__HTTP2__nghttp2__Session_want_write)
{
    dVAR; dXSARGS;
    if (items != 1)
       croak_xs_usage(cv,  "self");
    {
	SV *	self = ST(0)
;
#line 794 "nghttp2.xs"
        nghttp2_perl_session *ps;
#line 1310 "nghttp2.c"
	int	RETVAL;
	dXSTARG;
#line 796 "nghttp2.xs"
        ps = (nghttp2_perl_session *)SvIV(SvRV(self));
        RETVAL = nghttp2_session_want_write(ps->session);
#line 1316 "nghttp2.c"
	XSprePUSH;
	PUSHi((IV)RETVAL);
    }
    XSRETURN(1);
}


XS_EUPXS(XS_Net__HTTP2__nghttp2__Session_submit_settings); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_Net__HTTP2__nghttp2__Session_submit_settings)
{
    dVAR; dXSARGS;
    if (items != 2)
       croak_xs_usage(cv,  "self, settings_hv");
    {
	SV *	self = ST(0)
;
	HV *	settings_hv;
#line 807 "nghttp2.xs"
        nghttp2_perl_session *ps;
        nghttp2_settings_entry iv[16];
        int niv = 0;
        SV **svp;
        int rv;
#line 1340 "nghttp2.c"
	int	RETVAL;
	dXSTARG;

	STMT_START {
		SV* const xsub_tmp_sv = ST(1);
		SvGETMAGIC(xsub_tmp_sv);
		if (SvROK(xsub_tmp_sv) && SvTYPE(SvRV(xsub_tmp_sv)) == SVt_PVHV){
		    settings_hv = (HV*)SvRV(xsub_tmp_sv);
		}
		else{
		    Perl_croak_nocontext("%s: %s is not a HASH reference",
				"Net::HTTP2::nghttp2::Session::submit_settings",
				"settings_hv");
		}
	} STMT_END
;
#line 813 "nghttp2.xs"
        ps = (nghttp2_perl_session *)SvIV(SvRV(self));

        if ((svp = hv_fetch(settings_hv, "max_concurrent_streams", 22, 0))) {
            iv[niv].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS;
            iv[niv].value = SvUV(*svp);
            niv++;
        }
        if ((svp = hv_fetch(settings_hv, "initial_window_size", 19, 0))) {
            iv[niv].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE;
            iv[niv].value = SvUV(*svp);
            niv++;
        }
        if ((svp = hv_fetch(settings_hv, "max_frame_size", 14, 0))) {
            iv[niv].settings_id = NGHTTP2_SETTINGS_MAX_FRAME_SIZE;
            iv[niv].value = SvUV(*svp);
            niv++;
        }
        if ((svp = hv_fetch(settings_hv, "enable_push", 11, 0))) {
            iv[niv].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH;
            iv[niv].value = SvTRUE(*svp) ? 1 : 0;
            niv++;
        }
        if ((svp = hv_fetch(settings_hv, "enable_connect_protocol", 23, 0))) {
            iv[niv].settings_id = NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL;
            iv[niv].value = SvTRUE(*svp) ? 1 : 0;
            niv++;
        }

        rv = nghttp2_submit_settings(ps->session, NGHTTP2_FLAG_NONE, iv, niv);
        if (rv != 0) {
            croak("nghttp2_submit_settings failed: %s", nghttp2_strerror(rv));
        }
        RETVAL = rv;
#line 1391 "nghttp2.c"
	XSprePUSH;
	PUSHi((IV)RETVAL);
    }
    XSRETURN(1);
}


XS_EUPXS(XS_Net__HTTP2__nghttp2__Session__submit_response_with_body); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_Net__HTTP2__nghttp2__Session__submit_response_with_body)
{
    dVAR; dXSARGS;
    if (items != 4)
       croak_xs_usage(cv,  "self, stream_id, headers_av, body");
    {
	SV *	self = ST(0)
;
	int	stream_id = (int)SvIV(ST(1))
;
	AV *	headers_av;
	SV *	body = ST(3)
;
#line 857 "nghttp2.xs"
        nghttp2_perl_session *ps;
        nghttp2_nv *nva;
        size_t nvlen;
        nghttp2_data_provider data_prd;
        int rv;
        I32 i;
        STRLEN body_len;
        char *body_ptr;
#line 1422 "nghttp2.c"
	int	RETVAL;
	dXSTARG;

	STMT_START {
		SV* const xsub_tmp_sv = ST(2);
		SvGETMAGIC(xsub_tmp_sv);
		if (SvROK(xsub_tmp_sv) && SvTYPE(SvRV(xsub_tmp_sv)) == SVt_PVAV){
		    headers_av = (AV*)SvRV(xsub_tmp_sv);
		}
		else{
		    Perl_croak_nocontext("%s: %s is not an ARRAY reference",
				"Net::HTTP2::nghttp2::Session::_submit_response_with_body",
				"headers_av");
		}
	} STMT_END
;
#line 866 "nghttp2.xs"
        ps = (nghttp2_perl_session *)SvIV(SvRV(self));

        /* Build name-value array from Perl array of arrayrefs */
        nvlen = av_len(headers_av) + 1;
        Newxz(nva, nvlen, nghttp2_nv);

        for (i = 0; i < (I32)nvlen; i++) {
            SV **pair = av_fetch(headers_av, i, 0);
            if (pair && SvROK(*pair) && SvTYPE(SvRV(*pair)) == SVt_PVAV) {
                AV *pair_av = (AV *)SvRV(*pair);
                SV **name_sv = av_fetch(pair_av, 0, 0);
                SV **value_sv = av_fetch(pair_av, 1, 0);

                if (name_sv && value_sv) {
                    STRLEN name_len, value_len;
                    nva[i].name = (uint8_t *)SvPVbyte(*name_sv, name_len);
                    nva[i].namelen = name_len;
                    nva[i].value = (uint8_t *)SvPVbyte(*value_sv, value_len);
                    nva[i].valuelen = value_len;
                    nva[i].flags = NGHTTP2_NV_FLAG_NONE;
                }
            }
        }

        /* For now, submit without data provider (headers only) */
        /* TODO: Implement proper data provider for body */
        body_ptr = SvPVbyte(body, body_len);

        rv = nghttp2_submit_response(ps->session, stream_id, nva, nvlen, NULL);

        Safefree(nva);

        if (rv != 0) {
            croak("nghttp2_submit_response failed: %s", nghttp2_strerror(rv));
        }
        RETVAL = rv;
#line 1476 "nghttp2.c"
	XSprePUSH;
	PUSHi((IV)RETVAL);
    }
    XSRETURN(1);
}


XS_EUPXS(XS_Net__HTTP2__nghttp2__Session__submit_response_no_body); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_Net__HTTP2__nghttp2__Session__submit_response_no_body)
{
    dVAR; dXSARGS;
    if (items != 3)
       croak_xs_usage(cv,  "self, stream_id, headers_av");
    {
	SV *	self = ST(0)
;
	int	stream_id = (int)SvIV(ST(1))
;
	AV *	headers_av;
#line 912 "nghttp2.xs"
        nghttp2_perl_session *ps;
        nghttp2_nv *nva;
        size_t nvlen;
        int rv;
        I32 i;
#line 1502 "nghttp2.c"
	int	RETVAL;
	dXSTARG;

	STMT_START {
		SV* const xsub_tmp_sv = ST(2);
		SvGETMAGIC(xsub_tmp_sv);
		if (SvROK(xsub_tmp_sv) && SvTYPE(SvRV(xsub_tmp_sv)) == SVt_PVAV){
		    headers_av = (AV*)SvRV(xsub_tmp_sv);
		}
		else{
		    Perl_croak_nocontext("%s: %s is not an ARRAY reference",
				"Net::HTTP2::nghttp2::Session::_submit_response_no_body",
				"headers_av");
		}
	} STMT_END
;
#line 918 "nghttp2.xs"
        ps = (nghttp2_perl_session *)SvIV(SvRV(self));

        /* Build name-value array */
        nvlen = av_len(headers_av) + 1;
        Newxz(nva, nvlen, nghttp2_nv);

        for (i = 0; i < (I32)nvlen; i++) {
            SV **pair = av_fetch(headers_av, i, 0);
            if (pair && SvROK(*pair) && SvTYPE(SvRV(*pair)) == SVt_PVAV) {
                AV *pair_av = (AV *)SvRV(*pair);
                SV **name_sv = av_fetch(pair_av, 0, 0);
                SV **value_sv = av_fetch(pair_av, 1, 0);

                if (name_sv && value_sv) {
                    STRLEN name_len, value_len;
                    nva[i].name = (uint8_t *)SvPVbyte(*name_sv, name_len);
                    nva[i].namelen = name_len;
                    nva[i].value = (uint8_t *)SvPVbyte(*value_sv, value_len);
                    nva[i].valuelen = value_len;
                    nva[i].flags = NGHTTP2_NV_FLAG_NONE;
                }
            }
        }

        rv = nghttp2_submit_response(ps->session, stream_id, nva, nvlen, NULL);

        Safefree(nva);

        if (rv != 0) {
            croak("nghttp2_submit_response failed: %s", nghttp2_strerror(rv));
        }
        RETVAL = rv;
#line 1552 "nghttp2.c"
	XSprePUSH;
	PUSHi((IV)RETVAL);
    }
    XSRETURN(1);
}


XS_EUPXS(XS_Net__HTTP2__nghttp2__Session_resume_data); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_Net__HTTP2__nghttp2__Session_resume_data)
{
    dVAR; dXSARGS;
    if (items != 2)
       croak_xs_usage(cv,  "self, stream_id");
    {
	SV *	self = ST(0)
;
	int	stream_id = (int)SvIV(ST(1))
;
#line 959 "nghttp2.xs"
        nghttp2_perl_session *ps;
        int rv;
#line 1574 "nghttp2.c"
	int	RETVAL;
	dXSTARG;
#line 962 "nghttp2.xs"
        ps = (nghttp2_perl_session *)SvIV(SvRV(self));
        rv = nghttp2_session_resume_data(ps->session, stream_id);
        if (rv != 0 && rv != NGHTTP2_ERR_INVALID_ARGUMENT) {
            croak("nghttp2_session_resume_data failed: %s", nghttp2_strerror(rv));
        }
        RETVAL = rv;
#line 1584 "nghttp2.c"
	XSprePUSH;
	PUSHi((IV)RETVAL);
    }
    XSRETURN(1);
}


XS_EUPXS(XS_Net__HTTP2__nghttp2__Session_get_stream_user_data); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_Net__HTTP2__nghttp2__Session_get_stream_user_data)
{
    dVAR; dXSARGS;
    if (items != 2)
       croak_xs_usage(cv,  "self, stream_id");
    {
	SV *	self = ST(0)
;
	int	stream_id = (int)SvIV(ST(1))
;
#line 977 "nghttp2.xs"
        nghttp2_perl_session *ps;
        void *data;
#line 1606 "nghttp2.c"
	SV *	RETVAL;
#line 980 "nghttp2.xs"
        ps = (nghttp2_perl_session *)SvIV(SvRV(self));
        data = nghttp2_session_get_stream_user_data(ps->session, stream_id);
        if (data) {
            RETVAL = newSVsv((SV *)data);
        } else {
            RETVAL = &PL_sv_undef;
        }
#line 1616 "nghttp2.c"
	RETVAL = sv_2mortal(RETVAL);
	ST(0) = RETVAL;
    }
    XSRETURN(1);
}


XS_EUPXS(XS_Net__HTTP2__nghttp2__Session_set_stream_user_data); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_Net__HTTP2__nghttp2__Session_set_stream_user_data)
{
    dVAR; dXSARGS;
    if (items != 3)
       croak_xs_usage(cv,  "self, stream_id, data");
    {
	SV *	self = ST(0)
;
	int	stream_id = (int)SvIV(ST(1))
;
	SV *	data = ST(2)
;
#line 997 "nghttp2.xs"
        nghttp2_perl_session *ps;
        int rv;
#line 1640 "nghttp2.c"
	int	RETVAL;
	dXSTARG;
#line 1000 "nghttp2.xs"
        ps = (nghttp2_perl_session *)SvIV(SvRV(self));
        /* Note: caller must ensure data SV survives */
        rv = nghttp2_session_set_stream_user_data(ps->session, stream_id,
                                                   SvOK(data) ? newSVsv(data) : NULL);
        RETVAL = rv;
#line 1649 "nghttp2.c"
	XSprePUSH;
	PUSHi((IV)RETVAL);
    }
    XSRETURN(1);
}


XS_EUPXS(XS_Net__HTTP2__nghttp2__Session_terminate_session); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_Net__HTTP2__nghttp2__Session_terminate_session)
{
    dVAR; dXSARGS;
    if (items != 2)
       croak_xs_usage(cv,  "self, error_code");
    {
	SV *	self = ST(0)
;
	int	error_code = (int)SvIV(ST(1))
;
#line 1014 "nghttp2.xs"
        nghttp2_perl_session *ps;
        int rv;
#line 1671 "nghttp2.c"
	int	RETVAL;
	dXSTARG;
#line 1017 "nghttp2.xs"
        ps = (nghttp2_perl_session *)SvIV(SvRV(self));
        rv = nghttp2_session_terminate_session(ps->session, error_code);
        RETVAL = rv;
#line 1678 "nghttp2.c"
	XSprePUSH;
	PUSHi((IV)RETVAL);
    }
    XSRETURN(1);
}


XS_EUPXS(XS_Net__HTTP2__nghttp2__Session__submit_response_streaming); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_Net__HTTP2__nghttp2__Session__submit_response_streaming)
{
    dVAR; dXSARGS;
    if (items != 5)
       croak_xs_usage(cv,  "self, stream_id, headers_av, data_callback, cb_user_data");
    {
	SV *	self = ST(0)
;
	int	stream_id = (int)SvIV(ST(1))
;
	AV *	headers_av;
	SV *	data_callback = ST(3)
;
	SV *	cb_user_data = ST(4)
;
#line 1034 "nghttp2.xs"
        nghttp2_perl_session *ps;
        nghttp2_nv *nva;
        size_t nvlen;
        nghttp2_data_provider data_prd;
        nghttp2_perl_data_provider *dp;
        int rv;
        I32 i;
#line 1710 "nghttp2.c"
	int	RETVAL;
	dXSTARG;

	STMT_START {
		SV* const xsub_tmp_sv = ST(2);
		SvGETMAGIC(xsub_tmp_sv);
		if (SvROK(xsub_tmp_sv) && SvTYPE(SvRV(xsub_tmp_sv)) == SVt_PVAV){
		    headers_av = (AV*)SvRV(xsub_tmp_sv);
		}
		else{
		    Perl_croak_nocontext("%s: %s is not an ARRAY reference",
				"Net::HTTP2::nghttp2::Session::_submit_response_streaming",
				"headers_av");
		}
	} STMT_END
;
#line 1042 "nghttp2.xs"
        ps = (nghttp2_perl_session *)SvIV(SvRV(self));

        /* Build name-value array from Perl array of arrayrefs */
        nvlen = av_len(headers_av) + 1;
        Newxz(nva, nvlen, nghttp2_nv);

        for (i = 0; i < (I32)nvlen; i++) {
            SV **pair = av_fetch(headers_av, i, 0);
            if (pair && SvROK(*pair) && SvTYPE(SvRV(*pair)) == SVt_PVAV) {
                AV *pair_av = (AV *)SvRV(*pair);
                SV **name_sv = av_fetch(pair_av, 0, 0);
                SV **value_sv = av_fetch(pair_av, 1, 0);

                if (name_sv && value_sv) {
                    STRLEN name_len, value_len;
                    nva[i].name = (uint8_t *)SvPVbyte(*name_sv, name_len);
                    nva[i].namelen = name_len;
                    nva[i].value = (uint8_t *)SvPVbyte(*value_sv, value_len);
                    nva[i].valuelen = value_len;
                    nva[i].flags = NGHTTP2_NV_FLAG_NONE;
                }
            }
        }

        /* Create data provider state */
        Newxz(dp, 1, nghttp2_perl_data_provider);
        dp->stream_id = stream_id;
        dp->callback = newSVsv(data_callback);
        if (SvOK(cb_user_data)) {
            dp->user_data = newSVsv(cb_user_data);
        }
        dp->eof = 0;
        dp->deferred = 0;

        /* Track the data provider */
        add_data_provider(ps, dp);

        /* Set up nghttp2 data provider */
        data_prd.source.ptr = dp;
        data_prd.read_callback = perl_data_source_read_callback;

        rv = nghttp2_submit_response(ps->session, stream_id, nva, nvlen, &data_prd);

        Safefree(nva);

        if (rv != 0) {
            remove_data_provider(ps, stream_id);
            croak("nghttp2_submit_response failed: %s", nghttp2_strerror(rv));
        }
        RETVAL = rv;
#line 1778 "nghttp2.c"
	XSprePUSH;
	PUSHi((IV)RETVAL);
    }
    XSRETURN(1);
}


XS_EUPXS(XS_Net__HTTP2__nghttp2__Session_submit_data); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_Net__HTTP2__nghttp2__Session_submit_data)
{
    dVAR; dXSARGS;
    if (items != 4)
       croak_xs_usage(cv,  "self, stream_id, data, eof");
    {
	SV *	self = ST(0)
;
	int	stream_id = (int)SvIV(ST(1))
;
	SV *	data = ST(2)
;
	int	eof = (int)SvIV(ST(3))
;
#line 1103 "nghttp2.xs"
        nghttp2_perl_session *ps;
        STRLEN len;
        const uint8_t *buf;
        int rv;
        uint8_t flags = NGHTTP2_FLAG_NONE;
#line 1807 "nghttp2.c"
	int	RETVAL;
	dXSTARG;
#line 1109 "nghttp2.xs"
        ps = (nghttp2_perl_session *)SvIV(SvRV(self));

        if (eof) {
            flags |= NGHTTP2_FLAG_END_STREAM;
        }

        buf = (const uint8_t *)SvPVbyte(data, len);

        /* Use nghttp2_submit_data to queue data */
        /* Note: This requires the stream to already have a response submitted */
        rv = nghttp2_submit_data(ps->session, flags, stream_id, NULL);

        if (rv != 0) {
            croak("nghttp2_submit_data failed: %s", nghttp2_strerror(rv));
        }
        RETVAL = rv;
#line 1827 "nghttp2.c"
	XSprePUSH;
	PUSHi((IV)RETVAL);
    }
    XSRETURN(1);
}


XS_EUPXS(XS_Net__HTTP2__nghttp2__Session_is_stream_deferred); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_Net__HTTP2__nghttp2__Session_is_stream_deferred)
{
    dVAR; dXSARGS;
    if (items != 2)
       croak_xs_usage(cv,  "self, stream_id");
    {
	SV *	self = ST(0)
;
	int	stream_id = (int)SvIV(ST(1))
;
#line 1134 "nghttp2.xs"
        nghttp2_perl_session *ps;
        nghttp2_perl_data_provider *dp;
#line 1849 "nghttp2.c"
	int	RETVAL;
	dXSTARG;
#line 1137 "nghttp2.xs"
        ps = (nghttp2_perl_session *)SvIV(SvRV(self));
        dp = find_data_provider(ps, stream_id);
        RETVAL = dp ? dp->deferred : 0;
#line 1856 "nghttp2.c"
	XSprePUSH;
	PUSHi((IV)RETVAL);
    }
    XSRETURN(1);
}


XS_EUPXS(XS_Net__HTTP2__nghttp2__Session__clear_deferred); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_Net__HTTP2__nghttp2__Session__clear_deferred)
{
    dVAR; dXSARGS;
    if (items != 2)
       croak_xs_usage(cv,  "self, stream_id");
    {
	SV *	self = ST(0)
;
	int	stream_id = (int)SvIV(ST(1))
;
#line 1149 "nghttp2.xs"
        nghttp2_perl_session *ps;
        nghttp2_perl_data_provider *dp;
#line 1878 "nghttp2.c"
#line 1152 "nghttp2.xs"
        ps = (nghttp2_perl_session *)SvIV(SvRV(self));
        dp = find_data_provider(ps, stream_id);
        if (dp) {
            dp->deferred = 0;
        }
#line 1885 "nghttp2.c"
    }
    XSRETURN_EMPTY;
}


XS_EUPXS(XS_Net__HTTP2__nghttp2__Session__new_client_xs); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_Net__HTTP2__nghttp2__Session__new_client_xs)
{
    dVAR; dXSARGS;
    if (items != 3)
       croak_xs_usage(cv,  "class, callbacks_hv, user_data");
    {
	char *	class = (char *)SvPV_nolen(ST(0))
;
	HV *	callbacks_hv;
	SV *	user_data = ST(2)
;
#line 1165 "nghttp2.xs"
        nghttp2_perl_session *ps;
        nghttp2_session_callbacks *callbacks;
        int rv;
        SV **svp;
#line 1908 "nghttp2.c"
	SV *	RETVAL;

	STMT_START {
		SV* const xsub_tmp_sv = ST(1);
		SvGETMAGIC(xsub_tmp_sv);
		if (SvROK(xsub_tmp_sv) && SvTYPE(SvRV(xsub_tmp_sv)) == SVt_PVHV){
		    callbacks_hv = (HV*)SvRV(xsub_tmp_sv);
		}
		else{
		    Perl_croak_nocontext("%s: %s is not a HASH reference",
				"Net::HTTP2::nghttp2::Session::_new_client_xs",
				"callbacks_hv");
		}
	} STMT_END
;
#line 1170 "nghttp2.xs"
        /* Allocate our wrapper structure */
        Newxz(ps, 1, nghttp2_perl_session);

        /* Initialize send buffer */
        ps->send_buf_cap = 16384;
        ps->send_buf = (char *)malloc(ps->send_buf_cap);
        ps->send_buf_len = 0;

        /* Store user data */
        if (SvOK(user_data)) {
            ps->user_data = newSVsv(user_data);
        }

        /* Extract callbacks from hash */
        if (callbacks_hv) {
            if ((svp = hv_fetch(callbacks_hv, "on_begin_headers", 16, 0))) {
                ps->cb_on_begin_headers = newSVsv(*svp);
            }
            if ((svp = hv_fetch(callbacks_hv, "on_header", 9, 0))) {
                ps->cb_on_header = newSVsv(*svp);
            }
            if ((svp = hv_fetch(callbacks_hv, "on_frame_recv", 13, 0))) {
                ps->cb_on_frame_recv = newSVsv(*svp);
            }
            if ((svp = hv_fetch(callbacks_hv, "on_data_chunk_recv", 18, 0))) {
                ps->cb_on_data_chunk_recv = newSVsv(*svp);
            }
            if ((svp = hv_fetch(callbacks_hv, "on_stream_close", 15, 0))) {
                ps->cb_on_stream_close = newSVsv(*svp);
            }
        }

        /* Create nghttp2 callbacks */
        nghttp2_session_callbacks_new(&callbacks);
        nghttp2_session_callbacks_set_send_callback(callbacks, perl_send_callback);
        nghttp2_session_callbacks_set_on_begin_headers_callback(callbacks, perl_on_begin_headers_callback);
        nghttp2_session_callbacks_set_on_header_callback(callbacks, perl_on_header_callback);
        nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks, perl_on_frame_recv_callback);
        nghttp2_session_callbacks_set_on_data_chunk_recv_callback(callbacks, perl_on_data_chunk_recv_callback);
        nghttp2_session_callbacks_set_on_stream_close_callback(callbacks, perl_on_stream_close_callback);

        /* Create CLIENT session (difference from server) */
        rv = nghttp2_session_client_new(&ps->session, callbacks, ps);
        nghttp2_session_callbacks_del(callbacks);

        if (rv != 0) {
            if (ps->user_data) SvREFCNT_dec(ps->user_data);
            if (ps->cb_on_begin_headers) SvREFCNT_dec(ps->cb_on_begin_headers);
            if (ps->cb_on_header) SvREFCNT_dec(ps->cb_on_header);
            if (ps->cb_on_frame_recv) SvREFCNT_dec(ps->cb_on_frame_recv);
            if (ps->cb_on_data_chunk_recv) SvREFCNT_dec(ps->cb_on_data_chunk_recv);
            if (ps->cb_on_stream_close) SvREFCNT_dec(ps->cb_on_stream_close);
            free(ps->send_buf);
            Safefree(ps);
            croak("nghttp2_session_client_new failed: %s", nghttp2_strerror(rv));
        }

        /* Bless and return */
        RETVAL = sv_newmortal();
        sv_setref_pv(RETVAL, class, (void *)ps);
        SvREFCNT_inc(RETVAL);
#line 1986 "nghttp2.c"
	RETVAL = sv_2mortal(RETVAL);
	ST(0) = RETVAL;
    }
    XSRETURN(1);
}


XS_EUPXS(XS_Net__HTTP2__nghttp2__Session__submit_request_xs); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_Net__HTTP2__nghttp2__Session__submit_request_xs)
{
    dVAR; dXSARGS;
    if (items != 3)
       croak_xs_usage(cv,  "self, headers_av, body_sv");
    {
	SV *	self = ST(0)
;
	AV *	headers_av;
	SV *	body_sv = ST(2)
;
#line 1242 "nghttp2.xs"
        nghttp2_perl_session *ps;
        nghttp2_nv *nva;
        size_t nvlen;
        nghttp2_data_provider data_prd;
        nghttp2_data_provider *data_prd_ptr = NULL;
        nghttp2_perl_data_provider *dp = NULL;
        int32_t stream_id;
        I32 i;
        STRLEN body_len = 0;
#line 2016 "nghttp2.c"
	int	RETVAL;
	dXSTARG;

	STMT_START {
		SV* const xsub_tmp_sv = ST(1);
		SvGETMAGIC(xsub_tmp_sv);
		if (SvROK(xsub_tmp_sv) && SvTYPE(SvRV(xsub_tmp_sv)) == SVt_PVAV){
		    headers_av = (AV*)SvRV(xsub_tmp_sv);
		}
		else{
		    Perl_croak_nocontext("%s: %s is not an ARRAY reference",
				"Net::HTTP2::nghttp2::Session::_submit_request_xs",
				"headers_av");
		}
	} STMT_END
;
#line 1252 "nghttp2.xs"
        ps = (nghttp2_perl_session *)SvIV(SvRV(self));

        /* Build name-value array from Perl array of arrayrefs */
        nvlen = av_len(headers_av) + 1;
        Newxz(nva, nvlen, nghttp2_nv);

        for (i = 0; i < (I32)nvlen; i++) {
            SV **pair = av_fetch(headers_av, i, 0);
            if (pair && SvROK(*pair) && SvTYPE(SvRV(*pair)) == SVt_PVAV) {
                AV *pair_av = (AV *)SvRV(*pair);
                SV **name_sv = av_fetch(pair_av, 0, 0);
                SV **value_sv = av_fetch(pair_av, 1, 0);

                if (name_sv && value_sv) {
                    STRLEN name_len, value_len;
                    nva[i].name = (uint8_t *)SvPVbyte(*name_sv, name_len);
                    nva[i].namelen = name_len;
                    nva[i].value = (uint8_t *)SvPVbyte(*value_sv, value_len);
                    nva[i].valuelen = value_len;
                    nva[i].flags = NGHTTP2_NV_FLAG_NONE;
                }
            }
        }

        /* Check if we have a body to send */
        if (SvOK(body_sv) && SvPOK(body_sv)) {
            const char *body_ptr = SvPVbyte(body_sv, body_len);
            if (body_len > 0) {
                /* Create a simple one-shot data provider for the body */
                Newxz(dp, 1, nghttp2_perl_data_provider);
                dp->stream_id = 0;  /* Will be set after submit */
                dp->eof = 0;
                dp->deferred = 0;

                /* Store body as a callback that returns the body once */
                /* We'll use a closure-like approach: store body in user_data */
                dp->user_data = newSVsv(body_sv);
                dp->callback = NULL;  /* Special marker: use user_data as body */

                data_prd.source.ptr = dp;
                data_prd.read_callback = perl_data_source_read_callback;
                data_prd_ptr = &data_prd;
            }
        }

        stream_id = nghttp2_submit_request(ps->session, NULL, nva, nvlen, data_prd_ptr, NULL);

        Safefree(nva);

        if (stream_id < 0) {
            if (dp) {
                if (dp->user_data) SvREFCNT_dec(dp->user_data);
                Safefree(dp);
            }
            croak("nghttp2_submit_request failed: %s", nghttp2_strerror(stream_id));
        }

        /* Track data provider if we have one */
        if (dp) {
            dp->stream_id = stream_id;
            add_data_provider(ps, dp);
        }

        RETVAL = stream_id;
#line 2098 "nghttp2.c"
	XSprePUSH;
	PUSHi((IV)RETVAL);
    }
    XSRETURN(1);
}


XS_EUPXS(XS_Net__HTTP2__nghttp2__Session_submit_rst_stream); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_Net__HTTP2__nghttp2__Session_submit_rst_stream)
{
    dVAR; dXSARGS;
    if (items != 3)
       croak_xs_usage(cv,  "self, stream_id, error_code");
    {
	SV *	self = ST(0)
;
	int	stream_id = (int)SvIV(ST(1))
;
	unsigned int	error_code = (unsigned int)SvUV(ST(2))
;
#line 1326 "nghttp2.xs"
        nghttp2_perl_session *ps;
        int rv;
#line 2122 "nghttp2.c"
	int	RETVAL;
	dXSTARG;
#line 1329 "nghttp2.xs"
        ps = (nghttp2_perl_session *)SvIV(SvRV(self));
        rv = nghttp2_submit_rst_stream(ps->session, NGHTTP2_FLAG_NONE, stream_id, error_code);
        if (rv != 0) {
            croak("nghttp2_submit_rst_stream failed: %s", nghttp2_strerror(rv));
        }
        RETVAL = rv;
#line 2132 "nghttp2.c"
	XSprePUSH;
	PUSHi((IV)RETVAL);
    }
    XSRETURN(1);
}


XS_EUPXS(XS_Net__HTTP2__nghttp2__Session_submit_ping); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_Net__HTTP2__nghttp2__Session_submit_ping)
{
    dVAR; dXSARGS;
    if (items != 3)
       croak_xs_usage(cv,  "self, ack, opaque_data");
    {
	SV *	self = ST(0)
;
	int	ack = (int)SvIV(ST(1))
;
	SV *	opaque_data = ST(2)
;
#line 1345 "nghttp2.xs"
        nghttp2_perl_session *ps;
        STRLEN len;
        const uint8_t *data;
        uint8_t flags;
        int rv;
#line 2159 "nghttp2.c"
	int	RETVAL;
	dXSTARG;
#line 1351 "nghttp2.xs"
        ps = (nghttp2_perl_session *)SvIV(SvRV(self));
        flags = ack ? NGHTTP2_FLAG_ACK : NGHTTP2_FLAG_NONE;

        if (SvOK(opaque_data)) {
            data = (const uint8_t *)SvPVbyte(opaque_data, len);
            if (len != 8) {
                croak("PING opaque_data must be exactly 8 bytes");
            }
        } else {
            data = NULL;
        }

        rv = nghttp2_submit_ping(ps->session, flags, data);
        if (rv != 0) {
            croak("nghttp2_submit_ping failed: %s", nghttp2_strerror(rv));
        }
        RETVAL = rv;
#line 2180 "nghttp2.c"
	XSprePUSH;
	PUSHi((IV)RETVAL);
    }
    XSRETURN(1);
}


XS_EUPXS(XS_Net__HTTP2__nghttp2__Session_submit_window_update); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_Net__HTTP2__nghttp2__Session_submit_window_update)
{
    dVAR; dXSARGS;
    if (items != 3)
       croak_xs_usage(cv,  "self, stream_id, window_size_increment");
    {
	SV *	self = ST(0)
;
	int	stream_id = (int)SvIV(ST(1))
;
	int	window_size_increment = (int)SvIV(ST(2))
;
#line 1378 "nghttp2.xs"
        nghttp2_perl_session *ps;
        int rv;
#line 2204 "nghttp2.c"
	int	RETVAL;
	dXSTARG;
#line 1381 "nghttp2.xs"
        ps = (nghttp2_perl_session *)SvIV(SvRV(self));
        rv = nghttp2_submit_window_update(ps->session, NGHTTP2_FLAG_NONE, stream_id, window_size_increment);
        if (rv != 0) {
            croak("nghttp2_submit_window_update failed: %s", nghttp2_strerror(rv));
        }
        RETVAL = rv;
#line 2214 "nghttp2.c"
	XSprePUSH;
	PUSHi((IV)RETVAL);
    }
    XSRETURN(1);
}

#ifdef __cplusplus
extern "C" {
#endif
XS_EXTERNAL(boot_Net__HTTP2__nghttp2); /* prototype to pass -Wmissing-prototypes */
XS_EXTERNAL(boot_Net__HTTP2__nghttp2)
{
#if PERL_VERSION_LE(5, 21, 5)
    dVAR; dXSARGS;
#else
    dVAR; dXSBOOTARGSXSAPIVERCHK;
#endif
#if PERL_VERSION_LE(5, 8, 999) /* PERL_VERSION_LT is 5.33+ */
    char* file = __FILE__;
#else
    const char* file = __FILE__;
#endif

    PERL_UNUSED_VAR(file);

    PERL_UNUSED_VAR(cv); /* -W */
    PERL_UNUSED_VAR(items); /* -W */
#if PERL_VERSION_LE(5, 21, 5)
    XS_VERSION_BOOTCHECK;
#  ifdef XS_APIVERSION_BOOTCHECK
    XS_APIVERSION_BOOTCHECK;
#  endif
#endif

        newXS_deffile("Net::HTTP2::nghttp2::_check_nghttp2_available", XS_Net__HTTP2__nghttp2__check_nghttp2_available);
        newXS_deffile("Net::HTTP2::nghttp2::version_string", XS_Net__HTTP2__nghttp2_version_string);
        newXS_deffile("Net::HTTP2::nghttp2::version_num", XS_Net__HTTP2__nghttp2_version_num);
        newXS_deffile("Net::HTTP2::nghttp2::NGHTTP2_ERR_WOULDBLOCK", XS_Net__HTTP2__nghttp2_NGHTTP2_ERR_WOULDBLOCK);
        newXS_deffile("Net::HTTP2::nghttp2::NGHTTP2_ERR_CALLBACK_FAILURE", XS_Net__HTTP2__nghttp2_NGHTTP2_ERR_CALLBACK_FAILURE);
        newXS_deffile("Net::HTTP2::nghttp2::NGHTTP2_ERR_DEFERRED", XS_Net__HTTP2__nghttp2_NGHTTP2_ERR_DEFERRED);
        newXS_deffile("Net::HTTP2::nghttp2::NGHTTP2_FLAG_NONE", XS_Net__HTTP2__nghttp2_NGHTTP2_FLAG_NONE);
        newXS_deffile("Net::HTTP2::nghttp2::NGHTTP2_FLAG_END_STREAM", XS_Net__HTTP2__nghttp2_NGHTTP2_FLAG_END_STREAM);
        newXS_deffile("Net::HTTP2::nghttp2::NGHTTP2_FLAG_END_HEADERS", XS_Net__HTTP2__nghttp2_NGHTTP2_FLAG_END_HEADERS);
        newXS_deffile("Net::HTTP2::nghttp2::NGHTTP2_FLAG_ACK", XS_Net__HTTP2__nghttp2_NGHTTP2_FLAG_ACK);
        newXS_deffile("Net::HTTP2::nghttp2::NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS", XS_Net__HTTP2__nghttp2_NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS);
        newXS_deffile("Net::HTTP2::nghttp2::NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE", XS_Net__HTTP2__nghttp2_NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE);
        newXS_deffile("Net::HTTP2::nghttp2::NGHTTP2_SETTINGS_MAX_FRAME_SIZE", XS_Net__HTTP2__nghttp2_NGHTTP2_SETTINGS_MAX_FRAME_SIZE);
        newXS_deffile("Net::HTTP2::nghttp2::NGHTTP2_SETTINGS_ENABLE_PUSH", XS_Net__HTTP2__nghttp2_NGHTTP2_SETTINGS_ENABLE_PUSH);
        newXS_deffile("Net::HTTP2::nghttp2::NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL", XS_Net__HTTP2__nghttp2_NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL);
        newXS_deffile("Net::HTTP2::nghttp2::NGHTTP2_DATA_FLAG_NONE", XS_Net__HTTP2__nghttp2_NGHTTP2_DATA_FLAG_NONE);
        newXS_deffile("Net::HTTP2::nghttp2::NGHTTP2_DATA_FLAG_EOF", XS_Net__HTTP2__nghttp2_NGHTTP2_DATA_FLAG_EOF);
        newXS_deffile("Net::HTTP2::nghttp2::NGHTTP2_DATA_FLAG_NO_END_STREAM", XS_Net__HTTP2__nghttp2_NGHTTP2_DATA_FLAG_NO_END_STREAM);
        newXS_deffile("Net::HTTP2::nghttp2::NGHTTP2_DATA_FLAG_NO_COPY", XS_Net__HTTP2__nghttp2_NGHTTP2_DATA_FLAG_NO_COPY);
        newXS_deffile("Net::HTTP2::nghttp2::NGHTTP2_DATA", XS_Net__HTTP2__nghttp2_NGHTTP2_DATA);
        newXS_deffile("Net::HTTP2::nghttp2::NGHTTP2_HEADERS", XS_Net__HTTP2__nghttp2_NGHTTP2_HEADERS);
        newXS_deffile("Net::HTTP2::nghttp2::NGHTTP2_SETTINGS", XS_Net__HTTP2__nghttp2_NGHTTP2_SETTINGS);
        newXS_deffile("Net::HTTP2::nghttp2::NGHTTP2_PUSH_PROMISE", XS_Net__HTTP2__nghttp2_NGHTTP2_PUSH_PROMISE);
        newXS_deffile("Net::HTTP2::nghttp2::NGHTTP2_GOAWAY", XS_Net__HTTP2__nghttp2_NGHTTP2_GOAWAY);
        newXS_deffile("Net::HTTP2::nghttp2::Session::_new_server_xs", XS_Net__HTTP2__nghttp2__Session__new_server_xs);
        newXS_deffile("Net::HTTP2::nghttp2::Session::DESTROY", XS_Net__HTTP2__nghttp2__Session_DESTROY);
        newXS_deffile("Net::HTTP2::nghttp2::Session::mem_recv", XS_Net__HTTP2__nghttp2__Session_mem_recv);
        newXS_deffile("Net::HTTP2::nghttp2::Session::mem_send", XS_Net__HTTP2__nghttp2__Session_mem_send);
        newXS_deffile("Net::HTTP2::nghttp2::Session::want_read", XS_Net__HTTP2__nghttp2__Session_want_read);
        newXS_deffile("Net::HTTP2::nghttp2::Session::want_write", XS_Net__HTTP2__nghttp2__Session_want_write);
        newXS_deffile("Net::HTTP2::nghttp2::Session::submit_settings", XS_Net__HTTP2__nghttp2__Session_submit_settings);
        newXS_deffile("Net::HTTP2::nghttp2::Session::_submit_response_with_body", XS_Net__HTTP2__nghttp2__Session__submit_response_with_body);
        newXS_deffile("Net::HTTP2::nghttp2::Session::_submit_response_no_body", XS_Net__HTTP2__nghttp2__Session__submit_response_no_body);
        newXS_deffile("Net::HTTP2::nghttp2::Session::resume_data", XS_Net__HTTP2__nghttp2__Session_resume_data);
        newXS_deffile("Net::HTTP2::nghttp2::Session::get_stream_user_data", XS_Net__HTTP2__nghttp2__Session_get_stream_user_data);
        newXS_deffile("Net::HTTP2::nghttp2::Session::set_stream_user_data", XS_Net__HTTP2__nghttp2__Session_set_stream_user_data);
        newXS_deffile("Net::HTTP2::nghttp2::Session::terminate_session", XS_Net__HTTP2__nghttp2__Session_terminate_session);
        newXS_deffile("Net::HTTP2::nghttp2::Session::_submit_response_streaming", XS_Net__HTTP2__nghttp2__Session__submit_response_streaming);
        newXS_deffile("Net::HTTP2::nghttp2::Session::submit_data", XS_Net__HTTP2__nghttp2__Session_submit_data);
        newXS_deffile("Net::HTTP2::nghttp2::Session::is_stream_deferred", XS_Net__HTTP2__nghttp2__Session_is_stream_deferred);
        newXS_deffile("Net::HTTP2::nghttp2::Session::_clear_deferred", XS_Net__HTTP2__nghttp2__Session__clear_deferred);
        newXS_deffile("Net::HTTP2::nghttp2::Session::_new_client_xs", XS_Net__HTTP2__nghttp2__Session__new_client_xs);
        newXS_deffile("Net::HTTP2::nghttp2::Session::_submit_request_xs", XS_Net__HTTP2__nghttp2__Session__submit_request_xs);
        newXS_deffile("Net::HTTP2::nghttp2::Session::submit_rst_stream", XS_Net__HTTP2__nghttp2__Session_submit_rst_stream);
        newXS_deffile("Net::HTTP2::nghttp2::Session::submit_ping", XS_Net__HTTP2__nghttp2__Session_submit_ping);
        newXS_deffile("Net::HTTP2::nghttp2::Session::submit_window_update", XS_Net__HTTP2__nghttp2__Session_submit_window_update);
#if PERL_VERSION_LE(5, 21, 5)
#  if PERL_VERSION_GE(5, 9, 0)
    if (PL_unitcheckav)
        call_list(PL_scopestack_ix, PL_unitcheckav);
#  endif
    XSRETURN_YES;
#else
    Perl_xs_boot_epilog(aTHX_ ax);
#endif
}

#ifdef __cplusplus
}
#endif
