diff --git a/modules/njet-python-module/config b/modules/njet-python-module/config new file mode 100644 index 0000000000000000000000000000000000000000..7496fb996265dbd531e07cfd9c605009da35aaec --- /dev/null +++ b/modules/njet-python-module/config @@ -0,0 +1,56 @@ +njt_addon_name="njt_python_module" + +if [ "$NGX_SYSTEM" = "Darwin" ]; then + CFLAGS="$CFLAGS -D_XOPEN_SOURCE" +fi + +PYTHON_CONFIG=python3.8-config +PYTHON_CONFIG=${PYTHON_CONFIG:-python-config} + +PYTHON_CORE_INCS=`$PYTHON_CONFIG --includes|sed -e 's/^-I//g' -e 's/ -I/ /g'` +PYTHON_CORE_LIBS=`$PYTHON_CONFIG --ldflags --embed` + +PYTHON_CORE_DEPS="$njt_addon_dir/src/njt_python.h" +PYTHON_CORE_SRCS="$njt_addon_dir/src/njt_python.c" + +PYTHON_STREAM_PROTO_DEPS="$njt_addon_dir/src/njt_stream_proto_python_session.h" +PYTHON_STREAM_PROTO_SRCS="$njt_addon_dir/src/njt_stream_proto_python_module.c \ + $njt_addon_dir/src/njt_stream_proto_python_session.c" + + +njt_module_incs=$PYTHON_CORE_INCS +njt_module_libs=$PYTHON_CORE_LIBS + +if [ $njt_module_link = DYNAMIC ] ; then + + njt_module_type=CORE + njt_module_name=njt_python_module + njt_module_deps=$PYTHON_CORE_DEPS + njt_module_srcs=$PYTHON_CORE_SRCS + + if [ $STREAM != NO ]; then + njt_module_name="$njt_module_name njt_stream_proto_python_module" + njt_module_deps="$njt_module_deps $PYTHON_STREAM_PROTO_DEPS" + njt_module_srcs="$njt_module_srcs $PYTHON_STREAM_PROTO_SRCS" + fi + + . auto/module + +else + njt_module_type=CORE + njt_module_name=njt_python_module + njt_module_deps=$PYTHON_CORE_DEPS + njt_module_srcs=$PYTHON_CORE_SRCS + + . auto/module + + if [ $STREAM != NO ]; then + njt_module_type=STREAM + njt_module_name="$njt_module_name njt_stream_proto_python_module" + njt_module_deps="$njt_module_deps $PYTHON_STREAM_PROTO_DEPS" + njt_module_srcs="$njt_module_srcs $PYTHON_STREAM_PROTO_SRCS" + + . auto/module + fi +fi + have=PY_SSIZE_T_CLEAN . auto/have \ No newline at end of file diff --git a/modules/njet-python-module/src/njt_python.c b/modules/njet-python-module/src/njt_python.c new file mode 100644 index 0000000000000000000000000000000000000000..9ac9416a50fcdbedfc7819bafb46d7a418e1e185 --- /dev/null +++ b/modules/njet-python-module/src/njt_python.c @@ -0,0 +1,1146 @@ + +/* + * Copyright (C) Roman Arutyunyan + * Copyright (C) 2021-2024 TMLake(Beijing) Technology Co., Ltd. + */ + + +#include + +/* + * The Python.h file is included first in contrast with normal njet + * practice. Python headers deliberately define the following macros: + * _GNU_SOURCE, _POSIX_C_SOURCE, _XOPEN_SOURCE. + * When compiling on Linux, they are defined by njet or system headers + * with Python possibly redefining them. Since njet is built with -Werror + * compilation option, macros redefinition causes compilation errors. + */ + +#include +#include +#include +#include +#include "njt_python.h" + + +typedef struct { + PyObject *ns; + PyThreadState *main_ps; + njt_array_t *init_codes; //init_worker func + size_t stack_size; + njt_resolver_t *resolver; +} njt_python_conf_t; + + +struct njt_python_ctx_s { + PyObject *code; + PyObject *ns; + PyObject *local; + PyObject *result; + + njt_event_t *wake; + njt_pool_t *pool; + njt_log_t *log; + + njt_resolver_t *resolver; + njt_msec_t resolver_timeout; + + size_t stack_size; + +#if !(NJT_PYTHON_SYNC) + + void *stack; + + ucontext_t uc; + ucontext_t ruc; + + int recursion_depth; + struct _frame *frame; + + struct _frame *func_frame; // should be dec ref + PyThreadState *ps; // should be return or deleted + PyThreadState *main_ps; + //PyObject *exc_type; by stdanley + //PyObject *exc_value; + //PyObject *exc_traceback; + + njt_uint_t terminate; /* unsigned terminate:1; */ + +#endif +}; + + +typedef struct { + PyObject *ns; + u_char *name; + PyThreadState *ps; +} njt_python_ns_cleanup_t; + + +#if !(NJT_PYTHON_SYNC) +static njt_python_ctx_t *njt_python_set_ctx(njt_python_ctx_t *ctx); +static void njt_python_task_handler(); +static void njt_python_cleanup_ctx(void *data); +#endif +static char *njt_python_include_file(njt_conf_t *cf, PyObject *ns, char *file); +static char *njt_python_load_app(njt_conf_t *cf, PyObject *ns, char *file); +static void njt_python_decref(void *data); +//static PyObject *njt_python_init_namespace(njt_conf_t *cf); +static void njt_python_cleanup_namespace(void *data); + +static void *njt_python_create_conf(njt_cycle_t *cycle); +static char *njt_python_init_conf(njt_cycle_t *cycle, void *conf); +static njt_int_t njt_python_init_worker(njt_cycle_t *cycle); +//static char * njt_python_resolver(njt_conf_t *cf, njt_command_t *cmd, void *conf); + +static njt_command_t njt_python_commands[] = { +/* + { njt_string("python_resolver"), + NJT_MAIN_CONF|NJT_DIRECT_CONF|NJT_CONF_TAKE1, + njt_python_resolver, + 0, + 0, + NULL }, +*/ + { njt_string("python_include"), + NJT_MAIN_CONF|NJT_CONF_TAKE1, + njt_python_include_set_slot, + 0, + 0, + NULL }, + + { njt_string("python_stack_size"), + NJT_MAIN_CONF|NJT_DIRECT_CONF|NJT_CONF_TAKE1, + njt_conf_set_size_slot, + 0, + offsetof(njt_python_conf_t, stack_size), + NULL }, + + njt_null_command +}; +/* +static char * +njt_python_resolver(njt_conf_t *cf, njt_command_t *cmd, void *conf) +{ + njt_python_conf_t *pcf = (njt_python_conf_t *)conf; + + njt_str_t *value; + + if (pcf->resolver) { + return "is duplicate"; + } + + value = cf->args->elts; + + pcf->resolver = njt_resolver_create(cf, &value[1], cf->args->nelts - 1); + if (pcf->resolver == NULL) { + return NJT_CONF_ERROR; + } + njt_log_error(NJT_LOG_ERR,cf->log,0,"set resolver:%p,%p",pcf,pcf->resolver); + return NJT_CONF_OK; +} +*/ +static njt_core_module_t njt_python_module_ctx = { + njt_string("python"), + njt_python_create_conf, + njt_python_init_conf +}; + + +njt_module_t njt_python_module = { + NJT_MODULE_V1, + &njt_python_module_ctx, /* module context */ + njt_python_commands, /* module directives */ + NJT_CORE_MODULE, /* module type */ + NULL, /* init master */ + NULL, /* init module */ + njt_python_init_worker, /* init process */ + NULL, /* init thread */ + NULL, /* exit thread */ + NULL, /* exit process */ + NULL, /* exit master */ + NJT_MODULE_V1_PADDING +}; + +/* +static int py_trace(PyObject *obj, PyFrameObject *frame, int what, PyObject *arg) { + njt_log_error(NJT_LOG_DEBUG,ngx_cycle->log,0,"f:%p,w:%d",frame,what); + return 0; +} +*/ + +#if !(NJT_PYTHON_SYNC) + +njt_python_ctx_t * volatile njt_python_ctx; + + +njt_python_ctx_t * +njt_python_get_ctx() +{ + return njt_python_ctx; +} + + +static njt_python_ctx_t * +njt_python_set_ctx(njt_python_ctx_t *ctx) +{ + njt_python_ctx_t *pctx; + + pctx = njt_python_ctx; + njt_python_ctx = ctx; + + return pctx; +} + + +void * +njt_python_add_init_code(njt_conf_t *cf,PyObject* init_code){ + njt_python_conf_t *pcf= (njt_python_conf_t*)njt_get_conf(cf->cycle->conf_ctx,njt_python_module); + PyObject **pcode = njt_array_push(pcf->init_codes); + + if (pcode == NULL) { + return NJT_CONF_ERROR; + } + *pcode= init_code; + return NJT_CONF_OK; +} + + +njt_int_t +njt_python_yield() +{ + njt_python_ctx_t *ctx; + + ctx = njt_python_get_ctx(); + if (ctx == NULL) { + PyErr_SetString(PyExc_RuntimeError, "blocking calls are not allowed"); + return NJT_ERROR; + } + + njt_log_debug1(NJT_LOG_DEBUG_CORE, ctx->log, 0, "python yield,with ctx:%p",ctx); + + if (swapcontext(&ctx->uc, &ctx->ruc)) { + PyErr_SetFromErrno(PyExc_OSError); + return NJT_ERROR; + } + + njt_log_debug0(NJT_LOG_DEBUG_CORE, ctx->log, 0, "python regain"); + + if (ctx->terminate) { + PyErr_SetString(PyExc_RuntimeError, "terminated"); + njt_log_debug0(NJT_LOG_DEBUG_CORE, ctx->log, 0, "python terminate"); + return NJT_ERROR; + } + + return NJT_OK; +} + + +void +njt_python_wakeup(njt_python_ctx_t *ctx) +{ + if (!ctx->terminate) { + // njt_log_debug2(NJT_LOG_DEBUG_CORE, ctx->log, 0, "python wakeup,with ctx:%p,wake event:%p",ctx,ctx->wake); + njt_post_event(ctx->wake, &njt_posted_events); + } +} + +#endif + + +njt_python_ctx_t * +njt_python_create_ctx(njt_pool_t *pool, njt_log_t *log) +{ + njt_python_ctx_t *ctx; + njt_python_conf_t *pcf; +#if !(NJT_PYTHON_SYNC) + njt_pool_cleanup_t *cln; +#endif + + pcf = (njt_python_conf_t *) njt_get_conf(njt_cycle->conf_ctx, + njt_python_module); + if (pcf->ns == NULL) { + return NULL; + } + + ctx = njt_pcalloc(pool, sizeof(njt_python_ctx_t)); + if (ctx == NULL) { + return NULL; + } + +#if !(NJT_PYTHON_SYNC) + + cln = njt_pool_cleanup_add(pool, 0); + if (cln == NULL) { + return NULL; + } + + cln->handler = njt_python_cleanup_ctx; + cln->data = ctx; + +#endif + + ctx->pool = pool; + ctx->log = log; + ctx->ns = pcf->ns; + ctx->stack_size = pcf->stack_size; + ctx->main_ps = pcf->main_ps; + ctx->local = ctx->ns; + + return ctx; +} + + +#if !(NJT_PYTHON_SYNC) +// static void njt_python_reset_thread_ctx(void *data){ + +// njt_python_ctx_t *ctx = data; +// njt_python_conf_t *pcf; + +// if (ctx->terminate) { +// return; +// } + +// pcf = (njt_python_conf_t *) njt_get_conf(njt_cycle->conf_ctx, +// njt_python_module); +// if (pcf->ns == NULL) { +// return; +// } + +// void *stack = ctx->stack; +// njt_pool_t *pool = ctx->pool; +// njt_log_t *log = ctx->log; +// njt_memzero(ctx, sizeof(njt_python_ctx_t)); +// ctx->stack = stack; +// ctx->pool = pool; +// ctx->log = log; +// ctx->ns = pcf->ns; +// ctx->stack_size = pcf->stack_size; +// // njt_memzero(ctx->stack, ctx->stack_size); +// ctx->main_ps = pcf->main_ps; +// ctx->local = ctx->ns; + +// } + + +// void +// njt_python_cleanup_thread_ctx(njt_python_ctx_t *ctx) +// { +// PyObject *result; + +// if (ctx->terminate) { +// return; +// } + +// result = ctx->result; + +// Py_XDECREF(result); +// njt_log_debug2(NJT_LOG_DEBUG_CORE, njt_cycle->log, 0, "python_cleanup_ctx,f:%d,l:%d",Py_REFCNT(ctx->func_frame),Py_REFCNT(ctx->local)); +// // f should be 1 , l:2 + +// PyThreadState_Swap(ctx->main_ps); +// Py_XDECREF(ctx->func_frame); +// //f use l ,so after dealloc f, l should be 1 + +// printf("ctx->local ref count: %ld\n", Py_REFCNT(ctx->local)); +// if (ctx->local != ctx->ns) { +// Py_XDECREF(ctx->local); +// } + +// if (ctx->ps) { +// printf("ctx-state-clear\n"); +// PyThreadState_Clear(ctx->ps); +// PyThreadState_Delete(ctx->ps); +// } + +// ctx->ps = NULL; + +// njt_python_reset_thread_ctx(ctx); +// } + + +static void +njt_python_cleanup_ctx(void *data) +{ + njt_python_ctx_t *ctx = data; + + PyObject *result; + + ctx->terminate = 1; + + result = ctx->result; + + while (result == NJT_PYTHON_AGAIN) { + result = njt_python_eval(ctx, NULL, ctx->wake, NULL, NULL); + } + + Py_XDECREF(result); + // njt_log_debug2(NJT_LOG_DEBUG_CORE, njt_cycle->log, 0, "python_cleanup_ctx,f:%d,l:%d",Py_REFCNT(ctx->func_frame),Py_REFCNT(ctx->local)); + // f should be 1 , l:2 + + PyThreadState_Swap(ctx->main_ps); + Py_XDECREF(ctx->func_frame); + //f use l ,so after dealloc f, l should be 1 + + if (ctx->local != ctx->ns) { + Py_XDECREF(ctx->local); + } + if (ctx->ps) { + PyThreadState_Clear(ctx->ps); + PyThreadState_Delete(ctx->ps); + } + /* + PyObject *forum= PyModule_GetDict(PyDict_GetItemString(ctx->ns,"forum")); + PyObject * keys= PyDict_Keys(forum); + int idx,cnt=PyList_GET_SIZE(keys); + for (idx=0;idxlog, 0, "python_cleanup_ctx,ns key:%s", _PyUnicode_AsString(PyList_GetItem(keys,idx))); + */ + +} + +#endif + + +PyObject * +njt_python_eval(njt_python_ctx_t *ctx, PyObject *code, njt_event_t *wake, PyObject* arg, char *key) +{ + PyObject *result; + +#if !(NJT_PYTHON_SYNC) + + int recursion_depth; + //PyObject *exc_value, *exc_traceback; + struct _frame *frame; + PyThreadState *ps; + njt_python_ctx_t *pctx; + + if (wake) { + + if (ctx->result == NULL) { + if (ctx->stack == NULL) { + ctx->stack = njt_palloc(ctx->pool, ctx->stack_size); + if (ctx->stack == NULL) { + return NULL; + } + } + + if (getcontext(&ctx->uc) == -1) { + njt_log_debug0(NJT_LOG_DEBUG_CORE, ctx->log, njt_errno, + "getcontext() failed"); + return NULL; + } + + ctx->uc.uc_stack.ss_size = ctx->stack_size; + ctx->uc.uc_stack.ss_sp = ctx->stack; + ctx->uc.uc_link = &ctx->ruc; + + makecontext(&ctx->uc, &njt_python_task_handler, 0); + + ctx->code = code; + ctx->wake = wake; + ctx->result = NJT_PYTHON_AGAIN; + ctx->ps = PyThreadState_New(PyInterpreterState_Main()); + ctx->local = PyDict_New(); + if (arg && key) { + PyDict_SetItemString(ctx->local, key, arg); // free in clean ctx + //tips: we leave local's clear to free arg later + Py_DECREF(arg); + } + ctx->func_frame = PyFrame_New(ctx->ps, (PyCodeObject*)code, ctx->ns, ctx->local); + ctx->frame = ctx->func_frame; + } + PyThreadState_Swap(ctx->ps); + ps = PyThreadState_GET(); //hack + pctx = njt_python_set_ctx(ctx); + + recursion_depth = ps->recursion_depth; + frame = ps->frame; + + ps->recursion_depth = ctx->recursion_depth; + ps->frame = ctx->frame; + + njt_log_debug3(NJT_LOG_DEBUG_CORE, ctx->log, 0, "run vm use frame:%p,stack:%d,overflow:%d",ps->frame,ps->recursion_depth,ps->overflowed); + if (swapcontext(&ctx->ruc, &ctx->uc) == -1) { + njt_log_error(NJT_LOG_ERR, ctx->log, njt_errno, + "swapcontext() failed"); + } + + ctx->recursion_depth = ps->recursion_depth; + ctx->frame = ps->frame; + + ps->recursion_depth = recursion_depth; + ps->frame = frame; + + njt_log_debug1(NJT_LOG_DEBUG_CORE, ctx->log, 0, "after eval,restore frame:%p",ps->frame); + (void) njt_python_set_ctx(pctx); + + result = ctx->result; + if (result != NJT_PYTHON_AGAIN) { + ctx->code = NULL; + ctx->wake = NULL; + ctx->result = NULL; + } + + njt_log_debug2(NJT_LOG_DEBUG_CORE, ctx->log, 0, "python http eval return :ctx:%p,ret:%p",ctx,result); + return result; + } + + pctx = njt_python_set_ctx(NULL); + +#endif + + result = PyEval_EvalCode(code, ctx->ns, ctx->ns); + if (result == NULL) { + njt_log_error(NJT_LOG_ERR, ctx->log, 0, "python error pos 1: %s", + njt_python_get_error(ctx->pool)); + } + +#if !(NJT_PYTHON_SYNC) + (void) njt_python_set_ctx(pctx); +#endif + + return result; +} + + +#if !(NJT_PYTHON_SYNC) + +static void +njt_python_task_handler() +{ + njt_python_ctx_t *ctx; + + ctx = njt_python_get_ctx(); + PyThreadState_Swap(ctx->ps); + njt_log_debug0(NJT_LOG_DEBUG_CORE, ctx->log, 0, "python task handler"); + //ctx->result = PyObject_CallFunctionObjArgs(ctx->code, ctx->arg,NULL); + ctx->result = PyEval_EvalFrame(ctx->frame); + //ctx->result = PyEval_EvalCode(ctx->code, ctx->ns, ctx->ns); + if (ctx->result == NULL) { + njt_log_debug1(NJT_LOG_DEBUG_CORE, ctx->log, 0, "python result null pos 2: %s", + njt_python_get_error(ctx->pool)); + } + njt_log_debug2(NJT_LOG_DEBUG_CORE, ctx->log, 0, "python task handler,ctx:%p,ret:%p",ctx,ctx->result); +} + +#endif + + +void +njt_python_set_resolver(njt_python_ctx_t *ctx, njt_resolver_t *resolver, + njt_msec_t timeout) +{ + ctx->resolver = resolver; + ctx->resolver_timeout = timeout; +} + + +njt_resolver_t * +njt_python_get_resolver(njt_python_ctx_t *ctx, njt_msec_t *timeout) +{ + *timeout = ctx->resolver_timeout; + return ctx->resolver; +} + + +PyObject * +njt_python_set_value(njt_python_ctx_t *ctx, const char *name, PyObject *value) +{ + PyObject *old; + old = PyDict_GetItemString(ctx->local, name); + + if (old == NULL) { + if (PyDict_SetItemString(ctx->local, name, value) < 0) { + njt_log_error(NJT_LOG_ERR, ctx->log, 0, + "python error pos 3: %s", njt_python_get_error(ctx->pool)); + } + } + + return old; +} + + +void +njt_python_reset_value(njt_python_ctx_t *ctx, const char *name, PyObject *old) +{ + if (old == NULL) { + if (PyDict_DelItemString(ctx->local, name) < 0) { + njt_log_error(NJT_LOG_ERR, ctx->log, 0, + "python error pos 4: %s", njt_python_get_error(ctx->pool)); + } + } +} + + +njt_int_t +njt_python_active(njt_conf_t *cf) +{ + njt_python_conf_t *pcf; + + pcf = (njt_python_conf_t *) njt_get_conf(cf->cycle->conf_ctx, + njt_python_module); + + return pcf->ns ? NJT_OK : NJT_DECLINED; +} + + +char * +njt_python_set_slot(njt_conf_t *cf, njt_command_t *cmd, void *conf) +{ + PyObject *ret, *ns; + njt_str_t *value; + + ns = njt_python_init_namespace(cf); + if (ns == NULL) { + return NJT_CONF_ERROR; + } + + value = cf->args->elts; + + ret = PyRun_StringFlags((char *) value[1].data, Py_file_input, ns, ns, + NULL); + if (ret == NULL) { + njt_conf_log_error(NJT_LOG_EMERG, cf, 0, "python error pos 5: %s", + njt_python_get_error(cf->pool)); + return NJT_CONF_ERROR; + } + + Py_DECREF(ret); + + return NJT_CONF_OK; +} + + +char * +njt_python_include_set_slot(njt_conf_t *cf, njt_command_t *cmd, void *conf) +{ + char *rv; + PyObject *ns; + njt_int_t n; + njt_str_t *value, file, name; + njt_glob_t gl; + + ns = njt_python_init_namespace(cf); + if (ns == NULL) { + return NJT_CONF_ERROR; + } + + value = cf->args->elts; + file = value[1]; + + njt_log_debug2(NJT_LOG_DEBUG_CORE, cf->log, 0, "python_include %s,%p", + file.data,conf); + + if (njt_conf_full_name(cf->cycle, &file, 1) != NJT_OK) { + return NJT_CONF_ERROR; + } + + if (strpbrk((char *) file.data, "*?[") == NULL) { + + njt_log_debug1(NJT_LOG_DEBUG_CORE, cf->log, 0, "python_include %s", + file.data); + if (memcmp(file.data+file.len-3,".py",3)==0) + return njt_python_include_file(cf, ns, (char *) file.data); + else + return njt_python_load_app(cf, ns, (char *) file.data); + } + + njt_memzero(&gl, sizeof(njt_glob_t)); + + gl.pattern = file.data; + gl.log = cf->log; + gl.test = 1; + + if (njt_open_glob(&gl) != NJT_OK) { + njt_conf_log_error(NJT_LOG_EMERG, cf, njt_errno, + njt_open_glob_n " \"%s\" failed", file.data); + return NJT_CONF_ERROR; + } + + rv = NJT_CONF_OK; + + for ( ;; ) { + n = njt_read_glob(&gl, &name); + + if (n != NJT_OK) { + break; + } + + file.len = name.len++; + file.data = njt_pstrdup(cf->pool, &name); + if (file.data == NULL) { + return NJT_CONF_ERROR; + } + + njt_log_debug1(NJT_LOG_DEBUG_CORE, cf->log, 0, "python_include %s", + file.data); + //hack, + //todo: howto check it's a library, instead of py file? + if (memcmp(file.data+file.len-3,".py",3)==0) + rv = njt_python_include_file(cf, ns, (char *) file.data); + else + rv = njt_python_load_app(cf, ns, (char *) file.data); + if (rv != NJT_CONF_OK) { + break; + } + } + + njt_close_glob(&gl); + + return rv; +} + +static char *njt_python_load_app(njt_conf_t *cf, PyObject *ns, char *file){ + void* hd= dlopen(file,RTLD_LAZY); + if (!hd) { + njt_conf_log_error(NJT_LOG_EMERG, cf, njt_errno, + "fopen() \"%s\" failed", file); + return NJT_CONF_ERROR; + } + else njt_conf_log_error(NJT_LOG_INFO, cf, njt_errno, + "fopen() \"%s\" ok", file); + + PyObject* ( * md)() =dlsym(hd,"PyInit_run_doc"); + if (!md){ + njt_conf_log_error(NJT_LOG_EMERG, cf, njt_errno, + "no sym() \"%s\" failed", "PyInit_run_doc"); + return NJT_CONF_ERROR; + } + if (NULL==(md)()){ + njt_conf_log_error(NJT_LOG_EMERG, cf, 0, "python error pos 6: %s", + njt_python_get_error(cf->pool)); + return NJT_CONF_ERROR; + } + PyObject *pmodule = PyImport_ImportModuleEx("run_doc",ns,ns,0); + if (NULL==pmodule){ + njt_conf_log_error(NJT_LOG_EMERG, cf, 0, "python error pos 7: %s", + njt_python_get_error(cf->pool)); + return NJT_CONF_ERROR; + } + + PyDict_SetItemString(ns,"run_doc",pmodule); + + return NJT_CONF_OK; +}; +static char * +njt_python_include_file(njt_conf_t *cf, PyObject *ns, char *file) +{ + FILE *fp; + PyObject *ret; + + fp = fopen(file, "r"); + if (fp == NULL) { + njt_conf_log_error(NJT_LOG_EMERG, cf, njt_errno, + "fopen() \"%s\" failed", file); + return NJT_CONF_ERROR; + } + + ret = PyRun_FileExFlags(fp, file, Py_file_input, ns, ns, 0, NULL); + + fclose(fp); + + if (ret == NULL) { + njt_conf_log_error(NJT_LOG_EMERG, cf, 0, "python error pos 8: %s", + njt_python_get_error(cf->pool)); + return NJT_CONF_ERROR; + } + + Py_DECREF(ret); + + return NJT_CONF_OK; +} + + +PyObject * +njt_python_compile(njt_conf_t *cf, u_char *script) +{ + u_char *p; + size_t len; + PyObject *code; + njt_pool_cleanup_t *cln; + + if (njt_python_init_namespace(cf) == NULL) { + return NULL; + } + + len = cf->conf_file->file.name.len + 1 + NJT_INT_T_LEN + 1; + + p = njt_pnalloc(cf->pool, len); + if (p == NULL) { + return NULL; + } + + njt_sprintf(p, "%V:%ui%Z", &cf->conf_file->file.name, cf->conf_file->line); + + code = Py_CompileString((char *) script, (char *) p, Py_eval_input); + + if (code == NULL) { + njt_conf_log_error(NJT_LOG_EMERG, cf, 0, "python error pos 9: %s", + njt_python_get_error(cf->pool)); + return NULL; + } + + cln = njt_pool_cleanup_add(cf->pool, 0); + if (cln == NULL) { + Py_DECREF(code); + return NULL; + } + + cln->handler = njt_python_decref; + cln->data = code; + + return code; +} + + +static void +njt_python_decref(void *data) +{ + PyObject *obj = data; + + Py_DECREF(obj); +} + +/* +static PyMethodDef njtPyMethods[]= { + {NULL, NULL, 0, NULL} +}; +*/ + +static struct PyModuleDef njtPyDem = +{ + PyModuleDef_HEAD_INIT, + "njt", /* name of module */ + "", /* module documentation, may be NULL */ + -1, /* size of per-interpreter state of the module, or -1 if the module keeps state in global variables. */ + NULL, + NULL, + NULL, + NULL, + NULL +}; + +PyMODINIT_FUNC +PyInit_njt(void) +{ + PyObject* m= PyModule_Create(&njtPyDem); + PyModule_AddIntConstant(m, "OK", NJT_OK); + PyModule_AddIntConstant(m, "ERROR", NJT_ERROR); + PyModule_AddIntConstant(m, "AGAIN", NJT_AGAIN); + PyModule_AddIntConstant(m, "BUSY", NJT_BUSY); + PyModule_AddIntConstant(m, "DONE", NJT_DONE); + PyModule_AddIntConstant(m, "DECLINED", NJT_DECLINED); + PyModule_AddIntConstant(m, "ABORT", NJT_ABORT); + + PyModule_AddIntConstant(m, "LOG_EMERG", NJT_LOG_EMERG); + PyModule_AddIntConstant(m, "LOG_ALERT", NJT_LOG_ALERT); + PyModule_AddIntConstant(m, "LOG_CRIT", NJT_LOG_CRIT); + PyModule_AddIntConstant(m, "LOG_ERR", NJT_LOG_ERR); + PyModule_AddIntConstant(m, "LOG_WARN", NJT_LOG_WARN); + PyModule_AddIntConstant(m, "LOG_NOTICE", NJT_LOG_NOTICE); + PyModule_AddIntConstant(m, "LOG_INFO", NJT_LOG_INFO); + PyModule_AddIntConstant(m, "LOG_DEBUG", NJT_LOG_DEBUG); + + PyModule_AddIntConstant(m, "SEND_LAST", 1); + PyModule_AddIntConstant(m, "SEND_FLUSH", 2); + return m; +} +PyObject * njt_python_get_namespace(njt_cycle_t* cycle){ + njt_python_conf_t *pcf; + pcf = (njt_python_conf_t *) njt_get_conf(cycle->conf_ctx, + njt_python_module); + if (pcf->ns) { + return pcf->ns; + } + return NULL; +}; +PyObject * +njt_python_init_namespace(njt_conf_t *cf) +{ + u_char *name; + PyObject *ns, *m; + njt_python_conf_t *pcf; + njt_pool_cleanup_t *cln; + njt_python_ns_cleanup_t *nc; + static njt_int_t initialized; + static njt_uint_t counter; + + pcf = (njt_python_conf_t *) njt_get_conf(cf->cycle->conf_ctx, + njt_python_module); + if (pcf->ns) { + return pcf->ns; + } + + if (!initialized) { + initialized = 1; + + PyImport_AppendInittab("njt",PyInit_njt) ; + Py_Initialize(); + printf("py init succeed.\n"); + } + + nc = njt_palloc(cf->pool, sizeof(njt_python_ns_cleanup_t)); + if (nc == NULL) { + return NULL; + } + + cln = njt_pool_cleanup_add(cf->pool, 0); + if (cln == NULL) { + return NULL; + } + + name = njt_pnalloc(cf->pool, 4 + NJT_INT_T_LEN); + if (name == NULL) { + return NULL; + } + + /* generate a unique namespace name */ + + njt_sprintf(name, "njt%ui%Z", counter++); + + m = PyImport_AddModule((char *) name); + if (m == NULL) { + njt_conf_log_error(NJT_LOG_EMERG, cf, 0, + "could not add \"%s\" Python module", name); + return NULL; + } + + ns = PyModule_GetDict(m); + if (ns == NULL) { + njt_conf_log_error(NJT_LOG_EMERG, cf, 0, + "could not get \"%s\" Python module dictionary", + name); + return NULL; + } + + Py_INCREF(ns); + + nc->ns = ns; + nc->name = name; + nc->ps = PyThreadState_GET(); + + cln->handler = njt_python_cleanup_namespace; + cln->data = nc; + + if (PyDict_SetItemString(ns, "__builtins__", PyEval_GetBuiltins()) < 0) { + return NULL; + } + + pcf->ns = ns; + pcf->main_ps = nc->ps; + + return ns; +} + + +static void +njt_python_cleanup_namespace(void *data) +{ + njt_python_ns_cleanup_t *nc = data; + + PyObject *modules; + + PyThreadState_Swap(nc->ps); + Py_DECREF(nc->ns); + + modules = PyImport_GetModuleDict(); + + if (PyDict_GetItemString(modules, (char *) nc->name) == NULL) { + return; + } + + if (PyDict_DelItemString(modules, (char *) nc->name) < 0) { + /* XXX error removing module from sys.modules */ + } +} + + +u_char * +njt_python_get_error(njt_pool_t *pool) +{ + long line; + const char *text, *file; + size_t len; + u_char *p; + PyObject *type, *value, *traceback, *str, *module, *func, *ret, *frame, + *obj; + Py_ssize_t size; + + /* PyErr_Print(); */ + + str = NULL; + module = NULL; + func = NULL; + ret = NULL; + + text = ""; + file = ""; + line = 0; + + PyErr_Fetch(&type, &value, &traceback); + if (type == NULL) { + goto done; + } + + PyErr_NormalizeException(&type, &value, &traceback); + if (type == NULL) { + goto done; + } + + str = PyObject_Str(value); + if (str && PyUnicode_Check(str)) { + text = PyUnicode_AsUTF8(str); + } + + module = PyImport_ImportModule("traceback"); + if (module == NULL) { + goto done; + } + + func = PyObject_GetAttrString(module, "extract_tb"); + if (func == NULL || !PyCallable_Check(func)) { + goto done; + } + + ret = PyObject_CallFunctionObjArgs(func, traceback, NULL); + + + if (ret == NULL || !PyList_Check(ret)) { + goto done; + } + + size = PyList_Size(ret); + if (size <= 0) { + goto done; + } + ssize_t cnt=size-1; + frame = PyList_GetItem(ret, cnt); + if (frame == NULL || !PyTuple_Check(frame)) { + goto done; + } + + obj = PyTuple_GetItem(frame, 0); + if (obj && PyUnicode_Check(obj)) { + file = PyUnicode_AsUTF8(obj); + } + + obj = PyTuple_GetItem(frame, 1); + if (obj && PyLong_Check(obj)) { + line = PyLong_AsLong(obj); + } + + +done: + + PyErr_Clear(); + + len = njt_strlen(text) + 2 + njt_strlen(file) + 1 + NJT_INT_T_LEN + 2; + + p = njt_pnalloc(pool, len); + if (p == NULL) { + return (u_char *) ""; + } + + njt_sprintf(p, "%s [%s:%l]%Z", text, file, line); + + Py_XDECREF(str); + Py_XDECREF(type); + Py_XDECREF(value); + Py_XDECREF(traceback); + Py_XDECREF(module); + Py_XDECREF(func); + Py_XDECREF(ret); + + return p; +} + + +static void * +njt_python_create_conf(njt_cycle_t *cycle) +{ + njt_python_conf_t *pcf; + + pcf = njt_pcalloc(cycle->pool, sizeof(njt_python_conf_t)); + if (pcf == NULL) { + return NULL; + } + + /* + * set by njt_pcalloc(): + * + * pcf->ns = NULL; + * + */ + + pcf->stack_size = NJT_CONF_UNSET_SIZE; + pcf->init_codes = njt_array_create(cycle->pool, 1, sizeof(PyObject *)); + pcf->init_codes->nelts =0; + // njt_log_error(NJT_LOG_ERR,cycle->log,0,"create cf:%p,%p",pcf,pcf->resolver); + return pcf; +} + + +static char * +njt_python_init_conf(njt_cycle_t *cycle, void *conf) +{ + njt_python_conf_t *pcf = conf; + + njt_conf_init_size_value(pcf->stack_size, 32768); + + return NJT_CONF_OK; +} +/* +static void timer_handler(njt_event_t *ev) { + return; +} +static njt_event_t *add_timer(njt_cycle_t *cycle,void (* handler)(njt_event_t *) ){ + njt_event_t *ev; + njt_connection_t *c = njt_palloc(cycle->pool, sizeof(njt_connection_t)); + njt_memzero(c, sizeof(njt_connection_t)); + + ev = njt_palloc(cycle->pool, sizeof(njt_event_t)); + njt_memzero(ev, sizeof(njt_event_t)); + ev->log = cycle->log; + ev->handler = handler; + ev->cancelable = 1; + ev->data = c; + c->fd = (njt_socket_t)-1; + c->data = NULL; + //njt_add_timer(ev, interval); + return ev; +} +*/ +static njt_int_t +njt_python_init_worker(njt_cycle_t *cycle) +{ +#if !(NJT_PYTHON_SYNC) + + njt_python_conf_t *pcf; + + pcf = (njt_python_conf_t *) njt_get_conf(cycle->conf_ctx, + njt_python_module); + + if (pcf->ns) { + + // if (njt_python_sleep_install(cycle) != NJT_OK) { + // njt_log_error(NJT_LOG_ERR, cycle->log, 0, "failed to load python sleep module"); + // return NJT_ERROR; + // } + + // if (njt_python_socket_install(cycle) != NJT_OK) { + // njt_log_error(NJT_LOG_ERR, cycle->log, 0, "failed to load python socket module"); + // return NJT_ERROR; + // } + + // if (njt_python_resolve_install(cycle) != NJT_OK) { + // njt_log_error(NJT_LOG_ERR, cycle->log, 0, "failed to load python resolve module"); + // return NJT_ERROR; + // } + + } + +#endif + + return NJT_OK; +} diff --git a/modules/njet-python-module/src/njt_python.h b/modules/njet-python-module/src/njt_python.h new file mode 100644 index 0000000000000000000000000000000000000000..8ec8275ed7a10877424937b0bc3a569960c49ece --- /dev/null +++ b/modules/njet-python-module/src/njt_python.h @@ -0,0 +1,60 @@ + +/* + * Copyright (C) Roman Arutyunyan + * Copyright (C) 2021-2024 TMLake(Beijing) Technology Co., Ltd. + */ + + +#ifndef _NJT_PYTHON_H_INCLUDED_ +#define _NJT_PYTHON_H_INCLUDED_ + + +#include +#include +#include +#include + + +#define NJT_PYTHON_AGAIN (void *) -1 + +typedef struct njt_python_ctx_s njt_python_ctx_t; + + +#if !(NJT_PYTHON_SYNC) + +njt_python_ctx_t *njt_python_get_ctx(); +njt_int_t njt_python_yield(); +void njt_python_wakeup(njt_python_ctx_t *ctx); + +// njt_int_t njt_python_sleep_install(njt_cycle_t *cycle); +// njt_int_t njt_python_socket_install(njt_cycle_t *cycle); +// njt_int_t njt_python_resolve_install(njt_cycle_t *cycle); +PyObject *njt_python_socket_create_wrapper(njt_connection_t *c); + +#endif + +njt_python_ctx_t *njt_python_create_ctx(njt_pool_t *pool, njt_log_t *log); +PyObject *njt_python_eval(njt_python_ctx_t *ctx, PyObject *code, + njt_event_t *wake, PyObject* arg, char *key); +void njt_python_set_resolver(njt_python_ctx_t *ctx, njt_resolver_t *resolver, + njt_msec_t timeout); +njt_resolver_t *njt_python_get_resolver(njt_python_ctx_t *ctx, + njt_msec_t *timeout); +PyObject *njt_python_set_value(njt_python_ctx_t *ctx, const char *name, + PyObject *value); +void njt_python_reset_value(njt_python_ctx_t *ctx, const char *name, + PyObject *old); +u_char *njt_python_get_error(njt_pool_t *pool); + +char *njt_python_set_slot(njt_conf_t *cf, njt_command_t *cmd, void *conf); +char *njt_python_include_set_slot(njt_conf_t *cf, njt_command_t *cmd, + void *conf); +PyObject *njt_python_compile(njt_conf_t *cf, u_char *script); +njt_int_t njt_python_active(njt_conf_t *cf); + +PyObject * njt_python_init_namespace(njt_conf_t *cf); +PyObject * njt_python_get_namespace(njt_cycle_t* cycle); +void* njt_python_add_init_code(njt_conf_t *cf,PyObject* init_code); +// void njt_python_cleanup_thread_ctx(njt_python_ctx_t *ctx); + +#endif /* _NJT_PYTHON_H_INCLUDED_ */ diff --git a/modules/njet-python-module/src/njt_stream_proto_python_module.c b/modules/njet-python-module/src/njt_stream_proto_python_module.c new file mode 100644 index 0000000000000000000000000000000000000000..0e142749722ab5577f2df51672a30c3529fdf044 --- /dev/null +++ b/modules/njet-python-module/src/njt_stream_proto_python_module.c @@ -0,0 +1,367 @@ + +/* + * Copyright (C) Roman Arutyunyan + * Copyright (C) 2021-2024 TMLake(Beijing) Technology Co., Ltd. + */ + + +#include +#include +#include +#include +#include "njt_python.h" +#include +#include +#include +#include "njt_stream_proto_python_session.h" + +#include + +#define NJT_STREEAM_PROTO_SVR_PY_WS_APP 0x00000001; +typedef struct { + PyObject *on_msg; + njt_uint_t app_type; // only ws type now +} njt_stream_proto_svr_py_conf_t; + + +typedef struct { + njt_pool_t *pool; + njt_uint_t in_sleep; + PyObject *session; + njt_python_ctx_t *python; +} njt_stream_proto_python_ctx_t; + + +static void *njt_stream_proto_python_create_srv_conf(njt_conf_t *cf); +static char *njt_stream_proto_python_merge_srv_conf(njt_conf_t *cf, void *parent, + void *child); +static char *njt_proto_server_python_include_set(njt_conf_t *cf, njt_command_t *cmd, + void *conf); +static njt_int_t njt_stream_proto_python_init(njt_conf_t *cf); +static PyObject * njt_stream_proto_python_eval_code(tcc_stream_request_t *r, PyObject *code, + njt_event_t *wake, char *msg, size_t msg_len); + + +static njt_command_t njt_stream_proto_python_commands[] = { + + { njt_string("proto_server_py_module"), + NJT_STREAM_MAIN_CONF|NJT_STREAM_SRV_CONF|NJT_CONF_TAKE12, + njt_proto_server_python_include_set, + NJT_STREAM_SRV_CONF_OFFSET, + 0, + NULL }, + + njt_null_command +}; + + +static njt_stream_module_t njt_stream_proto_python_module_ctx = { + NULL, /* preconfiguration */ + njt_stream_proto_python_init, /* postconfiguration */ + + NULL, /* create main configuration */ + NULL, /* init main configuration */ + + njt_stream_proto_python_create_srv_conf, /* create server configuration */ + njt_stream_proto_python_merge_srv_conf /* merge server configuration */ +}; + + +njt_module_t njt_stream_proto_python_module = { + NJT_MODULE_V1, + &njt_stream_proto_python_module_ctx, /* module context */ + njt_stream_proto_python_commands, /* module directives */ + NJT_STREAM_MODULE, /* module type */ + NULL, /* init master */ + NULL, /* init module */ + NULL, /* init process */ + NULL, /* init thread */ + NULL, /* exit thread */ + NULL, /* exit process */ + NULL, /* exit master */ + NJT_MODULE_V1_PADDING +}; + + +static void +njt_stream_proto_python_ctx_cleanup(void *data) +{ + njt_stream_session_t *s = data; + njt_stream_proto_python_ctx_t *ctx; + + ctx = njt_stream_get_module_ctx(s, njt_stream_proto_python_module); + + ctx->python = NULL; + ctx->session = NULL; +} + + +int +njt_stream_proto_python_on_msg(tcc_stream_request_t *r, char *msg, size_t msg_len) +{ + PyObject *ret; + njt_int_t rc; + njt_stream_session_t *s; + njt_stream_proto_svr_py_conf_t *pscf; + njt_stream_proto_python_ctx_t *ctx; + + s = (njt_stream_session_t*) r->s; + pscf = njt_stream_get_module_srv_conf(s, njt_stream_proto_python_module); + + ret = njt_stream_proto_python_eval_code(r, pscf->on_msg, s->connection->read, msg, msg_len); + + if (ret == NULL) { + return NJT_ERROR; + } + + ctx = njt_stream_get_module_ctx(s, njt_stream_proto_python_module); // ctx is created in eval code + + if (ret == NJT_PYTHON_AGAIN) { + njt_log_error(NJT_LOG_ERR, s->connection->log, 0, + "stream python doesn't support async op now"); + + return NJT_AGAIN; + } + + rc = PyLong_Check(ret) ? PyLong_AsLong(ret) : NJT_ERROR; + Py_DECREF(ret); + + njt_destroy_pool(ctx->pool); + ctx->session = NULL; + ctx->python = NULL; + ctx->in_sleep = 0; + ctx->pool = NULL; + + return rc; +} + + +static PyObject * +njt_stream_proto_python_eval_code(tcc_stream_request_t *r, PyObject *code, + njt_event_t *wake, char *msg, size_t msg_len) +{ + PyObject *result, *proto_session, *old; + njt_stream_session_t *s; + njt_pool_cleanup_t *cln; + njt_pool_t *pool; + njt_stream_proto_python_ctx_t *ctx; + + s = (njt_stream_session_t*) r->s; + + njt_log_debug2(NJT_LOG_DEBUG_STREAM, s->connection->log, 0, + "stream python eval start code:%p, wake:%p", code, wake); + + ctx = njt_stream_get_module_ctx(s, njt_stream_proto_python_module); + if (ctx == NULL) { + ctx = njt_pcalloc(s->connection->pool, sizeof(njt_stream_proto_python_ctx_t)); + if (ctx == NULL) { + njt_log_error(NJT_LOG_ERR, s->connection->log, 0, + "stream python failed to allocate ctx"); + + return NULL; + } + + cln = njt_pool_cleanup_add(s->connection->pool, 0); + if (cln == NULL) { + njt_log_error(NJT_LOG_ERR, s->connection->log, 0, + "stream python failed to add cln"); + + return NULL; + } + cln->data = s; + cln->handler = njt_stream_proto_python_ctx_cleanup; + + njt_stream_set_ctx(s, ctx, njt_stream_proto_python_module); + } + + if (ctx->in_sleep == 0) { + pool = njt_create_pool(NJT_CYCLE_POOL_SIZE, s->connection->log); + if (pool == NULL) { + return NULL; + } + ctx->pool = pool; + njt_sub_pool(s->connection->pool, ctx->pool); + + if (ctx->python == NULL) { + ctx->python = njt_python_create_ctx(ctx->pool, + s->connection->log); + if (ctx->python == NULL) { + return NULL; + } + } + + njt_log_debug1(NJT_LOG_DEBUG_STREAM, s->connection->log, 0, + "stream python create ctx->python: %p", ctx->python); + } + + if(ctx->session == NULL) { + proto_session = njt_stream_proto_python_create(r, msg, msg_len); + if (proto_session == NULL) { + return NULL; + } + ctx->session = proto_session; + + njt_log_debug1(NJT_LOG_DEBUG_STREAM, s->connection->log, 0, + "stream python create ctx->session: %p", ctx->session); + } + + old = njt_python_set_value(ctx->python, "r", ctx->session); + result = njt_python_eval(ctx->python, code, NULL, NULL, NULL); + njt_python_reset_value(ctx->python, "r", old); + + + njt_log_debug3(NJT_LOG_DEBUG_STREAM, s->connection->log, 0, + "stream python eval end code:%p, wake:%p, result:%p", + code, wake, result); + + return result; +} + + +static void * +njt_stream_proto_python_create_srv_conf(njt_conf_t *cf) +{ + njt_stream_proto_svr_py_conf_t *pscf; + + pscf = njt_pcalloc(cf->pool, sizeof(njt_stream_proto_svr_py_conf_t)); + if (pscf == NULL) { + return NULL; + } + + /* + * set by njt_pcalloc(): + * + * pscf->content = NULL; + */ + + // pscf->on_conn = NJT_CONF_UNSET_PTR; + // pscf->on_msg = NJT_CONF_UNSET_PTR; + pscf->app_type = NJT_CONF_UNSET_UINT; + + return pscf; +} + + +static char * +njt_stream_proto_python_merge_srv_conf(njt_conf_t *cf, void *parent, void *child) +{ + njt_stream_proto_svr_py_conf_t *prev = parent; + njt_stream_proto_svr_py_conf_t *conf = child; + + // njt_conf_merge_ptr_value(conf->on_conn, prev->on_conn, NULL); + njt_conf_merge_ptr_value(conf->on_msg, prev->on_msg, NULL); + njt_conf_merge_uint_value(conf->app_type, prev->app_type, 0); + + return NJT_CONF_OK; +} + + +static njt_int_t +njt_stream_proto_python_init(njt_conf_t *cf) +{ + if (njt_python_active(cf) != NJT_OK) { + return NJT_OK; + } + + if (njt_stream_proto_python_session_init(cf) != NJT_OK) { + return NJT_ERROR; + } + + return NJT_OK; +} + + +static char *njt_proto_server_python_app_ws_set(njt_conf_t *cf, njt_command_t *cmd, void *conf) +{ + + njt_str_t *value, full_name; + PyObject *ns, *func_def; + PyObject *app; + const char* file; + njt_stream_proto_svr_py_conf_t *pscf = conf; + + if (pscf->on_msg) { + return "is duplicate (proto_server_py_module)"; + } + + ns = njt_python_init_namespace(cf); + if (ns == NULL) { + return NJT_CONF_ERROR; + } + + value = cf->args->elts; + full_name = value[1]; + + file = (char *)full_name.data; + app = PyImport_ImportModuleEx(file, ns, ns, 0); + + if (app == NULL) { + njt_conf_log_error(NJT_LOG_EMERG, cf, 0, "python error: %s", + njt_python_get_error(cf->pool)); + return NJT_CONF_ERROR; + } + + PyDict_SetItemString(ns, file, app); + + func_def = NULL; + func_def = PyObject_GetAttrString(app,"on_msg"); + if (!func_def) { + njt_conf_log_error(NJT_LOG_INFO, cf, 0, "no python on_msg found in: %s",file); + PyErr_Clear(); + } else { + if (!PyCallable_Check(func_def)) { + //todo: verify signature + njt_conf_log_error(NJT_LOG_EMERG, cf, 0, "python on_msg signature error: %s",file); + return NJT_CONF_ERROR; + } + } + + if (func_def) { + char buf[128]; + snprintf(buf,128,"%s.on_msg(r)",file); + PyObject * msg_code = Py_CompileString(buf, "" ,Py_eval_input); + njt_conf_log_error(NJT_LOG_INFO, cf, 0, "python on_msg found: "); + pscf->on_msg = msg_code; + } + + return NJT_CONF_OK; +} + +// extern char* njt_prefix; +static char *njt_proto_server_python_include_set(njt_conf_t *cf, njt_command_t *cmd, void *conf) { + char *py_path; + njt_str_t *value; + char *ori_env; + njt_uint_t env_len; + + ori_env = getenv("PYTHONPATH"); + env_len = ori_env ? strlen(ori_env) : 0; + + + if (cf->args->nelts == 2) { + py_path = njt_pcalloc(cf->pool, cf->cycle->conf_prefix.len + 2 + env_len); + if (env_len) { + memcpy(py_path, ori_env, env_len); + py_path[env_len] = ':'; + memcpy(py_path + env_len + 1, cf->cycle->conf_prefix.data, cf->cycle->conf_prefix.len); + } else { + memcpy(py_path, cf->cycle->conf_prefix.data, cf->cycle->conf_prefix.len); + } + } else { + value = cf->args->elts; + py_path = njt_pcalloc(cf->pool, value[2].len + 2 + env_len); + if (env_len) { + memcpy(py_path, ori_env, env_len); + py_path[env_len] = ':'; + memcpy(py_path + env_len + 1, value[2].data, value[2].len); + } else { + memcpy(py_path, value[2].data, value[2].len); + } + } + + setenv("PYTHONPATH", py_path, 1); + + // if (value[2].len == 2 && njt_strcmp(value[2].data, "ws") == 0) { + return njt_proto_server_python_app_ws_set(cf, cmd, conf); + // } +} diff --git a/modules/njet-python-module/src/njt_stream_proto_python_session.c b/modules/njet-python-module/src/njt_stream_proto_python_session.c new file mode 100644 index 0000000000000000000000000000000000000000..917ae73901f5921471f967a5b8cfffdbd4c49206 --- /dev/null +++ b/modules/njet-python-module/src/njt_stream_proto_python_session.c @@ -0,0 +1,448 @@ + +/* + * Copyright (C) Roman Arutyunyan + * Copyright (C) 2021-2024 TMLake(Beijing) Technology Co., Ltd. + */ + + +#include +#include +#include +#include +#include "njt_stream_proto_python_session.h" +#include "njt_stream_proto_server_module.h" +#include "tcc_ws.h" + + +typedef struct{ + WSOpcode code; + char *msg; + size_t msg_len; +} njt_ws_in_data_t; + +typedef struct app_client_s { + tcc_str_t *init_data; + int id; +} app_client_t; // same as tcc_ws.c + +typedef struct { + PyObject_HEAD + njt_stream_session_t *session; + PyObject *msg; + tcc_stream_request_t *ws_req; +} njt_stream_proto_python_session_t; + + +// typedef struct { +// PyObject_HEAD +// njt_stream_proto_python_session_t *ps; +// } njt_stream_proto_python_session_var_t; + + +static PyObject *njt_stream_proto_python_session_log( + njt_stream_proto_python_session_t* self, PyObject* args); +static PyObject *njt_stream_proto_python_send( + njt_stream_proto_python_session_t* self, PyObject* args); +static PyObject *njt_stream_proto_python_send_others( + njt_stream_proto_python_session_t* self, PyObject* args); +static PyObject *njt_stream_proto_python_broadcast( + njt_stream_proto_python_session_t* self, PyObject* args); +static PyObject *njt_stream_proto_python_session_msg( + njt_stream_proto_python_session_t *self); +static PyObject *njt_stream_proto_python_session_client_id( + njt_stream_proto_python_session_t *self); +static void njt_stream_proto_python_session_dealloc( + njt_stream_proto_python_session_t *self); + +// static PyObject *njt_stream_proto_python_session_var_subscript( +// njt_stream_proto_python_session_var_t *self, PyObject *key); +// static void njt_stream_proto_python_session_var_dealloc( +// njt_stream_proto_python_session_var_t *self); + +// static void njt_stream_proto_python_session_cleanup(void *data); + + +static PyMethodDef njt_stream_proto_python_session_methods[] = { + + { "log", + (PyCFunction) njt_stream_proto_python_session_log, + METH_VARARGS, + "output a message to the error log" }, + + { "send", + (PyCFunction) njt_stream_proto_python_send, + METH_VARARGS, + "output a message to the error log" }, + + { "send_others", + (PyCFunction) njt_stream_proto_python_send_others, + METH_VARARGS, + "output a message to the error log" }, + + { "broadcast", + (PyCFunction) njt_stream_proto_python_broadcast, + METH_VARARGS, + "output a message to the error log" }, + + { NULL, NULL, 0, NULL } +}; + + +static PyGetSetDef njt_stream_proto_python_session_getset[] = { + + { "msg", + (getter) njt_stream_proto_python_session_msg, + NULL, + "njet per-session variables", + NULL }, + + { "client_id", + (getter) njt_stream_proto_python_session_client_id, + NULL, + "njet per-session variables", + NULL }, + + { NULL, NULL, NULL, NULL, NULL } +}; + + +static PyTypeObject njt_stream_proto_python_session_type = { + .tp_name = "njt.StreamSession", + .tp_basicsize = sizeof(njt_stream_proto_python_session_t), + .tp_dealloc = (destructor) njt_stream_proto_python_session_dealloc, + .tp_flags = Py_TPFLAGS_DEFAULT, + .tp_doc = "Stream session", + .tp_methods = njt_stream_proto_python_session_methods, + .tp_getset = njt_stream_proto_python_session_getset +}; + + +// static PyMappingMethods njt_stream_proto_python_session_var_mapping = { +// NULL, /*mp_length*/ +// (binaryfunc) njt_stream_proto_python_session_var_subscript, +// /*mp_subscript*/ +// NULL, /*mp_ass_subscript*/ +// }; + + +// static PyTypeObject njt_stream_proto_python_session_var_type = { +// //.ob_refcnt = 1, +// .tp_name = "njt.StreamVariables", +// .tp_basicsize = sizeof(njt_stream_proto_python_session_var_t), +// .tp_dealloc = (destructor) njt_stream_proto_python_session_var_dealloc, +// .tp_as_mapping = &njt_stream_proto_python_session_var_mapping, +// .tp_flags = Py_TPFLAGS_DEFAULT, +// .tp_doc = "Stream variables" +// }; + + +static PyObject *njt_stream_proto_python_session_error; + + +PyObject * +njt_stream_proto_python_session_log(njt_stream_proto_python_session_t *self, PyObject *args) +{ + int level; + const char *msg; + njt_stream_session_t *s; + + s = self->session; + if (s == NULL) { + PyErr_SetString(njt_stream_proto_python_session_error, "session finalized"); + return NULL; + } + + njt_log_debug0(NJT_LOG_DEBUG_STREAM, s->connection->log, 0, + "stream python log()"); + + level = NJT_LOG_INFO; + + if (!PyArg_ParseTuple(args, "s|i:log", &msg, &level)) { + return NULL; + } + + njt_log_error((njt_uint_t) level, s->connection->log, 0, msg); + + Py_RETURN_NONE; +} + + +PyObject * +njt_stream_proto_python_send(njt_stream_proto_python_session_t* self, PyObject *args) +{ + int level; + char *msg; + tcc_stream_request_t *r; + njt_stream_session_t *s; + tcc_str_t out_data; + njt_ws_in_data_t in_data; + + s = self->session; + r = self->ws_req; + + if (s == NULL) { + PyErr_SetString(njt_stream_proto_python_session_error, "session finalized"); + return NULL; + } + + njt_log_debug0(NJT_LOG_DEBUG_STREAM, s->connection->log, 0, + "stream python send()"); + + level = NJT_LOG_INFO; + + if (!PyArg_ParseTuple(args, "s|i:log", &msg, &level)) { + return NULL; + } + + in_data.code = WS_OPCODE_TEXT; + in_data.msg = msg; + in_data.msg_len = strlen(msg); + + proto_server_build_message(r, (void *)&in_data, &out_data); + proto_server_send(r, (char *)out_data.data, out_data.len); + // proto_free(r, out_data.data); + free(out_data.data); + + Py_RETURN_NONE; +} + +PyObject * +njt_stream_proto_python_send_others(njt_stream_proto_python_session_t *self, PyObject *args) +{ + int level; + char *msg; + tcc_stream_request_t *r; + njt_stream_session_t *s; + tcc_str_t out_data; + njt_ws_in_data_t in_data; + + s = self->session; + r = self->ws_req; + + if (s == NULL) { + PyErr_SetString(njt_stream_proto_python_session_error, "session finalized"); + return NULL; + } + + njt_log_debug0(NJT_LOG_DEBUG_STREAM, s->connection->log, 0, + "stream python send()"); + + level = NJT_LOG_INFO; + + if (!PyArg_ParseTuple(args, "s|i:log", &msg, &level)) { + return NULL; + } + + in_data.code = WS_OPCODE_TEXT; + in_data.msg = msg; + in_data.msg_len = strlen(msg); + + proto_server_build_message(r, (void *)&in_data, &out_data); + proto_server_send_others(r, (char *)out_data.data, out_data.len); + // proto_free(r, out_data.data); + free(out_data.data); + + Py_RETURN_NONE; +} + + +PyObject * +njt_stream_proto_python_broadcast(njt_stream_proto_python_session_t* self, PyObject* args) +{ + int level; + char *msg; + tcc_stream_request_t *r; + njt_stream_session_t *s; + tcc_str_t out_data; + njt_ws_in_data_t in_data; + + s = self->session; + r = self->ws_req; + + if (s == NULL) { + PyErr_SetString(njt_stream_proto_python_session_error, "session finalized"); + return NULL; + } + + njt_log_debug0(NJT_LOG_DEBUG_STREAM, s->connection->log, 0, + "stream python send()"); + + level = NJT_LOG_INFO; + + if (!PyArg_ParseTuple(args, "s|i:log", &msg, &level)) { + return NULL; + } + + in_data.code = WS_OPCODE_TEXT; + in_data.msg = msg; + in_data.msg_len = strlen(msg); + + proto_server_build_message(r, (void *)&in_data, &out_data); + proto_server_send_broadcast(r->tcc_server, (char *)out_data.data, out_data.len); + // proto_free(r, out_data.data); + free(out_data.data); + + Py_RETURN_NONE; +} + +static PyObject * +njt_stream_proto_python_session_msg(njt_stream_proto_python_session_t *self) +{ + Py_INCREF(self->msg); + return self->msg; +} + + +static PyObject * +njt_stream_proto_python_session_client_id(njt_stream_proto_python_session_t *self) +{ + app_client_t *app_client = tcc_get_client_app_ctx(self->ws_req); + PyObject *client_id = PyLong_FromLong(app_client->id); + return client_id; +} + + +static void +njt_stream_proto_python_session_dealloc(njt_stream_proto_python_session_t *self) +{ + Py_XDECREF(self->msg); + + self->session = NULL; + self->ws_req = NULL; + + Py_TYPE(self)->tp_free((PyObject*) self); +} + + +// static PyObject * +// njt_stream_python_session_var_subscript(njt_stream_python_session_var_t *self, +// PyObject *key) +// { +// char *data; +// njt_str_t name; +// njt_uint_t hash; +// Py_ssize_t len; +// njt_stream_session_t *s; +// njt_stream_variable_value_t *vv; + +// s = self->ps->session; +// if (s == NULL) { +// PyErr_SetString(njt_stream_python_session_error, "session finalized"); +// return NULL; +// } + +// njt_log_debug0(NJT_LOG_DEBUG_STREAM, s->connection->log, 0, +// "stream python var subscript()"); + +// if (PyBytes_AsStringAndSize(key, &data, &len) < 0 ) { +// return NULL; +// } + +// name.data = (u_char *) data; +// name.len = len; + +// hash = njt_hash_strlow(name.data, name.data, name.len); + +// vv = njt_stream_get_variable(s, &name, hash); +// if (vv == NULL) { +// PyErr_SetNone(njt_stream_python_session_error); +// return NULL; +// } + +// if (vv->not_found) { +// return PyBytes_FromStringAndSize(NULL, 0); +// } + +// return PyBytes_FromStringAndSize((char *) vv->data, vv->len); +// } + + +// static void +// njt_stream_python_session_var_dealloc(njt_stream_python_session_var_t *self) +// { +// Py_DECREF(self->ps); + +// Py_TYPE(self)->tp_free((PyObject*) self); +// } + + +njt_int_t +njt_stream_proto_python_session_init(njt_conf_t *cf) +{ + static njt_int_t initialized; + + if (initialized) { + return NJT_OK; + } + + initialized = 1; + + if (PyType_Ready(&njt_stream_proto_python_session_type) < 0) { + njt_conf_log_error(NJT_LOG_EMERG, cf, 0, "could not add %s type", + njt_stream_proto_python_session_type.tp_name); + return NJT_ERROR; + } + + // if (PyType_Ready(&njt_stream_python_session_var_type) < 0) { + // njt_conf_log_error(NJT_LOG_EMERG, cf, 0, "could not add %s type", + // njt_stream_python_session_var_type.tp_name); + // return NJT_ERROR; + // } + + njt_stream_proto_python_session_error = PyErr_NewException( + "njt.StreamSessionError", + PyExc_RuntimeError, + NULL); + if (njt_stream_proto_python_session_error == NULL) { + return NJT_ERROR; + } + + return NJT_OK; +} + + +PyObject * +njt_stream_proto_python_create(tcc_stream_request_t *r, char *msg, size_t msg_len) +{ + njt_stream_session_t *s; + njt_stream_proto_python_session_t *ps; + + s = (njt_stream_session_t*) r->s; + njt_log_debug0(NJT_LOG_DEBUG_STREAM, s->connection->log, 0, + "stream python create session"); + + ps = PyObject_New(njt_stream_proto_python_session_t, + &njt_stream_proto_python_session_type); + if (ps == NULL) { + goto failed; + } + + ps->session = s; + ps->ws_req = r; + + ps->msg = PyBytes_FromStringAndSize(msg, msg_len); + if (ps->msg == NULL) { + Py_DECREF(ps); + goto failed; + } + + return (PyObject *) ps; + +failed: + + njt_log_error(NJT_LOG_ERR, s->connection->log, 0, "python error: %s", + njt_python_get_error(s->connection->pool)); + + return NULL; +} + + +// static void +// njt_stream_python_session_cleanup(void *data) +// { +// njt_stream_python_session_t *ps = data; + +// ps->session = NULL; + +// Py_DECREF(ps); +// } diff --git a/modules/njet-python-module/src/njt_stream_proto_python_session.h b/modules/njet-python-module/src/njt_stream_proto_python_session.h new file mode 100644 index 0000000000000000000000000000000000000000..29642ce260f4c39efc9b4a85c1e0c4cd8206719d --- /dev/null +++ b/modules/njet-python-module/src/njt_stream_proto_python_session.h @@ -0,0 +1,23 @@ + +/* + * Copyright (C) Roman Arutyunyan + * Copyright (C) 2021-2024 TMLake(Beijing) Technology Co., Ltd. + */ + + +#ifndef _NJT_STREAM_PYTHON_SESSION_H_INCLUDED_ +#define _NJT_STREAM_PYTHON_SESSION_H_INCLUDED_ + + +#include +#include +#include +#include "njt_python.h" +#include "njt_tcc.h" + + +njt_int_t njt_stream_proto_python_session_init(njt_conf_t *cf); +PyObject *njt_stream_proto_python_create(tcc_stream_request_t *s, char *msg, size_t len); +int njt_stream_proto_python_on_msg(tcc_stream_request_t *s, char *msg, size_t len); + +#endif /* _NJT_STREAM_PYTHON_SESSION_H_INCLUDED_ */ diff --git a/modules/njet-stream-proto-server-module/.DS_Store b/modules/njet-stream-proto-server-module/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..30c8b25bbeec94a5d33c58cf47a4e49ae71046e7 Binary files /dev/null and b/modules/njet-stream-proto-server-module/.DS_Store differ diff --git a/modules/njet-stream-proto-server-module/config b/modules/njet-stream-proto-server-module/config new file mode 100644 index 0000000000000000000000000000000000000000..4dce65a3ebdce27ed9fa2ba2b985dd4b42a1fb86 --- /dev/null +++ b/modules/njet-stream-proto-server-module/config @@ -0,0 +1,9 @@ +njt_module_type=STREAM +njt_module_name="njt_stream_proto_server_module" +njt_module_srcs="$njt_addon_dir/src/njt_stream_proto_server_module.c" +NJT_ADDON_DEPS="auto/lib/tcc-0.9.26/libtcc.a" +njt_module_deps="$njt_addon_dir/src/njt_stream_proto_server_module.h" +njt_module_libs="auto/lib/tcc-0.9.26/libtcc.a" +CORE_INCS="$CORE_INCS $njt_addon_dir/src/" +have=NJT_STREAM_PROTOCOL_SERVER_MODULE . auto/have +. auto/module diff --git a/modules/njet-stream-proto-server-module/src/njt_stream_proto_server_module.c b/modules/njet-stream-proto-server-module/src/njt_stream_proto_server_module.c new file mode 100644 index 0000000000000000000000000000000000000000..5cb06fe3ad5de732a2a3550587279bb08c6df264 --- /dev/null +++ b/modules/njet-stream-proto-server-module/src/njt_stream_proto_server_module.c @@ -0,0 +1,3956 @@ +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + * Copyright (C) 2021-2023 TMLake(Beijing) Technology Co., Ltd. + */ + +#include +#include +#include +#include +#include "njt_sha1.h" +#include "njt_stream_proto_server_module.h" +#include +#if (NJT_STREAM_PROTOCOL_V2) +#include +#endif +#if (NJT_STREAM_FTP_PROXY) +#include +#endif + + +extern njt_int_t njt_stream_proxy_process(njt_stream_session_t *s, njt_uint_t from_upstream, + njt_uint_t do_write,njt_uint_t internal); +static char *njt_stream_proto_server_set(njt_conf_t *cf, njt_command_t *cmd, void *conf); +static njt_int_t njt_stream_proto_server_init(njt_conf_t *cf); +static void *njt_stream_proto_server_create_srv_conf(njt_conf_t *cf); +static char *njt_stream_proto_server_merge_srv_conf(njt_conf_t *cf, void *parent, void *child); +static void njt_stream_proto_server_handler(njt_stream_session_t *s); +static void +njt_stream_proto_server_write_handler(njt_event_t *ev); +static void +njt_stream_proto_server_read_handler(njt_event_t *ev); +static njt_int_t njt_stream_proto_server_process(njt_cycle_t *cycle); +static void *njt_stream_proto_server_create_main_conf(njt_conf_t *cf); +static njt_int_t njt_stream_proto_server_del_session(njt_stream_session_t *s, njt_uint_t code, njt_uint_t close_session); +static void njt_stream_proto_server_update_in_buf(tcc_buf_t *b, size_t used_len); +static int proto_server_proxy_send(tcc_stream_request_t *r, njt_uint_t from_upstream, char *data, size_t len); +static char *njt_stream_proto_upstream_set(njt_conf_t *cf, njt_command_t *cmd, void *conf); +static char * +njt_stream_proto_pass(njt_conf_t *cf, njt_command_t *cmd, void *conf); +static void njt_stream_proto_handler(njt_stream_session_t *s); +static void +njt_stream_proto_resolve_handler(njt_resolver_ctx_t *ctx); +static void +njt_stream_proto_downstream_handler(njt_event_t *ev); +static void +njt_stream_proto_process_connection(njt_event_t *ev, njt_uint_t from_upstream); +static void +njt_stream_proto_connect(njt_stream_session_t *s); +static void +njt_stream_proto_connect_handler(njt_event_t *ev); +static void +njt_stream_proto_init_upstream(njt_stream_session_t *s); +static void +njt_stream_proto_ssl_init_connection(njt_stream_session_t *s); +static u_char * +njt_stream_proto_log_error(njt_log_t *log, u_char *buf, size_t len); +static njt_int_t +njt_stream_proto_set_local(njt_stream_session_t *s, njt_stream_upstream_t *u, + njt_stream_upstream_local_t *local); +static njt_int_t +njt_stream_proto_eval(njt_stream_session_t *s, + njt_stream_proxy_srv_conf_t *pscf); +static njt_int_t njt_stream_proto_process(njt_stream_session_t *s, njt_uint_t from_upstream, + njt_uint_t do_write,njt_uint_t internal); +static njt_int_t +njt_stream_proto_test_finalize(njt_stream_session_t *s, + njt_uint_t from_upstream); +static void +njt_stream_proto_next_upstream(njt_stream_session_t *s); +static njt_int_t +njt_stream_proto_test_connect(njt_connection_t *c); +static njt_int_t +njt_stream_proto_send_proxy_protocol(njt_stream_session_t *s); +static njt_int_t +njt_proto_write_filter(njt_stream_session_t *s, njt_chain_t *in,njt_uint_t from_upstream); + +extern u_char * +njt_proxy_protocol_v2_write(njt_stream_session_t *s, u_char *buf, u_char *last); + +static void +njt_stream_proto_upstream_handler(njt_event_t *ev); + +static njt_int_t +njt_stream_proto_ssl_name(njt_stream_session_t *s); + +static njt_int_t +njt_stream_proto_ssl_certificates(njt_stream_session_t *s); + +static void +njt_stream_proto_ssl_save_session(njt_connection_t *c); + +static void +njt_stream_proto_ssl_handshake(njt_connection_t *pc); +static void * +njt_prealloc(njt_pool_t *pool,void *p, size_t size); +static void * +njt_realloc(void *ptr,size_t size, njt_log_t *log); +static void +njt_stream_proto_finalize(njt_stream_session_t *s, njt_uint_t rc); +/** + * This module provide callback to istio for http traffic + * + */ +static njt_command_t njt_stream_proto_server_commands[] = { + {njt_string("proto_server"), + NJT_STREAM_SRV_CONF | NJT_CONF_FLAG, + njt_stream_proto_server_set, + NJT_STREAM_SRV_CONF_OFFSET, + 0, + NULL}, + {njt_string("proto_buffer_size"), + NJT_STREAM_MAIN_CONF | NJT_STREAM_SRV_CONF | NJT_CONF_TAKE1, + njt_conf_set_size_slot, + NJT_STREAM_SRV_CONF_OFFSET, + offsetof(njt_stream_proto_server_srv_conf_t, buffer_size), + NULL}, + {njt_string("proto_session_max_mem_size"), + NJT_STREAM_MAIN_CONF | NJT_STREAM_SRV_CONF | NJT_CONF_TAKE1, + njt_conf_set_size_slot, + NJT_STREAM_SRV_CONF_OFFSET, + offsetof(njt_stream_proto_server_srv_conf_t, session_max_mem_size), + NULL}, + {njt_string("proto_server_code_file"), + NJT_STREAM_MAIN_CONF | NJT_STREAM_SRV_CONF | NJT_CONF_TAKE1, + njt_conf_set_str_array_slot, // do custom config + NJT_STREAM_SRV_CONF_OFFSET, + offsetof(njt_stream_proto_server_srv_conf_t,tcc_files), + NULL}, + {njt_string("proto_server_idle_timeout"), + NJT_STREAM_MAIN_CONF | NJT_STREAM_SRV_CONF | NJT_CONF_TAKE1, + njt_conf_set_msec_slot, + NJT_STREAM_SRV_CONF_OFFSET, + offsetof(njt_stream_proto_server_srv_conf_t, connect_timeout), + NULL}, + {njt_string("proto_server_client_update_interval"), + NJT_STREAM_MAIN_CONF | NJT_STREAM_SRV_CONF | NJT_CONF_TAKE1, + njt_conf_set_msec_slot, + NJT_STREAM_SRV_CONF_OFFSET, + offsetof(njt_stream_proto_server_srv_conf_t, client_update_interval), + NULL}, + {njt_string("proto_server_update_interval"), + NJT_STREAM_MAIN_CONF | NJT_STREAM_SRV_CONF | NJT_CONF_TAKE1, + njt_conf_set_msec_slot, + NJT_STREAM_SRV_CONF_OFFSET, + offsetof(njt_stream_proto_server_srv_conf_t, server_update_interval), + NULL}, + {njt_string("proto_upstream"), + NJT_STREAM_UPS_CONF | NJT_CONF_FLAG, + njt_stream_proto_upstream_set, + NJT_STREAM_SRV_CONF_OFFSET, + 0, + NULL}, + { njt_string("proto_pass"), + NJT_STREAM_SRV_CONF|NJT_CONF_TAKE1, + njt_stream_proto_pass, + NJT_STREAM_SRV_CONF_OFFSET, + 0, + NULL }, + njt_null_command /* command termination */ +}; + +/* The module context. */ +static njt_stream_module_t njt_stream_proto_server_module_ctx = { + NULL, /* preconfiguration */ + njt_stream_proto_server_init, /* postconfiguration */ + &njt_stream_proto_server_create_main_conf, + NULL, /* init main configuration */ + njt_stream_proto_server_create_srv_conf, /* create server configuration */ + njt_stream_proto_server_merge_srv_conf /* merge server configuration */ + +}; + +/* Module definition. */ +njt_module_t njt_stream_proto_server_module = { + NJT_MODULE_V1, + &njt_stream_proto_server_module_ctx, /* module context */ + njt_stream_proto_server_commands, /* module directives */ + NJT_STREAM_MODULE, /* module type */ + NULL, /* init master */ + NULL, /* init module */ + &njt_stream_proto_server_process, /* init process */ + NULL, /* init thread */ + NULL, /* exit thread */ + NULL, /* exit process */ + NULL, /* exit master */ + NJT_MODULE_V1_PADDING}; + +static void *njt_stream_proto_server_create_main_conf(njt_conf_t *cf) +{ + njt_stream_proto_server_main_conf_t *cmf; + + njt_log_debug(NJT_LOG_DEBUG_EVENT, njt_cycle->log, 0, "stream_proto create main config"); + + cmf = njt_pcalloc(cf->pool, sizeof(njt_stream_proto_server_main_conf_t)); + if (cmf == NULL) + { + return NULL; + } + njt_array_init(&cmf->srv_info, cf->pool, 1, sizeof(njt_stream_proto_server_srv_conf_t *)); + + return cmf; +} +static njt_int_t njt_stream_script_upstream_get_peer(njt_peer_connection_t *pc, + void *data) +{ + + njt_int_t rc; + njt_int_t peer_num; + njt_stream_upstream_rr_peer_data_t *rrp; + njt_stream_proto_upstream_peer_data_t *sp = data; + njt_stream_proto_server_srv_conf_t *sscf; + njt_stream_upstream_rr_peer_t *peer; + njt_stream_upstream_rr_peer_t *selected; + tcc_stream_client_upstream_data_t peer_data; + njt_stream_upstream_rr_peers_t *back_peers; + /* try to select a peer */ + + rrp = sp->data; + sscf = njt_stream_get_module_srv_conf(sp->s,njt_stream_proto_server_module); + njt_stream_upstream_rr_peers_rlock(rrp->peers); + back_peers = rrp->peers->next; + peer_num = rrp->peers->number; + if(back_peers != NULL) { + peer_num = peer_num + back_peers->number; + } + + pc->cached = 0; + pc->connection = NULL; + + /* goto the current session */ + peer = rrp->peers->peer; + selected = NULL; + + peer_data.cli_addr_text = (tcc_str_t *)&sp->s->connection->addr_text; + peer_data.peer_list = njt_pcalloc(sp->s->connection->pool,sizeof(tcc_stream_upstream_rr_peer_t) * peer_num); + if(peer_data.peer_list == NULL) { + return NJT_ERROR; + } + peer_num = 0; + /* find the proposed peer */ + while (peer != NULL) + { + if(njt_stream_upstream_pre_handle_peer(peer) == NJT_ERROR) + { + peer = peer->next; + continue; + } + peer_data.peer_list[peer_num].name = (tcc_str_t *)&peer->name; + peer_data.peer_list[peer_num].server = (tcc_str_t *)&peer->server; + peer_data.peer_list[peer_num].peer = peer; + peer_num++; + peer = peer->next; + } + peer = NULL; + if(back_peers != NULL) { + peer = back_peers->peer; + } + + while (peer != NULL) + { + if(njt_stream_upstream_pre_handle_peer(peer) == NJT_ERROR) + { + peer = peer->next; + continue; + } + peer_data.peer_list[peer_num].name = (tcc_str_t *)&peer->name; + peer_data.peer_list[peer_num].server = (tcc_str_t *)&peer->server; + peer_data.peer_list[peer_num].peer = peer; + peer_num++; + peer = peer->next; + } + + if(peer_num > 0) { + peer_data.peer_num = peer_num; + selected = sscf->check_upstream_peer_handler(&peer_data); + } + + /* apply the peer */ + if (selected == NULL) + { + goto round_robin; + } + selected->selected_time = ((njt_timeofday())->sec) * 1000 + (njt_uint_t)((njt_timeofday())->msec); + rrp->current = selected; + + pc->sockaddr = selected->sockaddr; + pc->socklen = selected->socklen; + pc->name = &selected->name; + + selected->conns++; + selected->requests++; + + njt_stream_upstream_rr_peers_unlock(rrp->peers); + + return NJT_OK; + +round_robin: + njt_stream_upstream_rr_peers_unlock(rrp->peers); + + //rc = njt_stream_upstream_get_round_robin_peer(pc, rrp); + rc = NJT_BUSY; + return rc; +} + +static void +njt_stream_proto_upstream_notify_peer(njt_peer_connection_t *pc, + void *data, njt_uint_t type) +{ + njt_stream_upstream_rr_peer_data_t *rrp; + njt_stream_proto_upstream_peer_data_t *sp = data; + rrp = sp->data; + if(sp->original_notify != NULL) { + sp->original_notify(pc,rrp,type); + } + +} +static void njt_stream_proto_upstream_free_peer(njt_peer_connection_t *pc, void *data, + njt_uint_t state) { + njt_stream_upstream_rr_peer_data_t *rrp; + njt_stream_proto_upstream_peer_data_t *sp = data; + rrp = sp->data; + if(sp->original_free_peer != NULL) { + sp->original_free_peer(pc,rrp,state); + } +} + +static njt_int_t njt_stream_proto_upstream_init_peer(njt_stream_session_t *s, + njt_stream_upstream_srv_conf_t *us) +{ + + njt_stream_proto_server_srv_conf_t *ups, *scf; + njt_stream_proto_upstream_peer_data_t *sp; + + ups = njt_stream_conf_upstream_srv_conf(us, njt_stream_proto_server_module); + if (ups->original_init_peer(s, us) != NJT_OK) + { + return NJT_ERROR; + } + scf = njt_stream_get_module_srv_conf(s,njt_stream_proto_server_module); + if(scf->check_upstream_peer_handler == NULL) { + return NJT_OK; + } + sp = njt_palloc(s->connection->pool, sizeof(njt_stream_proto_upstream_peer_data_t)); + if (sp == NULL) + { + return NJT_ERROR; + } + + sp->conf = ups; + sp->s = s; + sp->data = s->upstream->peer.data; + sp->original_free_peer = s->upstream->peer.free; + sp->original_notify = s->upstream->peer.notify; + sp->original_get_peer = s->upstream->peer.get; + s->upstream->peer.get = njt_stream_script_upstream_get_peer; + s->upstream->peer.notify = njt_stream_proto_upstream_notify_peer; + s->upstream->peer.data = sp; + s->upstream->peer.free = njt_stream_proto_upstream_free_peer; + + return NJT_OK; +} + +static njt_int_t njt_stream_proto_upstream_init(njt_conf_t *cf, + njt_stream_upstream_srv_conf_t *us) +{ + njt_stream_proto_server_srv_conf_t *scf; + scf = njt_stream_conf_upstream_srv_conf(us, + njt_stream_proto_server_module); + + if (scf->original_init_upstream(cf, us) != NJT_OK) + { + return NJT_ERROR; + } + + scf->original_init_peer = us->peer.init; + + us->peer.init = njt_stream_proto_upstream_init_peer; + + return NJT_OK; +} + +static char * +njt_stream_proto_upstream_set(njt_conf_t *cf, njt_command_t *cmd, void *conf) +{ + njt_stream_proto_server_srv_conf_t *sscf = conf; + njt_stream_upstream_srv_conf_t *uscf; + + njt_str_t *value; + if (sscf->proto_upstream_enabled != NJT_CONF_UNSET) + { + return "is duplicate"; + } + + value = cf->args->elts; + + if (njt_strcasecmp(value[1].data, (u_char *)"on") == 0) + { + sscf->proto_upstream_enabled = 1; + } + else if (njt_strcasecmp(value[1].data, (u_char *)"off") == 0) + { + sscf->proto_upstream_enabled = 0; + } + else + { + njt_conf_log_error(NJT_LOG_EMERG, cf, 0, + "invalid value \"%s\" in \"%s\" directive, " + "it must be \"on\" or \"off\"", + value[1].data, cmd->name.data); + return NJT_CONF_ERROR; + } + if (sscf->proto_upstream_enabled == 1) + { + uscf = njt_stream_conf_get_module_srv_conf(cf, njt_stream_upstream_module); + sscf->original_init_upstream = uscf->peer.init_upstream + ? uscf->peer.init_upstream + : njt_stream_upstream_init_round_robin; + + uscf->peer.init_upstream = njt_stream_proto_upstream_init; + } + + return NJT_CONF_OK; +} + +static void njt_stream_proto_server_update(njt_event_t *ev) +{ + njt_stream_proto_server_srv_conf_t *sscf; + sscf = ev->data; + if (sscf->server_update_handler) + { + sscf->server_update_handler(&sscf->srv_ctx); + if (sscf->server_update_interval > 0) + { + njt_add_timer(&sscf->timer, sscf->server_update_interval); + } + } + return; +} +static void njt_stream_proto_client_update(njt_event_t *ev) +{ + tcc_stream_request_t *r; + njt_stream_proto_server_client_ctx_t *ctx; + njt_stream_proto_server_srv_conf_t *sscf; + njt_connection_t *c; + njt_stream_session_t *s; + njt_int_t rc = NJT_OK; + tcc_str_t msg; + size_t max_len, len; + + ctx = ev->data; + s = ctx->r.s; + c = s->connection; + r = &ctx->r; + sscf = njt_stream_get_module_srv_conf((njt_stream_session_t *)r->s, njt_stream_proto_server_module); + if (sscf->client_update_handler) + { + msg.data = ctx->r.in_buf.pos; + msg.len = ctx->r.in_buf.last - ctx->r.in_buf.pos; + ctx->r.used_len = 0; + rc = sscf->client_update_handler(&ctx->r, &msg); + if (rc == NJT_ERROR || ctx->r.status == TCC_SESSION_CLOSING) + { + ctx->r.status = TCC_SESSION_CLOSING; + goto end; + } + njt_stream_proto_server_update_in_buf(&ctx->r.in_buf, ctx->r.used_len); + max_len = ctx->r.in_buf.end - ctx->r.in_buf.start; + len = ctx->r.in_buf.last - ctx->r.in_buf.pos; + if (max_len == sscf->buffer_size && max_len == len && max_len > 0) + { + ctx->r.status = TCC_SESSION_CLOSING; // 没空间了。 + } + if (ctx->r.status == TCC_SESSION_CLOSING) + { + goto end; + } + if (sscf->client_update_interval > 0) + { + njt_add_timer(&ctx->timer, sscf->client_update_interval); + } + } + return; +end: + njt_log_error(NJT_LOG_INFO, c->log, 0, "close client"); + njt_stream_proto_server_del_session(s, NJT_STREAM_OK, 1); + return; +} +static njt_int_t njt_stream_proto_server_process(njt_cycle_t *cycle) +{ + njt_stream_proto_server_main_conf_t *cmf; + njt_uint_t i; + njt_stream_proto_server_srv_conf_t *sscf, **sscfp; + + cmf = njt_stream_cycle_get_module_main_conf(cycle, njt_stream_proto_server_module); + if (cmf == NULL) + { + return NJT_OK; + } + sscfp = cmf->srv_info.elts; + + for (i = 0; i < cmf->srv_info.nelts; i++) + { + sscf = sscfp[i]; + sscf->timer.handler = njt_stream_proto_server_update; + sscf->timer.log = cycle->log; + sscf->timer.data = sscf; + sscf->timer.cancelable = 1; + if (sscf->server_update_interval > 0 && sscf->server_update_handler != NULL) + { + njt_add_timer(&sscf->timer, sscf->server_update_interval); + } + } + return NJT_OK; +} + +static char * +njt_stream_proto_server_set(njt_conf_t *cf, njt_command_t *cmd, void *conf) +{ + njt_stream_proto_server_srv_conf_t *sscf = conf; + njt_stream_core_srv_conf_t *cscf; + + njt_str_t *value; + if (sscf->proto_server_enabled != NJT_CONF_UNSET) + { + return "is duplicate"; + } + + value = cf->args->elts; + + if (njt_strcasecmp(value[1].data, (u_char *)"on") == 0) + { + sscf->proto_server_enabled = 1; + } + else if (njt_strcasecmp(value[1].data, (u_char *)"off") == 0) + { + sscf->proto_server_enabled = 0; + } + else + { + njt_conf_log_error(NJT_LOG_EMERG, cf, 0, + "invalid value \"%s\" in \"%s\" directive, " + "it must be \"on\" or \"off\"", + value[1].data, cmd->name.data); + return NJT_CONF_ERROR; + } + if (sscf->proto_server_enabled == 1) + { + cscf = njt_stream_conf_get_module_srv_conf(cf, njt_stream_core_module); + cscf->handler = njt_stream_proto_server_handler; + } + + return NJT_CONF_OK; +} + +static void +njt_stream_proto_server_delete_tcc(void *data) +{ + TCCState *tcc = data; + tcc_delete(tcc); +} + +static TCCState *njt_stream_proto_server_create_tcc(njt_conf_t *cf) +{ + u_char *p; + njt_pool_cleanup_t *cln; + njt_str_t full_path, path = njt_string("lib/tcc"); + + TCCState *tcc = tcc_new(); + if (tcc == NULL) + { + return NULL; + } + cln = njt_pool_cleanup_add(cf->cycle->pool, 0); + if (cln == NULL) + { + return NJT_CONF_ERROR; + } + cln->handler = njt_stream_proto_server_delete_tcc; + cln->data = tcc; + + full_path.len = cf->cycle->prefix.len + path.len + 10; + full_path.data = njt_pcalloc(cf->pool, full_path.len); + if (full_path.data == NULL) + { + return NULL; + } + p = njt_snprintf(full_path.data, full_path.len, "%V%V\0", &cf->cycle->prefix, &path); + full_path.len = p - full_path.data; + + tcc_set_options(tcc, "-Werror -g"); + tcc_set_output_type(tcc, TCC_OUTPUT_MEMORY); + // tcc_set_options(tcc, "-Werror "); + tcc_set_lib_path(tcc, (const char *)full_path.data); + tcc_add_include_path(tcc, (const char *)full_path.data); + tcc_add_sysinclude_path(tcc, (const char *)full_path.data); + return tcc; +} +static void *njt_stream_proto_server_create_srv_conf(njt_conf_t *cf) +{ + njt_stream_proto_server_srv_conf_t *conf; + njt_int_t rc; + + njt_log_debug(NJT_LOG_DEBUG_EVENT, njt_cycle->log, 0, "stream_proto create serv config"); + + conf = njt_pcalloc(cf->pool, sizeof(njt_stream_proto_server_srv_conf_t)); + if (conf == NULL) + { + return NULL; + } + conf->proto_server_enabled = NJT_CONF_UNSET; + conf->proto_upstream_enabled = NJT_CONF_UNSET; + conf->s = NJT_CONF_UNSET_PTR; + conf->tcc_files = NJT_CONF_UNSET_PTR; + conf->connect_timeout = NJT_CONF_UNSET_MSEC; + conf->client_update_interval = NJT_CONF_UNSET_MSEC; + conf->server_update_interval = NJT_CONF_UNSET_MSEC; + conf->buffer_size = NJT_CONF_UNSET_SIZE; + conf->session_max_mem_size = NJT_CONF_UNSET_SIZE; + conf->srv_ctx.client_list = njt_pcalloc(cf->pool, sizeof(njt_array_t)); + conf->srv_ctx.tcc_pool = njt_create_dynamic_pool(njt_pagesize, njt_cycle->log); + if (conf->srv_ctx.tcc_pool == NULL) + { + return NULL; + } + rc = njt_sub_pool(cf->cycle->pool, conf->srv_ctx.tcc_pool); + if (rc == NJT_ERROR) + { + return NULL; + } + + njt_array_init(conf->srv_ctx.client_list, cf->pool, 1, sizeof(tcc_stream_request_t *)); + return conf; +} + +static char *njt_stream_proto_server_merge_srv_conf(njt_conf_t *cf, void *parent, void *child) +{ + njt_str_t *pp, value; + char *filename; + njt_uint_t i; + int filetype; + njt_str_t full_name; + njt_stream_proto_server_main_conf_t *cmf; + njt_stream_proto_server_srv_conf_t **psscf; + + + njt_stream_proto_server_srv_conf_t *prev = parent; + njt_stream_proto_server_srv_conf_t *conf = child; + njt_conf_merge_value(conf->proto_server_enabled, prev->proto_server_enabled, 0); + njt_conf_merge_size_value(conf->buffer_size, + prev->buffer_size, 16384); + njt_conf_merge_size_value(conf->session_max_mem_size, + prev->session_max_mem_size, 16384); + njt_conf_merge_msec_value(conf->connect_timeout, + prev->connect_timeout, 60000); + njt_conf_merge_msec_value(conf->client_update_interval, + prev->client_update_interval, 60000); + njt_conf_merge_msec_value(conf->server_update_interval, + prev->server_update_interval, 60000); + if (conf->proto_pass_enabled && !conf->proto_server_enabled) { + njt_conf_log_error(NJT_LOG_EMERG, cf, 0, + "proto_pass: need proto_server directive!"); + } + if (conf->proto_server_enabled && conf->s == NJT_CONF_UNSET_PTR && conf->tcc_files != NJT_CONF_UNSET_PTR) { + conf->s = njt_stream_proto_server_create_tcc(cf); // todo + if (conf->s == NULL) + { + njt_conf_log_error(NJT_LOG_EMERG, cf, 0, + "njt_stream_proto_server_create_tcc error!"); + return NJT_CONF_ERROR; + } + + pp = conf->tcc_files->elts; + for (i = 0; i < conf->tcc_files->nelts; i++) { + value = pp[i]; + + full_name = value; + if(njt_conf_full_name((void *)cf->cycle, &full_name, 0) != NJT_OK) { + njt_conf_log_error(NJT_LOG_EMERG, cf, 0, + "sniffer_filter_file \"%V\", njt_conf_full_name error!", &full_name); + return NJT_CONF_ERROR; + } + + filename = njt_pcalloc(cf->pool,full_name.len + 1); + if(filename == NULL) { + return NJT_CONF_ERROR; + } + njt_memcpy(filename,full_name.data,full_name.len); + filetype = TCC_FILETYPE_C; + if (tcc_add_file(conf->s, filename, filetype) < 0) { + njt_conf_log_error(NJT_LOG_EMERG, cf, 0, + "tcc_add_file error!"); + return NJT_CONF_ERROR; + } + } + if (tcc_relocate(conf->s, TCC_RELOCATE_AUTO) < 0) { + njt_conf_log_error(NJT_LOG_EMERG, cf, 0, + "tcc_relocate error!"); + return NJT_CONF_ERROR; + } + } + if (conf->proto_server_enabled && conf->s != NJT_CONF_UNSET_PTR) + { + conf = njt_stream_conf_get_module_srv_conf(cf, njt_stream_proto_server_module); + conf->connection_handler = tcc_get_symbol(conf->s, "proto_server_process_connection"); + conf->preread_handler = tcc_get_symbol(conf->s, "proto_server_process_preread"); + conf->log_handler = tcc_get_symbol(conf->s, "proto_server_process_log"); + conf->message_handler = tcc_get_symbol(conf->s, "proto_server_process_message"); + conf->abort_handler = tcc_get_symbol(conf->s, "proto_server_process_connection_close"); + conf->client_update_handler = tcc_get_symbol(conf->s, "proto_server_process_client_update"); + conf->server_update_handler = tcc_get_symbol(conf->s, "proto_server_update"); + conf->server_init_handler = tcc_get_symbol(conf->s, "proto_server_init"); + conf->build_proto_message = tcc_get_symbol(conf->s, "proto_server_create_message"); + conf->upstream_message_handler = tcc_get_symbol(conf->s, "proto_server_upstream_message"); + conf->check_upstream_peer_handler = tcc_get_symbol(conf->s, "proto_server_check_upstream_peer"); + conf->upstream_abort_handler = tcc_get_symbol(conf->s, "proto_server_upstream_connection_close"); + if (conf->server_init_handler) + { + conf->server_init_handler(&conf->srv_ctx); + } + if (conf->server_update_interval != 0 && conf->server_update_handler != NULL) + { + cmf = njt_stream_conf_get_module_main_conf(cf, njt_stream_proto_server_module); + psscf = njt_array_push(&cmf->srv_info); + *psscf = conf; + } + } + njt_log_debug(NJT_LOG_DEBUG_EVENT, njt_cycle->log, 0, "stream_proto merge serv config"); + return NJT_CONF_OK; +} + +static njt_int_t njt_stream_proto_server_access_handler(njt_stream_session_t *s) +{ + njt_stream_proto_server_srv_conf_t *sscf; + njt_stream_proto_server_client_ctx_t *ctx; + njt_connection_t *c; + njt_int_t rc; + u_char *p; + + c = s->connection; + sscf = njt_stream_get_module_srv_conf(s, njt_stream_proto_server_module); + if (!sscf->proto_server_enabled) + { + return NJT_DECLINED; + } + ctx = njt_pcalloc(c->pool, sizeof(njt_stream_proto_server_client_ctx_t)); + if (ctx == NULL) + { + goto end; + } + if (ctx->out_buf.start == NULL) + { + p = njt_pcalloc(c->pool, sscf->buffer_size); + if (p == NULL) + { + goto end; + } + + ctx->out_buf.start = p; + ctx->out_buf.end = p + sscf->buffer_size; + ctx->out_buf.pos = p; + ctx->out_buf.last = p; + } + ctx->r.s = s; + ctx->r.tcc_server = &sscf->srv_ctx; + ctx->r.addr_text = (tcc_str_t *)&s->connection->addr_text; + ctx->r.tcc_pool = njt_create_dynamic_pool(njt_pagesize, njt_cycle->log); + if (ctx->r.tcc_pool == NULL) + { + goto end; + } + rc = njt_sub_pool(c->pool, ctx->r.tcc_pool); + if (rc == NJT_ERROR) + { + goto end; + } + njt_stream_set_ctx(s, ctx, njt_stream_proto_server_module); + rc = NJT_DECLINED; + if (sscf->connection_handler) + { + rc = sscf->connection_handler(&ctx->r); + if (rc == NJT_ERROR || ctx->r.status == TCC_SESSION_CLOSING) + { + return NJT_STREAM_FORBIDDEN; + } + } + return rc; +end: + return NJT_DECLINED; +} +static void njt_stream_proto_server_update_in_buf(tcc_buf_t *b, size_t used_len) +{ + njt_uint_t len; + if (used_len <= 0) + { + if(b->last == b->end && b->pos > b->start) { //收到结尾,但不够一个包,移动位置。 + len = b->last - b->pos; + if(len > 0) { + njt_memmove(b->start,b->pos,len); + } + b->pos = b->start; + b->last = b->start + len; + } + return; + } + b->pos = b->pos + used_len; + if (b->pos >= b->last) + { + // 消费完,重置。 + b->pos = b->start; + b->last = b->start; + } +} +static njt_int_t njt_stream_proto_server_preread_handler(njt_stream_session_t *s) +{ + + njt_stream_proto_server_srv_conf_t *sscf; + njt_stream_proto_server_client_ctx_t *ctx; + njt_connection_t *c; + njt_int_t rc = NJT_DECLINED; + tcc_str_t msg; + size_t max_len, len; + + c = s->connection; + + + sscf = njt_stream_get_module_srv_conf(s, njt_stream_proto_server_module); + if (!sscf->proto_server_enabled ) + { + return NJT_DECLINED; + } + if (sscf->preread_handler) + { ctx = njt_stream_get_module_ctx(s, njt_stream_proto_server_module); + ctx->r.s = s; + ctx->r.addr_text = (tcc_str_t *)&s->connection->addr_text; + if (c->buffer != NULL && ctx->r.in_buf.pos == NULL) + { + ctx->r.in_buf.end = c->buffer->end; + ctx->r.in_buf.start = c->buffer->start; + ctx->r.in_buf.pos = c->buffer->pos; + ctx->r.in_buf.last = c->buffer->last; + } + else if (c->buffer != NULL) + { + ctx->r.in_buf.last = c->buffer->last; + } + // tcc_stream_request_t *r,void *data,size_t len,size_t *used_len + msg.data = ctx->r.in_buf.pos; + msg.len = ctx->r.in_buf.last - ctx->r.in_buf.pos; + ctx->r.used_len = 0; + rc = sscf->preread_handler(&ctx->r, &msg); + //njt_stream_proto_server_update_in_buf(&ctx->r.in_buf, ctx->r.used_len); + if(ctx->r.in_buf.last - ctx->r.in_buf.pos >= ctx->r.used_len) { + ctx->r.in_buf.pos = ctx->r.in_buf.pos + ctx->r.used_len; + } + max_len = ctx->r.in_buf.end - ctx->r.in_buf.start; + len = ctx->r.in_buf.last - ctx->r.in_buf.pos; + if (rc == NJT_AGAIN && max_len == len && max_len > 0) + { + rc = NJT_ERROR; // 没空间了。 + } + } + return rc; +} +static njt_int_t njt_stream_proto_server_log_handler(njt_stream_session_t *s) +{ + + njt_stream_proto_server_srv_conf_t *sscf; + njt_stream_proto_server_client_ctx_t *ctx; + njt_int_t rc = NJT_OK; + + sscf = njt_stream_get_module_srv_conf(s, njt_stream_proto_server_module); + if (!sscf->proto_server_enabled) + { + return NJT_OK; + } + ctx = njt_stream_get_module_ctx(s, njt_stream_proto_server_module); + ctx->r.s = s; + ctx->r.addr_text = (tcc_str_t *)&s->connection->addr_text; + + njt_stream_proto_server_del_session(s, NJT_STREAM_OK, 0); + if(s->upstream && sscf->upstream_abort_handler) { + rc = sscf->upstream_abort_handler(&ctx->r); + } + if (sscf->abort_handler) + { + rc = sscf->abort_handler(&ctx->r); + } + if (sscf->log_handler) + { + rc = sscf->log_handler(&ctx->r); + } + return rc; +} +static void njt_stream_proto_server_handler(njt_stream_session_t *s) +{ + njt_connection_t *c; + njt_stream_proto_server_client_ctx_t *ctx; + njt_uint_t flags; + njt_stream_proto_server_srv_conf_t *sscf; + tcc_stream_request_t **r; + + c = s->connection; + + c->log->action = "proto_server_handler"; + ctx = njt_stream_get_module_ctx(s, njt_stream_proto_server_module); + sscf = njt_stream_get_module_srv_conf(s, njt_stream_proto_server_module); + + if (c->buffer != NULL) + { + ctx->r.in_buf.end = c->buffer->end; + ctx->r.in_buf.start = c->buffer->start; + ctx->r.in_buf.pos = c->buffer->pos; + ctx->r.in_buf.last = c->buffer->last; + } + + ctx->timer.handler = njt_stream_proto_client_update; + ctx->timer.log = njt_cycle->log; + ctx->timer.data = ctx; + ctx->timer.cancelable = 1; + + flags = s->connection->read->eof ? NJT_CLOSE_EVENT : 0; + + if (njt_handle_read_event(s->connection->read, flags) != NJT_OK) + { + goto end; + } + + c->write->handler = njt_stream_proto_server_write_handler; + c->read->handler = njt_stream_proto_server_read_handler; + + if (c->read->ready) + { + njt_post_event(c->read, &njt_posted_events); + } + if (sscf->connect_timeout != NJT_CONF_UNSET_MSEC && sscf->connect_timeout > 0) + { + njt_add_timer(c->read, sscf->connect_timeout); + } + r = njt_array_push(sscf->srv_ctx.client_list); + *r = &ctx->r; + + if (sscf->client_update_interval > 0 && sscf->client_update_handler != NULL) + { + njt_add_timer(&ctx->timer, sscf->client_update_interval); + } + + njt_stream_proto_server_read_handler(c->read); + return; +end: + njt_stream_proto_server_del_session(s, NJT_STREAM_INTERNAL_SERVER_ERROR, 1); + return; +} +static void +njt_stream_proto_server_read_handler(njt_event_t *ev) +{ + njt_stream_session_t *s; + njt_connection_t *c; + njt_stream_proto_server_client_ctx_t *ctx; + u_char *p; + size_t size, len, max_len; + tcc_buf_t *b; + ssize_t n; + njt_stream_proto_server_srv_conf_t *sscf; + tcc_str_t msg; + njt_int_t rc = NJT_OK; + njt_int_t msg_rc; + njt_uint_t code = NJT_STREAM_OK; + + c = ev->data; + s = c->data; + sscf = njt_stream_get_module_srv_conf(s, njt_stream_proto_server_module); + ctx = njt_stream_get_module_ctx(s, njt_stream_proto_server_module); + + if (ev->timedout) + { + njt_log_error(NJT_LOG_INFO, c->log, NJT_ETIMEDOUT, "client timed out"); + + if (ctx->timer.timer_set) + { + njt_del_timer(&ctx->timer); + } + code = NJT_STREAM_OK; + goto end; + } + + if (ctx->r.status == TCC_SESSION_CLOSING) + { + njt_log_error(NJT_LOG_INFO, c->log, 0, "tcc close client"); + code = NJT_STREAM_OK; + goto end; + } +for(;;) +{ + for (;;) + { + if (ctx->r.in_buf.start == NULL) + { + p = njt_pcalloc(c->pool, sscf->buffer_size); + if (p == NULL) + { + code = NJT_STREAM_INTERNAL_SERVER_ERROR; + goto end; + } + + ctx->r.in_buf.start = p; + ctx->r.in_buf.end = p + sscf->buffer_size; + ctx->r.in_buf.pos = p; + ctx->r.in_buf.last = p; + } + b = &ctx->r.in_buf; + size = b->end - b->last; + if (size && c != NULL && c->read->ready && !c->read->delayed) + { + n = c->recv(c, b->last, size); + if (n == 0) + { + code = NJT_STREAM_OK; + rc = NJT_ERROR; + break; + } + if (n == NJT_AGAIN) + { + break; + } + if (n == NJT_ERROR) + { + c->read->eof = 1; + n = 0; + } + b->last += n; + continue; + } + break; + } + msg.data = ctx->r.in_buf.pos; + msg.len = ctx->r.in_buf.last - ctx->r.in_buf.pos; + if (sscf->message_handler) + { + for (;msg.len > 0;) { + ctx->r.used_len = 0; + msg_rc = NJT_OK; + if (ctx->r.status == TCC_SESSION_CONNECT) + { + msg_rc = sscf->message_handler(&ctx->r, &msg); + } + if (ctx->r.status == TCC_SESSION_CLOSING || msg_rc == NJT_ERROR) + { + code = NJT_STREAM_OK; + goto end; + } + if( ctx->r.used_len == 0) { + break; + } + njt_stream_proto_server_update_in_buf(&ctx->r.in_buf,ctx->r.used_len); + max_len = ctx->r.in_buf.end - ctx->r.in_buf.start; + len = ctx->r.in_buf.last - ctx->r.in_buf.pos; + if (max_len == sscf->buffer_size && max_len == len && max_len > 0) + { + ctx->r.status = TCC_SESSION_CLOSING; // 没空间了。 + } + if (max_len != sscf->buffer_size && ctx->r.in_buf.pos == ctx->r.in_buf.last) + { + ctx->r.in_buf.start = NULL; // by zyg,由之前的预读阶段buffer 大小,切换为本模块的定义大小。 + } + if(ctx->r.in_buf.start != NULL && msg_rc == NJT_AGAIN) { + msg.data = ctx->r.in_buf.pos; + msg.len = ctx->r.in_buf.last - ctx->r.in_buf.pos; + continue; + } + break; + } + } + if(rc == NJT_ERROR) { + njt_log_error(NJT_LOG_INFO, c->log, 0, "tcc close client"); + code = NJT_STREAM_OK; + goto end; + } + if(c->read->ready && ((ctx->r.in_buf.start == NULL) || (ctx->r.in_buf.end - ctx->r.in_buf.last > 0))) + { + continue; + } + break; +} + if (sscf->connect_timeout != NJT_CONF_UNSET_MSEC && sscf->connect_timeout > 0) + { + njt_add_timer(ev, sscf->connect_timeout); + } + return; +end: + njt_stream_proto_server_del_session(s, code, 1); + return; +} +static njt_int_t +njt_stream_proto_server_write_data(njt_event_t *ev) +{ + njt_connection_t *c; + njt_stream_session_t *s; + njt_chain_t **busy; + njt_stream_proto_server_client_ctx_t *ctx; + + c = ev->data; + s = c->data; + if (ev->timedout) + { + ev->timedout = 0; + if (njt_handle_write_event(ev, 0) != NJT_OK) + { + return NJT_ERROR; + } + return NJT_OK; + } + + ctx = njt_stream_get_module_ctx(s, njt_stream_proto_server_module); + busy = &ctx->out_busy; + if (ctx->out_chain || *busy || s->connection->buffered) { + if (njt_proto_write_filter(s, ctx->out_chain, 1) == NJT_ERROR) + { + return NJT_ERROR; + } + njt_chain_update_chains(c->pool, &ctx->free, busy, &ctx->out_chain, + (njt_buf_tag_t)&njt_stream_proto_server_module); + + if (*busy == NULL) + { + ctx->out_buf.pos = ctx->out_buf.start; + ctx->out_buf.last = ctx->out_buf.start; + njt_log_error(NJT_LOG_DEBUG, c->log, 0, "tcc send out ok!"); + } + else + { + njt_log_error(NJT_LOG_DEBUG, c->log, 0, "tcc send out busy!"); + } + } + + if (njt_handle_write_event(ev, 0) != NJT_OK) + { + return NJT_ERROR; + } + + njt_add_timer(ev, 5000); + return NJT_OK; +} +static void +njt_stream_proto_server_write_handler(njt_event_t *ev) +{ + njt_int_t rc; + njt_connection_t *c; + njt_stream_session_t *s; + + rc = njt_stream_proto_server_write_data(ev); + if (rc == NJT_ERROR) { + c = ev->data; + s = c->data; + njt_stream_proto_server_del_session(s, NJT_STREAM_INTERNAL_SERVER_ERROR, 1); + } +} +// add handler to pre-access +// otherwise, handler can't be add as part of config handler if proxy handler is involved. + +static njt_int_t njt_stream_proto_server_init(njt_conf_t *cf) +{ + njt_stream_handler_pt *h; + njt_stream_core_main_conf_t *cmcf; + + njt_log_debug(NJT_LOG_DEBUG_EVENT, njt_cycle->log, 0, "ngin proto_server init invoked"); + + cmcf = njt_stream_conf_get_module_main_conf(cf, njt_stream_core_module); + + h = njt_array_push(&cmcf->phases[NJT_STREAM_ACCESS_PHASE].handlers); + if (h == NULL) + { + return NJT_ERROR; + } + + *h = njt_stream_proto_server_access_handler; + + h = njt_array_push(&cmcf->phases[NJT_STREAM_PREREAD_PHASE].handlers); + if (h == NULL) + { + return NJT_ERROR; + } + + *h = njt_stream_proto_server_preread_handler; + + h = njt_array_push(&cmcf->phases[NJT_STREAM_LOG_PHASE].handlers); + if (h == NULL) + { + return NJT_ERROR; + } + + *h = njt_stream_proto_server_log_handler; + + return NJT_OK; +} + + +int proto_server_send(tcc_stream_request_t *r, char *data, size_t len) +{ + + njt_connection_t *c; + njt_stream_proto_server_client_ctx_t *ctx; + njt_chain_t *cl; + njt_int_t rc; + njt_stream_proto_server_srv_conf_t *sscf; + njt_stream_session_t *s = r->s; + //size_t size; + + c = s->connection; + if(s->upstream != NULL) { + rc = proto_server_proxy_send(r,1,data,len); + return rc; + } + sscf = njt_stream_get_module_srv_conf(s, njt_stream_proto_server_module); + ctx = njt_stream_get_module_ctx(s, njt_stream_proto_server_module); + if(ctx->r.status == TCC_SESSION_CLOSING) { + return NJT_ERROR; + } + if(ctx->r.session_max_mem_size + len > sscf->session_max_mem_size) { + len = sscf->session_max_mem_size - ctx->r.session_max_mem_size; + } + + cl = njt_chain_get_free_buf(c->pool, &ctx->free); + if (cl == NULL) { + return NJT_ERROR; + } + + //njt_memcpy(ctx->out_buf.last, data, size); + + cl->buf->tag = (njt_buf_tag_t)&njt_stream_proto_server_module; + cl->buf->memory = 1; + cl->buf->flush = 1; + cl->buf->pos = njt_pcalloc(ctx->r.tcc_pool,len); + if(cl->buf->pos == NULL) { + return NJT_ERROR; + } + cl->buf->start = cl->buf->pos; + cl->buf->end = cl->buf->pos + len; + njt_memcpy(cl->buf->pos, data, len); + cl->buf->last = cl->buf->pos + len; + cl->buf->last_buf = 1; + cl->next = ctx->out_chain; + ctx->out_chain = cl; + ctx->r.session_max_mem_size = ctx->r.session_max_mem_size + len; + + njt_stream_proto_server_write_data(c->write); + rc = len; + return rc; +} +int proto_server_send_broadcast(tcc_stream_server_ctx *srv_ctx, char *data, size_t len) +{ + + tcc_stream_request_t **pr, *r; + njt_uint_t i; + njt_array_t *client_list; + + client_list = srv_ctx->client_list; + pr = client_list->elts; + + for (i = 0; i < client_list->nelts; i++) + { + r = pr[i]; + proto_server_send(r, data, len); + } + return NJT_OK; +} +int proto_server_send_others(tcc_stream_request_t *sender, char *data, size_t len) +{ + + tcc_stream_request_t **pr, *r; + njt_uint_t i; + njt_array_t *client_list; + tcc_stream_server_ctx *srv_ctx = sender->tcc_server; + + client_list = srv_ctx->client_list; + pr = client_list->elts; + + for (i = 0; i < client_list->nelts; i++) + { + r = pr[i]; + if(r != sender) { + proto_server_send(r, data, len); + } + } + return NJT_OK; +} + +static njt_int_t njt_stream_proto_server_del_session(njt_stream_session_t *s, njt_uint_t code, njt_uint_t close_session) +{ + + njt_array_t *client_list; + njt_stream_proto_server_srv_conf_t *sscf; + tcc_stream_request_t **pr, *r; + njt_uint_t i; + njt_stream_proto_server_client_ctx_t *ctx; + njt_int_t rc; + + rc = NJT_ERROR; + sscf = njt_stream_get_module_srv_conf(s, njt_stream_proto_server_module); + ctx = njt_stream_get_module_ctx(s, njt_stream_proto_server_module); + client_list = sscf->srv_ctx.client_list; + pr = client_list->elts; + for (i = 0; i < client_list->nelts; i++) + { + r = pr[i]; + if (r->s == s) + { + njt_array_delete_idx(client_list, i); + if (ctx->timer.timer_set) + { + njt_del_timer(&ctx->timer); + } + rc = NJT_OK; + } + } + if (rc == NJT_OK && close_session == 1) + { + if(s->upstream) { + njt_stream_proto_finalize(s,code); + } else { + njt_stream_finalize_session(s, code); + } + } + return NJT_OK; +} + +void cli_close(tcc_stream_request_t *r) +{ + if (r != NULL) + { + r->status = TCC_SESSION_CLOSING; + } + return; +} + +tcc_str_t cli_get_variable(tcc_stream_request_t *r, char *name) +{ + njt_conf_t conf; + njt_uint_t var_index; + njt_str_t var; + njt_stream_variable_value_t *value; + tcc_str_t ret_val = njt_string(""); + njt_stream_core_main_conf_t *cmcf; + njt_uint_t i; + njt_stream_variable_t *v; + njt_stream_session_t *s = r->s; + if (name == NULL) + { + return ret_val; + } + var.data = (u_char *)name; + var.len = njt_strlen(name); + + cmcf = njt_stream_cycle_get_module_main_conf(njt_cycle, njt_stream_core_module); + v = cmcf->variables.elts; + for (i = 0; i < cmcf->variables.nelts; i++) + { + if (var.len != v[i].name.len || njt_strncasecmp(var.data, v[i].name.data, var.len) != 0) + { + continue; + } + + break; + } + if (i == cmcf->variables.nelts) + { + return ret_val; + } + + njt_memzero(&conf, sizeof(njt_conf_t)); + conf.pool = s->connection->pool; + conf.temp_pool = s->connection->pool; + conf.module_type = NJT_STREAM_MODULE; + conf.cycle = (njt_cycle_t *)njt_cycle; + conf.ctx = njt_get_conf(njt_cycle->conf_ctx, njt_stream_module); + conf.log = njt_cycle->log; + + var_index = njt_stream_get_variable_index(&conf, &var); + value = njt_stream_get_indexed_variable(s, var_index); + if (value != NULL && value->not_found == 0) + { + ret_val.data = value->data; + ret_val.len = value->len; + } + + return ret_val; +} +size_t srv_get_client_num(tcc_stream_server_ctx *srv) +{ + njt_array_t *client_list; + if (srv != NULL && srv->client_list != NULL) + { + client_list = srv->client_list; + return client_list->nelts; + } + return 0; // tcc_stream_request_t * +} +tcc_stream_request_t *srv_get_client_index(tcc_stream_server_ctx *srv, size_t index) +{ + njt_array_t *client_list; + tcc_stream_request_t **pr; + if (srv != NULL && srv->client_list != NULL) + { + client_list = srv->client_list; + if (index < client_list->nelts) + { + pr = client_list->elts; + return pr[index]; + } + } + return NULL; +} + +void *proto_malloc(void *ctx, int len) +{ + u_char **ptr; + njt_pool_t *pool; + if (ctx != NULL) + { + ptr = (u_char **)ctx; + pool = (njt_pool_t *)*ptr; + return njt_palloc(pool, len); + } + return NULL; +} +void proto_free(void *ctx, void *p) +{ + u_char **ptr; + njt_pool_t *pool; + if (ctx != NULL) + { + ptr = (u_char **)ctx; + pool = (njt_pool_t *)*ptr; + njt_pfree(pool, p); + } + return; +} +void *proto_realloc(void *ctx, void *p, int len) +{ + u_char **ptr; + njt_pool_t *pool; + if (ctx != NULL) + { + ptr = (u_char **)ctx; + pool = (njt_pool_t *)*ptr; + return njt_prealloc(pool, p, len); + } + return NULL; +} +int proto_server_send_upstream(tcc_stream_request_t *r,char *data, size_t len){ + njt_int_t rc; + rc = proto_server_proxy_send(r,0,data,len); + return rc; +} +static int proto_server_proxy_send(tcc_stream_request_t *r, njt_uint_t from_upstream, char *data, size_t len) +{ + + char *recv_action; + size_t n; + njt_chain_t *cl, **ll, **out; + njt_connection_t *c, *pc, *src, *dst; + njt_stream_upstream_t *u; + njt_stream_session_t *s; + njt_int_t rc; + njt_stream_proto_server_client_ctx_t *ctx; + njt_stream_proto_server_srv_conf_t *sscf; + + s = r->s; + u = s->upstream; + c = s->connection; + if(u == NULL) { + return 0; + } + pc = u->connected ? u->peer.connection : NULL; + n = 0; + sscf = njt_stream_get_module_srv_conf(s, njt_stream_proto_server_module); + ctx = njt_stream_get_module_ctx(s, njt_stream_proto_server_module); + if (from_upstream) + { + if(ctx->r.session_max_mem_size + len > sscf->session_max_mem_size) { + len = sscf->session_max_mem_size - ctx->r.session_max_mem_size; + } + src = pc; + dst = c; + out = &u->downstream_out; + recv_action = "proto_server_proxy_send from upstream"; + + } + else + { + if(ctx->r.session_up_max_mem_size + len > sscf->session_max_mem_size) { + len = sscf->session_max_mem_size - ctx->r.session_up_max_mem_size; + } + src = c; + dst = pc; + out = &u->upstream_out; + recv_action = "proto_server_proxy_send from client"; + } + + if (src != NULL) + { + + c->log->action = recv_action; + n = len; + if (n > 0) + { + cl = njt_chain_get_free_buf(c->pool, &u->free); + if (cl == NULL) + { + return NJT_ERROR; + } + cl->buf->pos = njt_pcalloc(ctx->r.tcc_pool,len); + if(cl->buf->pos == NULL) { + return NJT_ERROR; + } + + if (from_upstream) + { + if (u->state->first_byte_time == (njt_msec_t)-1) + { + u->state->first_byte_time = njt_current_msec - u->start_time; + } + ctx->r.session_max_mem_size = ctx->r.session_max_mem_size + len; + } else { + ctx->r.session_up_max_mem_size = ctx->r.session_up_max_mem_size + len; + } + for (ll = out; *ll; ll = &(*ll)->next) + { /* void */ + } + + *ll = cl; + njt_memcpy(cl->buf->pos, data, len); + cl->buf->start = cl->buf->pos; + cl->buf->end = cl->buf->pos + len; + cl->buf->last = cl->buf->pos + len; + cl->buf->tag = (njt_buf_tag_t)&njt_stream_proto_server_module; + + cl->buf->temporary = (n ? 1 : 0); + cl->buf->last_buf = 0; + cl->buf->flush = 1; + } + } + c->log->action = "proto_server_proxy_send"; + if (dst) + { + if (njt_handle_write_event(dst->write, 0) != NJT_OK) + { + //njt_stream_proto_finalize(s, NJT_STREAM_INTERNAL_SERVER_ERROR); + return NJT_ERROR; + } + rc = njt_stream_proto_process(s,from_upstream,1,0); + if(rc != NJT_OK) { + + if(ctx) { + ctx->r.status = TCC_SESSION_CLOSING; + return NJT_ERROR; + } + } + } + return n; +} + +njt_int_t njt_stream_proto_server_init_upstream(njt_stream_session_t *s) +{ + njt_stream_proto_server_srv_conf_t *sscf; + njt_stream_proto_server_client_ctx_t *ctx; + u_char *p; + size_t len,len2; + tcc_str_t msg; + njt_connection_t *c = s->connection; + + sscf = njt_stream_get_module_srv_conf(s, njt_stream_proto_server_module); + if(sscf && sscf->message_handler != NULL) { + ctx = njt_stream_get_module_ctx(s, njt_stream_proto_server_module); + len = ctx->r.in_buf.end - ctx->r.in_buf.start; + if (ctx->r.in_buf.start == NULL || len != sscf->buffer_size) + { + p = njt_pcalloc(c->pool, sscf->buffer_size); + if (p == NULL) + { + return NJT_ERROR; + } + + ctx->r.in_buf.start = p; + ctx->r.in_buf.end = p + sscf->buffer_size; + ctx->r.in_buf.pos = p; + ctx->r.in_buf.last = p; + } + if(c->buffer != NULL) { + len = c->buffer->last - c->buffer->pos; + len2 = ctx->r.in_buf.end - ctx->r.in_buf.last; + if (len2 >= len) { + njt_memcpy(ctx->r.in_buf.last,c->buffer->pos,len); + ctx->r.in_buf.last = ctx->r.in_buf.last + len; + msg.data = ctx->r.in_buf.pos; + msg.len = ctx->r.in_buf.last - ctx->r.in_buf.pos; + sscf->message_handler(&ctx->r,&msg); + njt_stream_proto_server_update_in_buf(&ctx->r.in_buf,ctx->r.used_len); + } else { + njt_log_error(NJT_LOG_INFO, c->log, 0,"stream proxy add preread buffer error!"); + return NJT_ERROR; + } + } + return NJT_OK; + + } + return NJT_DECLINED; + +} + +njt_int_t njt_stream_proto_server_process_proxy_message(njt_stream_session_t *s, njt_buf_t *b, njt_uint_t from_upstream) +{ + njt_stream_proto_server_srv_conf_t *sscf; + njt_stream_proto_server_client_ctx_t *ctx; + tcc_str_t msg; + njt_int_t rc; + njt_uint_t code; + + sscf = njt_stream_get_module_srv_conf(s, njt_stream_proto_server_module); + ctx = njt_stream_get_module_ctx(s, njt_stream_proto_server_module); + rc = NJT_OK; + if (ctx != NULL) + { + msg.data = b->pos; + msg.len = b->last - b->pos; + if(msg.len == 0) { + return NJT_OK; + } + ctx->r.used_len = 0; + if (from_upstream) + { + if (sscf->upstream_message_handler != NULL) + { + rc = sscf->upstream_message_handler(&ctx->r, &msg); + } + } + else + { + if (sscf->message_handler != NULL) + { + rc = sscf->message_handler(&ctx->r, &msg); + } + } + if (rc == NJT_ERROR || ctx->r.status == TCC_SESSION_CLOSING) + { + code = NJT_STREAM_INTERNAL_SERVER_ERROR; + goto end; + } + njt_stream_proto_server_update_in_buf((tcc_buf_t *)b,ctx->r.used_len); + + } + return NJT_OK; +end: + njt_stream_proto_server_del_session(s, code, 0); + return NJT_ERROR; +} +tcc_int_t proto_get_peer_weight(void *peer) { + njt_stream_upstream_rr_peer_t *p = peer; + return p->weight; +} +tcc_int_t proto_get_peer_conns(void *peer) { + njt_stream_upstream_rr_peer_t *p = peer; + return p->conns; +} +tcc_uint_t proto_get_peer_fails(void *peer) { + njt_stream_upstream_rr_peer_t *p = peer; + return p->fails; +} + + +void tcc_encode_base64(tcc_str_t *dst, tcc_str_t *src) { + njt_encode_base64((njt_str_t *)dst,(njt_str_t *)src); +} +void tcc_encode_base64url(tcc_str_t *dst, tcc_str_t *src){ + njt_encode_base64url((njt_str_t *)dst,(njt_str_t *)src); +} +tcc_int_t tcc_decode_base64(tcc_str_t *dst, tcc_str_t *src){ + return njt_decode_base64((njt_str_t *)dst,(njt_str_t *)src); +} +tcc_int_t tcc_decode_base64url(tcc_str_t *dst, tcc_str_t *src){ + return njt_decode_base64url((njt_str_t *)dst,(njt_str_t *)src); +} +void tcc_sha1_init(tcc_sha1_t *ctx){ + njt_sha1_init((njt_sha1_t *)ctx); +} +void tcc_sha1_update(tcc_sha1_t *ctx, const void *data, size_t size){ + njt_sha1_update((njt_sha1_t *)ctx,data,size); +} +void tcc_sha1_final(u_char result[20], tcc_sha1_t *ctx){ + njt_sha1_final(result,(njt_sha1_t *)ctx); +} + + +static char * +njt_stream_proto_pass(njt_conf_t *cf, njt_command_t *cmd, void *conf) +{ + njt_stream_proxy_srv_conf_t *pscf; + + njt_url_t u; + njt_str_t *value, *url; + njt_stream_complex_value_t cv; + njt_stream_core_srv_conf_t *cscf; + njt_stream_compile_complex_value_t ccv; + njt_stream_proto_server_srv_conf_t *sscf = conf; + + pscf = njt_stream_conf_get_module_srv_conf(cf, njt_stream_proxy_module); + if (pscf->upstream || pscf->upstream_value) { + return "is duplicate"; + } + + cscf = njt_stream_conf_get_module_srv_conf(cf, njt_stream_core_module); + + cscf->handler = njt_stream_proto_handler; + sscf->proto_pass_enabled = 1; + value = cf->args->elts; + + url = &value[1]; + + njt_memzero(&ccv, sizeof(njt_stream_compile_complex_value_t)); + + ccv.cf = cf; + ccv.value = url; + ccv.complex_value = &cv; + + if (njt_stream_compile_complex_value(&ccv) != NJT_OK) { + return NJT_CONF_ERROR; + } + + if (cv.lengths) { + pscf->upstream_value = njt_palloc(cf->pool, + sizeof(njt_stream_complex_value_t)); + if (pscf->upstream_value == NULL) { + return NJT_CONF_ERROR; + } + + *pscf->upstream_value = cv; + + return NJT_CONF_OK; + } + + njt_memzero(&u, sizeof(njt_url_t)); + + u.url = *url; + u.no_resolve = 1; + + pscf->upstream = njt_stream_upstream_add(cf, &u, 0); + if (pscf->upstream == NULL) { + return NJT_CONF_ERROR; + } + + return NJT_CONF_OK; +} + +static void njt_stream_proto_handler(njt_stream_session_t *s) +{ + u_char *p; + njt_str_t *host; + njt_uint_t i; + njt_connection_t *c; + njt_resolver_ctx_t *ctx, temp; + njt_stream_upstream_t *u; + njt_stream_core_srv_conf_t *cscf; + njt_stream_proxy_srv_conf_t *pscf; + njt_stream_upstream_srv_conf_t *uscf, **uscfp; + njt_stream_upstream_main_conf_t *umcf; + njt_stream_proxy_ctx_t *pctx; // openresty patch + + c = s->connection; + + pscf = njt_stream_get_module_srv_conf(s, njt_stream_proxy_module); + + // openresty patch + pctx = njt_palloc(c->pool, sizeof(njt_stream_proxy_ctx_t)); + if (pctx == NULL) { + njt_stream_proto_finalize(s, NJT_STREAM_INTERNAL_SERVER_ERROR); + return; + } + + pctx->connect_timeout = pscf->connect_timeout; + pctx->timeout = pscf->timeout; + + njt_stream_set_ctx(s, pctx, njt_stream_proxy_module); + // openresty patch end + + njt_log_debug0(NJT_LOG_DEBUG_STREAM, c->log, 0, + "proxy connection handler"); + + u = njt_pcalloc(c->pool, sizeof(njt_stream_upstream_t)); + if (u == NULL) { + njt_stream_proto_finalize(s, NJT_STREAM_INTERNAL_SERVER_ERROR); + return; + } + + s->upstream = u; + + s->log_handler = njt_stream_proto_log_error; + + u->requests = 1; + + u->peer.log = c->log; + u->peer.log_error = NJT_ERROR_ERR; + + if (njt_stream_proto_set_local(s, u, pscf->local) != NJT_OK) { + njt_stream_proto_finalize(s, NJT_STREAM_INTERNAL_SERVER_ERROR); + return; + } + + if (pscf->socket_keepalive) { + u->peer.so_keepalive = 1; + } + + u->peer.type = c->type; + u->start_sec = njt_time(); + + c->write->handler = njt_stream_proto_downstream_handler; + c->read->handler = njt_stream_proto_downstream_handler; + + s->upstream_states = njt_array_create(c->pool, 1, + sizeof(njt_stream_upstream_state_t)); + if (s->upstream_states == NULL) { + njt_stream_proto_finalize(s, NJT_STREAM_INTERNAL_SERVER_ERROR); + return; + } + + p = njt_pnalloc(c->pool, pscf->buffer_size); + if (p == NULL) { + njt_stream_proto_finalize(s, NJT_STREAM_INTERNAL_SERVER_ERROR); + return; + } + + u->downstream_buf.start = p; + u->downstream_buf.end = p + pscf->buffer_size; + u->downstream_buf.pos = p; + u->downstream_buf.last = p; + + if (c->read->ready) { + njt_post_event(c->read, &njt_posted_events); + } + + if (pscf->upstream_value) { + if (njt_stream_proto_eval(s, pscf) != NJT_OK) { + njt_stream_proto_finalize(s, NJT_STREAM_INTERNAL_SERVER_ERROR); + return; + } + } + + if (u->resolved == NULL) { +#if (NJT_STREAM_FTP_PROXY) + if(NJT_OK != njt_stream_ftp_proxy_replace_upstream(s, &uscf)){ + uscf = pscf->upstream; + } +#else + uscf = pscf->upstream; +#endif + } else { + +#if (NJT_STREAM_SSL) + u->ssl_name = u->resolved->host; +#endif + + host = &u->resolved->host; + + umcf = njt_stream_get_module_main_conf(s, njt_stream_upstream_module); + + uscfp = umcf->upstreams.elts; + + for (i = 0; i < umcf->upstreams.nelts; i++) { + + uscf = uscfp[i]; + + if (uscf->host.len == host->len + && ((uscf->port == 0 && u->resolved->no_port) + || uscf->port == u->resolved->port) + && njt_strncasecmp(uscf->host.data, host->data, host->len) == 0) + { + goto found; + } + } + + if (u->resolved->sockaddr) { + + if (u->resolved->port == 0 + && u->resolved->sockaddr->sa_family != AF_UNIX) + { + njt_log_error(NJT_LOG_ERR, c->log, 0, + "no port in upstream \"%V\"", host); + njt_stream_proto_finalize(s, NJT_STREAM_INTERNAL_SERVER_ERROR); + return; + } + + if (njt_stream_upstream_create_round_robin_peer(s, u->resolved) + != NJT_OK) + { + njt_stream_proto_finalize(s, NJT_STREAM_INTERNAL_SERVER_ERROR); + return; + } + + njt_stream_proto_connect(s); + + return; + } + + if (u->resolved->port == 0) { + njt_log_error(NJT_LOG_ERR, c->log, 0, + "no port in upstream \"%V\"", host); + njt_stream_proto_finalize(s, NJT_STREAM_INTERNAL_SERVER_ERROR); + return; + } + + temp.name = *host; + + cscf = njt_stream_get_module_srv_conf(s, njt_stream_core_module); + + ctx = njt_resolve_start(cscf->resolver, &temp); + if (ctx == NULL) { + njt_stream_proto_finalize(s, NJT_STREAM_INTERNAL_SERVER_ERROR); + return; + } + + if (ctx == NJT_NO_RESOLVER) { + njt_log_error(NJT_LOG_ERR, c->log, 0, + "no resolver defined to resolve %V", host); + njt_stream_proto_finalize(s, NJT_STREAM_INTERNAL_SERVER_ERROR); + return; + } + + ctx->name = *host; + ctx->handler = njt_stream_proto_resolve_handler; + ctx->data = s; + ctx->timeout = cscf->resolver_timeout; + + u->resolved->ctx = ctx; + + if (njt_resolve_name(ctx) != NJT_OK) { + u->resolved->ctx = NULL; + njt_stream_proto_finalize(s, NJT_STREAM_INTERNAL_SERVER_ERROR); + return; + } + + return; + } + +found: + + if (uscf == NULL) { + njt_log_error(NJT_LOG_ALERT, c->log, 0, "no upstream configuration"); + njt_stream_proto_finalize(s, NJT_STREAM_INTERNAL_SERVER_ERROR); + return; + } + + u->upstream = uscf; + +#if (NJT_STREAM_SSL) + u->ssl_name = uscf->host; +#endif + + if (uscf->peer.init(s, uscf) != NJT_OK) { + njt_stream_proto_finalize(s, NJT_STREAM_INTERNAL_SERVER_ERROR); + return; + } + + u->peer.start_time = njt_current_msec; + + if (pscf->next_upstream_tries + && u->peer.tries > pscf->next_upstream_tries) + { + u->peer.tries = pscf->next_upstream_tries; + } + + njt_stream_proto_connect(s); +} + +static void +njt_stream_proto_resolve_handler(njt_resolver_ctx_t *ctx) +{ + njt_stream_session_t *s; + njt_stream_upstream_t *u; + njt_stream_proxy_srv_conf_t *pscf; + njt_stream_upstream_resolved_t *ur; + + s = ctx->data; + + u = s->upstream; + ur = u->resolved; + + njt_log_debug0(NJT_LOG_DEBUG_STREAM, s->connection->log, 0, + "stream upstream resolve"); + + if (ctx->state) { + njt_log_error(NJT_LOG_ERR, s->connection->log, 0, + "%V could not be resolved (%i: %s)", + &ctx->name, ctx->state, + njt_resolver_strerror(ctx->state)); + + njt_stream_proto_finalize(s, NJT_STREAM_INTERNAL_SERVER_ERROR); + return; + } + + ur->naddrs = ctx->naddrs; + ur->addrs = ctx->addrs; + +#if (NJT_DEBUG) + { + u_char text[NJT_SOCKADDR_STRLEN]; + njt_str_t addr; + njt_uint_t i; + + addr.data = text; + + for (i = 0; i < ctx->naddrs; i++) { + addr.len = njt_sock_ntop(ur->addrs[i].sockaddr, ur->addrs[i].socklen, + text, NJT_SOCKADDR_STRLEN, 0); + + njt_log_debug1(NJT_LOG_DEBUG_STREAM, s->connection->log, 0, + "name was resolved to %V", &addr); + } + } +#endif + + if (njt_stream_upstream_create_round_robin_peer(s, ur) != NJT_OK) { + njt_stream_proto_finalize(s, NJT_STREAM_INTERNAL_SERVER_ERROR); + return; + } + + njt_resolve_name_done(ctx); + ur->ctx = NULL; + + u->peer.start_time = njt_current_msec; + + pscf = njt_stream_get_module_srv_conf(s, njt_stream_proxy_module); + + if (pscf->next_upstream_tries + && u->peer.tries > pscf->next_upstream_tries) + { + u->peer.tries = pscf->next_upstream_tries; + } + + njt_stream_proto_connect(s); +} + + +static void +njt_stream_proto_downstream_handler(njt_event_t *ev) +{ + njt_stream_proto_process_connection(ev, ev->write); +} + +static void +njt_stream_proto_process_connection(njt_event_t *ev, njt_uint_t from_upstream) +{ + njt_connection_t *c, *pc; + njt_log_handler_pt handler; + njt_stream_session_t *s; + njt_stream_upstream_t *u; + njt_stream_proxy_srv_conf_t *pscf; + njt_stream_proxy_ctx_t *ctx; // openresty patch + + c = ev->data; + s = c->data; + u = s->upstream; + + if (c->close) { + njt_log_error(NJT_LOG_INFO, c->log, 0, "shutdown timeout"); + njt_stream_proto_finalize(s, NJT_STREAM_OK); + return; + } + + ctx = njt_stream_get_module_ctx(s, njt_stream_proxy_module); // openresty patch + + c = s->connection; + pc = u->peer.connection; + + pscf = njt_stream_get_module_srv_conf(s, njt_stream_proxy_module); + + if (ev->timedout) { + ev->timedout = 0; + + if (ev->delayed) { + ev->delayed = 0; + + if (!ev->ready) { + if (njt_handle_read_event(ev, 0) != NJT_OK) { + njt_stream_proto_finalize(s, + NJT_STREAM_INTERNAL_SERVER_ERROR); + return; + } + + if (u->connected && !c->read->delayed && !pc->read->delayed) { + // njt_add_timer(c->write, pscf->timeout); openresty patch + njt_add_timer(c->write, ctx->timeout); // openresty patch + } + + return; + } + + } else { + if (s->connection->type == SOCK_DGRAM) { + + if (pscf->responses == NJT_MAX_INT32_VALUE + || (u->responses >= pscf->responses * u->requests)) + { + + /* + * successfully terminate timed out UDP session + * if expected number of responses was received + */ + + handler = c->log->handler; + c->log->handler = NULL; + + njt_log_error(NJT_LOG_INFO, c->log, 0, + "udp timed out" + ", packets from/to client:%ui/%ui" + ", bytes from/to client:%O/%O" + ", bytes from/to upstream:%O/%O", + u->requests, u->responses, + s->received, c->sent, u->received, + pc ? pc->sent : 0); + + c->log->handler = handler; + + njt_stream_proto_finalize(s, NJT_STREAM_OK); + return; + } + + njt_connection_error(pc, NJT_ETIMEDOUT, "upstream timed out"); + + pc->read->error = 1; + + njt_stream_proto_finalize(s, NJT_STREAM_BAD_GATEWAY); + + return; + } + + njt_connection_error(c, NJT_ETIMEDOUT, "connection timed out"); + + njt_stream_proto_finalize(s, NJT_STREAM_OK); + + return; + } + + } else if (ev->delayed) { + + njt_log_debug0(NJT_LOG_DEBUG_STREAM, c->log, 0, + "stream connection delayed"); + + if (njt_handle_read_event(ev, 0) != NJT_OK) { + njt_stream_proto_finalize(s, NJT_STREAM_INTERNAL_SERVER_ERROR); + } + + return; + } + + if (from_upstream && !u->connected) { + return; + } + + njt_stream_proto_process(s, from_upstream, ev->write,1); +} + + + +static njt_int_t njt_stream_proto_process(njt_stream_session_t *s, njt_uint_t from_upstream, + njt_uint_t do_write,njt_uint_t internal) +{ + char *recv_action, *send_action; + off_t *received, limit; + size_t size, limit_rate; + ssize_t n; + njt_buf_t *b; + njt_int_t rc; + njt_uint_t flags, *packets; + njt_msec_t delay; + njt_chain_t *cl, **ll, **out, **busy; + njt_connection_t *c, *pc, *src, *dst; + njt_log_handler_pt handler; + njt_stream_upstream_t *u; + njt_stream_proxy_srv_conf_t *pscf; + njt_stream_proxy_ctx_t *ctx; // openresty patch + #if (NJT_STREAM_PROTOCOL_SERVER_MODULE) + njt_stream_proto_server_srv_conf_t *sscf_proto; + njt_stream_proto_server_client_ctx_t *proto_ctx; + size_t avail_size; + njt_buf_t *senb; + sscf_proto = njt_stream_get_module_srv_conf(s, njt_stream_proto_server_module); + proto_ctx = njt_stream_get_module_ctx(s, njt_stream_proto_server_module); + #endif + + ctx = njt_stream_get_module_ctx(s, njt_stream_proxy_module); // openresty patch + + u = s->upstream; + + c = s->connection; + pc = u->connected ? u->peer.connection : NULL; + + if (c->type == SOCK_DGRAM && (njt_terminate || njt_exiting)) { + + /* socket is already closed on worker shutdown */ + + handler = c->log->handler; + c->log->handler = NULL; + + njt_log_error(NJT_LOG_INFO, c->log, 0, "disconnected on shutdown"); + + c->log->handler = handler; + if(internal == 1) + njt_stream_proto_finalize(s, NJT_STREAM_OK); + return NJT_STREAM_OK; + } + + pscf = njt_stream_get_module_srv_conf(s, njt_stream_proxy_module); + + if (from_upstream) { + src = pc; + dst = c; + b = &u->upstream_buf; +#if (NJT_STREAM_PROTOCOL_SERVER_MODULE) + senb = b; + if(sscf_proto && proto_ctx && sscf_proto->upstream_message_handler) { + b = &proto_ctx->out_buf; + } + + +#endif + limit_rate = u->download_rate; + received = &u->received; + packets = &u->responses; + out = &u->downstream_out; + busy = &u->downstream_busy; + recv_action = "proto_proxying and reading from upstream"; + send_action = "proto_proxying and sending to client"; + + } else { + src = c; + dst = pc; + b = &u->downstream_buf; +#if (NJT_STREAM_PROTOCOL_SERVER_MODULE) + senb = b; + if(sscf_proto && proto_ctx && sscf_proto->message_handler) { + b = (njt_buf_t *)&proto_ctx->r.in_buf; // + + } + +#endif + limit_rate = u->upload_rate; + received = &s->received; + packets = &u->requests; + out = &u->upstream_out; + busy = &u->upstream_busy; + recv_action = "proto_proxying and reading from client"; + send_action = "proto_proxying and sending to upstream"; + } + + for ( ;; ) { + + if (do_write && dst) { + + if (*out || *busy || dst->buffered) { + c->log->action = send_action; + + rc = njt_proto_write_filter(s, *out, from_upstream); + + if (rc == NJT_ERROR) { + if(internal == 1) + njt_stream_proto_finalize(s, NJT_STREAM_OK); + return NJT_STREAM_OK; + } + + njt_chain_update_chains(c->pool, &u->free, busy, out, + (njt_buf_tag_t) &njt_stream_proto_server_module); + + if (*busy == NULL) { +#if (NJT_STREAM_PROTOCOL_SERVER_MODULE) + if (b == senb) + { + senb->pos = senb->start; + senb->last = senb->start; + } +#else + b->pos = b->start; + b->last = b->start; +#endif + } + } + } + + size = b->end - b->last; +#if (NJT_STREAM_PROTOCOL_SERVER_MODULE) + if (internal == 0) { + return NJT_OK; + } + if(b == senb) { + avail_size = senb->end - senb->last; + } else { + if (from_upstream){ + avail_size = sscf_proto->session_max_mem_size - proto_ctx->r.session_max_mem_size; + //njt_log_debug2(NJT_LOG_DEBUG, c->log, 0,"proto_ctx->r.session_max_mem_size=%d,tcc have client=%d!",proto_ctx->r.session_max_mem_size,avail_size); + } else { + avail_size = sscf_proto->session_max_mem_size - proto_ctx->r.session_up_max_mem_size; + } + } + + size = (size > avail_size)? avail_size:size; +#endif + if (size && src != NULL && src->read->ready && !src->read->delayed ) { + + if (limit_rate) { + limit = (off_t) limit_rate * (njt_time() - u->start_sec + 1) + - *received; + + if (limit <= 0) { + src->read->delayed = 1; + delay = (njt_msec_t) (- limit * 1000 / limit_rate + 1); + njt_add_timer(src->read, delay); + break; + } + + if (c->type == SOCK_STREAM && (off_t) size > limit) { + size = (size_t) limit; + } + } + + c->log->action = recv_action; + + n = src->recv(src, b->last, size); + + if (n == NJT_AGAIN) { + break; + } + + if (n == NJT_ERROR) { + src->read->eof = 1; + n = 0; + } + + if (n >= 0) { +#ifdef NJT_STREAM_FTP_PROXY + //if ftp_proxy, need replace data port + if(from_upstream){ + njt_stream_ftp_proxy_filter_pasv(s, b->last, &n); + } +#endif + + if (limit_rate) { + delay = (njt_msec_t) (n * 1000 / limit_rate); + + if (delay > 0) { + src->read->delayed = 1; + njt_add_timer(src->read, delay); + } + } + + if (from_upstream) { + if (u->state->first_byte_time == (njt_msec_t) -1) { + u->state->first_byte_time = njt_current_msec + - u->start_time; + } + } +#if (NJT_STREAM_PROTOCOL_SERVER_MODULE) + if(proto_ctx == NULL || (sscf_proto->upstream_message_handler == NULL && from_upstream == 1 ) || (sscf_proto->message_handler == NULL && from_upstream == 0)) { +#endif + for (ll = out; *ll; ll = &(*ll)->next) + { /* void */ + } + + cl = njt_chain_get_free_buf(c->pool, &u->free); + if (cl == NULL) + { + if(internal == 1) + njt_stream_proto_finalize(s, + NJT_STREAM_INTERNAL_SERVER_ERROR); + return NJT_STREAM_INTERNAL_SERVER_ERROR; + } + + *ll = cl; + + cl->buf->pos = b->last; + cl->buf->last = b->last + n; + cl->buf->tag = (njt_buf_tag_t)&njt_stream_proto_server_module; + + cl->buf->temporary = (n ? 1 : 0); + cl->buf->last_buf = src->read->eof; + cl->buf->flush = !src->read->eof; + do_write = 1; + (*packets)++; + *received += n; + b->last += n; +#if (NJT_STREAM_PROTOCOL_SERVER_MODULE) + } else { + (*packets)++; + *received += n; + b->last += n; + do_write = 1; + rc = njt_stream_proto_server_process_proxy_message(s,b,from_upstream); + if(rc == NJT_ERROR){ + break; + } + + } +#endif + continue; + } + } + + break; + } + + c->log->action = "proto_proxying connection"; + + if (njt_stream_proto_test_finalize(s, from_upstream) == NJT_OK) { + return NJT_OK; + } + if(src == NULL || src->read == NULL) { + njt_log_error(NJT_LOG_ERR, s->connection->log, 0, + "src or src->read is null"); + return NJT_OK; + } + flags = src->read->eof ? NJT_CLOSE_EVENT : 0; + + if (njt_handle_read_event(src->read, flags) != NJT_OK) { + if(internal == 1) + njt_stream_proto_finalize(s, NJT_STREAM_INTERNAL_SERVER_ERROR); + return NJT_STREAM_INTERNAL_SERVER_ERROR; + } + + if (dst) { + + if (dst->type == SOCK_STREAM && pscf->half_close + && src != NULL && src->read->eof && !u->half_closed && !dst->buffered) + { + + if (njt_shutdown_socket(dst->fd, NJT_WRITE_SHUTDOWN) == -1) { + njt_connection_error(c, njt_socket_errno, + njt_shutdown_socket_n " failed"); + if(internal == 1) + njt_stream_proto_finalize(s, NJT_STREAM_INTERNAL_SERVER_ERROR); + return NJT_STREAM_INTERNAL_SERVER_ERROR; + } + + u->half_closed = 1; + njt_log_debug1(NJT_LOG_DEBUG_STREAM, s->connection->log, 0, + "stream proxy %s socket shutdown", + from_upstream ? "client" : "upstream"); + } + + if (njt_handle_write_event(dst->write, 0) != NJT_OK) { + if(internal == 1) + njt_stream_proto_finalize(s, NJT_STREAM_INTERNAL_SERVER_ERROR); + return NJT_STREAM_INTERNAL_SERVER_ERROR; + } + + if (!c->read->delayed && !pc->read->delayed) { + // njt_add_timer(c->write, pscf->timeout); openresty patch + njt_add_timer(c->write, ctx->timeout); // openresty patch + + } else if (c->write->timer_set) { + njt_del_timer(c->write); + } + } + return NJT_OK; +} + +static void +njt_stream_proto_connect(njt_stream_session_t *s) +{ + njt_int_t rc; + njt_connection_t *c, *pc; + njt_stream_upstream_t *u; + njt_stream_proxy_srv_conf_t *pscf; + njt_stream_proxy_ctx_t *ctx; // openresty patch +#if (NJT_STREAM_PROTOCOL_V2) + njt_flag_t flag; + njt_stream_variable_value_t *value; + njt_stream_proxy_protocol_tlv_srv_conf_t *scf = njt_stream_get_module_srv_conf(s,njt_stream_proxy_protocol_tlv_module); + flag = NJT_CONF_UNSET; + if(scf != NULL && scf->var_index != NJT_CONF_UNSET_UINT) { + value = njt_stream_get_indexed_variable(s, scf->var_index); + if (value != NULL && value->not_found == 0 && value->len == 1 && value->data[0] == '1') { + flag = 1; + } else { + flag = 0; + } + } +#endif + + c = s->connection; + + c->log->action = "connecting to upstream"; + + pscf = njt_stream_get_module_srv_conf(s, njt_stream_proxy_module); + + ctx = njt_stream_get_module_ctx(s, njt_stream_proxy_module); // openresty patch + + u = s->upstream; + + u->connected = 0; + u->proxy_protocol = pscf->proxy_protocol; +#if (NJT_STREAM_PROTOCOL_V2) + u->proxy_protocol = (flag != NJT_CONF_UNSET ? flag:pscf->proxy_protocol); +#endif + + if (u->state) { + u->state->response_time = njt_current_msec - u->start_time; + } + + u->state = njt_array_push(s->upstream_states); + if (u->state == NULL) { + njt_stream_proto_finalize(s, NJT_STREAM_INTERNAL_SERVER_ERROR); + return; + } + + njt_memzero(u->state, sizeof(njt_stream_upstream_state_t)); + + u->start_time = njt_current_msec; + + u->state->connect_time = (njt_msec_t) -1; + u->state->first_byte_time = (njt_msec_t) -1; + u->state->response_time = (njt_msec_t) -1; + + rc = njt_event_connect_peer(&u->peer); + + njt_log_debug1(NJT_LOG_DEBUG_STREAM, c->log, 0, "proxy connect: %i", rc); + + if (rc == NJT_ERROR) { + njt_stream_proto_finalize(s, NJT_STREAM_INTERNAL_SERVER_ERROR); + return; + } + + // openresy patch + if (rc >= NJT_STREAM_SPECIAL_RESPONSE) { + njt_stream_proto_finalize(s, rc); + return; + } + // openresy patch end + + + + u->state->peer = u->peer.name; + + if (rc == NJT_BUSY) { + njt_log_error(NJT_LOG_ERR, c->log, 0, "no live upstreams"); + njt_stream_proto_finalize(s, NJT_STREAM_BAD_GATEWAY); + return; + } + + if (rc == NJT_DECLINED) { + njt_stream_proto_next_upstream(s); + return; + } + + /* rc == NJT_OK || rc == NJT_AGAIN || rc == NJT_DONE */ + + pc = u->peer.connection; + + pc->data = s; + pc->log = c->log; + pc->pool = c->pool; + pc->read->log = c->log; + pc->write->log = c->log; + + if (rc != NJT_AGAIN) { + njt_stream_proto_init_upstream(s); + return; + } + + pc->read->handler = njt_stream_proto_connect_handler; + pc->write->handler = njt_stream_proto_connect_handler; + + // njt_add_timer(pc->write, pscf->connect_timeout); openresty patch + njt_add_timer(pc->write, ctx->connect_timeout); // openresty patch +} + +static void +njt_stream_proto_connect_handler(njt_event_t *ev) +{ + njt_connection_t *c; + njt_stream_session_t *s; + + c = ev->data; + s = c->data; + + if (ev->timedout) { + njt_log_error(NJT_LOG_ERR, c->log, NJT_ETIMEDOUT, "upstream timed out"); + njt_stream_proto_next_upstream(s); + return; + } + + njt_del_timer(c->write); + + njt_log_debug0(NJT_LOG_DEBUG_STREAM, c->log, 0, + "stream proxy connect upstream"); + + if (njt_stream_proto_test_connect(c) != NJT_OK) { + njt_stream_proto_next_upstream(s); + return; + } + + njt_stream_proto_init_upstream(s); +} + +static void +njt_stream_proto_init_upstream(njt_stream_session_t *s) +{ + u_char *p; + njt_chain_t *cl; + njt_connection_t *c, *pc; + njt_log_handler_pt handler; + njt_stream_upstream_t *u; + njt_stream_core_srv_conf_t *cscf; + njt_stream_proxy_srv_conf_t *pscf; + #if (NJT_STREAM_PROTOCOL_SERVER_MODULE) + njt_int_t rc; + + #endif + + u = s->upstream; + pc = u->peer.connection; + + cscf = njt_stream_get_module_srv_conf(s, njt_stream_core_module); + + if (pc->type == SOCK_STREAM + && cscf->tcp_nodelay + && njt_tcp_nodelay(pc) != NJT_OK) + { + njt_stream_proto_next_upstream(s); + return; + } + + pscf = njt_stream_get_module_srv_conf(s, njt_stream_proxy_module); + +#if (NJT_STREAM_SSL) + + if (pc->type == SOCK_STREAM && pscf->ssl_enable) { + + if (u->proxy_protocol) { + if (njt_stream_proto_send_proxy_protocol(s) != NJT_OK) { + return; + } + + u->proxy_protocol = 0; + } + + if (pc->ssl == NULL) { + njt_stream_proto_ssl_init_connection(s); + return; + } + } + +#endif + + c = s->connection; + + if (c->log->log_level >= NJT_LOG_INFO) { + njt_str_t str; + u_char addr[NJT_SOCKADDR_STRLEN]; + + str.len = NJT_SOCKADDR_STRLEN; + str.data = addr; + + if (njt_connection_local_sockaddr(pc, &str, 1) == NJT_OK) { + handler = c->log->handler; + c->log->handler = NULL; + + njt_log_error(NJT_LOG_INFO, c->log, 0, + "%sproxy %V connected to %V", + pc->type == SOCK_DGRAM ? "udp " : "", + &str, u->peer.name); + + c->log->handler = handler; + } + } + + u->state->connect_time = njt_current_msec - u->start_time; + + if (u->peer.notify) { + u->peer.notify(&u->peer, u->peer.data, + NJT_STREAM_UPSTREAM_NOTIFY_CONNECT); + } + + if (u->upstream_buf.start == NULL) { + p = njt_pnalloc(c->pool, pscf->buffer_size); + if (p == NULL) { + njt_stream_proto_finalize(s, NJT_STREAM_INTERNAL_SERVER_ERROR); + return; + } + + u->upstream_buf.start = p; + u->upstream_buf.end = p + pscf->buffer_size; + u->upstream_buf.pos = p; + u->upstream_buf.last = p; + } +#if (NJT_STREAM_PROTOCOL_SERVER_MODULE) + rc = njt_stream_proto_server_init_upstream(s); + if(rc == NJT_OK) { + goto next; + } else if(rc == NJT_ERROR){ + njt_stream_proto_finalize(s, NJT_STREAM_INTERNAL_SERVER_ERROR); + return; + } +#endif + if (c->buffer && c->buffer->pos <= c->buffer->last) { + njt_log_debug1(NJT_LOG_DEBUG_STREAM, c->log, 0, + "stream proxy add preread buffer: %uz", + c->buffer->last - c->buffer->pos); + cl = njt_chain_get_free_buf(c->pool, &u->free); + if (cl == NULL) { + njt_stream_proto_finalize(s, NJT_STREAM_INTERNAL_SERVER_ERROR); + return; + } + + *cl->buf = *c->buffer; + + cl->buf->tag = (njt_buf_tag_t) &njt_stream_proto_server_module; + cl->buf->temporary = (cl->buf->pos == cl->buf->last) ? 0 : 1; + cl->buf->flush = 1; + + cl->next = u->upstream_out; + u->upstream_out = cl; + + } +next: + if (u->proxy_protocol) { + njt_log_debug0(NJT_LOG_DEBUG_STREAM, c->log, 0, + "stream proxy add PROXY protocol header"); + + cl = njt_chain_get_free_buf(c->pool, &u->free); + if (cl == NULL) { + njt_stream_proto_finalize(s, NJT_STREAM_INTERNAL_SERVER_ERROR); + return; + } + + p = njt_pnalloc(c->pool, NJT_PROXY_PROTOCOL_MAX_HEADER); + if (p == NULL) { + njt_stream_proto_finalize(s, NJT_STREAM_INTERNAL_SERVER_ERROR); + return; + } + + cl->buf->pos = p; + + p = njt_proxy_protocol_v2_write(s, p, p + NJT_PROXY_PROTOCOL_MAX_HEADER); + if (p == NULL) { + njt_stream_proto_finalize(s, NJT_STREAM_INTERNAL_SERVER_ERROR); + return; + } + + cl->buf->last = p; + cl->buf->temporary = 1; + cl->buf->flush = 0; + cl->buf->last_buf = 0; + cl->buf->tag = (njt_buf_tag_t) &njt_stream_proto_server_module; + + cl->next = u->upstream_out; + u->upstream_out = cl; + + u->proxy_protocol = 0; + } + + u->upload_rate = njt_stream_complex_value_size(s, pscf->upload_rate, 0); + u->download_rate = njt_stream_complex_value_size(s, pscf->download_rate, 0); + + u->connected = 1; + + pc->read->handler = njt_stream_proto_upstream_handler; + pc->write->handler = njt_stream_proto_upstream_handler; + + if (pc->read->ready) { + njt_post_event(pc->read, &njt_posted_events); + } + + njt_stream_proto_process(s, 0, 1,1); +} + +static void +njt_stream_proto_upstream_handler(njt_event_t *ev) +{ + njt_stream_proto_process_connection(ev, !ev->write); +} + +static void +njt_stream_proto_ssl_init_connection(njt_stream_session_t *s) +{ + njt_int_t rc; + njt_connection_t *pc; + njt_stream_upstream_t *u; + njt_stream_proxy_srv_conf_t *pscf; + njt_stream_proxy_ctx_t *ctx; // openresy patch + + ctx = njt_stream_get_module_ctx(s, njt_stream_proxy_module); // openresty patch + + + u = s->upstream; + + pc = u->peer.connection; + + pscf = njt_stream_get_module_srv_conf(s, njt_stream_proxy_module); + +#if (NJT_HAVE_NTLS) + if (pscf->ssl_ntls) { + + SSL_CTX_set_ssl_version(pscf->ssl->ctx, NTLS_method()); + SSL_CTX_set_cipher_list(pscf->ssl->ctx, + (char *) pscf->ssl_ciphers.data); + SSL_CTX_enable_ntls(pscf->ssl->ctx); + } +#endif + + if (njt_ssl_create_connection(pscf->ssl, pc, NJT_SSL_BUFFER|NJT_SSL_CLIENT) + != NJT_OK) + { + njt_stream_proto_finalize(s, NJT_STREAM_INTERNAL_SERVER_ERROR); + return; + } + + if (pscf->ssl_server_name || pscf->ssl_verify) { + if (njt_stream_proto_ssl_name(s) != NJT_OK) { + njt_stream_proto_finalize(s, NJT_STREAM_INTERNAL_SERVER_ERROR); + return; + } + } + +#if (NJT_STREAM_MULTICERT) + + if (pscf->ssl_certificate_values) { + if (njt_stream_proto_ssl_certificates(s) != NJT_OK) { + njt_stream_proto_finalize(s, NJT_STREAM_INTERNAL_SERVER_ERROR); + return; + } + } + +#else + + if (pscf->ssl_certificate + && pscf->ssl_certificate->value.len + && (pscf->ssl_certificate->lengths + || pscf->ssl_certificate_key->lengths)) + { + if (njt_stream_proto_ssl_certificate(s) != NJT_OK) { + njt_stream_proto_finalize(s, NJT_STREAM_INTERNAL_SERVER_ERROR); + return; + } + } + +#endif + + if (pscf->ssl_session_reuse) { + pc->ssl->save_session = njt_stream_proto_ssl_save_session; + + if (u->peer.set_session(&u->peer, u->peer.data) != NJT_OK) { + njt_stream_proto_finalize(s, NJT_STREAM_INTERNAL_SERVER_ERROR); + return; + } + } + + s->connection->log->action = "SSL handshaking to upstream"; + + rc = njt_ssl_handshake(pc); + + if (rc == NJT_AGAIN) { + + if (!pc->write->timer_set) { + // njt_add_timer(pc->write, pscf->connect_timeout); openresty patch + njt_add_timer(pc->write, ctx->connect_timeout); // openresty patch + } + + pc->ssl->handler = njt_stream_proto_ssl_handshake; + return; + } + + njt_stream_proto_ssl_handshake(pc); +} + +static u_char * +njt_stream_proto_log_error(njt_log_t *log, u_char *buf, size_t len) +{ + u_char *p; + njt_connection_t *pc; + njt_stream_session_t *s; + njt_stream_upstream_t *u; + + s = log->data; + + u = s->upstream; + + p = buf; + + if (u->peer.name) { + p = njt_snprintf(p, len, ", upstream: \"%V\"", u->peer.name); + len -= p - buf; + } + + pc = u->peer.connection; + + p = njt_snprintf(p, len, + ", bytes from/to client:%O/%O" + ", bytes from/to upstream:%O/%O", + s->received, s->connection->sent, + u->received, pc ? pc->sent : 0); + + return p; +} + +static njt_int_t +njt_stream_proto_set_local(njt_stream_session_t *s, njt_stream_upstream_t *u, + njt_stream_upstream_local_t *local) +{ + njt_int_t rc; + njt_str_t val; + njt_addr_t *addr; + + if (local == NULL) { + u->peer.local = NULL; + return NJT_OK; + } + +#if (NJT_HAVE_TRANSPARENT_PROXY) + u->peer.transparent = local->transparent; +#endif + + if (local->value == NULL) { + u->peer.local = local->addr; + return NJT_OK; + } + + if (njt_stream_complex_value(s, local->value, &val) != NJT_OK) { + return NJT_ERROR; + } + + if (val.len == 0) { + return NJT_OK; + } + + addr = njt_palloc(s->connection->pool, sizeof(njt_addr_t)); + if (addr == NULL) { + return NJT_ERROR; + } + + rc = njt_parse_addr_port(s->connection->pool, addr, val.data, val.len); + if (rc == NJT_ERROR) { + return NJT_ERROR; + } + + if (rc != NJT_OK) { + njt_log_error(NJT_LOG_ERR, s->connection->log, 0, + "invalid local address \"%V\"", &val); + return NJT_OK; + } + + addr->name = val; + u->peer.local = addr; + + return NJT_OK; +} +static njt_int_t +njt_stream_proto_eval(njt_stream_session_t *s, + njt_stream_proxy_srv_conf_t *pscf) +{ + njt_str_t host; + njt_url_t url; + njt_stream_upstream_t *u; + + if (njt_stream_complex_value(s, pscf->upstream_value, &host) != NJT_OK) { + return NJT_ERROR; + } + + njt_memzero(&url, sizeof(njt_url_t)); + + url.url = host; + url.no_resolve = 1; + + if (njt_parse_url(s->connection->pool, &url) != NJT_OK) { + if (url.err) { + njt_log_error(NJT_LOG_ERR, s->connection->log, 0, + "%s in upstream \"%V\"", url.err, &url.url); + } + + return NJT_ERROR; + } + + u = s->upstream; + + u->resolved = njt_pcalloc(s->connection->pool, + sizeof(njt_stream_upstream_resolved_t)); + if (u->resolved == NULL) { + return NJT_ERROR; + } + + if (url.addrs) { + u->resolved->sockaddr = url.addrs[0].sockaddr; + u->resolved->socklen = url.addrs[0].socklen; + u->resolved->name = url.addrs[0].name; + u->resolved->naddrs = 1; + } + + u->resolved->host = url.host; + u->resolved->port = url.port; + u->resolved->no_port = url.no_port; + + return NJT_OK; +} + +static njt_int_t +njt_stream_proto_test_finalize(njt_stream_session_t *s, + njt_uint_t from_upstream) +{ + njt_connection_t *c, *pc; + njt_log_handler_pt handler; + njt_stream_upstream_t *u; + njt_stream_proxy_srv_conf_t *pscf; + + pscf = njt_stream_get_module_srv_conf(s, njt_stream_proxy_module); + + c = s->connection; + u = s->upstream; + pc = u->connected ? u->peer.connection : NULL; + + if (c->type == SOCK_DGRAM) { + + if (pscf->requests && u->requests < pscf->requests) { + return NJT_DECLINED; + } + + if (pscf->requests) { + njt_delete_udp_connection(c); + } + + if (pscf->responses == NJT_MAX_INT32_VALUE + || u->responses < pscf->responses * u->requests) + { + return NJT_DECLINED; + } + + if (pc == NULL || c->buffered || pc->buffered) { + return NJT_DECLINED; + } + + handler = c->log->handler; + c->log->handler = NULL; + + njt_log_error(NJT_LOG_INFO, c->log, 0, + "udp done" + ", packets from/to client:%ui/%ui" + ", bytes from/to client:%O/%O" + ", bytes from/to upstream:%O/%O", + u->requests, u->responses, + s->received, c->sent, u->received, pc ? pc->sent : 0); + + c->log->handler = handler; + + njt_stream_proto_finalize(s, NJT_STREAM_OK); + + return NJT_OK; + } + + /* c->type == SOCK_STREAM */ + njt_log_error(NJT_LOG_INFO, c->log, 0,"c->read->eof=%d,pc->read->eof=%d,c->buffered=%d,pc->buffered=%d",c->read->eof,pc->read->eof,c->buffered,pc->buffered); + if (pc == NULL + || (!c->read->eof && !pc->read->eof) + || (!c->read->eof && c->buffered) + || (!pc->read->eof && pc->buffered)) + { + return NJT_DECLINED; + } + + if (pscf->half_close) { + /* avoid closing live connections until both read ends get EOF */ + if (!(c->read->eof && pc->read->eof && !c->buffered && !pc->buffered)) { + return NJT_DECLINED; + } + } + + handler = c->log->handler; + c->log->handler = NULL; + + njt_log_error(NJT_LOG_INFO, c->log, 0, + "%s disconnected" + ", bytes from/to client:%O/%O" + ", bytes from/to upstream:%O/%O", + from_upstream ? "upstream" : "client", + s->received, c->sent, u->received, pc ? pc->sent : 0); + + c->log->handler = handler; + + njt_stream_proto_finalize(s, NJT_STREAM_OK); + + return NJT_OK; +} +static void +njt_stream_proto_next_upstream(njt_stream_session_t *s) +{ + njt_msec_t timeout; + njt_connection_t *pc; + njt_stream_upstream_t *u; + njt_stream_proxy_srv_conf_t *pscf; + + njt_log_debug0(NJT_LOG_DEBUG_STREAM, s->connection->log, 0, + "stream proxy next upstream"); + + u = s->upstream; + pc = u->peer.connection; + + if (pc && pc->buffered) { + njt_log_error(NJT_LOG_ERR, s->connection->log, 0, + "buffered data on next upstream"); + njt_stream_proto_finalize(s, NJT_STREAM_INTERNAL_SERVER_ERROR); + return; + } + + if (s->connection->type == SOCK_DGRAM) { + u->upstream_out = NULL; + } + + if (u->peer.sockaddr) { + u->peer.free(&u->peer, u->peer.data, NJT_PEER_FAILED); + u->peer.sockaddr = NULL; + } + + pscf = njt_stream_get_module_srv_conf(s, njt_stream_proxy_module); + + timeout = pscf->next_upstream_timeout; + + if (u->peer.tries == 0 + || !pscf->next_upstream + || (timeout && njt_current_msec - u->peer.start_time >= timeout)) + { + njt_stream_proto_finalize(s, NJT_STREAM_BAD_GATEWAY); + return; + } + + if (pc) { + njt_log_debug1(NJT_LOG_DEBUG_STREAM, s->connection->log, 0, + "close proxy upstream connection: %d", pc->fd); + +#if (NJT_STREAM_SSL) + if (pc->ssl) { + pc->ssl->no_wait_shutdown = 1; + pc->ssl->no_send_shutdown = 1; + + (void) njt_ssl_shutdown(pc); + } +#endif + + u->state->bytes_received = u->received; + u->state->bytes_sent = pc->sent; + + njt_close_connection(pc); + u->peer.connection = NULL; + } + + njt_stream_proto_connect(s); +} + +static njt_int_t +njt_stream_proto_test_connect(njt_connection_t *c) +{ + int err; + socklen_t len; + +#if (NJT_HAVE_KQUEUE) + + if (njt_event_flags & NJT_USE_KQUEUE_EVENT) { + err = c->write->kq_errno ? c->write->kq_errno : c->read->kq_errno; + + if (err) { + (void) njt_connection_error(c, err, + "kevent() reported that connect() failed"); + return NJT_ERROR; + } + + } else +#endif + { + err = 0; + len = sizeof(int); + + /* + * BSDs and Linux return 0 and set a pending error in err + * Solaris returns -1 and sets errno + */ + + if (getsockopt(c->fd, SOL_SOCKET, SO_ERROR, (void *) &err, &len) + == -1) + { + err = njt_socket_errno; + } + + if (err) { + (void) njt_connection_error(c, err, "connect() failed"); + return NJT_ERROR; + } + } + + return NJT_OK; +} + +static njt_int_t +njt_stream_proto_send_proxy_protocol(njt_stream_session_t *s) +{ + // openresty patch + // u_char *p; + // ssize_t n, size; + // njt_connection_t *c, *pc; + // njt_stream_upstream_t *u; + // njt_stream_proxy_srv_conf_t *pscf; + // u_char buf[NJT_PROXY_PROTOCOL_MAX_HEADER]; + u_char *p; + u_char buf[NJT_PROXY_PROTOCOL_MAX_HEADER]; + ssize_t n, size; + njt_connection_t *c, *pc; + njt_stream_upstream_t *u; + njt_stream_proxy_ctx_t *ctx; + + ctx = njt_stream_get_module_ctx(s, njt_stream_proxy_module); + // openresty patch end + + + c = s->connection; + + njt_log_debug0(NJT_LOG_DEBUG_STREAM, c->log, 0, + "stream proxy send PROXY protocol header"); + + p = njt_proxy_protocol_v2_write(s, buf, buf + NJT_PROXY_PROTOCOL_MAX_HEADER); + if (p == NULL) { + njt_stream_proto_finalize(s, NJT_STREAM_INTERNAL_SERVER_ERROR); + return NJT_ERROR; + } + + u = s->upstream; + + pc = u->peer.connection; + + size = p - buf; + + n = pc->send(pc, buf, size); + + if (n == NJT_AGAIN) { + if (njt_handle_write_event(pc->write, 0) != NJT_OK) { + njt_stream_proto_finalize(s, NJT_STREAM_INTERNAL_SERVER_ERROR); + return NJT_ERROR; + } + + // openresty patch + // pscf = njt_stream_get_module_srv_conf(s, njt_stream_proxy_module); + + // njt_add_timer(pc->write, pscf->timeout); + njt_add_timer(pc->write, ctx->timeout); + // openresty patch end + + pc->write->handler = njt_stream_proto_connect_handler; + + return NJT_AGAIN; + } + + if (n == NJT_ERROR) { + njt_stream_proto_finalize(s, NJT_STREAM_OK); + return NJT_ERROR; + } + + if (n != size) { + + /* + * PROXY protocol specification: + * The sender must always ensure that the header + * is sent at once, so that the transport layer + * maintains atomicity along the path to the receiver. + */ + + njt_log_error(NJT_LOG_ERR, c->log, 0, + "could not send PROXY protocol header at once"); + + njt_stream_proto_finalize(s, NJT_STREAM_INTERNAL_SERVER_ERROR); + + return NJT_ERROR; + } + + return NJT_OK; +} + + +static njt_int_t +njt_stream_proto_ssl_name(njt_stream_session_t *s) +{ + u_char *p, *last; + njt_str_t name; + njt_stream_upstream_t *u; + njt_stream_proxy_srv_conf_t *pscf; + + pscf = njt_stream_get_module_srv_conf(s, njt_stream_proxy_module); + + u = s->upstream; + + if (pscf->ssl_name) { + if (njt_stream_complex_value(s, pscf->ssl_name, &name) != NJT_OK) { + return NJT_ERROR; + } + + } else { + name = u->ssl_name; + } + + if (name.len == 0) { + goto done; + } + + /* + * ssl name here may contain port, strip it for compatibility + * with the http module + */ + + p = name.data; + last = name.data + name.len; + + if (*p == '[') { + p = njt_strlchr(p, last, ']'); + + if (p == NULL) { + p = name.data; + } + } + + p = njt_strlchr(p, last, ':'); + + if (p != NULL) { + name.len = p - name.data; + } + + if (!pscf->ssl_server_name) { + goto done; + } + +#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME + + /* as per RFC 6066, literal IPv4 and IPv6 addresses are not permitted */ + + if (name.len == 0 || *name.data == '[') { + goto done; + } + + if (njt_inet_addr(name.data, name.len) != INADDR_NONE) { + goto done; + } + + /* + * SSL_set_tlsext_host_name() needs a null-terminated string, + * hence we explicitly null-terminate name here + */ + + p = njt_pnalloc(s->connection->pool, name.len + 1); + if (p == NULL) { + return NJT_ERROR; + } + + (void) njt_cpystrn(p, name.data, name.len + 1); + + name.data = p; + + njt_log_debug1(NJT_LOG_DEBUG_STREAM, s->connection->log, 0, + "upstream SSL server name: \"%s\"", name.data); + + if (SSL_set_tlsext_host_name(u->peer.connection->ssl->connection, + (char *) name.data) + == 0) + { + njt_ssl_error(NJT_LOG_ERR, s->connection->log, 0, + "SSL_set_tlsext_host_name(\"%s\") failed", name.data); + return NJT_ERROR; + } + +#endif + +done: + + u->ssl_name = name; + + return NJT_OK; +} + +#if (NJT_STREAM_MULTICERT) + +static njt_int_t +njt_stream_proto_ssl_certificates(njt_stream_session_t *s) +{ + njt_str_t *certp, *keyp, cert, key; + njt_uint_t i, nelts; +#if (NJT_HAVE_NTLS) + njt_str_t tcert, tkey; +#endif + njt_connection_t *c; + njt_stream_complex_value_t *certs, *keys; + njt_stream_proxy_srv_conf_t *pscf; + + c = s->upstream->peer.connection; + + pscf = njt_stream_get_module_srv_conf(s, njt_stream_proxy_module); + + + nelts = pscf->ssl_certificate_values->nelts; + certs = pscf->ssl_certificate_values->elts; + keys = pscf->ssl_certificate_key_values->elts; + + for (i = 0; i < nelts; i++) { + certp = &cert; + keyp = &key; + + if (njt_stream_complex_value(s, &certs[i], certp) != NJT_OK) { + return NJT_ERROR; + } + +#if (NJT_HAVE_NTLS) + tcert = *certp; + njt_ssl_ntls_prefix_strip(&tcert); + certp = &cert; +#endif + + if (*certp->data == 0) { + continue; + } + + njt_log_debug1(NJT_LOG_DEBUG_STREAM, c->log, 0, + "stream upstream ssl cert: \"%s\"", certp->data); + + if (njt_stream_complex_value(s, &keys[i], keyp) != NJT_OK) { + return NJT_ERROR; + } + +#if (NJT_HAVE_NTLS) + tkey = *keyp; + njt_ssl_ntls_prefix_strip(&tkey); + keyp = &key; +#endif + + njt_log_debug1(NJT_LOG_DEBUG_STREAM, c->log, 0, + "stream upstream ssl key: \"%s\"", keyp->data); + + if (njt_ssl_connection_certificate(c, s->connection->pool, certp, keyp, + pscf->ssl_passwords) + != NJT_OK) + { + return NJT_ERROR; + } + } + + return NJT_OK; +} + +#else + +static njt_int_t +njt_stream_proto_ssl_certificate(njt_stream_session_t *s) +{ + njt_str_t cert, key; + njt_connection_t *c; + njt_stream_proxy_srv_conf_t *pscf; + + c = s->upstream->peer.connection; + + pscf = njt_stream_get_module_srv_conf(s, njt_stream_proxy_module); + + if (njt_stream_complex_value(s, pscf->ssl_certificate, &cert) + != NJT_OK) + { + return NJT_ERROR; + } + + njt_log_debug1(NJT_LOG_DEBUG_STREAM, c->log, 0, + "stream upstream ssl cert: \"%s\"", cert.data); + + if (*cert.data == '\0') { + return NJT_OK; + } + + if (njt_stream_complex_value(s, pscf->ssl_certificate_key, &key) + != NJT_OK) + { + return NJT_ERROR; + } + + njt_log_debug1(NJT_LOG_DEBUG_STREAM, c->log, 0, + "stream upstream ssl key: \"%s\"", key.data); + + if (njt_ssl_connection_certificate(c, c->pool, &cert, &key, + pscf->ssl_passwords) + != NJT_OK) + { + return NJT_ERROR; + } + + return NJT_OK; +} + +#endif + + +static void +njt_stream_proto_ssl_save_session(njt_connection_t *c) +{ + njt_stream_session_t *s; + njt_stream_upstream_t *u; + + s = c->data; + u = s->upstream; + + u->peer.save_session(&u->peer, u->peer.data); +} + +static void +njt_stream_proto_ssl_handshake(njt_connection_t *pc) +{ + long rc; + njt_stream_session_t *s; + njt_stream_upstream_t *u; + njt_stream_proxy_srv_conf_t *pscf; + + s = pc->data; + + pscf = njt_stream_get_module_srv_conf(s, njt_stream_proxy_module); + + if (pc->ssl->handshaked) { + + if (pscf->ssl_verify) { + rc = SSL_get_verify_result(pc->ssl->connection); + + if (rc != X509_V_OK) { + njt_log_error(NJT_LOG_ERR, pc->log, 0, + "upstream SSL certificate verify error: (%l:%s)", + rc, X509_verify_cert_error_string(rc)); + goto failed; + } + + u = s->upstream; + + if (njt_ssl_check_host(pc, &u->ssl_name) != NJT_OK) { + njt_log_error(NJT_LOG_ERR, pc->log, 0, + "upstream SSL certificate does not match \"%V\"", + &u->ssl_name); + goto failed; + } + } + + if (pc->write->timer_set) { + njt_del_timer(pc->write); + } + + njt_stream_proto_init_upstream(s); + + return; + } + +failed: + + njt_stream_proto_next_upstream(s); +} + +void proto_server_log(int level, const char *fmt, ...) +{ + u_char buf[NJT_MAX_ERROR_STR] = {0}; + va_list args; + u_char *p; + njt_str_t msg; + + va_start(args, fmt); + p = njt_vslprintf(buf, buf + NJT_MAX_ERROR_STR, fmt, args); + va_end(args); + + msg.data = buf; + msg.len = p - buf; + + njt_log_error((njt_uint_t)level, njt_cycle->log, 0, "[tcc]%V", &msg); +} +static void * +njt_prealloc(njt_pool_t *pool,void *p, size_t size) +{ + njt_pool_large_t **l,*large; + void *ptr; + + if(p == NULL) { + return njt_palloc(pool,size); + } + pool->log = njt_cycle->log; + for (l = &pool->large; *l; ) { + // by zyg + if (pool->dynamic){ + void *fp = (*l)->alloc; + void* data = ((njt_pool_large_t*)p)-1; + if (data == fp) { + *l = (*l)->next; + if(size == 0) { + njt_free(fp); + return NJT_OK; + } + ptr = njt_realloc(fp,size + sizeof(njt_pool_large_t),pool->log); + if(ptr == NULL) { + return NULL; + } + large = ptr; + large->alloc = ptr; + large->next = pool->large; + pool->large = large; + return (void*)(large+1); + } + + } + l = &(*l)->next; + } + njt_log_debug1(NJT_LOG_DEBUG_ALLOC, pool->log, 0, + "free error: %p", p); + return NULL; +} +static void +njt_stream_proto_finalize(njt_stream_session_t *s, njt_uint_t rc) +{ + njt_uint_t state; + njt_connection_t *pc; + njt_stream_upstream_t *u; + + njt_log_debug1(NJT_LOG_DEBUG_STREAM, s->connection->log, 0, + "finalize stream proxy: %i", rc); + + u = s->upstream; + + if (u == NULL) { + goto noupstream; + } + + if (u->resolved && u->resolved->ctx) { + njt_resolve_name_done(u->resolved->ctx); + u->resolved->ctx = NULL; + } + + pc = u->peer.connection; + + if (u->state) { + if (u->state->response_time == (njt_msec_t) -1) { + u->state->response_time = njt_current_msec - u->start_time; + } + + if (pc) { + u->state->bytes_received = u->received; + u->state->bytes_sent = pc->sent; + } + } + + if (u->peer.free && u->peer.sockaddr) { + state = 0; + + if (pc && pc->type == SOCK_DGRAM + && (pc->read->error || pc->write->error)) + { + state = NJT_PEER_FAILED; + } + + u->peer.free(&u->peer, u->peer.data, state); + u->peer.sockaddr = NULL; + } + + if (pc) { + njt_log_debug1(NJT_LOG_DEBUG_STREAM, s->connection->log, 0, + "close stream proxy upstream connection: %d", pc->fd); + +#if (NJT_STREAM_SSL) + if (pc->ssl) { + pc->ssl->no_wait_shutdown = 1; + (void) njt_ssl_shutdown(pc); + } +#endif + + njt_close_connection(pc); + u->peer.connection = NULL; + } + + +noupstream: + + njt_stream_finalize_session(s, rc); +} + + +static void * +njt_realloc(void *ptr,size_t size, njt_log_t *log) +{ + void *p; + + p = realloc(ptr,size); + if (p == NULL) { + njt_log_error(NJT_LOG_EMERG, log, njt_errno, + "realloc(%uz) failed", size); + } + + // njt_log_debug2(NJT_LOG_DEBUG_ALLOC, log, 0, "malloc: %p:%uz", p, size); + + return p; +} + + +static njt_int_t +njt_proto_write_filter(njt_stream_session_t *s, njt_chain_t *in, + njt_uint_t from_upstream) +{ + off_t size; + njt_uint_t last, flush, sync; + njt_chain_t *cl, *ln, **ll, **out, *chain; + njt_connection_t *c; + //njt_stream_write_filter_ctx_t *ctx; + njt_stream_proto_server_client_ctx_t *ctx; + + ctx = njt_stream_get_module_ctx(s, njt_stream_proto_server_module); + + if (ctx == NULL) { + ctx = njt_pcalloc(s->connection->pool, + sizeof(njt_stream_proto_server_client_ctx_t)); + if (ctx == NULL) { + return NJT_ERROR; + } + + njt_stream_set_ctx(s, ctx, njt_stream_proto_server_module); + } + + if (from_upstream) { + c = s->connection; + out = &ctx->from_upstream; + + }else { + c = s->upstream->peer.connection; + out = &ctx->from_downstream; + } + + if (c->error) { + return NJT_ERROR; + } + + size = 0; + flush = 0; + sync = 0; + last = 0; + ll = out; + + /* find the size, the flush point and the last link of the saved chain */ + + for (cl = *out; cl; cl = cl->next) { + ll = &cl->next; + if (njt_buf_size(cl->buf) == 0 && !njt_buf_special(cl->buf)) { + njt_log_error(NJT_LOG_ALERT, c->log, 0, + "zero size buf in writer " + "t:%d r:%d f:%d %p %p-%p %p %O-%O", + cl->buf->temporary, + cl->buf->recycled, + cl->buf->in_file, + cl->buf->start, + cl->buf->pos, + cl->buf->last, + cl->buf->file, + cl->buf->file_pos, + cl->buf->file_last); + + njt_debug_point(); + return NJT_ERROR; + } + + if (njt_buf_size(cl->buf) < 0) { + njt_log_error(NJT_LOG_ALERT, c->log, 0, + "negative size buf in writer " + "t:%d r:%d f:%d %p %p-%p %p %O-%O", + cl->buf->temporary, + cl->buf->recycled, + cl->buf->in_file, + cl->buf->start, + cl->buf->pos, + cl->buf->last, + cl->buf->file, + cl->buf->file_pos, + cl->buf->file_last); + + njt_debug_point(); + return NJT_ERROR; + } + + size += njt_buf_size(cl->buf); + + if (cl->buf->flush || cl->buf->recycled) { + flush = 1; + } + + if (cl->buf->sync) { + sync = 1; + } + + if (cl->buf->last_buf) { + last = 1; + } + } + + /* add the new chain to the existent one */ + + for (ln = in; ln; ln = ln->next) { + cl = njt_alloc_chain_link(c->pool); + if (cl == NULL) { + return NJT_ERROR; + } + + cl->buf = ln->buf; + *ll = cl; + ll = &cl->next; + + if (njt_buf_size(cl->buf) == 0 && !njt_buf_special(cl->buf)) { + njt_log_error(NJT_LOG_ALERT, c->log, 0, + "zero size buf in writer " + "t:%d r:%d f:%d %p %p-%p %p %O-%O", + cl->buf->temporary, + cl->buf->recycled, + cl->buf->in_file, + cl->buf->start, + cl->buf->pos, + cl->buf->last, + cl->buf->file, + cl->buf->file_pos, + cl->buf->file_last); + + njt_debug_point(); + return NJT_ERROR; + } + + if (njt_buf_size(cl->buf) < 0) { + njt_log_error(NJT_LOG_ALERT, c->log, 0, + "negative size buf in writer " + "t:%d r:%d f:%d %p %p-%p %p %O-%O", + cl->buf->temporary, + cl->buf->recycled, + cl->buf->in_file, + cl->buf->start, + cl->buf->pos, + cl->buf->last, + cl->buf->file, + cl->buf->file_pos, + cl->buf->file_last); + + njt_debug_point(); + return NJT_ERROR; + } + + size += njt_buf_size(cl->buf); + + if (cl->buf->flush || cl->buf->recycled) { + flush = 1; + } + + if (cl->buf->sync) { + sync = 1; + } + + if (cl->buf->last_buf) { + last = 1; + } + } + + *ll = NULL; + + njt_log_debug3(NJT_LOG_DEBUG_STREAM, c->log, 0, + "stream write filter: l:%ui f:%ui s:%O", last, flush, size); + + if (size == 0 + && !(c->buffered & NJT_LOWLEVEL_BUFFERED) + && !(last && c->need_last_buf) + && !(flush && c->need_flush_buf)) + { + if (last || flush || sync) { + for (cl = *out; cl; /* void */) { + ln = cl; + cl = cl->next; + njt_free_chain(c->pool, ln); + if(from_upstream) { + ctx->r.session_max_mem_size = ctx->r.session_max_mem_size - (ln->buf->end - ln->buf->start); + //njt_log_debug2(NJT_LOG_DEBUG, c->log, 0,"proto_ctx->r.session_max_mem_size=%d,tcc free client=%d!",ctx->r.session_max_mem_size,(ln->buf->end - ln->buf->start)); + } else { + ctx->r.session_up_max_mem_size = ctx->r.session_up_max_mem_size - (ln->buf->end - ln->buf->start); + } + if(ln->buf->start != NULL) { + njt_pfree(ctx->r.tcc_pool,ln->buf->start); + } + } + + *out = NULL; + c->buffered &= ~NJT_STREAM_WRITE_BUFFERED; + + return NJT_OK; + } + + njt_log_error(NJT_LOG_ALERT, c->log, 0, + "the stream output chain is empty"); + + njt_debug_point(); + + return NJT_ERROR; + } + + chain = c->send_chain(c, *out, 0); + + njt_log_debug1(NJT_LOG_DEBUG_STREAM, c->log, 0, + "stream write filter %p", chain); + + if (chain == NJT_CHAIN_ERROR) { + c->error = 1; + return NJT_ERROR; + } + + for (cl = *out; cl && cl != chain; /* void */) { + ln = cl; + cl = cl->next; + njt_free_chain(c->pool, ln); + if(from_upstream) { + ctx->r.session_max_mem_size = ctx->r.session_max_mem_size - (ln->buf->end - ln->buf->start); + //njt_log_debug2(NJT_LOG_DEBUG, c->log, 0,"proto_ctx->r.session_max_mem_size=%d,tcc free client=%d!",ctx->r.session_max_mem_size,(ln->buf->end - ln->buf->start)); + } else { + ctx->r.session_up_max_mem_size = ctx->r.session_up_max_mem_size - (ln->buf->end - ln->buf->start); + } + if(ln->buf->start != NULL) { + njt_pfree(ctx->r.tcc_pool,ln->buf->start); + } + } + + *out = chain; + + if (chain) { + if (c->shared) { + njt_log_error(NJT_LOG_ALERT, c->log, 0, + "shared connection is busy"); + return NJT_ERROR; + } + + c->buffered |= NJT_STREAM_WRITE_BUFFERED; + return NJT_AGAIN; + } + + c->buffered &= ~NJT_STREAM_WRITE_BUFFERED; + + if (c->buffered & NJT_LOWLEVEL_BUFFERED) { + return NJT_AGAIN; + } + + return NJT_OK; +} + +int +proto_server_build_message(tcc_stream_request_t *r, void *in_data, tcc_str_t *out_data) +{ + njt_stream_proto_server_srv_conf_t *sscf; + njt_stream_session_t *s; + + s =(njt_stream_session_t*) r->s; + sscf = njt_stream_get_module_srv_conf(s, njt_stream_proto_server_module); + + if (sscf->build_proto_message) { + sscf->build_proto_message((tcc_stream_server_ctx *)r->tcc_server, in_data, out_data); + return 0; + } + + return -1; +} diff --git a/modules/njet-stream-proto-server-module/src/njt_stream_proto_server_module.h b/modules/njet-stream-proto-server-module/src/njt_stream_proto_server_module.h new file mode 100644 index 0000000000000000000000000000000000000000..419c7e8feed26563dcf55917993b68c885459dba --- /dev/null +++ b/modules/njet-stream-proto-server-module/src/njt_stream_proto_server_module.h @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2021-2023 TMLake(Beijing) Technology Co., Ltd. + */ +#ifndef NJT_STREAM_PROTO_SERVER_MODULE_H_ +#define NJT_STREAM_PROTO_SERVER_MODULE_H_ +#include +#include "libtcc.h" +#include "njt_tcc.h" + +extern njt_module_t njt_stream_proto_server_module; +typedef int (*njt_proto_server_handler_pt)(tcc_stream_request_t *r); +typedef int (*njt_proto_server_data_handler_pt)(tcc_stream_request_t *r, tcc_str_t *msg); +typedef int (*njt_proto_server_update_pt)(tcc_stream_server_ctx *srv_ctx); +typedef int (*njt_proto_server_build_message_pt)(tcc_stream_server_ctx *srv_ctx,void *in_data,tcc_str_t *out_data); +typedef void* (*njt_script_upstream_peer_pt)(tcc_stream_client_upstream_data_t *cli_ups_info); +njt_int_t njt_stream_proto_server_init_upstream(njt_stream_session_t *s); +njt_int_t njt_stream_proto_server_process_proxy_message(njt_stream_session_t *s, njt_buf_t *b, njt_uint_t from_upstream); + + +typedef struct +{ + njt_chain_t *out_chain; + njt_chain_t *from_upstream; + njt_chain_t *from_downstream; + njt_chain_t *out_busy; + njt_buf_t out_buf; + tcc_stream_request_t r; + njt_chain_t *free; + njt_event_t timer; +} njt_stream_proto_server_client_ctx_t; +typedef struct +{ + njt_array_t srv_info; + +} njt_stream_proto_server_main_conf_t; + +typedef struct +{ + njt_flag_t proto_server_enabled; + TCCState *s; + njt_array_t *tcc_files; + tcc_stream_server_ctx srv_ctx; + njt_event_t timer; + size_t buffer_size; + size_t session_max_mem_size; + njt_msec_t connect_timeout; + njt_msec_t client_update_interval; + njt_msec_t server_update_interval; + njt_proto_server_handler_pt connection_handler; + njt_proto_server_data_handler_pt preread_handler; + njt_proto_server_handler_pt log_handler; + njt_proto_server_data_handler_pt message_handler; + njt_proto_server_handler_pt abort_handler; + njt_proto_server_update_pt server_update_handler; + njt_proto_server_update_pt server_init_handler; + njt_proto_server_data_handler_pt client_update_handler; + njt_proto_server_build_message_pt build_proto_message; + + //upstream + njt_flag_t proto_upstream_enabled; + njt_flag_t proto_pass_enabled; + njt_stream_upstream_init_pt original_init_upstream; + njt_stream_upstream_init_peer_pt original_init_peer; + njt_script_upstream_peer_pt check_upstream_peer_handler; + njt_proto_server_data_handler_pt upstream_message_handler; + njt_proto_server_handler_pt upstream_abort_handler; + +} njt_stream_proto_server_srv_conf_t; + +typedef struct +{ + void *data; + njt_stream_proto_server_srv_conf_t *conf; + njt_stream_session_t *s; + njt_event_free_peer_pt original_free_peer; + njt_event_get_peer_pt original_get_peer; //njt_event_notify_peer_pt notify; + njt_event_notify_peer_pt original_notify; + +} njt_stream_proto_upstream_peer_data_t; +#endif diff --git a/modules/njet-stream-proto-server-module/src/njt_tcc.h b/modules/njet-stream-proto-server-module/src/njt_tcc.h new file mode 100644 index 0000000000000000000000000000000000000000..0c8b9b8cfa1ed22895384f9e21272a65424475b7 --- /dev/null +++ b/modules/njet-stream-proto-server-module/src/njt_tcc.h @@ -0,0 +1,193 @@ +#ifndef NJT_TCC_H +#define NJT_TCC_H +#include +#define TCC_SESSION_CONNECT 0 +#define TCC_SESSION_CLOSING 1 +#define TCC_SESSION_CLOSED 2 +#define TCC_MAX_PROTO_CTX 128 +#define TCC_PROTO_CTX_ID 0 + +#define njt_string(str) { sizeof(str) - 1, (u_char *) str } +#define tcc_get_client_ctx(r, module_id) \ + (module_id < TCC_MAX_PROTO_CTX? \ + r->cli_ctx[module_id]: \ + NULL) + +#define tcc_set_client_ctx(r,module_id,c) \ + (module_id < TCC_MAX_PROTO_CTX? \ + r->cli_ctx[module_id] = c: \ + NULL) + +#define tcc_client_get_srv_ctx(r) (r)->tcc_server->srv_data + +#define tcc_get_client_app_ctx(r) (r)->cli_app_ctx +#define tcc_set_client_app_ctx(r, c) (r)->cli_app_ctx = c; + +#define tcc_client_get_app_srv_ctx(r) (r)->tcc_server->srv_app_ctx + +#define tcc_set_srv_ctx(s,c) (s)->srv_data = c; +#define tcc_get_srv_ctx(s) (s)->srv_data + +#define tcc_set_app_srv_ctx(s,c) (s)->srv_app_ctx = c; +#define tcc_get_app_srv_ctx(s) (s)->srv_app_ctx + +#define tcc_set_peer_srv_ctx(ctx,c) (ctx)->peer_data = c; +#define tcc_get_peer_srv_ctx(ctx) (ctx)->peer_data + +#define NJT_LOG_ERR 4 +#define NJT_LOG_DEBUG 8 +#define NJT_LOG_INFO 7 +#define NJT_OK 0 +#define NJT_ERROR -1 +#define NJT_AGAIN -2 +#define NJT_DECLINED -5 +#define NJT_STREAM_OK 200 +#define NJT_STREAM_SPECIAL_RESPONSE 300 +#define NJT_STREAM_BAD_REQUEST 400 +#define NJT_STREAM_FORBIDDEN 403 +#define NJT_STREAM_INTERNAL_SERVER_ERROR 500 +#define NJT_STREAM_BAD_GATEWAY 502 +#define NJT_STREAM_SERVICE_UNAVAILABLE 503 +#define GET_BIT(x,bit) ((x & (1 << bit)) >> bit) +#define njt_memzero(buf, n) (void) memset(buf, 0, n) +#define njt_memcpy(dst, src, n) (void) memcpy(dst, src, n) +#define njt_memcmp(s1, s2, n) memcmp(s1, s2, n) + +#define njt_base64_encoded_length(len) (((len + 2) / 3) * 4) +#define njt_base64_decoded_length(len) (((len + 3) / 4) * 3) + +typedef struct tcc_stream_request_s tcc_stream_request_t; +typedef struct tcc_stream_server_ctx_s tcc_stream_server_ctx; +typedef struct tcc_stream_upstream_rr_peer_s tcc_stream_upstream_rr_peer_t; +typedef struct tcc_stream_client_upstream_data_s tcc_stream_client_upstream_data_t; + + +typedef intptr_t tcc_int_t; +typedef uintptr_t tcc_uint_t; +typedef intptr_t tcc_flag_t; +typedef tcc_uint_t tcc_msec_t; + +typedef intptr_t njt_int_t; + +typedef void *tcc_buf_tag_t; +typedef void *tcc_file_t; + +typedef struct tcc_buf_s tcc_buf_t; +struct tcc_buf_s +{ + u_char *pos; + u_char *last; + off_t file_pos; + off_t file_last; + + u_char *start; /* start of buffer */ + u_char *end; /* end of buffer */ + tcc_buf_tag_t tag; + tcc_file_t *file; + tcc_buf_t *shadow; + + /* the buf's content could be changed */ + unsigned temporary : 1; + + /* + * the buf's content is in a memory cache or in a read only memory + * and must not be changed + */ + unsigned memory : 1; + + /* the buf's content is mmap()ed and must not be changed */ + unsigned mmap : 1; + + unsigned recycled : 1; + unsigned in_file : 1; + unsigned flush : 1; + unsigned sync : 1; + unsigned last_buf : 1; + unsigned last_in_chain : 1; + + unsigned last_shadow : 1; + unsigned temp_file : 1; + + /* STUB */ int num; +}; +typedef struct { + size_t len; + u_char *data; +} tcc_str_t; + + +struct tcc_stream_server_ctx_s +{ + /* the tcc_pool data must be first */ + void *tcc_pool; + void *client_list; + void *srv_data; + void *srv_app_ctx; + +}; +struct tcc_stream_upstream_rr_peer_s +{ + tcc_str_t *name; + tcc_str_t *server; + void *peer; +}; +struct tcc_stream_client_upstream_data_s +{ + tcc_str_t *cli_addr_text; + int peer_num; + tcc_stream_upstream_rr_peer_t *peer_list; + +}; + +struct tcc_stream_request_s +{ + /* the tcc_pool data must be first */ + void *tcc_pool; + void *s; + tcc_buf_t in_buf; + size_t session_max_mem_size; + size_t session_up_max_mem_size; + tcc_str_t *addr_text; + void *cli_ctx[TCC_MAX_PROTO_CTX]; + void *cli_app_ctx; + tcc_stream_server_ctx *tcc_server; + int status; + int used_len; +}; +typedef struct { + uint64_t bytes; + uint32_t a, b, c, d, e, f; + u_char buffer[64]; +} tcc_sha1_t; + + +extern tcc_str_t cli_get_variable(tcc_stream_request_t *r,char *name); +extern void cli_close(tcc_stream_request_t *r); +extern void proto_server_log(int level,const char *fmt, ...); +extern int proto_server_process_connection(tcc_stream_request_t *r); +extern int proto_server_process_preread(tcc_stream_request_t *r,tcc_str_t *msg); +extern int proto_server_process_log(tcc_stream_request_t *r); +extern int proto_server_process_message(tcc_stream_request_t *r,tcc_str_t *msg); +extern int proto_server_process_connection_close(tcc_stream_request_t *r); +extern int proto_server_send(tcc_stream_request_t *r,char *data,size_t len); +extern int proto_server_send_broadcast(tcc_stream_server_ctx *srv_ctx,char *data,size_t len); +extern int proto_server_send_others(tcc_stream_request_t *sender, char *data, size_t len); +extern u_char * njt_snprintf(u_char *buf, size_t max, const char *fmt, ...); +extern void *proto_malloc(void *ctx, int len); +extern void proto_free(void *ctx, void *p); +extern void *proto_realloc(void *ctx, void *p, int len); +extern tcc_int_t proto_get_peer_weight(void *peer); +extern tcc_int_t proto_get_peer_conns(void *peer); +extern tcc_uint_t proto_get_peer_fails(void *peer); +extern int proto_server_send_upstream(tcc_stream_request_t *r,char *data, size_t len); +extern void tcc_encode_base64(tcc_str_t *dst, tcc_str_t *src); +extern void tcc_encode_base64url(tcc_str_t *dst, tcc_str_t *src); +extern tcc_int_t tcc_decode_base64(tcc_str_t *dst, tcc_str_t *src); +extern tcc_int_t tcc_decode_base64url(tcc_str_t *dst, tcc_str_t *src); +extern void tcc_sha1_init(tcc_sha1_t *ctx); +extern void tcc_sha1_update(tcc_sha1_t *ctx, const void *data, size_t size); +extern void tcc_sha1_final(u_char result[20], tcc_sha1_t *ctx); +extern u_char *njt_strlcasestrn(u_char *s1, u_char *last, u_char *s2, size_t n); +extern int proto_server_build_message(tcc_stream_request_t *r, void *in_data, tcc_str_t *out_data); +extern int njt_stream_proto_python_on_msg(tcc_stream_request_t *r, char *msg, size_t msg_len); +#endif diff --git a/modules/njet-stream-proto-server-module/src/tcc_ws.c b/modules/njet-stream-proto-server-module/src/tcc_ws.c new file mode 100644 index 0000000000000000000000000000000000000000..61ad613467980833e86df26f7b7e38dd78a202e5 --- /dev/null +++ b/modules/njet-stream-proto-server-module/src/tcc_ws.c @@ -0,0 +1,129 @@ +#include +#include +#include +#include + +typedef struct app_server_s { + /* Server Status */ + int gen_client_id; + int server_id; +} app_server_t; + +typedef struct app_client_s { + /* Server Status */ + tcc_str_t *init_data; + int id; +} app_client_t; + +int ws_app_on_connection(tcc_stream_request_t *r,WSMessage *msg) { + app_server_t *app_server; + app_client_t *app_client = proto_malloc(r,sizeof(app_client_t)); + if(app_client != NULL) { + app_client->init_data = r->addr_text; + app_server = tcc_client_get_app_srv_ctx(r); + app_client->id = ++app_server->gen_client_id; + tcc_set_client_app_ctx(r,app_client); + } + ws_send_handshake_headers(r, msg->headers); + if(msg->headers != NULL) { + proto_server_log(NJT_LOG_DEBUG, "tcc from ws_app_on_connection path: %s!",msg->headers->path); + } + return NJT_OK; +} + +int ws_app_on_message(tcc_stream_request_t *r,WSMessage *msg) { + tcc_str_t data,out_data; + tcc_str_t buffer; + u_char *p; + int len; + app_client_t *app_client = tcc_get_client_app_ctx(r); + if(app_client == NULL) { + proto_server_log(NJT_LOG_DEBUG, "tcc app_client null!"); + return NJT_ERROR; + } + app_server_t *app_server = tcc_client_get_app_srv_ctx(r); + if(app_server == NULL) { + proto_server_log(NJT_LOG_DEBUG, "tcc app_server null!"); + return NJT_ERROR; + } + + if(msg->opcode == WS_OPCODE_PING) { + proto_server_log(NJT_LOG_DEBUG, "1 tcc from ws_app_on_message ping!"); + return NJT_OK; + } + proto_server_log(NJT_LOG_DEBUG, "1 tcc from ws_app_on_message msg->opcode=%d!",msg->opcode); + + data.data = msg->payload; + data.len = msg->payloadsz; + + len = msg->payloadsz + app_client->init_data->len + 100; + buffer.data = proto_malloc(r,len); + if(buffer.data == NULL) { + return NJT_ERROR; + } + buffer.len = len; + njt_memzero(buffer.data,len); + + p = njt_snprintf(buffer.data,buffer.len,"[server]:\nclient_id=%d,addr:[%V],data=%V,server_id=%d",app_client->id,app_client->init_data,&data,app_server->server_id); + buffer.len = p - buffer.data; + + proto_server_log(NJT_LOG_DEBUG, "3 tcc from ws_app_on_message %V!",&buffer); + + ws_generate_frame(WS_OPCODE_TEXT, buffer.data, buffer.len, &out_data); + proto_server_send(r,out_data.data, out_data.len); + proto_free(r,buffer.data); + free(out_data.data); + proto_server_log(NJT_LOG_DEBUG, "tcc from ws_app_on_message data=%V!",&data); + return NJT_OK; +} + +int ws_app_on_close(tcc_stream_request_t *r) { + + app_client_t *app_client = tcc_get_client_app_ctx(r); + if(app_client != NULL) { + proto_free(r,app_client); + } + proto_server_log(NJT_LOG_DEBUG, "tcc from ws_app_on_close!"); + return NJT_OK; +} + +int ws_app_client_update(tcc_stream_request_t *r) { + tcc_str_t out_data; + app_client_t *app_client = tcc_get_client_app_ctx(r); + tcc_str_t data = njt_string("tcc ws_app_client_update!\n"); + if(app_client != NULL) { + ws_generate_frame(WS_OPCODE_TEXT, data.data, data.len, &out_data); + proto_server_send(r,out_data.data, out_data.len); + free(out_data.data); + proto_server_log(NJT_LOG_DEBUG, "tcc from ws_app_client_update !"); + + } + return NJT_OK; +} +int ws_app_server_update(tcc_stream_server_ctx *srv_ctx) +{ + tcc_str_t data = njt_string("tcc ws_app_server_update!\n"); + tcc_str_t out_data; + app_server_t * srv_data = tcc_get_app_srv_ctx(srv_ctx); + if(srv_data) { + ws_generate_frame(WS_OPCODE_TEXT, data.data, data.len, &out_data); + if(out_data.len > 0) { + free(out_data.data); + + } + proto_server_log(NJT_LOG_DEBUG, "tcc from ws_app_server_update !"); + } + return NJT_OK; +} + +int ws_app_server_init(tcc_stream_server_ctx *srv_ctx) { + app_server_t *app_server = proto_malloc(srv_ctx,sizeof(app_server_t)); + if(app_server != NULL) { + app_server->gen_client_id = 0; + app_server->server_id = 1; + tcc_set_app_srv_ctx(srv_ctx,app_server); + } + proto_server_log(NJT_LOG_DEBUG, "tcc from ws_app_server_init!"); + return NJT_OK; +} + diff --git a/modules/njet-stream-proto-server-module/src/tcc_ws.h b/modules/njet-stream-proto-server-module/src/tcc_ws.h new file mode 100644 index 0000000000000000000000000000000000000000..91d4f18e4eb2d0be518d0eea253e03343c24b40b --- /dev/null +++ b/modules/njet-stream-proto-server-module/src/tcc_ws.h @@ -0,0 +1,174 @@ +#ifndef TCC_WS_H +#define TCC_WS_H + +#include +#include + + + + + +#define WS_HANDSHAKE_OK 1 + +#define UTF8_VALID 0 +#define UTF8_INVAL 1 + +#define HDR_SIZE 3 * 4 +#define WS_MAX_FRM_SZ 1048576 /* 1 MiB max frame size */ +#define WS_THROTTLE_THLD 2097152 /* 2 MiB throttle threshold */ +#define WS_MAX_HEAD_SZ 8192 /* a reasonable size for request headers */ + +#define WS_MAGIC_STR "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" +#define WS_PAYLOAD_EXT16 126 +#define WS_PAYLOAD_EXT64 127 +#define WS_PAYLOAD_FULL 125 +#define WS_FRM_HEAD_SZ 16 /* frame header size */ + +#define WS_FRM_FIN(x) (((x) >> 7) & 0x01) +#define WS_FRM_MASK(x) (((x) >> 7) & 0x01) +#define WS_FRM_R1(x) (((x) >> 6) & 0x01) +#define WS_FRM_R2(x) (((x) >> 5) & 0x01) +#define WS_FRM_R3(x) (((x) >> 4) & 0x01) +#define WS_FRM_OPCODE(x) ((x) & 0x0F) +#define WS_FRM_PAYLOAD(x) ((x) & 0x7F) + +#define WS_CLOSE_NORMAL 1000 +#define WS_CLOSE_GOING_AWAY 1001 +#define WS_CLOSE_PROTO_ERR 1002 +#define WS_CLOSE_INVALID_UTF8 1007 +#define WS_CLOSE_TOO_LARGE 1009 +#define WS_CLOSE_UNEXPECTED 1011 + +static const uint8_t utf8d[] = { + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 00..1f */ + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 20..3f */ + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 40..5f */ + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 60..7f */ + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, /* 80..9f */ + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, /* a0..bf */ + 8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, /* c0..df */ + 0xa,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x4,0x3,0x3, /* e0..ef */ + 0xb,0x6,0x6,0x6,0x5,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8, /* f0..ff */ + 0x0,0x1,0x2,0x3,0x5,0x8,0x7,0x1,0x1,0x1,0x4,0x6,0x1,0x1,0x1,0x1, /* s0..s0 */ + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,0,1,0,1,1,1,1,1,1, /* s1..s2 */ + 1,2,1,1,1,1,1,2,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1, /* s3..s4 */ + 1,2,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,3,1,3,1,1,1,1,1,1, /* s5..s6 */ + 1,3,1,1,1,1,1,3,1,3,1,1,1,1,1,1,1,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* s7..s8 */ +}; + +typedef enum WSOPCODE { + WS_OPCODE_CONTINUATION = 0x00, + WS_OPCODE_TEXT = 0x01, + WS_OPCODE_BIN = 0x02, + WS_OPCODE_END = 0x03, + WS_OPCODE_CLOSE = 0x08, + WS_OPCODE_PING = 0x09, + WS_OPCODE_PONG = 0x0A, +} WSOpcode; + +typedef enum WSSTATUS { + WS_OK = 0, + WS_ERR = (1 << 0), + WS_CLOSE = (1 << 1), + WS_READING = (1 << 2), + WS_SENDING = (1 << 3), + WS_THROTTLING = (1 << 4), + WS_TLS_ACCEPTING = (1 << 5), + WS_TLS_READING = (1 << 6), + WS_TLS_WRITING = (1 << 7), + WS_TLS_SHUTTING = (1 << 8), +} WSStatus; + +/* WS HTTP Headers */ +typedef struct ws_headers_s { + int reading; + int buflen; + char buf[WS_MAX_HEAD_SZ + 1]; + + char *agent; + char *path; + char *method; + char *protocol; + char *host; + char *origin; + char *upgrade; + char *referer; + char *connection; + char *ws_protocol; + char *ws_key; + char *ws_sock_ver; + + char *ws_accept; + char *ws_resp; +} ws_headers; + +/* A WebSocket Message */ +typedef struct WSMessage_ { + WSOpcode opcode; /* frame opcode */ + int fragmented; /* reading a fragmented frame */ + int mask_offset; /* for fragmented frames */ + + char *payload; /* payload message */ + int payloadsz; /* total payload size (whole message) */ + int buflen; /* recv'd buf length so far (for each frame) */ + ws_headers *headers; +} WSMessage; + +/* A WebSocket Message */ +typedef struct WSFrame_ { + /* frame format */ + WSOpcode opcode; /* frame opcode */ + unsigned char fin; /* frame fin flag */ + unsigned char mask[4]; /* mask key */ + uint8_t res; /* extensions */ + int payload_offset; /* end of header/start of payload */ + int payloadlen; /* payload length (for each frame) */ + + /* status flags */ + int reading; /* still reading frame's header part? */ + int masking; /* are we masking the frame? */ + + char buf[WS_FRM_HEAD_SZ + 1]; /* frame's header */ + int buflen; /* recv'd buf length so far (for each frame) */ +} WSFrame; + +/* A WebSocket Client */ +typedef struct TCCWSClient_ { + /* socket data */ + int listener; /* socket */ + char remote_ip[INET6_ADDRSTRLEN]; /* client IP */ + ws_headers headers; /* HTTP headers */ + WSFrame *frame; /* frame headers */ + WSMessage *message; /* message */ + WSStatus status; /* connection status */ + + struct timeval start_proc; + struct timeval end_proc; + int used_len; + tcc_str_t msg; + tcc_stream_request_t *r; +} WSClient; + +typedef struct WSctx_ { + unsigned handshake:1; + WSClient client; + +} WSctx; + + +/* A WebSocket Instance */ +typedef struct WSServer_ { + /* Server Status */ + int closing; +} WSServer; + +extern int ws_send_handshake_headers(tcc_stream_request_t *r, ws_headers *headers); +extern int ws_app_on_connection(tcc_stream_request_t *r,WSMessage *msg); +extern int ws_app_on_message(tcc_stream_request_t *r,WSMessage *msg); +extern int ws_app_on_close(tcc_stream_request_t *r); +extern int ws_app_server_init(tcc_stream_server_ctx *srv_ctx); +extern int ws_generate_frame(WSOpcode opcode, const char *p, int sz, tcc_str_t *out_message); +extern int ws_send_frame(tcc_stream_request_t *r, WSOpcode opcode, const char *p, int sz); +extern int ws_app_client_update(tcc_stream_request_t *r); +extern int ws_app_server_update(tcc_stream_server_ctx *srv_ctx); +#endif diff --git a/modules/njet-stream-proto-server-module/src/tcc_ws_py.c b/modules/njet-stream-proto-server-module/src/tcc_ws_py.c new file mode 100644 index 0000000000000000000000000000000000000000..779f41f78af6127d7998c2a9935e8f314122bd82 --- /dev/null +++ b/modules/njet-stream-proto-server-module/src/tcc_ws_py.c @@ -0,0 +1,110 @@ +#include +#include +#include +#include + +typedef struct app_server_s { + /* Server Status */ + int gen_client_id; + int server_id; +} app_server_t; + +typedef struct app_client_s { + /* Server Status */ + tcc_str_t *init_data; + int id; +} app_client_t; + +int ws_app_on_connection(tcc_stream_request_t *r,WSMessage *msg) { + app_server_t *app_server; + app_client_t *app_client = proto_malloc(r,sizeof(app_client_t)); + if(app_client != NULL) { + app_client->init_data = r->addr_text; + app_server = tcc_client_get_app_srv_ctx(r); + app_client->id = ++app_server->gen_client_id; + tcc_set_client_app_ctx(r,app_client); + } + ws_send_handshake_headers(r, msg->headers); + if(msg->headers != NULL) { + proto_server_log(NJT_LOG_DEBUG, "tcc from ws_app_on_connection path: %s!",msg->headers->path); + } + return NJT_OK; +} + +int ws_app_on_message(tcc_stream_request_t *r,WSMessage *msg) { + tcc_str_t data,out_data; + int len; + app_client_t *app_client = tcc_get_client_app_ctx(r); + if(app_client == NULL) { + proto_server_log(NJT_LOG_DEBUG, "tcc app_client null!"); + return NJT_ERROR; + } + app_server_t *app_server = tcc_client_get_app_srv_ctx(r); + if(app_server == NULL) { + proto_server_log(NJT_LOG_DEBUG, "tcc app_server null!"); + return NJT_ERROR; + } + + if(msg->opcode == WS_OPCODE_PING) { + proto_server_log(NJT_LOG_DEBUG, "1 tcc from ws_app_on_message ping!"); + return NJT_OK; + } + proto_server_log(NJT_LOG_DEBUG, "1 tcc from ws_app_on_message msg->opcode=%d!",msg->opcode); + + data.data = msg->payload; + data.len = msg->payloadsz; + + njt_stream_proto_python_on_msg(r, data.data, data.len); + return NJT_OK; +} + + + +int ws_app_on_close(tcc_stream_request_t *r) { + + app_client_t *app_client = tcc_get_client_app_ctx(r); + if(app_client != NULL) { + proto_free(r,app_client); + } + proto_server_log(NJT_LOG_DEBUG, "tcc from ws_app_on_close!"); + return NJT_OK; +} + +int ws_app_client_update(tcc_stream_request_t *r) { + app_client_t *app_client = tcc_get_client_app_ctx(r); + tcc_str_t data = njt_string("tcc ws_app_client_update!\n"); + if(app_client != NULL) { + //ws_send_frame(r, WS_OPCODE_TEXT, data.data, data.len); + proto_server_log(NJT_LOG_DEBUG, "tcc from ws_app_client_update !"); + + } + return NJT_OK; +} +int ws_app_server_update(tcc_stream_server_ctx *srv_ctx) +{ + tcc_str_t data = njt_string("tcc ws_app_server_update!\n"); + tcc_str_t out_data; + app_server_t * srv_data = tcc_get_app_srv_ctx(srv_ctx); + if(srv_data) { + ws_generate_frame(WS_OPCODE_TEXT, data.data, data.len, &out_data); + //proto_server_send_broadcast(srv_ctx,out_data.data, out_data.len); + if(out_data.len > 0) { + free(out_data.data); + + } + // proto_server_send_broadcast(srv_ctx,buf,strlen(buf)); + proto_server_log(NJT_LOG_DEBUG, "tcc from ws_app_server_update !"); + } + return NJT_OK; +} + +int ws_app_server_init(tcc_stream_server_ctx *srv_ctx) { + app_server_t *app_server = proto_malloc(srv_ctx,sizeof(app_server_t)); + if(app_server != NULL) { + app_server->gen_client_id = 0; + app_server->server_id = 1; + tcc_set_app_srv_ctx(srv_ctx,app_server); + } + proto_server_log(NJT_LOG_DEBUG, "tcc from ws_app_server_init!"); + return NJT_OK; +} diff --git a/modules/njet-stream-proto-server-module/src/ws_proto_server.c b/modules/njet-stream-proto-server-module/src/ws_proto_server.c new file mode 100644 index 0000000000000000000000000000000000000000..be6873f732ae1f67f9ef7737fa5eb428088cd08c --- /dev/null +++ b/modules/njet-stream-proto-server-module/src/ws_proto_server.c @@ -0,0 +1,1524 @@ +#include +#include +#include +#include +// global vari +int max_frm_size = 6553500; +// ws_headers headers; +typedef struct +{ + uint32_t state[5]; + uint32_t count[2]; + uint8_t buffer[64]; +} SHA1_CTX; +#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) + +/* blk0() and blk() perform the initial expand. */ +/* I got the idea of expanding during the round function from SSLeay */ +#ifdef LITTLE_ENDIAN +#define blk0(i) (block->l[i] = (rol(block->l[i], 24) & 0xFF00FF00) | (rol(block->l[i], 8) & 0x00FF00FF)) +#else +#define blk0(i) block->l[i] +#endif +#define blk(i) (block->l[i & 15] = rol(block->l[(i + 13) & 15] ^ block->l[(i + 8) & 15] ^ block->l[(i + 2) & 15] ^ block->l[i & 15], 1)) + +/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */ +#define R0(v, w, x, y, z, i) \ + z += ((w & (x ^ y)) ^ y) + blk0(i) + 0x5A827999 + rol(v, 5); \ + w = rol(w, 30); +#define R1(v, w, x, y, z, i) \ + z += ((w & (x ^ y)) ^ y) + blk(i) + 0x5A827999 + rol(v, 5); \ + w = rol(w, 30); +#define R2(v, w, x, y, z, i) \ + z += (w ^ x ^ y) + blk(i) + 0x6ED9EBA1 + rol(v, 5); \ + w = rol(w, 30); +#define R3(v, w, x, y, z, i) \ + z += (((w | x) & y) | (w & x)) + blk(i) + 0x8F1BBCDC + rol(v, 5); \ + w = rol(w, 30); +#define R4(v, w, x, y, z, i) \ + z += (w ^ x ^ y) + blk(i) + 0xCA62C1D6 + rol(v, 5); \ + w = rol(w, 30); + +#define CRLF "\r\n" +#define WS_MAGIC_STR "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" +#define WS_SWITCH_PROTO_STR "HTTP/1.1 101 Switching Protocols" +#ifndef SHA_DIGEST_LENGTH +#define SHA_DIGEST_LENGTH 20 +#endif + +// +void * +xmalloc(size_t size) +{ + void *ptr; + + if ((ptr = malloc(size)) == NULL) + proto_server_log(NJT_LOG_DEBUG, "Unable to allocate memory - failed."); + + return (ptr); +} + +char * +xstrdup(const char *s) +{ + char *ptr; + size_t len; + + len = strlen(s) + 1; + ptr = xmalloc(len); + + strncpy(ptr, s, len); + return (ptr); +} + +/* Self-checking wrapper to calloc() */ +void * +xcalloc(size_t nmemb, size_t size) +{ + void *ptr; + + if ((ptr = calloc(nmemb, size)) == NULL) + proto_server_log(NJT_LOG_DEBUG, "Unable to calloc memory - failed."); + + return (ptr); +} + +void * +xrealloc(void *oldptr, size_t size) +{ + void *newptr; + + if ((newptr = realloc(oldptr, size)) == NULL) + proto_server_log(NJT_LOG_DEBUG, "Unable to reallocate memory - failed"); + + return (newptr); +} + +static char * +strtoupper(char *str) +{ + char *p = str; + if (str == NULL || *str == '\0') + return str; + + while (*p != '\0') + { + *p = toupper((int)*p); + p++; + } + + return str; +} + +static const char * +ws_get_method(const char *token) +{ + const char *lookfor = NULL; + + if ((lookfor = "GET", !memcmp(token, "GET ", 4)) || + (lookfor = "get", !memcmp(token, "get ", 4))) + return lookfor; + return NULL; +} + +char * +base64_encode(const void *buf, size_t size) +{ + static const char base64[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + + char *str = (char *)xmalloc((size + 3) * 4 / 3 + 1); + + char *p = str; + const unsigned char *q = (const unsigned char *)buf; + size_t i = 0; + + while (i < size) + { + int c = q[i++]; + c *= 256; + if (i < size) + c += q[i]; + i++; + + c *= 256; + if (i < size) + c += q[i]; + i++; + + *p++ = base64[(c & 0x00fc0000) >> 18]; + *p++ = base64[(c & 0x0003f000) >> 12]; + + if (i > size + 1) + *p++ = '='; + else + *p++ = base64[(c & 0x00000fc0) >> 6]; + + if (i > size) + *p++ = '='; + else + *p++ = base64[c & 0x0000003f]; + } + + *p = 0; + + return str; +} + +static void +ws_set_header_key_value(ws_headers *headers, char *key, char *value) +{ + if (strcasecmp("Host", key) == 0) + headers->host = xstrdup(value); + else if (strcasecmp("Origin", key) == 0) + headers->origin = xstrdup(value); + else if (strcasecmp("Upgrade", key) == 0) + headers->upgrade = xstrdup(value); + else if (strcasecmp("Connection", key) == 0) + headers->connection = xstrdup(value); + else if (strcasecmp("Sec-WebSocket-Protocol", key) == 0) + headers->ws_protocol = xstrdup(value); + else if (strcasecmp("Sec-WebSocket-Key", key) == 0) + headers->ws_key = xstrdup(value); + else if (strcasecmp("Sec-WebSocket-Version", key) == 0) + headers->ws_sock_ver = xstrdup(value); + else if (strcasecmp("User-Agent", key) == 0) + headers->agent = xstrdup(value); + else if (strcasecmp("Referer", key) == 0) + headers->referer = xstrdup(value); +} + +static char * +ws_parse_request(char *line, char **method, char **protocol) +{ + const char *meth; + char *req = NULL, *request = NULL, *proto = NULL; + ptrdiff_t rlen; + + if ((meth = ws_get_method(line)) == NULL) + { + return NULL; + } + else + { + req = line + strlen(meth); + if ((proto = strstr(line, " HTTP/1.0")) == NULL && + (proto = strstr(line, " HTTP/1.1")) == NULL) + return NULL; + + req++; + if ((rlen = proto - req) <= 0) + return NULL; + + request = xmalloc(rlen + 1); + strncpy(request, req, rlen); + request[rlen] = 0; + + (*method) = strtoupper(xstrdup(meth)); + (*protocol) = strtoupper(xstrdup(++proto)); + } + + return request; +} + +static int +ws_set_header_fields(char *line, ws_headers *headers) +{ + char *path = NULL, *method = NULL, *proto = NULL, *p, *value; + + if (line[0] == '\n' || line[0] == '\r') + return 1; + + if ((strstr(line, "GET ")) || (strstr(line, "get "))) + { + if ((path = ws_parse_request(line, &method, &proto)) == NULL) + return 1; + headers->path = path; + headers->method = method; + headers->protocol = proto; + + return 0; + } + + if ((p = strchr(line, ':')) == NULL) + return 1; + + value = p + 1; + while (p != line && isspace((unsigned char)*(p - 1))) + p--; + + if (p == line) + return 1; + + *p = '\0'; + if (strpbrk(line, " \t") != NULL) + { + *p = ' '; + return 1; + } + while (isspace((unsigned char)*value)) + value++; + + ws_set_header_key_value(headers, line, value); + + return 0; +} + +static int +parse_headers(ws_headers *headers) +{ + char *tmp = NULL; + const char *buffer = headers->buf; + const char *line = buffer, *next = NULL; + int len = 0; + + while (line) + { + if ((next = strstr(line, "\r\n")) != NULL) + len = (next - line); + else + len = strlen(line); + + if (len <= 0) + { + proto_server_log(NJT_LOG_DEBUG, "1 tcc content parse_headers error!"); + return 1; + } + + tmp = xmalloc(len + 1); + memcpy(tmp, line, len); + tmp[len] = '\0'; + + if (ws_set_header_fields(tmp, headers) == 1) + { + free(tmp); + proto_server_log(NJT_LOG_DEBUG, "2 tcc content parse_headers error!"); + return 1; + } + + free(tmp); + line = next ? (next + 2) : NULL; + + if (next && strcmp(next, "\r\n\r\n") == 0) + break; + } + + return 0; +} +static int +ws_verify_req_headers(ws_headers *headers) +{ + if (!headers->host) + return 1; + if (!headers->method) + return 1; + if (!headers->protocol) + return 1; + if (!headers->path) + return 1; + if (!headers->connection) + return 1; + if (!headers->ws_key) + return 1; + if (!headers->ws_sock_ver) + return 1; + return 0; +} +static void +SHA1Init(SHA1_CTX *context) +{ + context->state[0] = 0x67452301; + context->state[1] = 0xEFCDAB89; + context->state[2] = 0x98BADCFE; + context->state[3] = 0x10325476; + context->state[4] = 0xC3D2E1F0; + context->count[0] = context->count[1] = 0; +} +void SHA1Transform(uint32_t state[5], uint8_t buffer[64]) +{ + uint32_t a, b, c, d, e; + typedef union + { + uint8_t c[64]; + uint32_t l[16]; + } CHAR64LONG16; + CHAR64LONG16 *block; +#ifdef SHA1HANDSOFF + static uint8_t workspace[64]; + block = (CHAR64LONG16 *)(void *)workspace; + memcpy(block, buffer, 64); +#else + block = (CHAR64LONG16 *)(void *)buffer; +#endif + /* Copy context->state[] to working vars */ + a = state[0]; + b = state[1]; + c = state[2]; + d = state[3]; + e = state[4]; + /* 4 rounds of 20 operations each. Loop unrolled. */ + R0(a, b, c, d, e, 0); + R0(e, a, b, c, d, 1); + R0(d, e, a, b, c, 2); + R0(c, d, e, a, b, 3); + R0(b, c, d, e, a, 4); + R0(a, b, c, d, e, 5); + R0(e, a, b, c, d, 6); + R0(d, e, a, b, c, 7); + R0(c, d, e, a, b, 8); + R0(b, c, d, e, a, 9); + R0(a, b, c, d, e, 10); + R0(e, a, b, c, d, 11); + R0(d, e, a, b, c, 12); + R0(c, d, e, a, b, 13); + R0(b, c, d, e, a, 14); + R0(a, b, c, d, e, 15); + R1(e, a, b, c, d, 16); + R1(d, e, a, b, c, 17); + R1(c, d, e, a, b, 18); + R1(b, c, d, e, a, 19); + R2(a, b, c, d, e, 20); + R2(e, a, b, c, d, 21); + R2(d, e, a, b, c, 22); + R2(c, d, e, a, b, 23); + R2(b, c, d, e, a, 24); + R2(a, b, c, d, e, 25); + R2(e, a, b, c, d, 26); + R2(d, e, a, b, c, 27); + R2(c, d, e, a, b, 28); + R2(b, c, d, e, a, 29); + R2(a, b, c, d, e, 30); + R2(e, a, b, c, d, 31); + R2(d, e, a, b, c, 32); + R2(c, d, e, a, b, 33); + R2(b, c, d, e, a, 34); + R2(a, b, c, d, e, 35); + R2(e, a, b, c, d, 36); + R2(d, e, a, b, c, 37); + R2(c, d, e, a, b, 38); + R2(b, c, d, e, a, 39); + R3(a, b, c, d, e, 40); + R3(e, a, b, c, d, 41); + R3(d, e, a, b, c, 42); + R3(c, d, e, a, b, 43); + R3(b, c, d, e, a, 44); + R3(a, b, c, d, e, 45); + R3(e, a, b, c, d, 46); + R3(d, e, a, b, c, 47); + R3(c, d, e, a, b, 48); + R3(b, c, d, e, a, 49); + R3(a, b, c, d, e, 50); + R3(e, a, b, c, d, 51); + R3(d, e, a, b, c, 52); + R3(c, d, e, a, b, 53); + R3(b, c, d, e, a, 54); + R3(a, b, c, d, e, 55); + R3(e, a, b, c, d, 56); + R3(d, e, a, b, c, 57); + R3(c, d, e, a, b, 58); + R3(b, c, d, e, a, 59); + R4(a, b, c, d, e, 60); + R4(e, a, b, c, d, 61); + R4(d, e, a, b, c, 62); + R4(c, d, e, a, b, 63); + R4(b, c, d, e, a, 64); + R4(a, b, c, d, e, 65); + R4(e, a, b, c, d, 66); + R4(d, e, a, b, c, 67); + R4(c, d, e, a, b, 68); + R4(b, c, d, e, a, 69); + R4(a, b, c, d, e, 70); + R4(e, a, b, c, d, 71); + R4(d, e, a, b, c, 72); + R4(c, d, e, a, b, 73); + R4(b, c, d, e, a, 74); + R4(a, b, c, d, e, 75); + R4(e, a, b, c, d, 76); + R4(d, e, a, b, c, 77); + R4(c, d, e, a, b, 78); + R4(b, c, d, e, a, 79); + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + state[4] += e; + a = b = c = d = e = 0; +} + +static void +SHA1Update(SHA1_CTX *context, uint8_t *data, unsigned int len) +{ + unsigned int i, j; + + j = (context->count[0] >> 3) & 63; + if ((context->count[0] += len << 3) < (len << 3)) + context->count[1]++; + context->count[1] += (len >> 29); + if ((j + len) > 63) + { + memcpy(&context->buffer[j], data, (i = 64 - j)); + SHA1Transform(context->state, context->buffer); + for (; i + 63 < len; i += 64) + { + SHA1Transform(context->state, &data[i]); + } + j = 0; + } + else + i = 0; + memcpy(&context->buffer[j], &data[i], len - i); +} + +static void +SHA1Final(uint8_t digest[20], SHA1_CTX *context) +{ + uint32_t i, j; + uint8_t finalcount[8]; + + for (i = 0; i < 8; i++) + { + finalcount[i] = (uint8_t)((context->count[(i >= 4 ? 0 : 1)] >> ((3 - (i & 3)) * 8)) & 255); /* Endian independent */ + } + SHA1Update(context, (uint8_t *)"\200", 1); + while ((context->count[0] & 504) != 448) + { + SHA1Update(context, (uint8_t *)"\0", 1); + } + SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform() */ + for (i = 0; i < 20; i++) + { + digest[i] = (uint8_t)((context->state[i >> 2] >> ((3 - (i & 3)) * 8)) & 255); + } + /* Wipe variables */ + i = j = 0; + memset(context->buffer, 0, 64); + memset(context->state, 0, 20); + memset(context->count, 0, 8); + memset(&finalcount, 0, 8); +#ifdef SHA1HANDSOFF /* make SHA1Transform overwrite its own static vars */ + SHA1Transform(context->state, context->buffer); +#endif +} +static void +ws_sha1_digest(char *s, int len, unsigned char *digest) +{ + SHA1_CTX sha; + + SHA1Init(&sha); + SHA1Update(&sha, (uint8_t *)s, len); + SHA1Final(digest, &sha); +} + +static void +ws_set_handshake_headers(ws_headers *headers) +{ + + size_t klen = strlen(headers->ws_key); + size_t mlen = strlen(WS_MAGIC_STR); + size_t len = klen + mlen; + char *s = xmalloc(klen + mlen + 1); + uint8_t digest[SHA_DIGEST_LENGTH]; + + memset(digest, 0, sizeof *digest); + + memcpy(s, headers->ws_key, klen); + memcpy(s + klen, WS_MAGIC_STR, mlen + 1); + + ws_sha1_digest(s, len, digest); + + headers->ws_accept = base64_encode((unsigned char *)digest, sizeof(digest)); + headers->ws_resp = xstrdup(WS_SWITCH_PROTO_STR); + + if (!headers->upgrade) + headers->upgrade = xstrdup("websocket"); + if (!headers->connection) + headers->connection = xstrdup("Upgrade"); + + free(s); +} +static void +ws_append_str(char **dest, const char *src) +{ + size_t curlen = strlen(*dest); + size_t srclen = strlen(src); + size_t newlen = curlen + srclen; + + char *str = xrealloc(*dest, newlen + 1); + memcpy(str + curlen, src, srclen + 1); + *dest = str; +} + +int ws_send_handshake_headers(tcc_stream_request_t *r, ws_headers *headers) +{ + int bytes = 0; + char *str = xstrdup(""); + + ws_append_str(&str, headers->ws_resp); + ws_append_str(&str, CRLF); + ws_append_str(&str, "Upgrade: "); + ws_append_str(&str, headers->upgrade); + ws_append_str(&str, CRLF); + ws_append_str(&str, "Connection: "); + ws_append_str(&str, headers->connection); + ws_append_str(&str, CRLF); + ws_append_str(&str, "Sec-WebSocket-Accept: "); + ws_append_str(&str, headers->ws_accept); + ws_append_str(&str, CRLF CRLF); + + bytes = proto_server_send(r, str, strlen(str)); + free(str); + return bytes; +} + +/* Allocate memory for a websocket frame */ +static WSFrame * +new_wsframe(void) +{ + WSFrame *frame = xcalloc(1, sizeof(WSFrame)); + memset(frame->buf, 0, sizeof(frame->buf)); + frame->reading = 1; + + return frame; +} + +static int ws_get_data(WSClient *client, char *buffer, int size) +{ + int len; + len = size; + if (client->msg.len < size) + { + len = client->msg.len; + } + memcpy(buffer, client->msg.data, len); + client->msg.data = client->msg.data + len; + client->msg.len = client->msg.len - len; + client->r->used_len = client->r->used_len + len; + return len; +} +static int +read_socket(WSClient *client, char *buffer, int size) +{ + int bytes = 0; + bytes = ws_get_data(client, buffer, size); + return bytes; +} + +/* Read a websocket frame's header. + * + * On success, the number of bytes read is returned. */ +static int +ws_read_header(WSClient *client, WSFrame *frm, int pos, int need) +{ + char *buf = frm->buf; + int bytes = 0; + if (client->msg.len == 0) + { + return 0; + } + + /* read the first 2 bytes for basic frame info */ + if ((bytes = read_socket(client, buf + pos, need)) < 1) + { + return bytes; + } + frm->buflen += bytes; + frm->buf[frm->buflen] = '\0'; /* null-terminate */ + + return bytes; +} +static int +ws_set_status(WSClient *client, WSStatus status, int bytes) +{ + client->status = status; + return bytes; +} + +static int +ws_set_front_header_fields(WSClient *client) +{ + WSFrame **frm = &client->frame; + char *buf = (*frm)->buf; + + (*frm)->fin = WS_FRM_FIN(*(buf)); + (*frm)->masking = WS_FRM_MASK(*(buf + 1)); + (*frm)->opcode = WS_FRM_OPCODE(*(buf)); + (*frm)->res = WS_FRM_R1(*(buf)) || WS_FRM_R2(*(buf)) || WS_FRM_R3(*(buf)); + + /* should be masked and can't be using RESVd bits */ + if (!(*frm)->masking || (*frm)->res) + { + proto_server_log(NJT_LOG_DEBUG, "tcc ws_set_front_header_fields masking=%d,res=%d", (*frm)->masking, (*frm)->res); + return ws_set_status(client, WS_ERR | WS_CLOSE, 1); + } + + return 0; +} + +/* Set the extended payload length into the given pointer. */ +static void +ws_set_extended_header_size(const char *buf, int *extended) +{ + uint64_t payloadlen = 0; + /* determine the payload length, else read more data */ + payloadlen = WS_FRM_PAYLOAD(*(buf + 1)); + switch (payloadlen) + { + case WS_PAYLOAD_EXT16: + *extended = 2; + break; + case WS_PAYLOAD_EXT64: + *extended = 8; + break; + } +} + +/* Set the masking key into our frame structure. */ +static void +ws_set_masking_key(WSFrame *frm, const char *buf) +{ + uint64_t payloadlen = 0; + + /* determine the payload length, else read more data */ + payloadlen = WS_FRM_PAYLOAD(*(buf + 1)); + switch (payloadlen) + { + case WS_PAYLOAD_EXT16: + memcpy(&frm->mask, buf + 4, sizeof(frm->mask)); + break; + case WS_PAYLOAD_EXT64: + memcpy(&frm->mask, buf + 10, sizeof(frm->mask)); + break; + default: + memcpy(&frm->mask, buf + 2, sizeof(frm->mask)); + } +} + +/* Set the extended payload length into our frame structure. */ +static void +ws_set_payloadlen(WSFrame *frm, const char *buf) +{ + uint64_t payloadlen = 0, len64; + uint16_t len16; + + /* determine the payload length, else read more data */ + payloadlen = WS_FRM_PAYLOAD(*(buf + 1)); + switch (payloadlen) + { + case WS_PAYLOAD_EXT16: + memcpy(&len16, (buf + 2), sizeof(uint16_t)); + frm->payloadlen = ntohs(len16); + break; + case WS_PAYLOAD_EXT64: + memcpy(&len64, (buf + 2), sizeof(uint64_t)); + frm->payloadlen = be64toh(len64); + break; + default: + frm->payloadlen = payloadlen; + } +} + +static int +ws_realloc_frm_payload(WSFrame *frm, WSMessage *msg) +{ + char *tmp = NULL; + uint64_t newlen = 0; + + newlen = msg->payloadsz + frm->payloadlen; + tmp = realloc(msg->payload, newlen); + if (tmp == NULL && newlen > 0) + { + free(msg->payload); + msg->payload = NULL; + return 1; + } + msg->payload = tmp; + + return 0; +} + +static void +ws_unmask_payload(char *buf, int len, int offset, unsigned char mask[]) +{ + int i, j = 0; + + /* unmask data */ + for (i = offset; i < len; ++i, ++j) + { + buf[i] ^= mask[j % 4]; + } +} + +static int +ws_error(tcc_stream_request_t *r, unsigned short code, const char *err) +{ + unsigned int len; + unsigned short code_be; + char buf[128] = {0}; + + len = 2; + code_be = htobe16(code); + memcpy(buf, &code_be, 2); + if (err) + len += snprintf(buf + 2, sizeof buf - 4, "%s", err); + + return ws_send_frame(r, WS_OPCODE_CLOSE, buf, len); +} +static int +ws_read_payload(WSClient *client, WSMessage *msg, int pos, int need) +{ + char *buf = msg->payload; + int bytes = 0; + + /* read the first 2 bytes for basic frame info */ + if ((bytes = read_socket(client, buf + pos, need)) < 1) + { + if (client->status & WS_CLOSE) + ws_error(client->r, WS_CLOSE_UNEXPECTED, "Unable to read payload"); + return bytes; + } + msg->buflen += bytes; + msg->payloadsz += bytes; + + return bytes; +} + +static void +ws_free_message(WSClient *client) +{ + if (client->message && client->message->payload) + free(client->message->payload); + if (client->message) + free(client->message); + client->message = NULL; +} + +static uint32_t +verify_utf8(uint32_t *state, const char *str, int len) +{ + int i; + uint32_t type; + + for (i = 0; i < len; ++i) + { + type = utf8d[(uint8_t)str[i]]; + *state = utf8d[256 + (*state) * 16 + type]; + + if (*state == UTF8_INVAL) + break; + } + + return *state; +} + +int ws_validate_string(const char *str, int len) +{ + uint32_t state = UTF8_VALID; + + if (verify_utf8(&state, str, len) == UTF8_INVAL) + { + return 1; + } + if (state != UTF8_VALID) + { + return 1; + } + + return 0; +} + +static int +ws_handle_err(WSClient *client, unsigned short code, WSStatus status, const char *m) +{ + client->status = status; + return ws_error(client->r, code, m); +} + +static void +ws_handle_text_bin(WSClient *client, WSServer *server) +{ + tcc_str_t content, out_data; + WSFrame **frm = &client->frame; + WSMessage **msg = &client->message; + int offset = (*msg)->mask_offset; + + if ((*frm)->opcode == WS_OPCODE_CONTINUATION) + { + // proto_server_log(NJT_LOG_DEBUG,"2 tcc websocket CONTINUATION\n"); + } + /* All data frames after the initial data frame must have opcode 0 */ + if ((*msg)->fragmented && (*frm)->opcode != WS_OPCODE_CONTINUATION) + { + client->status = WS_ERR | WS_CLOSE; + return; + } + + /* RFC states that there is a new masking key per frame, therefore, + * time to unmask... */ + ws_unmask_payload((*msg)->payload, (*msg)->payloadsz, offset, (*frm)->mask); + /* Done with the current frame's payload */ + (*msg)->buflen = 0; + /* Reading a fragmented frame */ + (*msg)->fragmented = 1; + + content.data = (*msg)->payload; + content.len = (*msg)->payloadsz; + + if (!(*frm)->fin) + { + proto_server_log(NJT_LOG_DEBUG, "tcc frm CONTINUATION ws_get_frm_payload = %V!", &content); + return; + } + else + { + proto_server_log(NJT_LOG_DEBUG, "tcc frm ws_get_frm_payload = %V!", &content); + } + // proto_server_log(NJT_LOG_DEBUG, "2 tcc ws_get_frm_payload = %V!",&content); + /* validate text data encoded as UTF-8 */ + if ((*msg)->opcode == WS_OPCODE_TEXT) + { + if (ws_validate_string((*msg)->payload, (*msg)->payloadsz) != 0) + { + ws_handle_err(client, WS_CLOSE_INVALID_UTF8, WS_ERR | WS_CLOSE, NULL); + proto_server_log(NJT_LOG_DEBUG, "3 tcc ws_get_frm_payload = %V!", &content); + return; + } + } + + if ((*msg)->opcode != WS_OPCODE_CONTINUATION) + { + // ws_write_fifo (server->pipeout, (*msg)->payload, (*msg)->payloadsz); + } + content.data = (*msg)->payload; + content.len = (*msg)->payloadsz; + + if ((*msg)->payloadsz > 0) + { + client->message->headers = &client->headers; + ws_app_on_message(client->r, client->message); + // free(out_data.data); + } + + // proto_server_log(NJT_LOG_DEBUG, "5 tcc ws_get_frm_payload = %V!", &content); + ws_free_message(client); +} + +static void +ws_handle_pong(WSClient *client) +{ + WSFrame **frm = &client->frame; + + if (!(*frm)->fin) + { + return; + } + ws_free_message(client); +} + +static int +ws_respond(tcc_stream_request_t *r, const char *buffer, int len) +{ + int bytes = 0; + // size_t length = len; + proto_server_send(r, (char *)buffer, len); + return bytes; +} + +static int +ws_send_frame(tcc_stream_request_t *r, WSOpcode opcode, const char *p, int sz) +{ + unsigned char buf[32] = {0}; + char *frm = NULL; + uint64_t payloadlen = 0, u64; + int hsize = 2; + + if (sz < 126) + { + payloadlen = sz; + } + else if (sz < (1 << 16)) + { + payloadlen = WS_PAYLOAD_EXT16; + hsize += 2; + } + else + { + payloadlen = WS_PAYLOAD_EXT64; + hsize += 8; + } + + buf[0] = 0x80 | ((uint8_t)opcode); + switch (payloadlen) + { + case WS_PAYLOAD_EXT16: + buf[1] = WS_PAYLOAD_EXT16; + buf[2] = (sz & 0xff00) >> 8; + buf[3] = (sz & 0x00ff) >> 0; + break; + case WS_PAYLOAD_EXT64: + buf[1] = WS_PAYLOAD_EXT64; + u64 = htobe64(sz); + memcpy(buf + 2, &u64, sizeof(uint64_t)); + break; + default: + buf[1] = (sz & 0xff); + } + frm = xcalloc(hsize + sz, sizeof(unsigned char)); + memcpy(frm, buf, hsize); + if (p != NULL && sz > 0) + memcpy(frm + hsize, p, sz); + + ws_respond(r, frm, hsize + sz); + free(frm); + + return 0; +} + +int ws_generate_frame(WSOpcode opcode, const char *p, int sz, tcc_str_t *out_message) +{ + unsigned char buf[32] = {0}; + char *frm = NULL; + uint64_t payloadlen = 0, u64; + int hsize = 2; + + if (sz < 126) + { + payloadlen = sz; + } + else if (sz < (1 << 16)) + { + payloadlen = WS_PAYLOAD_EXT16; + hsize += 2; + } + else + { + payloadlen = WS_PAYLOAD_EXT64; + hsize += 8; + } + + buf[0] = 0x80 | ((uint8_t)opcode); + switch (payloadlen) + { + case WS_PAYLOAD_EXT16: + buf[1] = WS_PAYLOAD_EXT16; + buf[2] = (sz & 0xff00) >> 8; + buf[3] = (sz & 0x00ff) >> 0; + break; + case WS_PAYLOAD_EXT64: + buf[1] = WS_PAYLOAD_EXT64; + u64 = htobe64(sz); + memcpy(buf + 2, &u64, sizeof(uint64_t)); + break; + default: + buf[1] = (sz & 0xff); + } + frm = xcalloc(hsize + sz, sizeof(unsigned char)); + memcpy(frm, buf, hsize); + if (p != NULL && sz > 0) + memcpy(frm + hsize, p, sz); + + out_message->data = frm; + out_message->len = hsize + sz; + + return 0; +} + +static void +ws_handle_ping(WSClient *client) +{ + WSFrame **frm = &client->frame; + tcc_str_t content; + WSMessage **msg = &client->message; + char *buf = NULL, *tmp = NULL; + int pos = 0, len = (*frm)->payloadlen, newlen = 0; + + /* RFC states that Control frames themselves MUST NOT be + * fragmented. */ + if (!(*frm)->fin) + { + ws_handle_err(client, WS_CLOSE_PROTO_ERR, WS_ERR | WS_CLOSE, NULL); + return; + } + + /* Control frames are only allowed to have payload up to and + * including 125 octets */ + if ((*frm)->payloadlen > 125) + { + ws_handle_err(client, WS_CLOSE_PROTO_ERR, WS_ERR | WS_CLOSE, NULL); + return; + } + + /* No payload from ping */ + if (len == 0) + { + // ws_send_frame(client->r, WS_OPCODE_PONG, NULL, 0); + client->message->headers = &client->headers; + ws_app_on_message(client->r, client->message); + return; + } + + /* Copy the ping payload */ + pos = (*msg)->payloadsz - len; + buf = xcalloc(len, sizeof(char)); + memcpy(buf, (*msg)->payload + pos, len); + + /* Unmask it */ + ws_unmask_payload(buf, len, 0, (*frm)->mask); + + /* Resize the current payload (keep an eye on this realloc) */ + newlen = (*msg)->payloadsz - len; + tmp = realloc((*msg)->payload, newlen); + if (tmp == NULL && newlen > 0) + { + + free((*msg)->payload); + free(buf); + + (*msg)->payload = NULL; + client->status = WS_ERR | WS_CLOSE; + return; + } + + (*msg)->payload = tmp; + (*msg)->payloadsz -= len; + + content.data = buf; + content.len = len; + // proto_server_log(NJT_LOG_DEBUG, "tcc ping!"); + + ws_send_frame(client->r, WS_OPCODE_PONG, buf, len); + // proto_server_log(NJT_LOG_DEBUG, "tcc ping len=%d,payloadsz=%d!",len,(*msg)->payloadsz); + + client->message->headers = &client->headers; + ws_app_on_message(client->r, client->message); + + (*msg)->buflen = 0; /* done with the current frame's payload */ + /* Control frame injected in the middle of a fragmented message. */ + if (!(*msg)->fragmented) + { + ws_free_message(client); + } + free(buf); +} + +static int +ws_handle_close(WSClient *client) +{ + client->status = WS_ERR | WS_CLOSE; + return ws_send_frame(client->r, WS_OPCODE_CLOSE, NULL, 0); +} + +static void +ws_manage_payload_opcode(WSClient *client, WSServer *server) +{ + WSFrame **frm = &client->frame; + WSMessage **msg = &client->message; + + switch ((*frm)->opcode) + { + case WS_OPCODE_CONTINUATION: + proto_server_log(NJT_LOG_DEBUG, "tcc websocket CONTINUATION\n"); + /* first frame can't be a continuation frame */ + if (!(*msg)->fragmented) + { + client->status = WS_ERR | WS_CLOSE; + break; + } + ws_handle_text_bin(client, server); + break; + case WS_OPCODE_TEXT: + proto_server_log(NJT_LOG_DEBUG, "tcc websocket TEXT\n"); + client->message->opcode = (*frm)->opcode; + ws_handle_text_bin(client, server); + break; + case WS_OPCODE_BIN: + proto_server_log(NJT_LOG_DEBUG, "tcc websocket BIN\n"); + client->message->opcode = (*frm)->opcode; + ws_handle_text_bin(client, server); + break; + case WS_OPCODE_PONG: + proto_server_log(NJT_LOG_DEBUG, "tcc websocket PONG\n"); + client->message->opcode = (*frm)->opcode; + ws_handle_pong(client); + break; + case WS_OPCODE_PING: + proto_server_log(NJT_LOG_DEBUG, "tcc websocket PING\n"); + client->message->opcode = (*frm)->opcode; + ws_handle_ping(client); + break; + default: + proto_server_log(NJT_LOG_DEBUG, "tcc websocket CLOSE\n"); + ws_handle_close(client); + } +} + +static void +ws_free_frame(WSClient *client) +{ + if (client->frame) + free(client->frame); + client->frame = NULL; +} + +static WSMessage * +new_wsmessage(void) +{ + WSMessage *msg = xcalloc(1, sizeof(WSMessage)); + + return msg; +} + +static int +ws_get_frm_payload(WSClient *client, WSServer *server) +{ + WSFrame **frm = NULL; + WSMessage **msg = NULL; + int bytes = 0, readh = 0, need = 0; + + if (client->message == NULL) + client->message = new_wsmessage(); + + frm = &client->frame; + msg = &client->message; + + /* message within the same frame */ + if ((*msg)->payload == NULL && (*frm)->payloadlen) + (*msg)->payload = xcalloc((*frm)->payloadlen, sizeof(char)); + /* handle a new frame */ + else if ((*msg)->buflen == 0 && (*frm)->payloadlen) + { + if (ws_realloc_frm_payload((*frm), (*msg)) == 1) + return ws_set_status(client, WS_ERR | WS_CLOSE, 0); + } + + readh = (*msg)->buflen; /* read from so far */ + need = (*frm)->payloadlen - readh; /* need to read */ + if (need > 0) + { + if ((bytes = ws_read_payload(client, (*msg), (*msg)->payloadsz, need)) < 0) + return bytes; + if (bytes != need) + return ws_set_status(client, WS_READING, bytes); + } + + (*msg)->mask_offset = (*msg)->payloadsz - (*msg)->buflen; + + ws_manage_payload_opcode(client, server); + ws_free_frame(client); + + return bytes; +} + +static int +ws_get_frm_header(WSClient *client) +{ + WSFrame **frm = NULL; + int bytes = 0, readh = 0, need = 0, offset = 0, extended = 0; + + if (client->frame == NULL) + { + client->frame = new_wsframe(); + proto_server_log(NJT_LOG_DEBUG, "tcc new_wsframe!"); + } + else + { + proto_server_log(NJT_LOG_DEBUG, "tcc client->frame->reading=%d!", client->frame->reading); + } + + frm = &client->frame; + + /* Read the first 2 bytes for basic frame info */ + readh = (*frm)->buflen; /* read from header so far */ + need = 2 - readh; /* need to read */ + if (need > 0) + { + if ((bytes = ws_read_header(client, (*frm), readh, need)) < 1) + { + // proto_server_log(NJT_LOG_DEBUG, "1 tcc read %d!",bytes); + return bytes; + } + if (bytes != need) + { + // proto_server_log(NJT_LOG_DEBUG, "2 tcc read %d!",bytes); + return ws_set_status(client, WS_READING, bytes); + } + } + offset += 2; + + if (ws_set_front_header_fields(client) != 0) + { + // proto_server_log(NJT_LOG_DEBUG, "3 tcc read %d!",bytes); + return bytes; + } + + ws_set_extended_header_size((*frm)->buf, &extended); + /* read the extended header */ + readh = (*frm)->buflen; /* read from header so far */ + need = (extended + offset) - readh; /* read from header field so far */ + if (need > 0) + { + if ((bytes = ws_read_header(client, (*frm), readh, need)) < 1) + { + // proto_server_log(NJT_LOG_DEBUG, "4 tcc read %d!",bytes); + return bytes; + } + if (bytes != need) + { + // proto_server_log(NJT_LOG_DEBUG, "5 tcc read %d!",bytes); + return ws_set_status(client, WS_READING, bytes); + } + } + offset += extended; + + /* read the masking key */ + readh = (*frm)->buflen; /* read from header so far */ + need = (4 + offset) - readh; + if (need > 0) + { + if ((bytes = ws_read_header(client, (*frm), readh, need)) < 1) + { + // proto_server_log(NJT_LOG_DEBUG, "6 tcc read %d!",bytes); + return bytes; + } + if (bytes != need) + { + // proto_server_log(NJT_LOG_DEBUG, "7 tcc read %d!",bytes); + return ws_set_status(client, WS_READING, bytes); + } + } + offset += 4; + + ws_set_payloadlen((*frm), (*frm)->buf); + ws_set_masking_key((*frm), (*frm)->buf); + + if ((*frm)->payloadlen > max_frm_size) + { + // proto_server_log(NJT_LOG_DEBUG, "8 tcc read %d!",bytes); + return ws_set_status(client, WS_ERR | WS_CLOSE, bytes); + } + + (*frm)->buflen = 0; + (*frm)->reading = 0; + (*frm)->payload_offset = offset; + // proto_server_log(NJT_LOG_DEBUG, "9 tcc read %d!",bytes); + return ws_set_status(client, WS_OK, bytes); +} + +static int +ws_get_message(WSClient *client, WSServer *server) +{ + int bytes = 0; + if ((client->frame == NULL) || (client->frame->reading)) + { + if ((bytes = ws_get_frm_header(client)) < 1 || client->frame->reading) + { + proto_server_log(NJT_LOG_DEBUG, "tcc ws_get_frm_header bytes=%d!", bytes); + return bytes; + } + } + proto_server_log(NJT_LOG_DEBUG, "tcc ws_get_frm_payload!"); + return ws_get_frm_payload(client, server); + return 1; +} + +//=============================================================== + +int proto_server_process_connection(tcc_stream_request_t *r) +{ + char ch = 'a'; + tcc_str_t ip = njt_string("127.0.0.1"); + if (ip.len == r->addr_text->len && memcmp((void *)r->addr_text->data, ip.data,ip.len) == 0) + { + proto_server_log(NJT_LOG_DEBUG, "1 tcc connetion ip=%V,NJT_STREAM_FORBIDDEN !",r->addr_text); + } + proto_server_log(NJT_LOG_DEBUG, "1 tcc connetion ip=%Xd ok!",ch); + + return NJT_OK; +} +int proto_server_process_preread(tcc_stream_request_t *r, tcc_str_t *msg) +{ + proto_server_log(NJT_LOG_DEBUG, "2 tcc preread ip=%V ok!",r->addr_text); + proto_server_log(NJT_LOG_DEBUG, "tcc preread msg=%V!",msg); + r->used_len = msg->len; + return NJT_DECLINED; +} +int proto_server_process_log(tcc_stream_request_t *r) +{ + + proto_server_log(NJT_LOG_DEBUG, "4 tcc log ip=%V ok!",r->addr_text); + return NJT_OK; +} + +int proto_server_process_message(tcc_stream_request_t *r, tcc_str_t *msg) +{ + + char *data = NULL; + WSctx *cli_ctx; + int bytes; + int rc; + char *p; + WSMessage message; + WSServer *server; + tcc_str_t end_flag = njt_string("\r\n\r\n"); + + proto_server_log(NJT_LOG_DEBUG, "3 tcc content tcc get=%V,len=%d", msg, msg->len); + + cli_ctx = tcc_get_client_ctx(r, TCC_PROTO_CTX_ID); + if (cli_ctx == NULL) + { + cli_ctx = proto_malloc(r, sizeof(WSctx)); + if(cli_ctx == NULL) { + return NJT_ERROR; + } + memset(cli_ctx, 0, sizeof(WSctx)); + cli_ctx->client.r = r; + tcc_set_client_ctx(r,TCC_PROTO_CTX_ID,cli_ctx); + } + + if (cli_ctx->handshake == 0) + { + p = njt_strlcasestrn(msg->data,msg->data + msg->len,end_flag.data,end_flag.len - 1); + if (p == NULL) + { + cli_ctx->handshake = 1; + } + } + if (cli_ctx->handshake == 0) + { + + cli_ctx->client.headers.buflen = msg->len; + + memcpy(cli_ctx->client.headers.buf, msg->data, cli_ctx->client.headers.buflen); + cli_ctx->client.headers.buf[cli_ctx->client.headers.buflen] = '\0'; + + data = cli_ctx->client.headers.buf; + + if (strstr(data, "\r\n\r\n") == NULL) + { + proto_server_log(NJT_LOG_DEBUG, "tcc http error!"); + return NJT_AGAIN; + } + if (parse_headers(&cli_ctx->client.headers) != 0) + { + proto_server_log(NJT_LOG_DEBUG, "tcc content parse_headers error!"); + return NJT_ERROR; + } + if (ws_verify_req_headers(&cli_ctx->client.headers) != 0) + { + proto_server_log(NJT_LOG_DEBUG, "tcc content ws_verify_req_headers error!"); + return NJT_ERROR; + } + ws_set_handshake_headers(&cli_ctx->client.headers); + njt_memzero(&message, sizeof(WSMessage)); + message.headers = &cli_ctx->client.headers; + rc = ws_app_on_connection(r, &message); + + + if (rc == NJT_OK) + { + cli_ctx->handshake = WS_HANDSHAKE_OK; + cli_ctx->client.r->used_len = msg->len; + proto_server_log(NJT_LOG_DEBUG, "3 tcc content WS_HANDSHAKE_OK [%p,%p]!", cli_ctx, cli_ctx->client); + } + + return NJT_OK; + } + else + { + if (msg->len > 0) + { + cli_ctx->client.msg = *msg; + server = tcc_client_get_srv_ctx(r); + bytes = ws_get_message(&cli_ctx->client, server); + } + } + + proto_server_log(NJT_LOG_DEBUG, "tcc get ws data3 msg->len=%d,used_len=%d!", msg->len, cli_ctx->client.r->used_len); + if (r->used_len != msg->len) + { + return NJT_AGAIN; + } + return NJT_OK; +} + +int proto_server_process_client_update(tcc_stream_request_t *r) +{ + ws_app_client_update(r); + return NJT_OK; +} + +int proto_server_process_connection_close(tcc_stream_request_t *r) +{ + proto_server_log(NJT_LOG_DEBUG, "tcc proto_server_process_connection_close!"); + ws_app_on_close(r); + return NJT_OK; +} +int proto_server_update(tcc_stream_server_ctx *srv_ctx) +{ + ws_app_server_update(srv_ctx); + return NJT_OK; +} + +int proto_server_init(tcc_stream_server_ctx *srv_ctx) +{ + WSServer *srv_data = proto_malloc(srv_ctx, sizeof(WSServer)); + if (srv_data != NULL) + { + tcc_set_srv_ctx(srv_ctx, srv_data); + } + ws_app_server_init(srv_ctx); + return NJT_OK; +} + +int proto_server_upstream_message(tcc_stream_request_t *r, tcc_str_t *msg){ + u_char *p; + tcc_str_t end_tok = njt_string("\r\n\r\n"); + tcc_str_t add_data = njt_string("\r\ntcc-down-set: downset"); + tcc_str_t new_msg; + tcc_str_t old_end_msg; + proto_server_log(NJT_LOG_DEBUG, "tcc from upstream msg=%V",msg); + + p = njt_strlcasestrn(msg->data,msg->data + msg->len,end_tok.data,end_tok.len - 1); + if (p == NULL) { + proto_server_send(r,msg->data,msg->len); + } else { + new_msg.data = msg->data; + new_msg.len = p - msg->data; + old_end_msg.data = p; + old_end_msg.len = msg->data + msg->len - old_end_msg.data; + proto_server_send(r,new_msg.data,new_msg.len); + proto_server_log(NJT_LOG_DEBUG, "tcc send client msg1=%V",&new_msg); + proto_server_send(r,add_data.data,add_data.len); + proto_server_log(NJT_LOG_DEBUG, "tcc send client msg2=%V",&add_data); + proto_server_send(r,old_end_msg.data,old_end_msg.len); + proto_server_log(NJT_LOG_DEBUG, "tcc send client len=%d,msg3=%V",old_end_msg.len,&old_end_msg); + } + r->used_len = msg->len; + return NJT_OK; +} + +void* proto_server_check_upstream_peer(tcc_stream_client_upstream_data_t *cli_ups_info) { + int i; + tcc_uint_t weight; + int mask_len = 7; + tcc_stream_upstream_rr_peer_t *peer_list = cli_ups_info->peer_list; + for(i = 0; i < cli_ups_info->peer_num; i++) { + if(peer_list[i].server->len >= mask_len && cli_ups_info->cli_addr_text->len >= mask_len && njt_memcmp(peer_list[i].server->data,cli_ups_info->cli_addr_text->data,mask_len) == 0) { + weight = proto_get_peer_weight(peer_list[i].peer); + proto_server_log(NJT_LOG_DEBUG, "1 tcc upstream server=%V,%d!",peer_list[i].server,weight); + return peer_list[i].peer; + } + } + if(cli_ups_info->peer_num > 0) { + i = 0; + weight = proto_get_peer_weight(peer_list[i].peer); + proto_server_log(NJT_LOG_DEBUG, "default tcc upstream server=%V,%d!",peer_list[i].server,weight); + return peer_list[i].peer; + } + return NULL; // NJT_BUSY; +} + + + +int proto_server_upstream_connection_close(tcc_stream_request_t *r) { + proto_server_log(NJT_LOG_DEBUG, "tcc proto_server_upstream_connection_close!"); +} diff --git a/modules/njet-stream-proto-server-module/src/ws_proto_server_py.c b/modules/njet-stream-proto-server-module/src/ws_proto_server_py.c new file mode 100644 index 0000000000000000000000000000000000000000..097916b4377bdf0a1ee7ac8a326628ab6ab7abb5 --- /dev/null +++ b/modules/njet-stream-proto-server-module/src/ws_proto_server_py.c @@ -0,0 +1,1536 @@ +#include +#include +#include +#include +#include + +int max_frm_size = 6553500; +typedef struct +{ + uint32_t state[5]; + uint32_t count[2]; + uint8_t buffer[64]; +} SHA1_CTX; +#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) + +#ifdef LITTLE_ENDIAN +#define blk0(i) (block->l[i] = (rol(block->l[i], 24) & 0xFF00FF00) | (rol(block->l[i], 8) & 0x00FF00FF)) +#else +#define blk0(i) block->l[i] +#endif +#define blk(i) (block->l[i & 15] = rol(block->l[(i + 13) & 15] ^ block->l[(i + 8) & 15] ^ block->l[(i + 2) & 15] ^ block->l[i & 15], 1)) + +/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */ +#define R0(v, w, x, y, z, i) \ + z += ((w & (x ^ y)) ^ y) + blk0(i) + 0x5A827999 + rol(v, 5); \ + w = rol(w, 30); +#define R1(v, w, x, y, z, i) \ + z += ((w & (x ^ y)) ^ y) + blk(i) + 0x5A827999 + rol(v, 5); \ + w = rol(w, 30); +#define R2(v, w, x, y, z, i) \ + z += (w ^ x ^ y) + blk(i) + 0x6ED9EBA1 + rol(v, 5); \ + w = rol(w, 30); +#define R3(v, w, x, y, z, i) \ + z += (((w | x) & y) | (w & x)) + blk(i) + 0x8F1BBCDC + rol(v, 5); \ + w = rol(w, 30); +#define R4(v, w, x, y, z, i) \ + z += (w ^ x ^ y) + blk(i) + 0xCA62C1D6 + rol(v, 5); \ + w = rol(w, 30); + +#define CRLF "\r\n" +#define WS_MAGIC_STR "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" +#define WS_SWITCH_PROTO_STR "HTTP/1.1 101 Switching Protocols" +#ifndef SHA_DIGEST_LENGTH +#define SHA_DIGEST_LENGTH 20 +#endif + +void * +xmalloc(size_t size) +{ + void *ptr; + + if ((ptr = malloc(size)) == NULL) + proto_server_log(NJT_LOG_DEBUG, "Unable to allocate memory - failed."); + + return (ptr); +} + +char * +xstrdup(const char *s) +{ + char *ptr; + size_t len; + + len = strlen(s) + 1; + ptr = xmalloc(len); + + strncpy(ptr, s, len); + return (ptr); +} + +/* Self-checking wrapper to calloc() */ +void * +xcalloc(size_t nmemb, size_t size) +{ + void *ptr; + + if ((ptr = calloc(nmemb, size)) == NULL) + proto_server_log(NJT_LOG_DEBUG, "Unable to calloc memory - failed."); + + return (ptr); +} + +void * +xrealloc(void *oldptr, size_t size) +{ + void *newptr; + + if ((newptr = realloc(oldptr, size)) == NULL) + proto_server_log(NJT_LOG_DEBUG, "Unable to reallocate memory - failed"); + + return (newptr); +} + +static char * +strtoupper(char *str) +{ + char *p = str; + if (str == NULL || *str == '\0') + return str; + + while (*p != '\0') + { + *p = toupper((int)*p); + p++; + } + + return str; +} + +static const char * +ws_get_method(const char *token) +{ + const char *lookfor = NULL; + + if ((lookfor = "GET", !memcmp(token, "GET ", 4)) || + (lookfor = "get", !memcmp(token, "get ", 4))) + return lookfor; + return NULL; +} + +char * +base64_encode(const void *buf, size_t size) +{ + static const char base64[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + + char *str = (char *)xmalloc((size + 3) * 4 / 3 + 1); + + char *p = str; + const unsigned char *q = (const unsigned char *)buf; + size_t i = 0; + + while (i < size) + { + int c = q[i++]; + c *= 256; + if (i < size) + c += q[i]; + i++; + + c *= 256; + if (i < size) + c += q[i]; + i++; + + *p++ = base64[(c & 0x00fc0000) >> 18]; + *p++ = base64[(c & 0x0003f000) >> 12]; + + if (i > size + 1) + *p++ = '='; + else + *p++ = base64[(c & 0x00000fc0) >> 6]; + + if (i > size) + *p++ = '='; + else + *p++ = base64[c & 0x0000003f]; + } + + *p = 0; + + return str; +} + +static void +ws_set_header_key_value(ws_headers *headers, char *key, char *value) +{ + if (strcasecmp("Host", key) == 0) + headers->host = xstrdup(value); + else if (strcasecmp("Origin", key) == 0) + headers->origin = xstrdup(value); + else if (strcasecmp("Upgrade", key) == 0) + headers->upgrade = xstrdup(value); + else if (strcasecmp("Connection", key) == 0) + headers->connection = xstrdup(value); + else if (strcasecmp("Sec-WebSocket-Protocol", key) == 0) + headers->ws_protocol = xstrdup(value); + else if (strcasecmp("Sec-WebSocket-Key", key) == 0) + headers->ws_key = xstrdup(value); + else if (strcasecmp("Sec-WebSocket-Version", key) == 0) + headers->ws_sock_ver = xstrdup(value); + else if (strcasecmp("User-Agent", key) == 0) + headers->agent = xstrdup(value); + else if (strcasecmp("Referer", key) == 0) + headers->referer = xstrdup(value); +} + +static char * +ws_parse_request(char *line, char **method, char **protocol) +{ + const char *meth; + char *req = NULL, *request = NULL, *proto = NULL; + ptrdiff_t rlen; + + if ((meth = ws_get_method(line)) == NULL) + { + return NULL; + } + else + { + req = line + strlen(meth); + if ((proto = strstr(line, " HTTP/1.0")) == NULL && + (proto = strstr(line, " HTTP/1.1")) == NULL) + return NULL; + + req++; + if ((rlen = proto - req) <= 0) + return NULL; + + request = xmalloc(rlen + 1); + strncpy(request, req, rlen); + request[rlen] = 0; + + (*method) = strtoupper(xstrdup(meth)); + (*protocol) = strtoupper(xstrdup(++proto)); + } + + return request; +} + +static int +ws_set_header_fields(char *line, ws_headers *headers) +{ + char *path = NULL, *method = NULL, *proto = NULL, *p, *value; + + if (line[0] == '\n' || line[0] == '\r') + return 1; + + if ((strstr(line, "GET ")) || (strstr(line, "get "))) + { + if ((path = ws_parse_request(line, &method, &proto)) == NULL) + return 1; + headers->path = path; + headers->method = method; + headers->protocol = proto; + + return 0; + } + + if ((p = strchr(line, ':')) == NULL) + return 1; + + value = p + 1; + while (p != line && isspace((unsigned char)*(p - 1))) + p--; + + if (p == line) + return 1; + + *p = '\0'; + if (strpbrk(line, " \t") != NULL) + { + *p = ' '; + return 1; + } + while (isspace((unsigned char)*value)) + value++; + + ws_set_header_key_value(headers, line, value); + + return 0; +} + +static int +parse_headers(ws_headers *headers) +{ + char *tmp = NULL; + const char *buffer = headers->buf; + const char *line = buffer, *next = NULL; + int len = 0; + + while (line) + { + if ((next = strstr(line, "\r\n")) != NULL) + len = (next - line); + else + len = strlen(line); + + if (len <= 0) + { + proto_server_log(NJT_LOG_DEBUG, "1 tcc content parse_headers error!"); + return 1; + } + + tmp = xmalloc(len + 1); + memcpy(tmp, line, len); + tmp[len] = '\0'; + + if (ws_set_header_fields(tmp, headers) == 1) + { + free(tmp); + proto_server_log(NJT_LOG_DEBUG, "2 tcc content parse_headers error!"); + return 1; + } + + free(tmp); + line = next ? (next + 2) : NULL; + + if (next && strcmp(next, "\r\n\r\n") == 0) + break; + } + + return 0; +} +static int +ws_verify_req_headers(ws_headers *headers) +{ + if (!headers->host) + return 1; + if (!headers->method) + return 1; + if (!headers->protocol) + return 1; + if (!headers->path) + return 1; + if (!headers->connection) + return 1; + if (!headers->ws_key) + return 1; + if (!headers->ws_sock_ver) + return 1; + return 0; +} +static void +SHA1Init(SHA1_CTX *context) +{ + context->state[0] = 0x67452301; + context->state[1] = 0xEFCDAB89; + context->state[2] = 0x98BADCFE; + context->state[3] = 0x10325476; + context->state[4] = 0xC3D2E1F0; + context->count[0] = context->count[1] = 0; +} +void SHA1Transform(uint32_t state[5], uint8_t buffer[64]) +{ + uint32_t a, b, c, d, e; + typedef union + { + uint8_t c[64]; + uint32_t l[16]; + } CHAR64LONG16; + CHAR64LONG16 *block; +#ifdef SHA1HANDSOFF + static uint8_t workspace[64]; + block = (CHAR64LONG16 *)(void *)workspace; + memcpy(block, buffer, 64); +#else + block = (CHAR64LONG16 *)(void *)buffer; +#endif + /* Copy context->state[] to working vars */ + a = state[0]; + b = state[1]; + c = state[2]; + d = state[3]; + e = state[4]; + /* 4 rounds of 20 operations each. Loop unrolled. */ + R0(a, b, c, d, e, 0); + R0(e, a, b, c, d, 1); + R0(d, e, a, b, c, 2); + R0(c, d, e, a, b, 3); + R0(b, c, d, e, a, 4); + R0(a, b, c, d, e, 5); + R0(e, a, b, c, d, 6); + R0(d, e, a, b, c, 7); + R0(c, d, e, a, b, 8); + R0(b, c, d, e, a, 9); + R0(a, b, c, d, e, 10); + R0(e, a, b, c, d, 11); + R0(d, e, a, b, c, 12); + R0(c, d, e, a, b, 13); + R0(b, c, d, e, a, 14); + R0(a, b, c, d, e, 15); + R1(e, a, b, c, d, 16); + R1(d, e, a, b, c, 17); + R1(c, d, e, a, b, 18); + R1(b, c, d, e, a, 19); + R2(a, b, c, d, e, 20); + R2(e, a, b, c, d, 21); + R2(d, e, a, b, c, 22); + R2(c, d, e, a, b, 23); + R2(b, c, d, e, a, 24); + R2(a, b, c, d, e, 25); + R2(e, a, b, c, d, 26); + R2(d, e, a, b, c, 27); + R2(c, d, e, a, b, 28); + R2(b, c, d, e, a, 29); + R2(a, b, c, d, e, 30); + R2(e, a, b, c, d, 31); + R2(d, e, a, b, c, 32); + R2(c, d, e, a, b, 33); + R2(b, c, d, e, a, 34); + R2(a, b, c, d, e, 35); + R2(e, a, b, c, d, 36); + R2(d, e, a, b, c, 37); + R2(c, d, e, a, b, 38); + R2(b, c, d, e, a, 39); + R3(a, b, c, d, e, 40); + R3(e, a, b, c, d, 41); + R3(d, e, a, b, c, 42); + R3(c, d, e, a, b, 43); + R3(b, c, d, e, a, 44); + R3(a, b, c, d, e, 45); + R3(e, a, b, c, d, 46); + R3(d, e, a, b, c, 47); + R3(c, d, e, a, b, 48); + R3(b, c, d, e, a, 49); + R3(a, b, c, d, e, 50); + R3(e, a, b, c, d, 51); + R3(d, e, a, b, c, 52); + R3(c, d, e, a, b, 53); + R3(b, c, d, e, a, 54); + R3(a, b, c, d, e, 55); + R3(e, a, b, c, d, 56); + R3(d, e, a, b, c, 57); + R3(c, d, e, a, b, 58); + R3(b, c, d, e, a, 59); + R4(a, b, c, d, e, 60); + R4(e, a, b, c, d, 61); + R4(d, e, a, b, c, 62); + R4(c, d, e, a, b, 63); + R4(b, c, d, e, a, 64); + R4(a, b, c, d, e, 65); + R4(e, a, b, c, d, 66); + R4(d, e, a, b, c, 67); + R4(c, d, e, a, b, 68); + R4(b, c, d, e, a, 69); + R4(a, b, c, d, e, 70); + R4(e, a, b, c, d, 71); + R4(d, e, a, b, c, 72); + R4(c, d, e, a, b, 73); + R4(b, c, d, e, a, 74); + R4(a, b, c, d, e, 75); + R4(e, a, b, c, d, 76); + R4(d, e, a, b, c, 77); + R4(c, d, e, a, b, 78); + R4(b, c, d, e, a, 79); + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + state[4] += e; + a = b = c = d = e = 0; +} + +static void +SHA1Update(SHA1_CTX *context, uint8_t *data, unsigned int len) +{ + unsigned int i, j; + + j = (context->count[0] >> 3) & 63; + if ((context->count[0] += len << 3) < (len << 3)) + context->count[1]++; + context->count[1] += (len >> 29); + if ((j + len) > 63) + { + memcpy(&context->buffer[j], data, (i = 64 - j)); + SHA1Transform(context->state, context->buffer); + for (; i + 63 < len; i += 64) + { + SHA1Transform(context->state, &data[i]); + } + j = 0; + } + else + i = 0; + memcpy(&context->buffer[j], &data[i], len - i); +} + +static void +SHA1Final(uint8_t digest[20], SHA1_CTX *context) +{ + uint32_t i, j; + uint8_t finalcount[8]; + + for (i = 0; i < 8; i++) + { + finalcount[i] = (uint8_t)((context->count[(i >= 4 ? 0 : 1)] >> ((3 - (i & 3)) * 8)) & 255); /* Endian independent */ + } + SHA1Update(context, (uint8_t *)"\200", 1); + while ((context->count[0] & 504) != 448) + { + SHA1Update(context, (uint8_t *)"\0", 1); + } + SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform() */ + for (i = 0; i < 20; i++) + { + digest[i] = (uint8_t)((context->state[i >> 2] >> ((3 - (i & 3)) * 8)) & 255); + } + /* Wipe variables */ + i = j = 0; + memset(context->buffer, 0, 64); + memset(context->state, 0, 20); + memset(context->count, 0, 8); + memset(&finalcount, 0, 8); +#ifdef SHA1HANDSOFF /* make SHA1Transform overwrite its own static vars */ + SHA1Transform(context->state, context->buffer); +#endif +} +static void +ws_sha1_digest(char *s, int len, unsigned char *digest) +{ + SHA1_CTX sha; + + SHA1Init(&sha); + SHA1Update(&sha, (uint8_t *)s, len); + SHA1Final(digest, &sha); +} + +static void +ws_set_handshake_headers(ws_headers *headers) +{ + + size_t klen = strlen(headers->ws_key); + size_t mlen = strlen(WS_MAGIC_STR); + size_t len = klen + mlen; + char *s = xmalloc(klen + mlen + 1); + uint8_t digest[SHA_DIGEST_LENGTH]; + + memset(digest, 0, sizeof *digest); + + memcpy(s, headers->ws_key, klen); + memcpy(s + klen, WS_MAGIC_STR, mlen + 1); + + ws_sha1_digest(s, len, digest); + + headers->ws_accept = base64_encode((unsigned char *)digest, sizeof(digest)); + headers->ws_resp = xstrdup(WS_SWITCH_PROTO_STR); + + if (!headers->upgrade) + headers->upgrade = xstrdup("websocket"); + if (!headers->connection) + headers->connection = xstrdup("Upgrade"); + + free(s); +} +static void +ws_append_str(char **dest, const char *src) +{ + size_t curlen = strlen(*dest); + size_t srclen = strlen(src); + size_t newlen = curlen + srclen; + + char *str = xrealloc(*dest, newlen + 1); + memcpy(str + curlen, src, srclen + 1); + *dest = str; +} + +int ws_send_handshake_headers(tcc_stream_request_t *r, ws_headers *headers) +{ + int bytes = 0; + char *str = xstrdup(""); + + ws_append_str(&str, headers->ws_resp); + ws_append_str(&str, CRLF); + ws_append_str(&str, "Upgrade: "); + ws_append_str(&str, headers->upgrade); + ws_append_str(&str, CRLF); + ws_append_str(&str, "Connection: "); + ws_append_str(&str, headers->connection); + ws_append_str(&str, CRLF); + ws_append_str(&str, "Sec-WebSocket-Accept: "); + ws_append_str(&str, headers->ws_accept); + ws_append_str(&str, CRLF CRLF); + + bytes = proto_server_send(r, str, strlen(str)); + free(str); + return bytes; +} + +/* Allocate memory for a websocket frame */ +static WSFrame * +new_wsframe(void) +{ + WSFrame *frame = xcalloc(1, sizeof(WSFrame)); + memset(frame->buf, 0, sizeof(frame->buf)); + frame->reading = 1; + + return frame; +} + +static int ws_get_data(WSClient *client, char *buffer, int size) +{ + int len; + len = size; + if (client->msg.len < size) + { + len = client->msg.len; + } + memcpy(buffer, client->msg.data, len); + client->msg.data = client->msg.data + len; + client->msg.len = client->msg.len - len; + client->r->used_len = client->r->used_len + len; + return len; +} +static int +read_socket(WSClient *client, char *buffer, int size) +{ + int bytes = 0; + bytes = ws_get_data(client, buffer, size); + return bytes; +} + +/* Read a websocket frame's header. + * + * On success, the number of bytes read is returned. */ +static int +ws_read_header(WSClient *client, WSFrame *frm, int pos, int need) +{ + char *buf = frm->buf; + int bytes = 0; + if (client->msg.len == 0) + { + return 0; + } + + /* read the first 2 bytes for basic frame info */ + if ((bytes = read_socket(client, buf + pos, need)) < 1) + { + return bytes; + } + frm->buflen += bytes; + frm->buf[frm->buflen] = '\0'; /* null-terminate */ + + return bytes; +} +static int +ws_set_status(WSClient *client, WSStatus status, int bytes) +{ + client->status = status; + return bytes; +} + +static int +ws_set_front_header_fields(WSClient *client) +{ + WSFrame **frm = &client->frame; + char *buf = (*frm)->buf; + + (*frm)->fin = WS_FRM_FIN(*(buf)); + (*frm)->masking = WS_FRM_MASK(*(buf + 1)); + (*frm)->opcode = WS_FRM_OPCODE(*(buf)); + (*frm)->res = WS_FRM_R1(*(buf)) || WS_FRM_R2(*(buf)) || WS_FRM_R3(*(buf)); + + /* should be masked and can't be using RESVd bits */ + if (!(*frm)->masking || (*frm)->res) + { + proto_server_log(NJT_LOG_DEBUG, "tcc ws_set_front_header_fields masking=%d,res=%d", (*frm)->masking, (*frm)->res); + return ws_set_status(client, WS_ERR | WS_CLOSE, 1); + } + + return 0; +} + +/* Set the extended payload length into the given pointer. */ +static void +ws_set_extended_header_size(const char *buf, int *extended) +{ + uint64_t payloadlen = 0; + /* determine the payload length, else read more data */ + payloadlen = WS_FRM_PAYLOAD(*(buf + 1)); + switch (payloadlen) + { + case WS_PAYLOAD_EXT16: + *extended = 2; + break; + case WS_PAYLOAD_EXT64: + *extended = 8; + break; + } +} + +/* Set the masking key into our frame structure. */ +static void +ws_set_masking_key(WSFrame *frm, const char *buf) +{ + uint64_t payloadlen = 0; + + /* determine the payload length, else read more data */ + payloadlen = WS_FRM_PAYLOAD(*(buf + 1)); + switch (payloadlen) + { + case WS_PAYLOAD_EXT16: + memcpy(&frm->mask, buf + 4, sizeof(frm->mask)); + break; + case WS_PAYLOAD_EXT64: + memcpy(&frm->mask, buf + 10, sizeof(frm->mask)); + break; + default: + memcpy(&frm->mask, buf + 2, sizeof(frm->mask)); + } +} + +/* Set the extended payload length into our frame structure. */ +static void +ws_set_payloadlen(WSFrame *frm, const char *buf) +{ + uint64_t payloadlen = 0, len64; + uint16_t len16; + + /* determine the payload length, else read more data */ + payloadlen = WS_FRM_PAYLOAD(*(buf + 1)); + switch (payloadlen) + { + case WS_PAYLOAD_EXT16: + memcpy(&len16, (buf + 2), sizeof(uint16_t)); + frm->payloadlen = ntohs(len16); + break; + case WS_PAYLOAD_EXT64: + memcpy(&len64, (buf + 2), sizeof(uint64_t)); + frm->payloadlen = be64toh(len64); + break; + default: + frm->payloadlen = payloadlen; + } +} + +static int +ws_realloc_frm_payload(WSFrame *frm, WSMessage *msg) +{ + char *tmp = NULL; + uint64_t newlen = 0; + + newlen = msg->payloadsz + frm->payloadlen; + tmp = realloc(msg->payload, newlen); + if (tmp == NULL && newlen > 0) + { + free(msg->payload); + msg->payload = NULL; + return 1; + } + msg->payload = tmp; + + return 0; +} + +static void +ws_unmask_payload(char *buf, int len, int offset, unsigned char mask[]) +{ + int i, j = 0; + + /* unmask data */ + for (i = offset; i < len; ++i, ++j) + { + buf[i] ^= mask[j % 4]; + } +} + +static int +ws_error(tcc_stream_request_t *r, unsigned short code, const char *err) +{ + unsigned int len; + unsigned short code_be; + char buf[128] = {0}; + + len = 2; + code_be = htobe16(code); + memcpy(buf, &code_be, 2); + if (err) + len += snprintf(buf + 2, sizeof buf - 4, "%s", err); + + return ws_send_frame(r, WS_OPCODE_CLOSE, buf, len); +} +static int +ws_read_payload(WSClient *client, WSMessage *msg, int pos, int need) +{ + char *buf = msg->payload; + int bytes = 0; + + /* read the first 2 bytes for basic frame info */ + if ((bytes = read_socket(client, buf + pos, need)) < 1) + { + if (client->status & WS_CLOSE) + ws_error(client->r, WS_CLOSE_UNEXPECTED, "Unable to read payload"); + return bytes; + } + msg->buflen += bytes; + msg->payloadsz += bytes; + + return bytes; +} + +static void +ws_free_message(WSClient *client) +{ + if (client->message && client->message->payload) + free(client->message->payload); + if (client->message) + free(client->message); + client->message = NULL; +} + +static uint32_t +verify_utf8(uint32_t *state, const char *str, int len) +{ + int i; + uint32_t type; + + for (i = 0; i < len; ++i) + { + type = utf8d[(uint8_t)str[i]]; + *state = utf8d[256 + (*state) * 16 + type]; + + if (*state == UTF8_INVAL) + break; + } + + return *state; +} + +int ws_validate_string(const char *str, int len) +{ + uint32_t state = UTF8_VALID; + + if (verify_utf8(&state, str, len) == UTF8_INVAL) + { + return 1; + } + if (state != UTF8_VALID) + { + return 1; + } + + return 0; +} + +static int +ws_handle_err(WSClient *client, unsigned short code, WSStatus status, const char *m) +{ + client->status = status; + return ws_error(client->r, code, m); +} + +static void +ws_handle_text_bin(WSClient *client, WSServer *server) +{ + tcc_str_t content, out_data; + WSFrame **frm = &client->frame; + WSMessage **msg = &client->message; + int offset = (*msg)->mask_offset; + + // if ((*frm)->opcode == WS_OPCODE_CONTINUATION) + // { + // proto_server_log(NJT_LOG_DEBUG,"2 tcc websocket CONTINUATION\n"); + // } + /* All data frames after the initial data frame must have opcode 0 */ + if ((*msg)->fragmented && (*frm)->opcode != WS_OPCODE_CONTINUATION) + { + client->status = WS_ERR | WS_CLOSE; + return; + } + + /* RFC states that there is a new masking key per frame, therefore, + * time to unmask... */ + ws_unmask_payload((*msg)->payload, (*msg)->payloadsz, offset, (*frm)->mask); + /* Done with the current frame's payload */ + (*msg)->buflen = 0; + /* Reading a fragmented frame */ + (*msg)->fragmented = 1; + + content.data = (*msg)->payload; + content.len = (*msg)->payloadsz; + + if (!(*frm)->fin) + { + proto_server_log(NJT_LOG_DEBUG, "tcc frm CONTINUATION ws_get_frm_payload = %V!", &content); + return; + } + else + { + proto_server_log(NJT_LOG_DEBUG, "tcc frm ws_get_frm_payload = %V!", &content); + } + // proto_server_log(NJT_LOG_DEBUG, "2 tcc ws_get_frm_payload = %V!",&content); + /* validate text data encoded as UTF-8 */ + if ((*msg)->opcode == WS_OPCODE_TEXT) + { + if (ws_validate_string((*msg)->payload, (*msg)->payloadsz) != 0) + { + ws_handle_err(client, WS_CLOSE_INVALID_UTF8, WS_ERR | WS_CLOSE, NULL); + proto_server_log(NJT_LOG_DEBUG, "3 tcc ws_get_frm_payload = %V!", &content); + return; + } + } + + // if ((*msg)->opcode != WS_OPCODE_CONTINUATION) + // { + // ws_write_fifo (server->pipeout, (*msg)->payload, (*msg)->payloadsz); + // } + content.data = (*msg)->payload; + content.len = (*msg)->payloadsz; + + if ((*msg)->payloadsz > 0) + { + client->message->headers = &client->headers; + ws_app_on_message(client->r, client->message); + } + + ws_free_message(client); +} + +static void +ws_handle_pong(WSClient *client) +{ + WSFrame **frm = &client->frame; + + if (!(*frm)->fin) + { + return; + } + ws_free_message(client); +} + +static int +ws_respond(tcc_stream_request_t *r, const char *buffer, int len) +{ + int bytes = 0; + proto_server_send(r, (char *)buffer, len); + return bytes; +} + +static int +ws_send_frame(tcc_stream_request_t *r, WSOpcode opcode, const char *p, int sz) +{ + unsigned char buf[32] = {0}; + char *frm = NULL; + uint64_t payloadlen = 0, u64; + int hsize = 2; + + if (sz < 126) + { + payloadlen = sz; + } + else if (sz < (1 << 16)) + { + payloadlen = WS_PAYLOAD_EXT16; + hsize += 2; + } + else + { + payloadlen = WS_PAYLOAD_EXT64; + hsize += 8; + } + + buf[0] = 0x80 | ((uint8_t)opcode); + switch (payloadlen) + { + case WS_PAYLOAD_EXT16: + buf[1] = WS_PAYLOAD_EXT16; + buf[2] = (sz & 0xff00) >> 8; + buf[3] = (sz & 0x00ff) >> 0; + break; + case WS_PAYLOAD_EXT64: + buf[1] = WS_PAYLOAD_EXT64; + u64 = htobe64(sz); + memcpy(buf + 2, &u64, sizeof(uint64_t)); + break; + default: + buf[1] = (sz & 0xff); + } + frm = xcalloc(hsize + sz, sizeof(unsigned char)); + memcpy(frm, buf, hsize); + if (p != NULL && sz > 0) + memcpy(frm + hsize, p, sz); + + ws_respond(r, frm, hsize + sz); + free(frm); + + return 0; +} + +int ws_generate_frame(WSOpcode opcode, const char *p, int sz, tcc_str_t *out_message) +{ + unsigned char buf[32] = {0}; + char *frm = NULL; + uint64_t payloadlen = 0, u64; + int hsize = 2; + + if (sz < 126) + { + payloadlen = sz; + } + else if (sz < (1 << 16)) + { + payloadlen = WS_PAYLOAD_EXT16; + hsize += 2; + } + else + { + payloadlen = WS_PAYLOAD_EXT64; + hsize += 8; + } + + buf[0] = 0x80 | ((uint8_t)opcode); + switch (payloadlen) + { + case WS_PAYLOAD_EXT16: + buf[1] = WS_PAYLOAD_EXT16; + buf[2] = (sz & 0xff00) >> 8; + buf[3] = (sz & 0x00ff) >> 0; + break; + case WS_PAYLOAD_EXT64: + buf[1] = WS_PAYLOAD_EXT64; + u64 = htobe64(sz); + memcpy(buf + 2, &u64, sizeof(uint64_t)); + break; + default: + buf[1] = (sz & 0xff); + } + frm = xcalloc(hsize + sz, sizeof(unsigned char)); + memcpy(frm, buf, hsize); + if (p != NULL && sz > 0) + memcpy(frm + hsize, p, sz); + + out_message->data = frm; + out_message->len = hsize + sz; + + return 0; +} + +static void +ws_handle_ping(WSClient *client) +{ + WSFrame **frm = &client->frame; + tcc_str_t content; + WSMessage **msg = &client->message; + char *buf = NULL, *tmp = NULL; + int pos = 0, len = (*frm)->payloadlen, newlen = 0; + + /* RFC states that Control frames themselves MUST NOT be + * fragmented. */ + if (!(*frm)->fin) + { + ws_handle_err(client, WS_CLOSE_PROTO_ERR, WS_ERR | WS_CLOSE, NULL); + return; + } + + /* Control frames are only allowed to have payload up to and + * including 125 octets */ + if ((*frm)->payloadlen > 125) + { + ws_handle_err(client, WS_CLOSE_PROTO_ERR, WS_ERR | WS_CLOSE, NULL); + return; + } + + /* No payload from ping */ + if (len == 0) + { + // ws_send_frame(client->r, WS_OPCODE_PONG, NULL, 0); + client->message->headers = &client->headers; + ws_app_on_message(client->r, client->message); + return; + } + + /* Copy the ping payload */ + pos = (*msg)->payloadsz - len; + buf = xcalloc(len, sizeof(char)); + memcpy(buf, (*msg)->payload + pos, len); + + /* Unmask it */ + ws_unmask_payload(buf, len, 0, (*frm)->mask); + + /* Resize the current payload (keep an eye on this realloc) */ + newlen = (*msg)->payloadsz - len; + tmp = realloc((*msg)->payload, newlen); + if (tmp == NULL && newlen > 0) + { + + free((*msg)->payload); + free(buf); + + (*msg)->payload = NULL; + client->status = WS_ERR | WS_CLOSE; + return; + } + + (*msg)->payload = tmp; + (*msg)->payloadsz -= len; + + content.data = buf; + content.len = len; + // proto_server_log(NJT_LOG_DEBUG, "tcc ping!"); + + ws_send_frame(client->r, WS_OPCODE_PONG, buf, len); + // proto_server_log(NJT_LOG_DEBUG, "tcc ping len=%d,payloadsz=%d!",len,(*msg)->payloadsz); + + client->message->headers = &client->headers; + ws_app_on_message(client->r, client->message); + + (*msg)->buflen = 0; /* done with the current frame's payload */ + /* Control frame injected in the middle of a fragmented message. */ + if (!(*msg)->fragmented) + { + ws_free_message(client); + } + free(buf); +} + +static int +ws_handle_close(WSClient *client) +{ + client->status = WS_ERR | WS_CLOSE; + return ws_send_frame(client->r, WS_OPCODE_CLOSE, NULL, 0); +} + +static void +ws_manage_payload_opcode(WSClient *client, WSServer *server) +{ + WSFrame **frm = &client->frame; + WSMessage **msg = &client->message; + + switch ((*frm)->opcode) + { + case WS_OPCODE_CONTINUATION: + proto_server_log(NJT_LOG_DEBUG, "tcc websocket CONTINUATION\n"); + /* first frame can't be a continuation frame */ + if (!(*msg)->fragmented) + { + client->status = WS_ERR | WS_CLOSE; + break; + } + ws_handle_text_bin(client, server); + break; + case WS_OPCODE_TEXT: + proto_server_log(NJT_LOG_DEBUG, "tcc websocket TEXT\n"); + client->message->opcode = (*frm)->opcode; + ws_handle_text_bin(client, server); + break; + case WS_OPCODE_BIN: + proto_server_log(NJT_LOG_DEBUG, "tcc websocket BIN\n"); + client->message->opcode = (*frm)->opcode; + ws_handle_text_bin(client, server); + break; + case WS_OPCODE_PONG: + proto_server_log(NJT_LOG_DEBUG, "tcc websocket PONG\n"); + client->message->opcode = (*frm)->opcode; + ws_handle_pong(client); + break; + case WS_OPCODE_PING: + proto_server_log(NJT_LOG_DEBUG, "tcc websocket PING\n"); + client->message->opcode = (*frm)->opcode; + ws_handle_ping(client); + break; + default: + proto_server_log(NJT_LOG_DEBUG, "tcc websocket CLOSE\n"); + ws_handle_close(client); + } +} + +static void +ws_free_frame(WSClient *client) +{ + if (client->frame) + free(client->frame); + client->frame = NULL; +} + +static WSMessage * +new_wsmessage(void) +{ + WSMessage *msg = xcalloc(1, sizeof(WSMessage)); + + return msg; +} + +static int +ws_get_frm_payload(WSClient *client, WSServer *server) +{ + WSFrame **frm = NULL; + WSMessage **msg = NULL; + int bytes = 0, readh = 0, need = 0; + + if (client->message == NULL) + client->message = new_wsmessage(); + + frm = &client->frame; + msg = &client->message; + + /* message within the same frame */ + if ((*msg)->payload == NULL && (*frm)->payloadlen) + (*msg)->payload = xcalloc((*frm)->payloadlen, sizeof(char)); + /* handle a new frame */ + else if ((*msg)->buflen == 0 && (*frm)->payloadlen) + { + if (ws_realloc_frm_payload((*frm), (*msg)) == 1) + return ws_set_status(client, WS_ERR | WS_CLOSE, 0); + } + + readh = (*msg)->buflen; /* read from so far */ + need = (*frm)->payloadlen - readh; /* need to read */ + if (need > 0) + { + if ((bytes = ws_read_payload(client, (*msg), (*msg)->payloadsz, need)) < 0) + return bytes; + if (bytes != need) + return ws_set_status(client, WS_READING, bytes); + } + + (*msg)->mask_offset = (*msg)->payloadsz - (*msg)->buflen; + + ws_manage_payload_opcode(client, server); + ws_free_frame(client); + + return bytes; +} + +static int +ws_get_frm_header(WSClient *client) +{ + WSFrame **frm = NULL; + int bytes = 0, readh = 0, need = 0, offset = 0, extended = 0; + + if (client->frame == NULL) + { + client->frame = new_wsframe(); + proto_server_log(NJT_LOG_DEBUG, "tcc new_wsframe!"); + } + else + { + proto_server_log(NJT_LOG_DEBUG, "tcc client->frame->reading=%d!", client->frame->reading); + } + + frm = &client->frame; + + /* Read the first 2 bytes for basic frame info */ + readh = (*frm)->buflen; /* read from header so far */ + need = 2 - readh; /* need to read */ + if (need > 0) + { + if ((bytes = ws_read_header(client, (*frm), readh, need)) < 1) + { + // proto_server_log(NJT_LOG_DEBUG, "1 tcc read %d!",bytes); + return bytes; + } + if (bytes != need) + { + // proto_server_log(NJT_LOG_DEBUG, "2 tcc read %d!",bytes); + return ws_set_status(client, WS_READING, bytes); + } + } + offset += 2; + + if (ws_set_front_header_fields(client) != 0) + { + // proto_server_log(NJT_LOG_DEBUG, "3 tcc read %d!",bytes); + return bytes; + } + + ws_set_extended_header_size((*frm)->buf, &extended); + /* read the extended header */ + readh = (*frm)->buflen; /* read from header so far */ + need = (extended + offset) - readh; /* read from header field so far */ + if (need > 0) + { + if ((bytes = ws_read_header(client, (*frm), readh, need)) < 1) + { + // proto_server_log(NJT_LOG_DEBUG, "4 tcc read %d!",bytes); + return bytes; + } + if (bytes != need) + { + // proto_server_log(NJT_LOG_DEBUG, "5 tcc read %d!",bytes); + return ws_set_status(client, WS_READING, bytes); + } + } + offset += extended; + + /* read the masking key */ + readh = (*frm)->buflen; /* read from header so far */ + need = (4 + offset) - readh; + if (need > 0) + { + if ((bytes = ws_read_header(client, (*frm), readh, need)) < 1) + { + // proto_server_log(NJT_LOG_DEBUG, "6 tcc read %d!",bytes); + return bytes; + } + if (bytes != need) + { + // proto_server_log(NJT_LOG_DEBUG, "7 tcc read %d!",bytes); + return ws_set_status(client, WS_READING, bytes); + } + } + offset += 4; + + ws_set_payloadlen((*frm), (*frm)->buf); + ws_set_masking_key((*frm), (*frm)->buf); + + if ((*frm)->payloadlen > max_frm_size) + { + // proto_server_log(NJT_LOG_DEBUG, "8 tcc read %d!",bytes); + return ws_set_status(client, WS_ERR | WS_CLOSE, bytes); + } + + (*frm)->buflen = 0; + (*frm)->reading = 0; + (*frm)->payload_offset = offset; + // proto_server_log(NJT_LOG_DEBUG, "9 tcc read %d!",bytes); + return ws_set_status(client, WS_OK, bytes); +} + +static int +ws_get_message(WSClient *client, WSServer *server) +{ + int bytes = 0; + if ((client->frame == NULL) || (client->frame->reading)) + { + if ((bytes = ws_get_frm_header(client)) < 1 || client->frame->reading) + { + proto_server_log(NJT_LOG_DEBUG, "tcc ws_get_frm_header bytes=%d!", bytes); + return bytes; + } + } + proto_server_log(NJT_LOG_DEBUG, "tcc ws_get_frm_payload!"); + return ws_get_frm_payload(client, server); + return 1; +} + +int proto_server_process_connection(tcc_stream_request_t *r) +{ + + tcc_str_t ip = njt_string("127.0.0.1"); + if (ip.len == r->addr_text->len && memcmp((void *)r->addr_text->data, ip.data,ip.len) == 0) + { + proto_server_log(NJT_LOG_DEBUG, "1 tcc connetion ip=%V,NJT_STREAM_FORBIDDEN !",r->addr_text); + } + proto_server_log(NJT_LOG_DEBUG, "1 tcc connetion ip=%V ok!", r->addr_text); + + return NJT_OK; +} +int proto_server_process_preread(tcc_stream_request_t *r, tcc_str_t *msg) +{ + proto_server_log(NJT_LOG_DEBUG, "2 tcc preread ip=%V ok!",r->addr_text); + proto_server_log(NJT_LOG_DEBUG, "tcc preread msg=%V!",msg); + r->used_len = msg->len; + return NJT_DECLINED; +} +int proto_server_process_log(tcc_stream_request_t *r) +{ + + proto_server_log(NJT_LOG_DEBUG, "4 tcc log ip=%V ok!",r->addr_text); + return NJT_OK; +} + +int proto_server_process_message(tcc_stream_request_t *r, tcc_str_t *msg) +{ + + //proto_server_log(NJT_LOG_DEBUG, "tcc proto_server_send_upstream msg=%V",msg); + //proto_server_send_upstream(r,msg->data,msg->len); + //r->used_len = msg->len; + + //return NJT_OK; + + char *data = NULL; + WSctx *cli_ctx; + int bytes; + int rc; + WSMessage message; + WSServer *server; + + cli_ctx = tcc_get_client_ctx(r, TCC_PROTO_CTX_ID); + if (cli_ctx == NULL) + { + cli_ctx = proto_malloc(r, sizeof(WSctx)); + if(cli_ctx == NULL) { + return NJT_ERROR; + } + memset(cli_ctx, 0, sizeof(WSctx)); + cli_ctx->client.r = r; + tcc_set_client_ctx(r,TCC_PROTO_CTX_ID,cli_ctx); + } + proto_server_log(NJT_LOG_DEBUG, "3 tcc content tcc get=%V,len=%d", msg, msg->len); + + if (cli_ctx->handshake == 0) + { + if (strstr(msg->data, "\r\n\r\n") == NULL) + { + cli_ctx->handshake = 1; + } + } + if (cli_ctx->handshake == 0) + { + + cli_ctx->client.headers.buflen = msg->len; + + memcpy(cli_ctx->client.headers.buf, msg->data, cli_ctx->client.headers.buflen); + cli_ctx->client.headers.buf[cli_ctx->client.headers.buflen] = '\0'; + + data = cli_ctx->client.headers.buf; + //proto_server_log(NJT_LOG_DEBUG, "1 tcc http error!"); + + if (strstr(data, "\r\n\r\n") == NULL) + { + proto_server_log(NJT_LOG_DEBUG, "tcc http error!"); + return NJT_AGAIN; + } + // proto_server_log(NJT_LOG_DEBUG, "2 tcc http error!"); + if (parse_headers(&cli_ctx->client.headers) != 0) + { + proto_server_log(NJT_LOG_DEBUG, "tcc content parse_headers error!"); + return NJT_ERROR; + } + if (ws_verify_req_headers(&cli_ctx->client.headers) != 0) + { + proto_server_log(NJT_LOG_DEBUG, "tcc content ws_verify_req_headers error!"); + return NJT_ERROR; + } + // proto_server_log(NJT_LOG_DEBUG, "3 tcc http error!"); + ws_set_handshake_headers(&cli_ctx->client.headers); + njt_memzero(&message, sizeof(WSMessage)); + message.headers = &cli_ctx->client.headers; + // proto_server_log(NJT_LOG_DEBUG, "3 tcc http error!"); + rc = ws_app_on_connection(r, &message); + // proto_server_log(NJT_LOG_DEBUG, "4 tcc http error!"); + // ws_send_handshake_headers(r, &headers); + + if (rc == NJT_OK) + { + cli_ctx->handshake = WS_HANDSHAKE_OK; + cli_ctx->client.r->used_len = msg->len; + proto_server_log(NJT_LOG_DEBUG, "3 tcc content WS_HANDSHAKE_OK [%p,%p]!", cli_ctx, cli_ctx->client); + } + + return NJT_OK; + } + else + { + if (msg->len > 0) + { + cli_ctx->client.msg = *msg; + server = tcc_client_get_srv_ctx(r); + bytes = ws_get_message(&cli_ctx->client, server); + } + } + + proto_server_log(NJT_LOG_DEBUG, "tcc get ws data3 msg->len=%d,used_len=%d!", msg->len, cli_ctx->client.r->used_len); + if (r->used_len != msg->len) + { + //return NJT_AGAIN; + } + return NJT_OK; +} + + +int proto_server_process_client_update(tcc_stream_request_t *r) +{ + //ws_app_client_update(r); + return NJT_OK; +} + +int proto_server_process_connection_close(tcc_stream_request_t *r) +{ + proto_server_log(NJT_LOG_DEBUG, "tcc proto_server_process_connection_close!"); + ws_app_on_close(r); + return NJT_OK; +} +int proto_server_update(tcc_stream_server_ctx *srv_ctx) +{ + //ws_app_server_update(srv_ctx); + return NJT_OK; +} + +int proto_server_init(tcc_stream_server_ctx *srv_ctx) +{ + WSServer *srv_data = proto_malloc(srv_ctx, sizeof(WSServer)); + if (srv_data != NULL) + { + tcc_set_srv_ctx(srv_ctx, srv_data); + } + ws_app_server_init(srv_ctx); + return NJT_OK; +} + +void* proto_server_check_upstream_peer(tcc_stream_client_upstream_data_t *cli_ups_info) { + int i; + tcc_uint_t weight; + int mask_len = 7; + tcc_stream_upstream_rr_peer_t *peer_list = cli_ups_info->peer_list; + for(i = 0; i < cli_ups_info->peer_num; i++) { + if(peer_list[i].server->len >= mask_len && cli_ups_info->cli_addr_text->len >= mask_len && njt_memcmp(peer_list[i].server->data,cli_ups_info->cli_addr_text->data,mask_len) == 0) { + weight = proto_get_peer_weight(peer_list[i].peer); + proto_server_log(NJT_LOG_DEBUG, "1 tcc upstream server=%V,%d!",peer_list[i].server,weight); + return peer_list[i].peer; + } + } + if(cli_ups_info->peer_num > 0) { + i = 0; + weight = proto_get_peer_weight(peer_list[i].peer); + proto_server_log(NJT_LOG_DEBUG, "default tcc upstream server=%V,%d!",peer_list[i].server,weight); + return peer_list[i].peer; + } + return NULL; // NJT_BUSY; +} + +int proto_server_upstream_connection_close(tcc_stream_request_t *r) { + proto_server_log(NJT_LOG_DEBUG, "tcc proto_server_upstream_connection_close!"); +} + +int proto_server_upstream_message(tcc_stream_request_t *r, tcc_str_t *msg){ + u_char *p; + tcc_str_t end_tok = njt_string("\r\n\r\n"); + tcc_str_t add_data = njt_string("\r\ntcc-down-set: downset"); + tcc_str_t new_msg; + tcc_str_t old_end_msg; + proto_server_log(NJT_LOG_DEBUG, "tcc from upstream msg=%V",msg); + + p = njt_strlcasestrn(msg->data,msg->data + msg->len,end_tok.data,end_tok.len - 1); + if (p == NULL) { + proto_server_send(r,msg->data,msg->len); + } else { + new_msg.data = msg->data; + new_msg.len = p - msg->data; + old_end_msg.data = p; + old_end_msg.len = msg->data + msg->len - old_end_msg.data; + proto_server_send(r,new_msg.data,new_msg.len); + proto_server_log(NJT_LOG_DEBUG, "tcc send client msg1=%V",&new_msg); + proto_server_send(r,add_data.data,add_data.len); + proto_server_log(NJT_LOG_DEBUG, "tcc send client msg2=%V",&add_data); + proto_server_send(r,old_end_msg.data,old_end_msg.len); + proto_server_log(NJT_LOG_DEBUG, "tcc send client len=%d,msg3=%V",old_end_msg.len,&old_end_msg); + } + r->used_len = msg->len; + return NJT_OK; +} + +typedef struct{ + WSOpcode code; + char *msg; + size_t msg_len; +} ws_in_data_t; + +int proto_server_create_message(tcc_stream_server_ctx *srv_ctx,void *in_data,tcc_str_t *out_data){ + ws_in_data_t *data; + + data = (ws_in_data_t *)in_data; + + ws_generate_frame(data->code, data->msg, data->msg_len, out_data); +} diff --git a/modules/njet-stream-proto-server-module/src/ws_py.py b/modules/njet-stream-proto-server-module/src/ws_py.py new file mode 100644 index 0000000000000000000000000000000000000000..f70206d939b0a32b8da6da6379f72a82e83a0121 --- /dev/null +++ b/modules/njet-stream-proto-server-module/src/ws_py.py @@ -0,0 +1,10 @@ +def on_msg(r): + msg = r.msg + # print("id", r.client_id) + #r.log("{}".format(msg)) + r.send("py test ok") + r.send("py test send {}".format(msg)) + #r.send_others("others send {}".format(msg)) + r.broadcast("broadcast") + r.log("=======TEST==========================") + r.send("test .log ok") diff --git a/modules_dynamic b/modules_dynamic index 83278e626482573a97ab77d19dd07f5e3a7317ce..5e4b895a4c68615444cf3cb3dd30d958e85bb6ca 100644 --- a/modules_dynamic +++ b/modules_dynamic @@ -107,4 +107,6 @@ #optional ./modules/njet-http-mqtt-module #optional -./modules/njet-stream-mqtt-proxy-module \ No newline at end of file +./modules/njet-python-module +#optional +./modules/njet-stream-mqtt-proxy-module diff --git a/modules_static b/modules_static index 7750f0fcb7acc64dc34347a02dbae42a8b071268..b7d722fd8a6be13dd0369b25df989a9fc779ffa9 100644 --- a/modules_static +++ b/modules_static @@ -37,3 +37,6 @@ # required ./modules/njet-http-api-register-module ./modules/njet-stream-sniffer-module + +# optional +./modules/njet-stream-proto-server-module \ No newline at end of file