You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
717 lines
16 KiB
717 lines
16 KiB
/*
|
|
* libwebsockets - small server side websockets and web server implementation
|
|
*
|
|
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to
|
|
* deal in the Software without restriction, including without limitation the
|
|
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
|
* sell copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
* IN THE SOFTWARE.
|
|
*/
|
|
|
|
#if !defined (LWS_PLUGIN_STATIC)
|
|
#define LWS_DLL
|
|
#define LWS_INTERNAL
|
|
#include <libwebsockets.h>
|
|
#endif
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <fcntl.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <dirent.h>
|
|
#ifdef WIN32
|
|
#include <io.h>
|
|
#endif
|
|
#include <stdio.h>
|
|
#include <errno.h>
|
|
|
|
struct dir_entry {
|
|
lws_list_ptr next; /* sorted by mtime */
|
|
char user[32];
|
|
unsigned long long size;
|
|
time_t mtime;
|
|
};
|
|
/* filename follows */
|
|
|
|
#define lp_to_dir_entry(p, _n) lws_list_ptr_container(p, struct dir_entry, _n)
|
|
|
|
struct pss_deaddrop;
|
|
|
|
struct vhd_deaddrop {
|
|
struct lws_context *context;
|
|
struct lws_vhost *vh;
|
|
const struct lws_protocols *protocol;
|
|
|
|
struct pss_deaddrop *pss_head;
|
|
|
|
const char *upload_dir;
|
|
|
|
struct lwsac *lwsac_head;
|
|
struct dir_entry *dire_head;
|
|
int filelist_version;
|
|
|
|
unsigned long long max_size;
|
|
};
|
|
|
|
struct pss_deaddrop {
|
|
struct lws_spa *spa;
|
|
struct vhd_deaddrop *vhd;
|
|
struct lws *wsi;
|
|
char result[LWS_PRE + LWS_RECOMMENDED_MIN_HEADER_SPACE];
|
|
char filename[256];
|
|
char user[32];
|
|
unsigned long long file_length;
|
|
lws_filefd_type fd;
|
|
int response_code;
|
|
|
|
struct pss_deaddrop *pss_list;
|
|
|
|
struct lwsac *lwsac_head;
|
|
struct dir_entry *dire;
|
|
int filelist_version;
|
|
|
|
uint8_t completed:1;
|
|
uint8_t sent_headers:1;
|
|
uint8_t sent_body:1;
|
|
uint8_t first:1;
|
|
};
|
|
|
|
static const char * const param_names[] = {
|
|
"text",
|
|
"send",
|
|
"file",
|
|
"upload",
|
|
};
|
|
|
|
enum enum_param_names {
|
|
EPN_TEXT,
|
|
EPN_SEND,
|
|
EPN_FILE,
|
|
EPN_UPLOAD,
|
|
};
|
|
|
|
static int
|
|
de_mtime_sort(lws_list_ptr a, lws_list_ptr b)
|
|
{
|
|
struct dir_entry *p1 = lp_to_dir_entry(a, next),
|
|
*p2 = lp_to_dir_entry(b, next);
|
|
|
|
return (int)(p2->mtime - p1->mtime);
|
|
}
|
|
|
|
static void
|
|
start_sending_dir(struct pss_deaddrop *pss)
|
|
{
|
|
if (pss->vhd->lwsac_head)
|
|
lwsac_reference(pss->vhd->lwsac_head);
|
|
pss->lwsac_head = pss->vhd->lwsac_head;
|
|
pss->dire = pss->vhd->dire_head;
|
|
pss->filelist_version = pss->vhd->filelist_version;
|
|
pss->first = 1;
|
|
}
|
|
|
|
static int
|
|
scan_upload_dir(struct vhd_deaddrop *vhd)
|
|
{
|
|
char filepath[256], subdir[3][128], *p;
|
|
int m, sp = 0, initial, found = 0;
|
|
struct lwsac *lwsac_head = NULL;
|
|
lws_list_ptr sorted_head = NULL;
|
|
struct dir_entry *dire;
|
|
struct dirent *de;
|
|
struct stat s;
|
|
DIR *dir[3];
|
|
|
|
initial = strlen(vhd->upload_dir) + 1;
|
|
lws_strncpy(subdir[sp], vhd->upload_dir, sizeof(subdir[sp]));
|
|
dir[sp] = opendir(vhd->upload_dir);
|
|
if (!dir[sp]) {
|
|
lwsl_err("%s: Unable to walk upload dir '%s'\n", __func__,
|
|
vhd->upload_dir);
|
|
return -1;
|
|
}
|
|
|
|
do {
|
|
de = readdir(dir[sp]);
|
|
if (!de) {
|
|
closedir(dir[sp]);
|
|
#if !defined(__COVERITY__)
|
|
if (!sp)
|
|
#endif
|
|
break;
|
|
#if !defined(__COVERITY__)
|
|
sp--;
|
|
continue;
|
|
#endif
|
|
}
|
|
|
|
p = filepath;
|
|
|
|
for (m = 0; m <= sp; m++)
|
|
p += lws_snprintf(p, (filepath + sizeof(filepath)) - p,
|
|
"%s/", subdir[m]);
|
|
|
|
lws_snprintf(p, (filepath + sizeof(filepath)) - p, "%s",
|
|
de->d_name);
|
|
|
|
/* ignore temp files */
|
|
if (de->d_name[strlen(de->d_name) - 1] == '~')
|
|
continue;
|
|
#if defined(__COVERITY__)
|
|
s.st_size = 0;
|
|
s.st_mtime = 0;
|
|
#else
|
|
/* coverity[toctou] */
|
|
if (stat(filepath, &s))
|
|
continue;
|
|
|
|
if (S_ISDIR(s.st_mode)) {
|
|
if (!strcmp(de->d_name, ".") ||
|
|
!strcmp(de->d_name, ".."))
|
|
continue;
|
|
sp++;
|
|
if (sp == LWS_ARRAY_SIZE(dir)) {
|
|
lwsl_err("%s: Skipping too-deep subdir %s\n",
|
|
__func__, filepath);
|
|
sp--;
|
|
continue;
|
|
}
|
|
lws_strncpy(subdir[sp], de->d_name, sizeof(subdir[sp]));
|
|
dir[sp] = opendir(filepath);
|
|
if (!dir[sp]) {
|
|
lwsl_err("%s: Unable to open subdir '%s'\n",
|
|
__func__, filepath);
|
|
goto bail;
|
|
}
|
|
continue;
|
|
}
|
|
#endif
|
|
|
|
m = strlen(filepath + initial) + 1;
|
|
dire = lwsac_use(&lwsac_head, sizeof(*dire) + m, 0);
|
|
if (!dire) {
|
|
lwsac_free(&lwsac_head);
|
|
|
|
goto bail;
|
|
}
|
|
|
|
dire->next = NULL;
|
|
dire->size = s.st_size;
|
|
dire->mtime = s.st_mtime;
|
|
dire->user[0] = '\0';
|
|
#if !defined(__COVERITY__)
|
|
if (sp)
|
|
lws_strncpy(dire->user, subdir[1], sizeof(dire->user));
|
|
#endif
|
|
|
|
found++;
|
|
|
|
memcpy(&dire[1], filepath + initial, m);
|
|
|
|
lws_list_ptr_insert(&sorted_head, &dire->next, de_mtime_sort);
|
|
} while (1);
|
|
|
|
/* the old lwsac continues to live while someone else is consuming it */
|
|
if (vhd->lwsac_head)
|
|
lwsac_detach(&vhd->lwsac_head);
|
|
|
|
/* we replace it with the fresh one */
|
|
vhd->lwsac_head = lwsac_head;
|
|
if (sorted_head)
|
|
vhd->dire_head = lp_to_dir_entry(sorted_head, next);
|
|
else
|
|
vhd->dire_head = NULL;
|
|
|
|
vhd->filelist_version++;
|
|
|
|
lwsl_info("%s: found %d\n", __func__, found);
|
|
|
|
lws_start_foreach_llp(struct pss_deaddrop **, ppss, vhd->pss_head) {
|
|
start_sending_dir(*ppss);
|
|
lws_callback_on_writable((*ppss)->wsi);
|
|
} lws_end_foreach_llp(ppss, pss_list);
|
|
|
|
return 0;
|
|
|
|
bail:
|
|
while (sp >= 0)
|
|
closedir(dir[sp--]);
|
|
|
|
return -1;
|
|
}
|
|
|
|
static int
|
|
file_upload_cb(void *data, const char *name, const char *filename,
|
|
char *buf, int len, enum lws_spa_fileupload_states state)
|
|
{
|
|
struct pss_deaddrop *pss = (struct pss_deaddrop *)data;
|
|
char filename2[256];
|
|
int n;
|
|
|
|
(void)n;
|
|
|
|
switch (state) {
|
|
case LWS_UFS_OPEN:
|
|
lws_urldecode(filename2, filename, sizeof(filename2) - 1);
|
|
lws_filename_purify_inplace(filename2);
|
|
if (pss->user[0]) {
|
|
lws_filename_purify_inplace(pss->user);
|
|
lws_snprintf(pss->filename, sizeof(pss->filename),
|
|
"%s/%s", pss->vhd->upload_dir, pss->user);
|
|
if (mkdir(pss->filename
|
|
#if !defined(WIN32)
|
|
, 0700
|
|
#endif
|
|
) < 0)
|
|
lwsl_debug("%s: mkdir failed\n", __func__);
|
|
lws_snprintf(pss->filename, sizeof(pss->filename),
|
|
"%s/%s/%s~", pss->vhd->upload_dir,
|
|
pss->user, filename2);
|
|
} else
|
|
lws_snprintf(pss->filename, sizeof(pss->filename),
|
|
"%s/%s~", pss->vhd->upload_dir, filename2);
|
|
lwsl_notice("%s: filename '%s'\n", __func__, pss->filename);
|
|
|
|
pss->fd = (lws_filefd_type)(long long)lws_open(pss->filename,
|
|
O_CREAT | O_TRUNC | O_RDWR, 0600);
|
|
if (pss->fd == LWS_INVALID_FILE) {
|
|
pss->response_code = HTTP_STATUS_INTERNAL_SERVER_ERROR;
|
|
lwsl_err("%s: unable to open %s (errno %d)\n", __func__,
|
|
pss->filename, errno);
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
case LWS_UFS_FINAL_CONTENT:
|
|
case LWS_UFS_CONTENT:
|
|
if (len) {
|
|
pss->file_length += len;
|
|
|
|
/* if the file length is too big, drop it */
|
|
if (pss->file_length > pss->vhd->max_size) {
|
|
pss->response_code =
|
|
HTTP_STATUS_REQ_ENTITY_TOO_LARGE;
|
|
close((int)(long long)pss->fd);
|
|
pss->fd = LWS_INVALID_FILE;
|
|
unlink(pss->filename);
|
|
|
|
return -1;
|
|
}
|
|
|
|
if (pss->fd != LWS_INVALID_FILE) {
|
|
n = write((int)(long long)pss->fd, buf, len);
|
|
lwsl_debug("%s: write %d says %d\n", __func__,
|
|
len, n);
|
|
lws_set_timeout(pss->wsi, PENDING_TIMEOUT_HTTP_CONTENT, 30);
|
|
}
|
|
}
|
|
if (state == LWS_UFS_CONTENT)
|
|
break;
|
|
|
|
if (pss->fd != LWS_INVALID_FILE)
|
|
close((int)(long long)pss->fd);
|
|
|
|
/* the temp filename without the ~ */
|
|
lws_strncpy(filename2, pss->filename, sizeof(filename2));
|
|
filename2[strlen(filename2) - 1] = '\0';
|
|
if (rename(pss->filename, filename2) < 0)
|
|
lwsl_err("%s: unable to rename\n", __func__);
|
|
|
|
pss->fd = LWS_INVALID_FILE;
|
|
pss->response_code = HTTP_STATUS_OK;
|
|
scan_upload_dir(pss->vhd);
|
|
|
|
break;
|
|
case LWS_UFS_CLOSE:
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* returns length in bytes
|
|
*/
|
|
|
|
static int
|
|
format_result(struct pss_deaddrop *pss)
|
|
{
|
|
unsigned char *p, *start, *end;
|
|
|
|
p = (unsigned char *)pss->result + LWS_PRE;
|
|
start = p;
|
|
end = p + sizeof(pss->result) - LWS_PRE - 1;
|
|
|
|
p += lws_snprintf((char *)p, end -p,
|
|
"<!DOCTYPE html><html lang=\"en\"><head>"
|
|
"<meta charset=utf-8 http-equiv=\"Content-Language\" "
|
|
"content=\"en\"/>"
|
|
"</head>");
|
|
p += lws_snprintf((char *)p, end - p, "</body></html>");
|
|
|
|
return (int)lws_ptr_diff(p, start);
|
|
}
|
|
|
|
static int
|
|
callback_deaddrop(struct lws *wsi, enum lws_callback_reasons reason,
|
|
void *user, void *in, size_t len)
|
|
{
|
|
struct vhd_deaddrop *vhd = (struct vhd_deaddrop *)
|
|
lws_protocol_vh_priv_get(lws_get_vhost(wsi),
|
|
lws_get_protocol(wsi));
|
|
struct pss_deaddrop *pss = (struct pss_deaddrop *)user;
|
|
uint8_t buf[LWS_PRE + LWS_RECOMMENDED_MIN_HEADER_SPACE],
|
|
*start = &buf[LWS_PRE], *p = start,
|
|
*end = &buf[sizeof(buf) - LWS_PRE - 1];
|
|
char fname[256], *wp;
|
|
const char *cp;
|
|
int n, m, was;
|
|
|
|
switch (reason) {
|
|
|
|
case LWS_CALLBACK_PROTOCOL_INIT: /* per vhost */
|
|
lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi),
|
|
lws_get_protocol(wsi),
|
|
sizeof(struct vhd_deaddrop));
|
|
|
|
vhd = (struct vhd_deaddrop *)
|
|
lws_protocol_vh_priv_get(lws_get_vhost(wsi),
|
|
lws_get_protocol(wsi));
|
|
|
|
vhd->context = lws_get_context(wsi);
|
|
vhd->vh = lws_get_vhost(wsi);
|
|
vhd->protocol = lws_get_protocol(wsi);
|
|
vhd->max_size = 20 * 1024 * 1024; /* default without pvo */
|
|
|
|
if (!lws_pvo_get_str(in, "max-size", &cp))
|
|
vhd->max_size = atoll(cp);
|
|
if (lws_pvo_get_str(in, "upload-dir", &vhd->upload_dir)) {
|
|
lwsl_err("%s: requires 'upload-dir' pvo\n", __func__);
|
|
return -1;
|
|
}
|
|
|
|
scan_upload_dir(vhd);
|
|
|
|
lwsl_notice(" deaddrop: vh %s, upload dir %s, max size %llu\n",
|
|
lws_get_vhost_name(vhd->vh), vhd->upload_dir,
|
|
vhd->max_size);
|
|
break;
|
|
|
|
case LWS_CALLBACK_PROTOCOL_DESTROY:
|
|
lwsac_free(&vhd->lwsac_head);
|
|
break;
|
|
|
|
/* WS-related */
|
|
|
|
case LWS_CALLBACK_ESTABLISHED:
|
|
pss->vhd = vhd;
|
|
pss->wsi = wsi;
|
|
/* add ourselves to the list of live pss held in the vhd */
|
|
pss->pss_list = vhd->pss_head;
|
|
vhd->pss_head = pss;
|
|
|
|
m = lws_hdr_copy(wsi, pss->user, sizeof(pss->user),
|
|
WSI_TOKEN_HTTP_AUTHORIZATION);
|
|
if (m > 0)
|
|
lwsl_info("%s: basic auth user: %s\n",
|
|
__func__, pss->user);
|
|
else
|
|
pss->user[0] = '\0';
|
|
|
|
start_sending_dir(pss);
|
|
lws_callback_on_writable(wsi);
|
|
return 0;
|
|
|
|
case LWS_CALLBACK_CLOSED:
|
|
if (pss->lwsac_head)
|
|
lwsac_unreference(&pss->lwsac_head);
|
|
/* remove our closing pss from the list of live pss */
|
|
lws_start_foreach_llp(struct pss_deaddrop **,
|
|
ppss, vhd->pss_head) {
|
|
if (*ppss == pss) {
|
|
*ppss = pss->pss_list;
|
|
break;
|
|
}
|
|
} lws_end_foreach_llp(ppss, pss_list);
|
|
return 0;
|
|
|
|
case LWS_CALLBACK_RECEIVE:
|
|
/* we get this kind of thing {"del":"agreen/no-entry.svg"} */
|
|
if (!pss || len < 10)
|
|
break;
|
|
|
|
if (strncmp((const char *)in, "{\"del\":\"", 8))
|
|
break;
|
|
|
|
cp = strchr((const char *)in, '/');
|
|
if (cp) {
|
|
n = ((void *)cp - in) - 8;
|
|
|
|
if ((int)strlen(pss->user) != n ||
|
|
memcmp(pss->user, ((const char *)in) + 8, n)) {
|
|
lwsl_notice("%s: del: auth mismatch "
|
|
" '%s' '%s' (%d)\n",
|
|
__func__, pss->user,
|
|
((const char *)in) + 8, n);
|
|
break;
|
|
}
|
|
}
|
|
|
|
lws_strncpy(fname, ((const char *)in) + 8, sizeof(fname));
|
|
lws_filename_purify_inplace(fname);
|
|
wp = strchr((const char *)fname, '\"');
|
|
if (wp)
|
|
*wp = '\0';
|
|
|
|
lws_snprintf((char *)buf, sizeof(buf), "%s/%s", vhd->upload_dir,
|
|
fname);
|
|
|
|
lwsl_notice("%s: del: path %s\n", __func__, (const char *)buf);
|
|
|
|
if (unlink((const char *)buf) < 0)
|
|
lwsl_err("%s: unlink %s failed\n", __func__,
|
|
(const char *)buf);
|
|
|
|
scan_upload_dir(vhd);
|
|
break;
|
|
|
|
case LWS_CALLBACK_SERVER_WRITEABLE:
|
|
if (pss->lwsac_head && !pss->dire)
|
|
return 0;
|
|
|
|
was = 0;
|
|
if (pss->first) {
|
|
p += lws_snprintf((char *)p, lws_ptr_diff(end, p),
|
|
"{\"max_size\":%llu, \"files\": [",
|
|
vhd->max_size);
|
|
was = 1;
|
|
}
|
|
|
|
m = 5;
|
|
while (m-- && pss->dire) {
|
|
p += lws_snprintf((char *)p, lws_ptr_diff(end, p),
|
|
"%c{\"name\":\"%s\", "
|
|
"\"size\":%llu,"
|
|
"\"mtime\":%llu,"
|
|
"\"yours\":%d}",
|
|
pss->first ? ' ' : ',',
|
|
(const char *)&pss->dire[1],
|
|
pss->dire->size,
|
|
(unsigned long long)pss->dire->mtime,
|
|
!strcmp(pss->user, pss->dire->user) &&
|
|
pss->user[0]);
|
|
pss->first = 0;
|
|
pss->dire = lp_to_dir_entry(pss->dire->next, next);
|
|
}
|
|
|
|
if (!pss->dire) {
|
|
p += lws_snprintf((char *)p, lws_ptr_diff(end, p),
|
|
"]}");
|
|
if (pss->lwsac_head) {
|
|
lwsac_unreference(&pss->lwsac_head);
|
|
pss->lwsac_head = NULL;
|
|
}
|
|
}
|
|
|
|
n = lws_write(wsi, start, lws_ptr_diff(p, start),
|
|
lws_write_ws_flags(LWS_WRITE_TEXT, was,
|
|
!pss->dire));
|
|
if (n < 0) {
|
|
lwsl_notice("%s: ws write failed\n", __func__);
|
|
return 1;
|
|
}
|
|
if (pss->dire) {
|
|
lws_callback_on_writable(wsi);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* ie, we finished */
|
|
|
|
if (pss->filelist_version != pss->vhd->filelist_version) {
|
|
lwsl_info("%s: restart send\n", __func__);
|
|
/* what we just sent is already out of date */
|
|
start_sending_dir(pss);
|
|
lws_callback_on_writable(wsi);
|
|
}
|
|
|
|
return 0;
|
|
|
|
/* POST-related */
|
|
|
|
case LWS_CALLBACK_HTTP_BODY:
|
|
|
|
/* create the POST argument parser if not already existing */
|
|
if (!pss->spa) {
|
|
pss->vhd = vhd;
|
|
pss->wsi = wsi;
|
|
pss->spa = lws_spa_create(wsi, param_names,
|
|
LWS_ARRAY_SIZE(param_names),
|
|
1024, file_upload_cb, pss);
|
|
if (!pss->spa)
|
|
return -1;
|
|
|
|
pss->filename[0] = '\0';
|
|
pss->file_length = 0;
|
|
/* catchall */
|
|
pss->response_code = HTTP_STATUS_SERVICE_UNAVAILABLE;
|
|
|
|
m = lws_hdr_copy(wsi, pss->user, sizeof(pss->user),
|
|
WSI_TOKEN_HTTP_AUTHORIZATION);
|
|
if (m > 0)
|
|
lwsl_info("basic auth user: %s\n", pss->user);
|
|
else
|
|
pss->user[0] = '\0';
|
|
}
|
|
|
|
/* let it parse the POST data */
|
|
if (lws_spa_process(pss->spa, in, (int)len)) {
|
|
lwsl_notice("spa saw a problem\n");
|
|
/* some problem happened */
|
|
lws_spa_finalize(pss->spa);
|
|
|
|
pss->completed = 1;
|
|
lws_callback_on_writable(wsi);
|
|
}
|
|
break;
|
|
|
|
case LWS_CALLBACK_HTTP_BODY_COMPLETION:
|
|
/* call to inform no more payload data coming */
|
|
lws_spa_finalize(pss->spa);
|
|
|
|
pss->completed = 1;
|
|
lws_callback_on_writable(wsi);
|
|
break;
|
|
|
|
case LWS_CALLBACK_HTTP_WRITEABLE:
|
|
if (!pss->completed)
|
|
break;
|
|
|
|
p = (unsigned char *)pss->result + LWS_PRE;
|
|
start = p;
|
|
end = p + sizeof(pss->result) - LWS_PRE - 1;
|
|
|
|
if (!pss->sent_headers) {
|
|
n = format_result(pss);
|
|
|
|
if (lws_add_http_header_status(wsi, pss->response_code,
|
|
&p, end))
|
|
goto bail;
|
|
|
|
if (lws_add_http_header_by_token(wsi,
|
|
WSI_TOKEN_HTTP_CONTENT_TYPE,
|
|
(unsigned char *)"text/html", 9,
|
|
&p, end))
|
|
goto bail;
|
|
if (lws_add_http_header_content_length(wsi, n, &p, end))
|
|
goto bail;
|
|
if (lws_finalize_http_header(wsi, &p, end))
|
|
goto bail;
|
|
|
|
/* first send the headers ... */
|
|
n = lws_write(wsi, start, lws_ptr_diff(p, start),
|
|
LWS_WRITE_HTTP_HEADERS |
|
|
LWS_WRITE_H2_STREAM_END);
|
|
if (n < 0)
|
|
goto bail;
|
|
|
|
pss->sent_headers = 1;
|
|
lws_callback_on_writable(wsi);
|
|
break;
|
|
}
|
|
|
|
if (!pss->sent_body) {
|
|
n = format_result(pss);
|
|
n = lws_write(wsi, (unsigned char *)start, n,
|
|
LWS_WRITE_HTTP_FINAL);
|
|
|
|
pss->sent_body = 1;
|
|
if (n < 0) {
|
|
lwsl_err("%s: writing body failed\n", __func__);
|
|
return 1;
|
|
}
|
|
goto try_to_reuse;
|
|
}
|
|
break;
|
|
|
|
case LWS_CALLBACK_HTTP_DROP_PROTOCOL:
|
|
/* called when our wsi user_space is going to be destroyed */
|
|
if (pss->spa) {
|
|
lws_spa_destroy(pss->spa);
|
|
pss->spa = NULL;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
|
|
bail:
|
|
|
|
return 1;
|
|
|
|
try_to_reuse:
|
|
if (lws_http_transaction_completed(wsi))
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
#define LWS_PLUGIN_PROTOCOL_DEADDROP \
|
|
{ \
|
|
"lws-deaddrop", \
|
|
callback_deaddrop, \
|
|
sizeof(struct pss_deaddrop), \
|
|
1024, \
|
|
0, NULL, 0 \
|
|
}
|
|
|
|
#if !defined (LWS_PLUGIN_STATIC)
|
|
|
|
static const struct lws_protocols protocols[] = {
|
|
LWS_PLUGIN_PROTOCOL_DEADDROP
|
|
};
|
|
|
|
LWS_VISIBLE int
|
|
init_protocol_deaddrop(struct lws_context *context,
|
|
struct lws_plugin_capability *c)
|
|
{
|
|
if (c->api_magic != LWS_PLUGIN_API_MAGIC) {
|
|
lwsl_err("Plugin API %d, library API %d", LWS_PLUGIN_API_MAGIC,
|
|
c->api_magic);
|
|
return 1;
|
|
}
|
|
|
|
c->protocols = protocols;
|
|
c->count_protocols = LWS_ARRAY_SIZE(protocols);
|
|
c->extensions = NULL;
|
|
c->count_extensions = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
LWS_VISIBLE int
|
|
destroy_protocol_deaddrop(struct lws_context *context)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
#endif
|