Warmcat homepage andy@warmcat.com
libwebsockets
{"schema":"libjg2-1", "vpath":"/git/", "avatar":"/git/avatar/", "alang":"", "gen_ut":1713882802, "reponame":"gitohashi", "desc":"Git web frontend with clientside rendering", "owner": { "name": "Andy Green", "email": "andy@warmcat.com", "md5": "c50933ca2aa61e0fe2c43d46bb6b59cb" },"url":"https://warmcat.com/repo/gitohashi", "f":3, "items": [ {"schema":"libjg2-1", "cid":"61aef7af370b961b59004543311f2171", "oid":{ "oid": "9961e6fef5f0f2458efc1c2a40d63f7cc50ebfce", "alias": [ "refs/heads/main","refs/heads/master"]},"blobname": "src/protocol_gitohashi.c", "blob": "/*\n * gitohashi protocol\n *\n * Copyright (C) 2018 Andy Green \u003candy@warmcat.com\u003e\n *\n * This library is free software; you can redistribute it and/or\n * modify it under the terms of the GNU Lesser General Public\n * License as published by the Free Software Foundation:\n * version 2.1 of the License.\n *\n * This library is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * Lesser General Public License for more details.\n *\n * You should have received a copy of the GNU Lesser General Public\n * License along with this library; if not, write to the Free Software\n * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,\n * MA 02110-1301 USA\n */\n\n#define LWS_DLL\n#define LWS_INTERNAL\n#include \u003clibwebsockets.h\u003e\n#include \u003cstring.h\u003e\n#include \u003cstdlib.h\u003e\n\n#include \u003clibjsongit2.h\u003e\n\n/*\n * This belongs to the opaque lws task once it is enqueued, and is freed when\n * the task goes out of scope\n */\n\nstruct task_data_gitohashi {\n\tchar buf[LWS_PRE + 4096];\n\tchar url[1024], alang[128], ua[256], inm[36];\n\tint frametype;\n\tstruct jg2_ctx *ctx;\n\tsize_t used;\n\tchar final;\n\tchar outlive;\n};\n\nstruct pss_gitohashi {\n\tstruct lws *wsi;\n\tint state;\n};\n\nstruct vhd_gitohashi {\n\tconst char *html, *vpath, *repo_base_dir, *acl_user, *avatar_url;\n\tconst struct lws_protocols *cache_protocol;\n\tstruct lws_context *context;\n\tstruct jg2_vhost *jg2_vhost;\n\tlws_sorted_usec_list_t sul;\n\tstruct lws_threadpool *tp;\n\tstruct lws_vhost *vhost;\n};\n\n\nstatic void\ncleanup_task_private_data(struct lws *wsi, void *user)\n{\n\tstruct task_data_gitohashi *priv \u003d (struct task_data_gitohashi *)user;\n\n\tif (priv-\u003ectx)\n\t\tjg2_ctx_destroy(priv-\u003ectx);\n\n\tfree(priv);\n}\n\nstatic enum lws_threadpool_task_return\ntask_function(void *user, enum lws_threadpool_task_status s)\n{\n\tstruct task_data_gitohashi *priv \u003d (struct task_data_gitohashi *)user;\n\tint n, flags \u003d 0, opa;\n\tchar outlive \u003d 0;\n\n\t/*\n\t * first time, we must do the http reply, and either acquire the\n\t * jg2 ctx or finish the transaction\n\t */\n\tif (!priv-\u003ectx)\n\t\treturn LWS_TP_RETURN_SYNC;\n\n\t/* we sent the last bit already */\n\n\tif (!priv-\u003eoutlive \u0026\u0026 priv-\u003eframetype \u003d\u003d LWS_WRITE_HTTP_FINAL)\n\t\treturn LWS_TP_RETURN_FINISHED;\n\n\tpriv-\u003eframetype \u003d LWS_WRITE_HTTP;\n\tn \u003d jg2_ctx_fill(priv-\u003ectx, priv-\u003ebuf + LWS_PRE,\n\t\t\t sizeof(priv-\u003ebuf) - LWS_PRE, \u0026priv-\u003eused, \u0026outlive);\n\n\topa \u003d priv-\u003eoutlive;\n\tif (outlive) {\n\t\tpriv-\u003eoutlive \u003d 1;\n\t\tflags \u003d LWS_TP_RETURN_FLAG_OUTLIVE;\n\t}\n\n\tif (n \u003c 0)\n\t\treturn LWS_TP_RETURN_STOPPED;\n\n\tif (n || priv-\u003efinal) {\n\t\tpriv-\u003eframetype \u003d LWS_WRITE_HTTP_FINAL;\n\t\tpriv-\u003efinal \u003d 1;\n\t}\n\n\tif (priv-\u003eused) {\n\t\tif (opa)\n\t\t\t/*\n\t\t\t * he can't send anything when in outlive mode.\n\t\t\t * take it as his having finished.\n\t\t\t */\n\t\t\treturn LWS_TP_RETURN_FINISHED;\n\n\t\treturn LWS_TP_RETURN_SYNC | flags;\n\t}\n\n\treturn LWS_TP_RETURN_CHECKING_IN | flags;\n}\n\nstatic int\nhttp_reply(struct lws *wsi, struct vhd_gitohashi *vhd,\n\t struct pss_gitohashi *pss, struct task_data_gitohashi *priv)\n{\n\tunsigned char *p \u003d (unsigned char *)\u0026priv-\u003ebuf[LWS_PRE], *start \u003d p,\n\t\t *end \u003d (unsigned char *)priv-\u003ebuf + sizeof(priv-\u003ebuf);\n\tconst char *mimetype \u003d NULL;\n\tstruct jg2_ctx_create_args args;\n\tunsigned long length \u003d 0;\n\tchar etag[36];\n\tint n;\n\n\tmemset(\u0026args, 0, sizeof(args));\n\targs.repo_path \u003d priv-\u003eurl;\n\targs.flags \u003d JG2_CTX_FLAG_HTML;\n\targs.mimetype \u003d \u0026mimetype;\n\targs.length \u003d \u0026length;\n\targs.etag \u003d etag;\n\targs.etag_length \u003d sizeof(etag);\n\n\tif (priv-\u003ealang[0])\n\t\targs.accept_language \u003d priv-\u003ealang;\n\n\tif (priv-\u003einm[0])\n\t\targs.client_etag \u003d priv-\u003einm;\n\n\n\t/*\n\t * Let's assess from his user agent if he's a bot. Caching\n\t * content generated by a bot is less than worthless since they\n\t * spider the whole repo more or less randomly, flushing the\n\t * parts of the cache generated by real users which may be\n\t * interesting for other users too.\n\t */\n\n\tif (priv-\u003eua[0] \u0026\u0026\n\t (strstr(priv-\u003eua, \u0022bot\u0022) || strstr(priv-\u003eua, \u0022Bot\u0022)))\n\t\targs.flags |\u003d JG2_CTX_FLAG_BOT;\n\n\tp \u003d start;\n\n\tif (jg2_ctx_create(vhd-\u003ejg2_vhost, \u0026priv-\u003ectx, \u0026args)) {\n\n\t\tlwsl_info(\u0022%s: jg2_ctx_create fail: %s\u005cn\u0022, __func__, start);\n\n\t\tlws_return_http_status(wsi, HTTP_STATUS_FORBIDDEN,\n\t\t\t\t\u0022403 Forbidden\u0022);\n\t\treturn 0;\n\t}\n\n\t/* does he actually already have a current version of it? */\n\n\tn \u003d lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_IF_NONE_MATCH);\n\tif (etag[0] \u0026\u0026 n \u0026\u0026 !strcmp(etag, priv-\u003einm)) {\n\n\t\tlwsl_debug(\u0022%s: ETAG match %s\u005cn\u0022, __func__, etag);\n\n\t\t/* we don't need to send the payload... lose the ctx */\n\n\t\tjg2_ctx_destroy(priv-\u003ectx);\n\t\tpriv-\u003ectx \u003d NULL;\n\n\t\t/* inform the client he already has the latest guy */\n\n\t\tif (lws_add_http_header_status(wsi, HTTP_STATUS_NOT_MODIFIED,\n\t\t\t\t\t \u0026p, end))\n\t\t\treturn -1;\n\n\t\tif (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_ETAG,\n\t\t\t\t(unsigned char *)etag, n, \u0026p, end))\n\t\t\treturn -1;\n\n\t\tif (lws_finalize_http_header(wsi, \u0026p, end))\n\t\t\treturn 1;\n\n\t\tn \u003d lws_write(wsi, start, p - start, LWS_WRITE_HTTP_HEADERS |\n\t\t\t\t\t\t LWS_WRITE_H2_STREAM_END);\n\t\tif (n !\u003d (p - start)) {\n\t\t\tlwsl_err(\u0022_write returned %d from %ld\u005cn\u0022, n,\n\t\t\t\t (long)(p - start));\n\t\t\treturn -1;\n\t\t}\n\n\t\tgoto transaction_completed;\n\t}\n\n\t/* nope... he doesn't already have it, so we must issue it */\n\n\tif (lws_add_http_common_headers(wsi, HTTP_STATUS_OK,\n\t\t\tmimetype, length? length :\n\t\t\tLWS_ILLEGAL_HTTP_CONTENT_LEN, \u0026p, end))\n\t\treturn 1;\n\n\t/*\n\t * if we know the etag already, issue it so we can recognize\n\t * if he asks for it again while he already has it\n\t */\n\n\tif (etag[0] \u0026\u0026 lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_ETAG,\n\t\t\t\t\t\t (unsigned char *)etag,\n\t\t\t\t\t\t strlen(etag), \u0026p, end))\n\t\treturn 1;\n\n\tif (lws_finalize_write_http_header(wsi, start, \u0026p, end))\n\t\treturn 1;\n\n\treturn 0;\n\ntransaction_completed:\n\n\tif (lws_http_transaction_completed(wsi))\n\t\treturn -1;\n\n\treturn 0;\n}\n\n/*\n * Called from a threadpool thread context...\n */\n\nvoid refchange(void * user)\n{\n\tstruct pss_gitohashi *pss \u003d (struct pss_gitohashi *)user;\n\n\tlwsl_notice(\u0022%s: %p\u005cn\u0022, __func__, pss);\n\n//\tif (!pss)\n//\t\treturn;\n\n\t// lws_callback_on_writable(pss-\u003ewsi);\n}\n\nstatic const char *hex \u003d \u00220123456789abcdef\u0022;\n\nstatic const char *\nmd5_to_hex_cstr(char *md5_hex_33, const unsigned char *md5)\n{\n\tint n;\n\n\tif (!md5) {\n\t\t*md5_hex_33++ \u003d '?';\n\t\t*md5_hex_33++ \u003d '\u005c0';\n\n\t\treturn md5_hex_33 - 2;\n\t}\n\tfor (n \u003d 0; n \u003c 16; n++) {\n\t\t*md5_hex_33++ \u003d hex[((*md5) \u003e\u003e 4) \u0026 0xf];\n\t\t*md5_hex_33++ \u003d hex[*(md5++) \u0026 0xf];\n\t}\n\t*md5_hex_33 \u003d '\u005c0';\n\n\treturn md5_hex_33 - 32;\n}\n\n/*\n * Called from a threadpool thread context...\n */\n\nint avatar(void *avatar_arg, const unsigned char *md5)\n{\n\tstruct vhd_gitohashi *vhd \u003d (struct vhd_gitohashi *)avatar_arg;\n\ttypedef int (*mention_t)(const struct lws_protocols *pcol,\n\t\t\tstruct lws_vhost *vh, const char *path);\n\tchar md[256];\n\n\tif (!vhd-\u003ecache_protocol)\n\t\tvhd-\u003ecache_protocol \u003d lws_vhost_name_to_protocol(\n\t\t\t\t\tvhd-\u003evhost, \u0022avatar-proxy\u0022);\n\n\tif (!vhd-\u003ecache_protocol)\n\t\treturn 0;\n\n\tmd5_to_hex_cstr(md, md5);\n\n\t((mention_t)(void *)vhd-\u003ecache_protocol-\u003euser)\n\t\t\t(vhd-\u003ecache_protocol, vhd-\u003evhost, md);\n\n\treturn 0;\n}\n\nstatic void\ndump_cb(lws_sorted_usec_list_t *sul)\n{\n\tstruct vhd_gitohashi *vhd \u003d lws_container_of(sul, struct vhd_gitohashi, sul);\n\t/*\n\t * in debug mode, dump the threadpool stat to the logs once\n\t * a second\n\t */\n\t//lws_threadpool_dump(vhd-\u003etp);\n\tlws_sul_schedule(vhd-\u003econtext, 0, \u0026vhd-\u003esul, dump_cb, 1 * LWS_US_PER_SEC);\n}\n\nstatic int\ncallback_gitohashi(struct lws *wsi, enum lws_callback_reasons reason,\n\t void *user, void *in, size_t len)\n{\n\tstruct vhd_gitohashi *vhd \u003d (struct vhd_gitohashi *)\n\t\t\t lws_protocol_vh_priv_get(lws_get_vhost(wsi),\n\t\t\t\t\t\t lws_get_protocol(wsi));\n\tchar buf[LWS_PRE + 4096];\n\tunsigned char *p \u003d (unsigned char *)\u0026buf[LWS_PRE],\n\t\t *end \u003d (unsigned char *)buf + sizeof(buf);\n\tstruct pss_gitohashi *pss \u003d (struct pss_gitohashi *)user;\n\tstruct lws_threadpool_create_args cargs;\n\tstruct lws_threadpool_task_args targs;\n\tstruct task_data_gitohashi *priv;\n\tstruct jg2_vhost_config config;\n\tconst char *csize, *flags, *z;\n\tvoid *_user;\n\tuid_t uid;\n\tgid_t gid;\n\tint n;\n\n\tswitch (reason) {\n\n\t/* --------------- protocol --------------- */\n\n\tcase LWS_CALLBACK_PROTOCOL_INIT: /* per vhost */\n\t\tlws_protocol_vh_priv_zalloc(lws_get_vhost(wsi),\n\t\t\t\t\t lws_get_protocol(wsi),\n\t\t\t\t\t sizeof(struct vhd_gitohashi));\n\t\tvhd \u003d (struct vhd_gitohashi *)\n\t\t\tlws_protocol_vh_priv_get(lws_get_vhost(wsi),\n\t\t\t\t\t\t lws_get_protocol(wsi));\n\n\t\tvhd-\u003evhost \u003d lws_get_vhost(wsi);\n\t\tvhd-\u003econtext \u003d lws_get_context(wsi);\n\n\t\tif (lws_pvo_get_str(in, \u0022html-file\u0022, \u0026vhd-\u003ehtml) ||\n\t\t lws_pvo_get_str(in, \u0022vpath\u0022, \u0026vhd-\u003evpath) ||\n\t\t lws_pvo_get_str(in, \u0022repo-base-dir\u0022, \u0026vhd-\u003erepo_base_dir) ||\n\t\t lws_pvo_get_str(in, \u0022acl-user\u0022, \u0026vhd-\u003eacl_user) ||\n\t\t lws_pvo_get_str(in, \u0022avatar-url\u0022, \u0026vhd-\u003eavatar_url)) {\n\n\t\t\tlwsl_err(\u0022%s: required pvos: html-file, vpath,\u0022\n\t\t\t\t \u0022repo-base-dir, acl-user, avatar-url\u005cn\u0022,\n\t\t\t\t __func__);\n\n\t\t\treturn -1;\n\t\t}\n\n\n\t\tmemset(\u0026cargs, 0, sizeof(cargs));\n\n\t\tcargs.max_queue_depth \u003d 12;\n\t\tcargs.threads \u003d 4;\n\n\t\tif (!lws_pvo_get_str(in, \u0022threads\u0022, \u0026z))\n\t\t\tcargs.threads \u003d atoi(z);\n\t\tif (!lws_pvo_get_str(in, \u0022max_queue_depth\u0022, \u0026z))\n\t\t\tcargs.max_queue_depth \u003d atoi(z);\n\n\t\tvhd-\u003etp \u003d lws_threadpool_create(lws_get_context(wsi), \u0026cargs,\n\t\t\t\t\t\t\u0022%s\u0022,\n\t\t\t\t\t\tlws_get_vhost_name(vhd-\u003evhost));\n\t\tif (!vhd-\u003etp)\n\t\t\treturn -1;\n\n\t\tmemset(\u0026config, 0, sizeof(config));\n\t\tconfig.virtual_base_urlpath \u003d vhd-\u003evpath;\n\t\tconfig.refchange \u003d refchange;\n\t\tconfig.avatar \u003d avatar;\n\t\tconfig.avatar_arg \u003d vhd;\n\t\tconfig.avatar_url \u003d vhd-\u003eavatar_url;\n\t\tconfig.repo_base_dir \u003d vhd-\u003erepo_base_dir;\n\t\tconfig.vhost_html_filepath \u003d vhd-\u003ehtml;\n\t\tconfig.acl_user \u003d vhd-\u003eacl_user;\n\n\t\t/* optional... no caching if not set */\n\t\tif (!lws_pvo_get_str(in, \u0022cache-base\u0022,\n\t\t\t\t \u0026config.json_cache_base)) {\n\t\t\tlws_get_effective_uid_gid(lws_get_context(wsi), \u0026uid,\n\t\t\t\t\t\t \u0026gid);\n\t\t\tconfig.cache_uid \u003d uid;\n\n\t\t\t/* optional, default size if not set */\n\t\t\tif (!lws_pvo_get_str(in, \u0022cache-size\u0022, \u0026csize))\n\t\t\t\tconfig.cache_size_limit \u003d atoi(csize);\n\t\t}\n\n\t\t/* optional... flags */\n\t\tif (!lws_pvo_get_str(in, \u0022flags\u0022, \u0026flags))\n\t\t\tconfig.flags \u003d atoi(flags);\n\n\t\tif (config.flags \u0026 JG2_VHOST_BLOG_MODE \u0026\u0026\n\t\t lws_pvo_get_str(in, \u0022blog-repo-name\u0022,\n\t\t\t\t \u0026config.blog_repo_name)) {\n\t\t\tlwsl_err(\u0022%s: if blog_mode set in flags, \u0022\n\t\t\t\t \u0022blog_repo_name is required\u005cn\u0022, __func__);\n\n\t\t\tlws_threadpool_destroy(vhd-\u003etp);\n\n\t\t\treturn -1;\n\t\t}\n\n\t\tvhd-\u003ejg2_vhost \u003d jg2_vhost_create(\u0026config);\n\t\tif (!vhd-\u003ejg2_vhost) {\n\t\t\tlws_threadpool_destroy(vhd-\u003etp);\n\t\t\treturn -1;\n\t\t}\n\n\t\tlws_sul_schedule(vhd-\u003econtext, 0, \u0026vhd-\u003esul, dump_cb, 1);\n\t\tbreak;\n\n\tcase LWS_CALLBACK_PROTOCOL_DESTROY: /* per vhost */\n\t\tif (!vhd) {\n\t\t\tlwsl_err(\u0022%s: NULL vhd\u005cn\u0022, __func__);\n\t\t\tbreak;;\n\t\t}\n\t\tjg2_vhost_destroy(vhd-\u003ejg2_vhost);\n\t\tlws_threadpool_finish(vhd-\u003etp);\n\t\tlws_threadpool_destroy(vhd-\u003etp);\n\t\tvhd-\u003ejg2_vhost \u003d NULL;\n\t\tbreak;\n\n\t/* --------------- http --------------- */\n\n\tcase LWS_CALLBACK_HTTP:\n\n\t\t/*\n\t\t * The jg2 ctx is not going to be created until a thread\n\t\t * becomes available. But our headers will be scrubbed when\n\t\t * we return from this.\n\t\t *\n\t\t * So we must stash any interesting header content in the user\n\t\t * priv struct before queuing the task.\n\t\t */\n\n\t\tmemset(\u0026targs, 0, sizeof(targs));\n\t\tpriv \u003d targs.user \u003d malloc(sizeof(*priv));\n\t\tif (!priv)\n\t\t\treturn 1;\n\n\t\tmemset(priv, 0, sizeof(*priv));\n\n\t\ttargs.wsi \u003d wsi;\n\t\ttargs.task \u003d task_function;\n\t\ttargs.cleanup \u003d cleanup_task_private_data;\n\n\t\t/*\n\t\t * \u0022in\u0022 contains the url part after our mountpoint, if any.\n\t\t *\n\t\t * Our strategy is to record the URL for the duration of the\n\t\t * transaction and return the user's configured html template,\n\t\t * plus JSON prepared based on the URL. That lets the page\n\t\t * display remotely in one roundtrip (+tls) without having to\n\t\t * wait for the ws link to come up.\n\t\t *\n\t\t * Serving anything other than the configured html template\n\t\t * will have to come from outside this mount URL path.\n\t\t *\n\t\t * Stash in and the urlargs into pss-\u003epriv-\u003eurl[]\n\t\t */\n\n\t\tp \u003d (unsigned char *)priv-\u003eurl;\n\t\tend \u003d p + sizeof(priv-\u003eurl) - 1;\n\t\tif ((int)len \u003e\u003d end - p)\n\t\t\tlen \u003d end - p - 1;\n\t\tmemcpy(p, in, len);\n\t\tp +\u003d len;\n\n\t\tn \u003d 0;\n\t\twhile (lws_hdr_copy_fragment(wsi, (char *)p + 1, end - p - 2,\n\t\t\t\t\t WSI_TOKEN_HTTP_URI_ARGS, n) \u003e 0) {\n\t\t\tif (!n)\n\t\t\t\t*p \u003d '?';\n\t\t\telse\n\t\t\t\t*p \u003d '\u0026';\n\n\t\t\tp +\u003d strlen((char *)p);\n\t\t\tn++;\n\t\t}\n\n\t\t*p++ \u003d '\u005c0';\n\n\t\tif (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_USER_AGENT) \u0026\u0026\n\t\t lws_hdr_copy(wsi, priv-\u003eua, sizeof(priv-\u003eua),\n\t\t\t\t WSI_TOKEN_HTTP_USER_AGENT) \u003c 0)\n\t\t\tpriv-\u003eua[0] \u003d '\u005c0';\n\n\t\tif (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_ACCEPT_LANGUAGE) \u0026\u0026\n\t\t lws_hdr_copy(wsi, priv-\u003ealang,\n\t\t\t\t sizeof(priv-\u003ealang),\n\t\t\t\t WSI_TOKEN_HTTP_ACCEPT_LANGUAGE) \u003c 0)\n\t\t\tpriv-\u003ealang[0] \u003d '\u005c0';\n\n\t\tn \u003d lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_IF_NONE_MATCH);\n\t\tif (n \u0026\u0026 lws_hdr_copy(wsi, priv-\u003einm,\n\t\t\t\t sizeof(priv-\u003einm),\n\t\t\t\t WSI_TOKEN_HTTP_IF_NONE_MATCH) \u003c 0)\n\t\t\tpriv-\u003einm[0] \u003d '\u005c0';\n\n\t\t/*\n\t\t * that's all the info we need... queue the task to do the\n\t\t * actual business (priv is passed by targs.user)\n\t\t */\n\n\t\tif (!vhd) {\n\t\t\tlwsl_err(\u0022%s: NULL vhd\u005cn\u0022, __func__);\n\t\t\treturn -1;\n\t\t}\n\n\t\tif (!lws_threadpool_enqueue(vhd-\u003etp, \u0026targs, \u0022goh-%s\u0022,\n\t\t\t\t\t (const char *)in)) {\n\t\t\tlwsl_user(\u0022%s: Couldn't enqueue task\u005cn\u0022, __func__);\n\t\t\tcleanup_task_private_data(wsi, priv);\n\t\t\treturn 1;\n\t\t}\n\n\t\tlws_set_timeout(wsi, PENDING_TIMEOUT_THREADPOOL, 30);\n\n\t\t/*\n\t\t * the task will get serviced, see that it doesn't have any\n\t\t * jg2 ctx yet, and SYNC until we got a WRITEABLE callback\n\t\t * (which will usually be very quickly, because this HTTP\n\t\t * callback only happens when we are writeable, and we didn't\n\t\t * write anything yet)\n\t\t */\n\n\t\treturn 0;\n\n\tcase LWS_CALLBACK_HTTP_DROP_PROTOCOL:\n\t\tif (pss) {\n\t\t\tlwsl_info(\u0022%s: HTTP_DROP_PROTOCOL: %s %p\u005cn\u0022, __func__,\n\t\t\t\t (const char *)in, wsi);\n\t\t\tif (lws_threadpool_get_task_wsi(wsi))\n\t\t\t\tlws_threadpool_dequeue_task(lws_threadpool_get_task_wsi(wsi));\n\t\t}\n\t\treturn 0;\n\n\tcase LWS_CALLBACK_CLOSED_HTTP:\n\t\treturn 0;\n\n\tcase LWS_CALLBACK_HTTP_WRITEABLE:\n\n\t\tif (!pss)\n\t\t\tbreak;\n\n\t\tn \u003d lws_threadpool_task_status(lws_threadpool_get_task_wsi(wsi), \u0026_user);\n\t\tlwsl_info(\u0022%s: LWS_CALLBACK_SERVER_WRITEABLE: %p: \u0022\n\t\t\t \u0022priv %p, status %d\u005cn\u0022, __func__, wsi, _user, n);\n\t\tswitch(n) {\n\t\tcase LWS_TP_STATUS_FINISHED:\n\t\tcase LWS_TP_STATUS_STOPPED:\n\t\tcase LWS_TP_STATUS_QUEUED:\n\t\tcase LWS_TP_STATUS_RUNNING:\n\t\tcase LWS_TP_STATUS_STOPPING:\n\t\t\treturn 0;\n\n\t\tcase LWS_TP_STATUS_SYNCING:\n\t\t\t/* the task has paused for us to do something */\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\t/* wsi has no discernable task */\n\t\t\t//lwsl_err(\u0022%s: HTTP_WRITABLE: wsi has no task\u005cn\u0022,\n\t\t\t//\t\t__func__);\n\t\t\treturn 1;\n\t\t}\n\n\t\tpriv \u003d (struct task_data_gitohashi *)_user;\n\n\t\tif (!priv-\u003ectx) {\n\t\t\t/*\n\t\t\t * Do the http response and maybe acquire the jg2 ctx.\n\t\t\t * Sometimes that was all we needed to do (eg, ETAG\n\t\t\t * matched) and there's no jg2_ctx: the transaction is\n\t\t\t * completed then.\n\t\t\t */\n\t\t\tn \u003d http_reply(wsi, vhd, pss, priv);\n\t\t\tif (!priv-\u003ectx) {\n\t\t\t\t/* unblock him and stop him as we are done */\n\t\t\t\tlws_threadpool_task_sync(lws_threadpool_get_task_wsi(wsi), 1);\n\n\t\t\t\treturn n;\n\t\t\t}\n\n\t\t\tlws_set_timeout(wsi, PENDING_TIMEOUT_THREADPOOL_TASK,\n\t\t\t\t\t60);\n\n\t\t\t/*\n\t\t\t * otherwise we are just getting started... unblock the\n\t\t\t * worker thread and start the business of filling\n\t\t\t * buffers and sending them on.\n\t\t\t */\n\n\t\t\tgoto sync_end;\n\t\t}\n\n\t\tif (priv-\u003eused) {\n\t\t\tlwsl_info(\u0022 writing %d\u005cn\u0022, (int)priv-\u003eused);\n\t\t\tlwsl_hexdump_debug(priv-\u003ebuf + LWS_PRE, priv-\u003eused);\n\t\t\tif (lws_write(wsi, (unsigned char *)priv-\u003ebuf + LWS_PRE,\n\t\t\t\t priv-\u003eused, priv-\u003eframetype) !\u003d\n\t\t\t\t (int)priv-\u003eused) {\n\t\t\t\tlwsl_err(\u0022%s: lws_write failed\u005cn\u0022, __func__);\n\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t\tpriv-\u003eused \u003d 0;\n\n\t\t\tif (priv-\u003eframetype \u003d\u003d LWS_WRITE_HTTP_FINAL) {\n\t\t\t\tlws_threadpool_task_sync(lws_threadpool_get_task_wsi(wsi), !priv-\u003eoutlive);\n\t\t\t\tgoto transaction_completed;\n\t\t\t}\n\t\t}\n\nsync_end:\n\t\tlws_threadpool_task_sync(lws_threadpool_get_task_wsi(wsi), 0);\n\n\t\treturn 0;\n\n\tdefault:\n\t\tbreak;\n\t}\n\n\treturn lws_callback_http_dummy(wsi, reason, user, in, len);\n\ntransaction_completed:\n\tif (lws_http_transaction_completed(wsi))\n\t\treturn -1;\n\n\treturn 0;\n}\n\n#define LWS_PLUGIN_PROTOCOL_GITOHASHI \u005c\n\t{ \u005c\n\t\t\u0022gitohashi\u0022, \u005c\n\t\tcallback_gitohashi, \u005c\n\t\tsizeof(struct pss_gitohashi), \u005c\n\t\t4096, \u005c\n\t}\n\n#if !defined (LWS_PLUGIN_STATIC)\n\nstatic const struct lws_protocols protocols[] \u003d {\n\tLWS_PLUGIN_PROTOCOL_GITOHASHI\n};\n\nLWS_EXTERN LWS_VISIBLE int\ninit_protocol_gitohashi(struct lws_context *context,\n\t\t\t\tstruct lws_plugin_capability *c)\n{\n\tif (c-\u003eapi_magic !\u003d LWS_PLUGIN_API_MAGIC) {\n\t\tlwsl_err(\u0022Plugin API %d, library API %d\u0022,\n\t\t\t LWS_PLUGIN_API_MAGIC, c-\u003eapi_magic);\n\t\treturn 1;\n\t}\n\n\tc-\u003eprotocols \u003d protocols;\n\tc-\u003ecount_protocols \u003d LWS_ARRAY_SIZE(protocols);\n\tc-\u003eextensions \u003d NULL;\n\tc-\u003ecount_extensions \u003d 0;\n\n\treturn 0;\n}\n\nLWS_EXTERN LWS_VISIBLE int\ndestroy_protocol_gitohashi(struct lws_context *context)\n{\n\treturn 0;\n}\n#endif\n","s":{"c":1713882802,"u": 1663}} ],"g": 3373,"chitpc": 0,"ehitpc": 0,"indexed":0 , "ab": 1, "si": 0, "db":0, "di":0, "sat":0, "lfc": "0000"}