diff --git a/CMakeLists.txt b/CMakeLists.txt
index ad694cbb..eceddacd 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -100,6 +100,7 @@ option(LWS_WITH_SERVER_STATUS "Support json + jscript server monitoring" OFF)
option(LWS_WITH_LEJP "With the Lightweight JSON Parser" OFF)
option(LWS_WITH_LEJP_CONF "With LEJP configuration parser as used by lwsws" OFF)
option(LWS_WITH_SMTP "Provide SMTP support" OFF)
+option(LWS_WITH_STATEFUL_URLDECODE "Provide stateful URLDECODE apis" OFF)
if (LWS_WITH_LWSWS)
message(STATUS "LWS_WITH_LWSWS --> Enabling LWS_WITH_PLUGINS and LWS_WITH_LIBUV")
@@ -111,6 +112,10 @@ if (LWS_WITH_LWSWS)
set(LWS_WITH_LEJP_CONF 1)
endif()
+if (LWS_WITH_PLUGINS)
+ set(LWS_WITH_STATEFUL_URLDECODE 1)
+endif()
+
if (LWS_WITH_PLUGINS AND NOT LWS_WITH_LIBUV)
message(STATUS "LWS_WITH_PLUGINS --> Enabling LWS_WITH_LIBUV")
set(LWS_WITH_LIBUV 1)
@@ -1526,6 +1531,8 @@ message(" LWS_WITH_SERVER_STATUS = ${LWS_WITH_SERVER_STATUS}")
message(" LWS_WITH_LEJP = ${LWS_WITH_LEJP}")
message(" LWS_WITH_LEJP_CONF = ${LWS_WITH_LEJP_CONF}")
message(" LWS_WITH_SMTP = ${LWS_WITH_SMTP}")
+message(" LWS_WITH_STATEFUL_URLDECODE = ${LWS_WITH_STATEFUL_URLDECODE}")
+
message("---------------------------------------------------------------------")
# These will be available to parent projects including libwebsockets using add_subdirectory()
diff --git a/lib/libwebsockets.c b/lib/libwebsockets.c
index 44039670..e229ab3a 100755
--- a/lib/libwebsockets.c
+++ b/lib/libwebsockets.c
@@ -208,7 +208,7 @@ lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason)
wsi->u.http.fd != LWS_INVALID_FILE) {
lws_plat_file_close(wsi, wsi->u.http.fd);
wsi->u.http.fd = LWS_INVALID_FILE;
- wsi->vhost->protocols[0].callback(wsi,
+ wsi->vhost->protocols->callback(wsi,
LWS_CALLBACK_CLOSED_HTTP, wsi->user_space, NULL, 0);
}
if (wsi->socket_is_permanently_unusable ||
@@ -247,9 +247,14 @@ lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason)
wsi->mode == LWSCM_WSCL_ISSUE_HANDSHAKE)
goto just_kill_connection;
- if (wsi->mode == LWSCM_HTTP_SERVING)
- wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_CLOSED_HTTP,
+ if (wsi->mode == LWSCM_HTTP_SERVING) {
+ if (wsi->user_space)
+ wsi->vhost->protocols->callback(wsi,
+ LWS_CALLBACK_HTTP_DROP_PROTOCOL,
wsi->user_space, NULL, 0);
+ wsi->vhost->protocols->callback(wsi, LWS_CALLBACK_CLOSED_HTTP,
+ wsi->user_space, NULL, 0);
+ }
if (wsi->mode == LWSCM_HTTP_CLIENT)
wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_CLOSED_CLIENT_HTTP,
wsi->user_space, NULL, 0);
@@ -483,7 +488,7 @@ just_kill_connection:
wsi->user_space, NULL, 0);
} else if (wsi->mode == LWSCM_HTTP_SERVING_ACCEPTED) {
lwsl_debug("calling back CLOSED_HTTP\n");
- wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_CLOSED_HTTP,
+ wsi->vhost->protocols->callback(wsi, LWS_CALLBACK_CLOSED_HTTP,
wsi->user_space, NULL, 0 );
} else if (wsi->mode == LWSCM_WSCL_WAITING_SERVER_REPLY ||
wsi->mode == LWSCM_WSCL_WAITING_CONNECT) {
@@ -1688,10 +1693,11 @@ lws_socket_bind(struct lws_vhost *vhost, int sockfd, int port,
return port;
}
-LWS_VISIBLE LWS_EXTERN int
-lws_urlencode(const char *in, int inlen, char *out, int outlen)
+static const char *hex = "0123456789ABCDEF";
+
+static int
+urlencode(const char *in, int inlen, char *out, int outlen)
{
- const char *hex = "0123456789ABCDEF";
char *start = out, *end = out + outlen;
while (inlen-- && out < end - 4) {
@@ -1701,13 +1707,18 @@ lws_urlencode(const char *in, int inlen, char *out, int outlen)
*in == '-' ||
*in == '_' ||
*in == '.' ||
- *in == '~')
+ *in == '~') {
*out++ = *in++;
- else {
- *out++ = '%';
- *out++ = hex[(*in) >> 4];
- *out++ = hex[(*in++) & 15];
+ continue;
}
+ if (*in == ' ') {
+ *out++ = '+';
+ in++;
+ continue;
+ }
+ *out++ = '%';
+ *out++ = hex[(*in) >> 4];
+ *out++ = hex[(*in++) & 15];
}
*out = '\0';
@@ -1717,6 +1728,140 @@ lws_urlencode(const char *in, int inlen, char *out, int outlen)
return out - start;
}
+/**
+ * lws_sql_purify() - like strncpy but with escaping for sql quotes
+ *
+ * @escaped: output buffer
+ * @string: input buffer ('/0' terminated)
+ * @len: output buffer max length
+ *
+ * Because escaping expands the output string, it's not
+ * possible to do it in-place, ie, with escaped == string
+ */
+
+LWS_VISIBLE LWS_EXTERN const char *
+lws_sql_purify(char *escaped, const char *string, int len)
+{
+ const char *p = string;
+ char *q = escaped;
+
+ while (*p && len-- > 2) {
+ if (*p == '\'') {
+ *q++ = '\\';
+ *q++ = '\'';
+ len --;
+ p++;
+ } else
+ *q++ = *p++;
+ }
+ *q = '\0';
+
+ return escaped;
+}
+
+/**
+ * lws_urlencode() - like strncpy but with urlencoding
+ *
+ * @escaped: output buffer
+ * @string: input buffer ('/0' terminated)
+ * @len: output buffer max length
+ *
+ * Because urlencoding expands the output string, it's not
+ * possible to do it in-place, ie, with escaped == string
+ */
+
+LWS_VISIBLE LWS_EXTERN const char *
+lws_urlencode(char *escaped, const char *string, int len)
+{
+ const char *p = string;
+ char *q = escaped;
+
+ while (*p && len-- > 3) {
+ if (*p == ' ') {
+ *q++ = '+';
+ p++;
+ continue;
+ }
+ if ((*p >= '0' && *p <= '9') ||
+ (*p >= 'A' && *p <= 'Z') ||
+ (*p >= 'a' && *p <= 'z')) {
+ *q++ = *p++;
+ continue;
+ }
+ *q++ = '%';
+ *q++ = hex[(*p >> 4) & 0xf];
+ *q++ = hex[*p & 0xf];
+
+ len -= 2;
+ p++;
+ }
+ *q = '\0';
+
+ return escaped;
+}
+
+/**
+ * lws_urldecode() - like strncpy but with urldecoding
+ *
+ * @string: output buffer
+ * @escaped: input buffer ('\0' terminated)
+ * @len: output buffer max length
+ *
+ * This is only useful for '\0' terminated strings
+ *
+ * Since urldecoding only shrinks the output string, it is possible to
+ * do it in-place, ie, string == escaped
+ */
+
+LWS_VISIBLE LWS_EXTERN int
+lws_urldecode(char *string, const char *escaped, int len)
+{
+ int state = 0, n;
+ char sum = 0;
+
+ while (*escaped && len) {
+ switch (state) {
+ case 0:
+ if (*escaped == '%') {
+ state++;
+ escaped++;
+ continue;
+ }
+ if (*escaped == '+') {
+ escaped++;
+ *string++ = ' ';
+ len--;
+ continue;
+ }
+ *string++ = *escaped++;
+ len--;
+ break;
+ case 1:
+ n = char_to_hex(*escaped);
+ if (n < 0)
+ return -1;
+ escaped++;
+ sum = n << 4;
+ state++;
+ break;
+
+ case 2:
+ n = char_to_hex(*escaped);
+ if (n < 0)
+ return -1;
+ escaped++;
+ *string++ = sum | n;
+ len--;
+ state = 0;
+ break;
+ }
+
+ }
+ *string = '\0';
+
+ return 0;
+}
+
LWS_VISIBLE LWS_EXTERN int
lws_finalize_startup(struct lws_context *context)
{
@@ -1902,7 +2047,7 @@ lws_cgi(struct lws *wsi, const char * const *exec_array, int script_uri_path_len
*p++ = *t++;
if (*t == '=')
*p++ = *t++;
- i = lws_urlencode(t, i- (t - tok), p, end - p);
+ i = urlencode(t, i- (t - tok), p, end - p);
if (i > 0) {
p += i;
*p++ = '&';
diff --git a/lib/libwebsockets.h b/lib/libwebsockets.h
index 254d5721..bb7c6301 100644
--- a/lib/libwebsockets.h
+++ b/lib/libwebsockets.h
@@ -475,6 +475,7 @@ enum lws_callback_reasons {
LWS_CALLBACK_RECEIVE_CLIENT_HTTP = 46,
LWS_CALLBACK_COMPLETED_CLIENT_HTTP = 47,
LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ = 48,
+ LWS_CALLBACK_HTTP_DROP_PROTOCOL = 49,
/****** add new things just above ---^ ******/
@@ -1779,6 +1780,69 @@ lws_add_http_header_status(struct lws *wsi,
unsigned int code, unsigned char **p,
unsigned char *end);
+LWS_VISIBLE LWS_EXTERN const char *
+lws_urlencode(char *escaped, const char *string, int len);
+
+LWS_VISIBLE LWS_EXTERN const char *
+lws_sql_purify(char *escaped, const char *string, int len);
+
+
+
+/*
+ * URLDECODE 1 / 2
+ *
+ * This simple urldecode only operates until the first '\0' and requires the
+ * data to exist all at once
+ */
+
+LWS_VISIBLE LWS_EXTERN int
+lws_urldecode(char *string, const char *escaped, int len);
+
+
+/*
+ * URLDECODE 2 / 2
+ *
+ * These apis let you manage a form data parser that is capable of handling
+ * both urlencoded POST bodies and multipart ones (including file upload)
+ *
+ * Since it's stateful, and the decoded area is malloc'd, this is robust
+ * enough to handle the form data coming in multiple POST_BODY packets without
+ * having to get into any special code.
+ */
+
+enum lws_spa_fileupload_states {
+ LWS_UFS_CONTENT,
+ LWS_UFS_FINAL_CONTENT,
+ LWS_UFS_OPEN
+};
+
+typedef int (*lws_spa_fileupload_cb)(void *data, const char *name,
+ const char *filename, char *buf, int len,
+ enum lws_spa_fileupload_states state);
+
+struct lws_spa;
+
+LWS_VISIBLE LWS_EXTERN struct lws_spa *
+lws_spa_create(struct lws *wsi, const char * const *param_names,
+ int count_params, int max_storage, lws_spa_fileupload_cb opt_cb,
+ void *opt_data);
+
+LWS_VISIBLE LWS_EXTERN int
+lws_spa_process(struct lws_spa *ludspa, const char *in, int len);
+
+LWS_VISIBLE LWS_EXTERN int
+lws_spa_finalize(struct lws_spa *ludspa);
+
+LWS_VISIBLE LWS_EXTERN int
+lws_spa_get_length(struct lws_spa *ludspa, int n);
+
+LWS_VISIBLE LWS_EXTERN const char *
+lws_spa_get_string(struct lws_spa *ludspa, int n);
+
+LWS_VISIBLE LWS_EXTERN int
+lws_spa_destroy(struct lws_spa *ludspa);
+
+
LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT
lws_http_redirect(struct lws *wsi, int code, const unsigned char *loc, int len,
unsigned char **p, unsigned char *end);
diff --git a/lib/server.c b/lib/server.c
index ce885de2..e2456b19 100644
--- a/lib/server.c
+++ b/lib/server.c
@@ -716,14 +716,6 @@ lws_http_action(struct lws *wsi)
goto after;
}
- /* deferred cleanup and reset to protocols[0] */
-
- if (wsi->protocol != &wsi->vhost->protocols[0])
- if (!wsi->user_space_externally_allocated)
- lws_free_set_NULL(wsi->user_space);
-
- wsi->protocol = &wsi->vhost->protocols[0];
-
#ifdef LWS_WITH_CGI
/* did we hit something with a cgi:// origin? */
if (hit->origin_protocol == LWSMPRO_CGI) {
@@ -1279,10 +1271,17 @@ lws_http_transaction_completed(struct lws *wsi)
return 1;
}
+ n = wsi->protocol->callback(wsi, LWS_CALLBACK_HTTP_DROP_PROTOCOL,
+ wsi->user_space, NULL, 0);
+
+ if (!wsi->user_space_externally_allocated)
+ lws_free_set_NULL(wsi->user_space);
+
+ wsi->protocol = &wsi->vhost->protocols[0];
+
/* otherwise set ourselves up ready to go again */
wsi->state = LWSS_HTTP;
wsi->mode = LWSCM_HTTP_SERVING;
- /* reset of non [0] protocols (and freeing of user_space) is deferred */
wsi->u.http.content_length = 0;
wsi->hdr_parsing_completed = 0;
#ifdef LWS_WITH_ACCESS_LOG
@@ -1921,3 +1920,597 @@ lws_server_get_canonical_hostname(struct lws_context *context,
(void)context;
#endif
}
+
+#define LWS_MAX_ELEM_NAME 32
+
+enum urldecode_stateful {
+ US_NAME,
+ US_IDLE,
+ US_PC1,
+ US_PC2,
+
+ MT_LOOK_BOUND_IN,
+ MT_HNAME,
+ MT_DISP,
+ MT_TYPE,
+ MT_IGNORE1,
+ MT_IGNORE2,
+};
+
+static const char * const mp_hdr[] = {
+ "content-disposition: ",
+ "content-type: ",
+ "\x0d\x0a"
+};
+
+typedef int (*lws_urldecode_stateful_cb)(void *data,
+ const char *name, char **buf, int len, int final);
+
+struct lws_urldecode_stateful {
+ char *out;
+ void *data;
+ char name[LWS_MAX_ELEM_NAME];
+ char temp[LWS_MAX_ELEM_NAME];
+ char content_type[32];
+ char content_disp[32];
+ char content_disp_filename[256];
+ char mime_boundary[128];
+ int out_len;
+ int pos;
+ int hdr_idx;
+ int mp;
+
+ unsigned int multipart_form_data:1;
+ unsigned int inside_quote:1;
+ unsigned int subname:1;
+ unsigned int boundary_real_crlf:1;
+
+ enum urldecode_stateful state;
+
+ lws_urldecode_stateful_cb output;
+};
+
+static struct lws_urldecode_stateful *
+lws_urldecode_s_create(struct lws *wsi, char *out, int out_len, void *data,
+ lws_urldecode_stateful_cb output)
+{
+ struct lws_urldecode_stateful *s = lws_zalloc(sizeof(*s));
+ char buf[200], *p;
+ int m = 0;
+
+ if (!s)
+ return NULL;
+
+ s->out = out;
+ s->out_len = out_len;
+ s->output = output;
+ s->pos = 0;
+ s->mp = 0;
+ s->state = US_NAME;
+ s->name[0] = '\0';
+ s->data = data;
+
+ if (lws_hdr_copy(wsi, buf, sizeof(buf), WSI_TOKEN_HTTP_CONTENT_TYPE) > 0) {
+ /* multipart/form-data; boundary=----WebKitFormBoundarycc7YgAPEIHvgE9Bf */
+
+ if (!strncmp(buf, "multipart/form-data", 19)) {
+ s->multipart_form_data = 1;
+ s->state = MT_LOOK_BOUND_IN;
+ s->mp = 2;
+ p = strstr(buf, "boundary=");
+ if (p) {
+ p += 9;
+ s->mime_boundary[m++] = '\x0d';
+ s->mime_boundary[m++] = '\x0a';
+ s->mime_boundary[m++] = '-';
+ s->mime_boundary[m++] = '-';
+ while (m < sizeof(s->mime_boundary) - 1 &&
+ *p && *p != ' ')
+ s->mime_boundary[m++] = *p++;
+
+ s->mime_boundary[m] = '\0';
+
+ lwsl_notice("boundary '%s'\n", s->mime_boundary);
+ }
+ }
+ }
+
+ return s;
+}
+
+static int
+lws_urldecode_s_process(struct lws_urldecode_stateful *s, const char *in, int len)
+{
+ int n, m, hit = 0;
+ char sum = 0;
+
+ while (len--) {
+ if (s->pos == s->out_len - s->mp - 1) {
+ if (s->output(s->data, s->name, &s->out, s->pos, 0))
+ return -1;
+
+ s->pos = 0;
+ }
+
+ switch (s->state) {
+
+ /* states for url arg style */
+
+ case US_NAME:
+ //lwsl_notice("US_NAME: %c\n", *in);
+ s->inside_quote = 0;
+ if (*in == '=') {
+ s->name[s->pos] = '\0';
+ s->pos = 0;
+ s->state = US_IDLE;
+ in++;
+ continue;
+ }
+ if (*in == '&') {
+ s->name[s->pos] = '\0';
+ if (s->output(s->data, s->name, &s->out, s->pos, 1))
+ return -1;
+ s->pos = 0;
+ s->state = US_IDLE;
+ in++;
+ continue;
+ }
+ if (s->pos >= sizeof(s->name) - 1) {
+ lwsl_notice("Name too long\n");
+ return -1;
+ }
+ s->name[s->pos++] = *in++;
+ break;
+ case US_IDLE:
+ //lwsl_notice("US_IDLE: %c\n", *in);
+ if (*in == '%') {
+ s->state++;
+ in++;
+ continue;
+ }
+ if (*in == '&') {
+ s->out[s->pos] = '\0';
+ if (s->output(s->data, s->name, &s->out, s->pos, 1))
+ return -1;
+ s->pos = 0;
+ s->state = US_NAME;
+ in++;
+ continue;
+ }
+ if (*in == '+') {
+ in++;
+ s->out[s->pos++] = ' ';
+ continue;
+ }
+ s->out[s->pos++] = *in++;
+ break;
+ case US_PC1:
+ n = char_to_hex(*in);
+ if (n < 0)
+ return -1;
+
+ in++;
+ sum = n << 4;
+ s->state++;
+ break;
+
+ case US_PC2:
+ n = char_to_hex(*in);
+ if (n < 0)
+ return -1;
+
+ in++;
+ s->out[s->pos++] = sum | n;
+ s->state = US_IDLE;
+ break;
+
+
+ /* states for multipart / mime style */
+
+ case MT_LOOK_BOUND_IN:
+ // lwsl_notice("MT_LOOK_BOUND_IN: %02x (%d)\n", *in, s->mp);
+ if (*in == s->mime_boundary[s->mp] &&
+ s->mime_boundary[s->mp]) {
+ in++;
+ s->mp++;
+ if (!s->mime_boundary[s->mp]) {
+ s->mp = 0;
+ s->state = MT_IGNORE1;
+
+ if (s->pos)
+ if (s->output(s->data, s->name,
+ &s->out, s->pos, 1))
+ return -1;
+
+ s->pos = 0;
+
+ s->content_disp[0] = '\0';
+ s->name[0] = '\0';
+ s->content_disp_filename[0] = '\0';
+ s->boundary_real_crlf = 1;
+ }
+ continue;
+ }
+ if (s->mp) {
+ n = 0;
+ if (!s->boundary_real_crlf)
+ n = 2;
+ memcpy(s->out + s->pos, s->mime_boundary + n, s->mp - n);
+ s->pos += s->mp;
+ }
+
+ s->out[s->pos++] = *in;
+ in++;
+ s->mp = 0;
+ break;
+
+ case MT_HNAME:
+
+ m = 0;
+ for (n = 0; n < ARRAY_SIZE(mp_hdr); n++)
+ if (tolower(*in) == mp_hdr[n][s->mp]) {
+ m++;
+ hit = n;
+ }
+
+ in++;
+ if (m > 1) {
+ s->mp++;
+ continue;
+ }
+ if (!m) {
+ s->mp = 0;
+ continue;
+ }
+
+ s->mp++;
+ if (mp_hdr[hit][s->mp])
+ continue;
+
+ /* ie, m == 1 */
+
+ s->mp = 0;
+ s->temp[0] = '\0';
+ s->subname = 0;
+
+ if (hit == 2)
+ s->state = MT_LOOK_BOUND_IN;
+ else
+ s->state += hit + 1;
+ break;
+
+ case MT_DISP:
+ /* form-data; name="file"; filename="t.txt" */
+
+ if (*in == '\x0d') {
+ lwsl_debug("disp: '%s', '%s', '%s'\n",
+ s->content_disp, s->name,
+ s->content_disp_filename);
+
+ if (s->content_disp_filename[0])
+ if (s->output(s->data, s->name,
+ &s->out, s->pos, LWS_UFS_OPEN))
+ return -1;
+ s->state = MT_IGNORE2;
+ goto done;
+ }
+ if (*in == ';') {
+ s->subname = 1;
+ s->temp[0] = '\0';
+ s->mp = 0;
+ goto done;
+ }
+
+ if (*in == '\"') {
+ s->inside_quote ^= 1;
+ goto done;
+ }
+
+ if (s->subname) {
+ if (*in == '=') {
+ s->temp[s->mp] = '\0';
+ s->subname = 0;
+ s->mp = 0;
+ goto done;
+ }
+ if (s->mp < sizeof(s->temp) - 1 &&
+ (*in != ' ' || s->inside_quote))
+ s->temp[s->mp++] = *in;
+ goto done;
+ }
+
+ if (!s->temp[0]) {
+ if (s->mp < sizeof(s->content_disp) - 1)
+ s->content_disp[s->mp++] = *in;
+ s->content_disp[s->mp] = '\0';
+ goto done;
+ }
+
+ if (!strcmp(s->temp, "name")) {
+ if (s->mp < sizeof(s->name) - 1)
+ s->name[s->mp++] = *in;
+ s->name[s->mp] = '\0';
+ goto done;
+ }
+
+ if (!strcmp(s->temp, "filename")) {
+ if (s->mp < sizeof(s->content_disp_filename) - 1)
+ s->content_disp_filename[s->mp++] = *in;
+ s->content_disp_filename[s->mp] = '\0';
+ goto done;
+ }
+done:
+ in++;
+ break;
+
+ case MT_TYPE:
+ if (*in == '\x0d')
+ s->state = MT_IGNORE2;
+ else {
+ if (s->mp < sizeof(s->content_type) - 1)
+ s->content_type[s->mp++] = *in;
+ s->content_type[s->mp] = '\0';
+ }
+ in++;
+ break;
+
+ case MT_IGNORE1:
+ if (*in == '\x0d')
+ s->state = MT_IGNORE2;
+ in++;
+ break;
+
+ case MT_IGNORE2:
+ s->mp = 0;
+ if (*in == '\x0a')
+ s->state = MT_HNAME;
+ in++;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static int
+lws_urldecode_s_destroy(struct lws_urldecode_stateful *s)
+{
+ int ret = 0;
+
+ lwsl_notice("%s\n", __func__);
+
+ if (s->state != US_IDLE)
+ ret = -1;
+
+ if (!ret)
+ if (s->output(s->data, s->name, &s->out, s->pos, 1))
+ ret = -1;
+
+ lws_free(s);
+
+ return ret;
+}
+
+struct lws_spa {
+ struct lws_urldecode_stateful *s;
+ lws_spa_fileupload_cb opt_cb;
+ const char * const *param_names;
+ int count_params;
+ char **params;
+ int *param_length;
+ void *opt_data;
+
+ char *storage;
+ char *end;
+ int max_storage;
+};
+
+static int
+lws_urldecode_spa_lookup(struct lws_spa *spa,
+ const char *name)
+{
+ int n;
+
+ for (n = 0; n < spa->count_params; n++)
+ if (!strcmp(spa->param_names[n], name))
+ return n;
+
+ return -1;
+}
+
+static int
+lws_urldecode_spa_cb(void *data, const char *name, char **buf, int len,
+ int final)
+{
+ struct lws_spa *spa =
+ (struct lws_spa *)data;
+ int n;
+
+ if (spa->s->content_disp_filename[0]) {
+ if (spa->opt_cb) {
+ n = spa->opt_cb(spa->opt_data, name,
+ spa->s->content_disp_filename,
+ *buf, len, final);
+
+ if (n < 0)
+ return -1;
+ }
+ return 0;
+ }
+ n = lws_urldecode_spa_lookup(spa, name);
+
+ if (n == -1 || !len) /* unrecognized */
+ return 0;
+
+ if (!spa->params[n])
+ spa->params[n] = *buf;
+
+ if ((*buf) + len >= spa->end) {
+ lwsl_notice("%s: exceeded storage\n", __func__);
+ return -1;
+ }
+
+ spa->param_length[n] += len;
+
+ /* move it on inside storage */
+ (*buf) += len;
+ *((*buf)++) = '\0';
+
+ spa->s->out_len -= len + 1;
+
+ return 0;
+}
+
+/**
+ * lws_spa_create() - create urldecode parser
+ *
+ * @wsi: lws connection (used to find Content Type)
+ * @param_names: array of form parameter names, like "username"
+ * @count_params: count of param_names
+ * @max_storage: total amount of form parameter values we can store
+ * @opt_cb: NULL, or callback to receive file upload data.
+ * @opt_data: NULL, or user pointer provided to opt_cb.
+ *
+ * Creates a urldecode parser and initializes it.
+ *
+ * @opt_cb can be NULL if you just want normal name=value parsing, however
+ * if one or more entries in your form are bulk data (file transfer), you
+ * can provide this callback and filter on the name callback parameter to
+ * treat that urldecoded data separately. The callback should return -1
+ * in case of fatal error, and 0 if OK.
+ */
+
+LWS_VISIBLE LWS_EXTERN struct lws_spa *
+lws_spa_create(struct lws *wsi, const char * const *param_names,
+ int count_params, int max_storage,
+ lws_spa_fileupload_cb opt_cb, void *opt_data)
+{
+ struct lws_spa *spa = lws_malloc(sizeof(*spa));
+
+ if (!spa)
+ return NULL;
+
+ spa->param_names = param_names;
+ spa->count_params = count_params;
+ spa->max_storage = max_storage;
+ spa->opt_cb = opt_cb;
+ spa->opt_data = opt_data;
+
+ spa->storage = lws_malloc(max_storage);
+ if (!spa->storage)
+ goto bail2;
+ spa->end = spa->storage + max_storage - 1;
+
+ spa->params = lws_zalloc(sizeof(char *) * count_params);
+ if (!spa->params)
+ goto bail3;
+
+ spa->s = lws_urldecode_s_create(wsi, spa->storage, max_storage, spa,
+ lws_urldecode_spa_cb);
+ if (!spa->s)
+ goto bail4;
+
+ spa->param_length = lws_zalloc(sizeof(int) * count_params);
+ if (!spa->param_length)
+ goto bail5;
+
+ return spa;
+
+bail5:
+ lws_urldecode_s_destroy(spa->s);
+bail4:
+ lws_free(spa->params);
+bail3:
+ lws_free(spa->storage);
+bail2:
+ lws_free(spa);
+
+ return NULL;
+}
+
+/**
+ * lws_spa_process() - parses a chunk of input data
+ *
+ * @ludspa: the parser object previously created
+ * @in: incoming, urlencoded data
+ * @len: count of bytes valid at @in
+ */
+
+LWS_VISIBLE LWS_EXTERN int
+lws_spa_process(struct lws_spa *ludspa, const char *in, int len)
+{
+ return lws_urldecode_s_process(ludspa->s, in, len);
+}
+
+/**
+ * lws_spa_get_length() - return length of parameter value
+ *
+ * @ludspa: the parser object previously created
+ * @n: parameter ordinal to return length of value for
+ */
+
+LWS_VISIBLE LWS_EXTERN int
+lws_spa_get_length(struct lws_spa *ludspa, int n)
+{
+ if (n >= ludspa->count_params)
+ return 0;
+
+ return ludspa->param_length[n];
+}
+
+/**
+ * lws_spa_get_string() - return pointer to parameter value
+ *
+ * @ludspa: the parser object previously created
+ * @n: parameter ordinal to return pointer to value for
+ */
+
+LWS_VISIBLE LWS_EXTERN const char *
+lws_spa_get_string(struct lws_spa *ludspa, int n)
+{
+ if (n >= ludspa->count_params)
+ return NULL;
+
+ return ludspa->params[n];
+}
+
+/**
+ * lws_spa_finalize() - indicate incoming data completed
+ *
+ * @ludspa: the parser object previously created
+ */
+
+LWS_VISIBLE LWS_EXTERN int
+lws_spa_finalize(struct lws_spa *spa)
+{
+ if (spa->s) {
+ lws_urldecode_s_destroy(spa->s);
+ spa->s = NULL;
+ }
+
+ return 0;
+}
+
+/**
+ * lws_spa_destroy() - destroy parser object
+ *
+ * @ludspa: the parser object previously created
+ */
+
+LWS_VISIBLE LWS_EXTERN int
+lws_spa_destroy(struct lws_spa *spa)
+{
+ int n = 0;
+
+ if (spa->s)
+ lws_urldecode_s_destroy(spa->s);
+
+ lwsl_debug("%s\n", __func__);
+
+ lws_free(spa->param_length);
+ lws_free(spa->params);
+ lws_free(spa->storage);
+ lws_free(spa);
+
+ return n;
+}
diff --git a/libwebsockets-api-doc.html b/libwebsockets-api-doc.html
index 3500041f..73fab956 100644
--- a/libwebsockets-api-doc.html
+++ b/libwebsockets-api-doc.html
@@ -412,6 +412,30 @@ unused.
You will not need this unless you are doing something special
+lws_get_urlarg_by_name - return pointer to arg value if present
+LWS_EXTERN const char *
+lws_get_urlarg_by_name
+(struct lws * wsi,
+const char * name,
+char * buf,
+int len)
+Arguments
+
+- wsi
+
- the connection to check
+
- name
+
- the arg name, like "token="
+
- buf
+
- the buffer to receive the urlarg (including the name= part)
+
- len
+
- the length of the buffer to receive the urlarg
+
+Description
+
+Returns NULL if not found or a pointer inside buf to just after the
+name= part.
+
+
lws_get_peer_simple - Get client address information without RDNS
const char *
lws_get_peer_simple
@@ -739,6 +763,164 @@ Notice it does so by dropping '\0' into input string
and the leading / on the path is consequently lost
+lws_sql_purify - like strncpy but with escaping for sql quotes
+LWS_EXTERN const char *
+lws_sql_purify
+(char * escaped,
+const char * string,
+int len)
+Arguments
+
+- escaped
+
- output buffer
+
- string
+
- input buffer ('/0' terminated)
+
- len
+
- output buffer max length
+
+Description
+
+Because escaping expands the output string, it's not
+possible to do it in-place, ie, with escaped == string
+
+
+lws_urlencode - like strncpy but with urlencoding
+LWS_EXTERN const char *
+lws_urlencode
+(char * escaped,
+const char * string,
+int len)
+Arguments
+
+- escaped
+
- output buffer
+
- string
+
- input buffer ('/0' terminated)
+
- len
+
- output buffer max length
+
+Description
+
+Because urlencoding expands the output string, it's not
+possible to do it in-place, ie, with escaped == string
+
+
+lws_urldecode - like strncpy but with urldecoding
+LWS_EXTERN int
+lws_urldecode
+(char * string,
+const char * escaped,
+int len)
+Arguments
+
+- string
+
- output buffer
+
- escaped
+
- input buffer ('\0' terminated)
+
- len
+
- output buffer max length
+
+Description
+
+This is only useful for '\0' terminated strings
+
+Since urldecoding only shrinks the output string, it is possible to
+do it in-place, ie, string == escaped
+
+
+lws_urldecode_spa_create - create urldecode parser
+LWS_EXTERN struct lws_urldecode_stateful_param_array *
+lws_urldecode_spa_create
+(struct lws * wsi,
+const char *const * param_names,
+int count_params,
+int max_storage,
+lws_urldecode_fileupload_cb opt_cb,
+void * opt_data)
+Arguments
+
+- wsi
+
- lws connection (used to find Content Type)
+
- param_names
+
- array of form parameter names, like "username"
+
- count_params
+
- count of param_names
+
- max_storage
+
- total amount of form parameter values we can store
+
- opt_cb
+
- NULL, or callback to receive file upload data.
+
- opt_data
+
- NULL, or user pointer provided to opt_cb.
+
+Description
+
+Creates a urldecode parser and initializes it.
+
+opt_cb can be NULL if you just want normal name=value parsing, however
+if one or more entries in your form are bulk data (file transfer), you
+can provide this callback and filter on the name callback parameter to
+treat that urldecoded data separately. The callback should return -1
+in case of fatal error, and 0 if OK.
+
+
+lws_urldecode_spa_process - parses a chunk of input data
+LWS_EXTERN int
+lws_urldecode_spa_process
+(struct lws_urldecode_stateful_param_array * ludspa,
+const char * in,
+int len)
+Arguments
+
+- ludspa
+
- the parser object previously created
+
- in
+
- incoming, urlencoded data
+
- len
+
- count of bytes valid at in
+
+
+lws_urldecode_spa_get_length - return length of parameter value
+LWS_EXTERN int
+lws_urldecode_spa_get_length
+(struct lws_urldecode_stateful_param_array * ludspa,
+int n)
+Arguments
+
+- ludspa
+
- the parser object previously created
+
- n
+
- parameter ordinal to return length of value for
+
+
+lws_urldecode_spa_get_string - return pointer to parameter value
+LWS_EXTERN const char *
+lws_urldecode_spa_get_string
+(struct lws_urldecode_stateful_param_array * ludspa,
+int n)
+Arguments
+
+- ludspa
+
- the parser object previously created
+
- n
+
- parameter ordinal to return pointer to value for
+
+
+lws_urldecode_spa_finalize - indicate incoming data completed
+LWS_EXTERN int
+lws_urldecode_spa_finalize
+(struct lws_urldecode_stateful_param_array * spa)
+Arguments
+
+
+
+lws_urldecode_spa_destroy - destroy parser object
+LWS_EXTERN int
+lws_urldecode_spa_destroy
+(struct lws_urldecode_stateful_param_array * spa)
+Arguments
+
+
+
lws_cgi - connected cgi process
LWS_EXTERN int
lws_cgi
@@ -1231,6 +1413,55 @@ would call it with a timeout_ms of 0, so it returns immediately if
nothing is pending, or as soon as it services whatever was pending.
+lws_email_init - Initialize a struct lws_email
+LWS_EXTERN int
+lws_email_init
+(struct lws_email * email,
+uv_loop_t * loop,
+int max_content)
+Arguments
+
+- email
+
- struct lws_email to init
+
- loop
+
- libuv loop to use
+
- max_content
+
- max email content size
+
+Description
+
+Prepares a struct lws_email for use ending SMTP
+
+
+lws_email_check - Request check for new email
+LWS_EXTERN void
+lws_email_check
+(struct lws_email * email)
+Arguments
+
+- email
+
- struct lws_email context to check
+
+Description
+
+Schedules a check for new emails in 1s... call this when you have queued an
+email for send.
+
+
+lws_email_destroy - stop using the struct lws_email
+LWS_EXTERN void
+lws_email_destroy
+(struct lws_email * email)
+Arguments
+
+- email
+
- the struct lws_email context
+
+Description
+
+Stop sending email using email and free allocations
+
+
struct lws_plat_file_ops - Platform-specific file operations
struct lws_plat_file_ops {
lws_filefd_type (*open) (struct lws *wsi, const char *filename,unsigned long *filelen, int flags);
@@ -1860,6 +2091,9 @@ header.
const struct lws_http_mount * mounts;
const char * server_string;
unsigned int pt_serv_buf_size;
+ unsigned int max_http_header_data2;
+ long ssl_options_set;
+ long ssl_options_clear;
};
Members
@@ -1987,6 +2221,15 @@ various service related features including file serving, it
defines the max chunk of file that can be sent at once.
At the risk of lws having to buffer failed large sends, it
can be increased to, eg, 128KiB to improve throughput.
+- max_http_header_data2
+
- if max_http_header_data is 0 and this
+is nonzero, this will be used in place of the default. It's
+like this for compatibility with the original short version,
+this is unsigned int length.
+
- ssl_options_set
+
- VHOST: Any bits set here will be set as SSL options
+
- ssl_options_clear
+
- VHOST: Any bits set here will be cleared as SSL options
Description
diff --git a/lws_config.h.in b/lws_config.h.in
index 21f1b536..9e61d6f1 100644
--- a/lws_config.h.in
+++ b/lws_config.h.in
@@ -96,6 +96,8 @@
#cmakedefine LWS_WITH_ACCESS_LOG
#cmakedefine LWS_WITH_SERVER_STATUS
+#cmakedefine LWS_WITH_STATEFUL_URLDECODE
+
/* Maximum supported service threads */
#define LWS_MAX_SMP ${LWS_MAX_SMP}
diff --git a/plugins/protocol_post_demo.c b/plugins/protocol_post_demo.c
index e2e0b732..9f0cab89 100644
--- a/plugins/protocol_post_demo.c
+++ b/plugins/protocol_post_demo.c
@@ -25,11 +25,68 @@
#include
struct per_session_data__post_demo {
- char post_string[256];
- char result[500 + LWS_PRE];
+ struct lws_spa *spa;
+ char result[LWS_PRE + 500];
int result_len;
+
+ char filename[256];
+ long file_length;
+ int fd;
};
+static const char * const param_names[] = {
+ "text",
+ "send",
+ "file",
+ "upload",
+};
+
+enum enum_param_names {
+ EPN_TEXT,
+ EPN_SEND,
+ EPN_FILE,
+ EPN_UPLOAD,
+};
+
+static int
+file_upload_cb(void *data, const char *name, const char *filename,
+ char *buf, int len, enum lws_spa_fileupload_states state)
+{
+ struct per_session_data__post_demo *pss =
+ (struct per_session_data__post_demo *)data;
+ int n;
+
+ switch (state) {
+ case LWS_UFS_OPEN:
+ strncpy(pss->filename, filename, sizeof(pss->filename) - 1);
+ /* we get the original filename in @filename arg, but for
+ * simple demo use a fixed name so we don't have to deal with
+ * attacks */
+ pss->fd = open("/tmp/post-file",
+ O_CREAT | O_TRUNC | O_RDWR, 0600);
+ 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 > 100000)
+ return 1;
+
+ n = write(pss->fd, buf, len);
+ lwsl_notice("%s: write %d says %d\n", __func__, len, n);
+ }
+ if (state == LWS_UFS_CONTENT)
+ break;
+ close(pss->fd);
+ pss->fd = LWS_INVALID_FILE;
+ break;
+ }
+
+ return 0;
+}
+
static int
callback_post_demo(struct lws *wsi, enum lws_callback_reasons reason,
void *user, void *in, size_t len)
@@ -41,34 +98,47 @@ callback_post_demo(struct lws *wsi, enum lws_callback_reasons reason,
int n;
switch (reason) {
-
case LWS_CALLBACK_HTTP_BODY:
- lwsl_debug("LWS_CALLBACK_HTTP_BODY: len %d\n", (int)len);
- strncpy(pss->post_string, in, sizeof (pss->post_string) -1);
- pss->post_string[sizeof(pss->post_string) - 1] = '\0';
+ /* create the POST argument parser if not already existing */
+ if (!pss->spa) {
+ pss->spa = lws_spa_create(wsi, param_names,
+ ARRAY_SIZE(param_names), 1024,
+ file_upload_cb, pss);
+ if (!pss->spa)
+ return -1;
- if (len < sizeof(pss->post_string) - 1)
- pss->post_string[len] = '\0';
+ pss->filename[0] = '\0';
+ pss->file_length = 0;
+ }
+
+ /* let it parse the POST data */
+ if (lws_spa_process(pss->spa, in, len))
+ return -1;
break;
- case LWS_CALLBACK_HTTP_WRITEABLE:
- lwsl_debug("LWS_CALLBACK_HTTP_WRITEABLE: sending %d\n", pss->result_len);
- n = lws_write(wsi, (unsigned char *)pss->result + LWS_PRE,
- pss->result_len, LWS_WRITE_HTTP);
- if (n < 0)
- return 1;
- goto try_to_reuse;
-
case LWS_CALLBACK_HTTP_BODY_COMPLETION:
lwsl_debug("LWS_CALLBACK_HTTP_BODY_COMPLETION\n");
- /*
- * the whole of the sent body arrived,
- * respond to the client with a redirect to show the
- * results
- */
- pss->result_len = sprintf((char *)pss->result + LWS_PRE,
- "Form results
'%s'
"
- "", pss->post_string);
+ /* call to inform no more payload data coming */
+ lws_spa_finalize(pss->spa);
+
+ p = (unsigned char *)pss->result + LWS_PRE;
+ end = p + sizeof(pss->result) - LWS_PRE - 1;
+ p += sprintf((char *)p,
+ "Form results (after urldecoding)
"
+ "Name | Length | Value |
");
+
+ for (n = 0; n < ARRAY_SIZE(param_names); n++)
+ p += snprintf((char *)p, end - p,
+ "%s | %d | %s |
",
+ param_names[n],
+ lws_spa_get_length(pss->spa, n),
+ lws_spa_get_string(pss->spa, n));
+
+ p += snprintf((char *)p, end - p, "
filename: %s, length %ld",
+ pss->filename, pss->file_length);
+
+ p += snprintf((char *)p, end - p, "");
+ pss->result_len = p - (unsigned char *)(pss->result + LWS_PRE);
p = buffer + LWS_PRE;
start = p;
@@ -89,13 +159,26 @@ callback_post_demo(struct lws *wsi, enum lws_callback_reasons reason,
if (n < 0)
return 1;
- /*
- * send the payload next time, in case would block after
- * headers
- */
lws_callback_on_writable(wsi);
break;
+ case LWS_CALLBACK_HTTP_WRITEABLE:
+ lwsl_debug("LWS_CALLBACK_HTTP_WRITEABLE: sending %d\n",
+ pss->result_len);
+ n = lws_write(wsi, (unsigned char *)pss->result + LWS_PRE,
+ pss->result_len, LWS_WRITE_HTTP);
+ if (n < 0)
+ return 1;
+ goto try_to_reuse;
+
+ 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;
}
diff --git a/test-server/test-server-http.c b/test-server/test-server-http.c
index 215dc226..c7b4264a 100644
--- a/test-server/test-server-http.c
+++ b/test-server/test-server-http.c
@@ -121,6 +121,60 @@ const char * get_mimetype(const char *file)
return NULL;
}
+
+static const char * const param_names[] = {
+ "text",
+ "send",
+ "file",
+ "upload",
+};
+
+enum enum_param_names {
+ EPN_TEXT,
+ EPN_SEND,
+ EPN_FILE,
+ EPN_UPLOAD,
+};
+
+static int
+file_upload_cb(void *data, const char *name, const char *filename,
+ char *buf, int len, enum lws_spa_fileupload_states state)
+{
+ struct per_session_data__http *pss =
+ (struct per_session_data__http *)data;
+ int n;
+
+ switch (state) {
+ case LWS_UFS_OPEN:
+ strncpy(pss->filename, filename, sizeof(pss->filename) - 1);
+ /* we get the original filename in @filename arg, but for
+ * simple demo use a fixed name so we don't have to deal with
+ * attacks */
+ pss->post_fd = open("/tmp/post-file",
+ O_CREAT | O_TRUNC | O_RDWR, 0600);
+ 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 > 100000)
+ return 1;
+
+ n = write(pss->post_fd, buf, len);
+ lwsl_notice("%s: write %d says %d\n", __func__, len, n);
+ }
+ if (state == LWS_UFS_CONTENT)
+ break;
+ close(pss->post_fd);
+ pss->post_fd = LWS_INVALID_FILE;
+ break;
+ }
+
+ return 0;
+}
+
/* this protocol server (always the first one) handles HTTP,
*
* Some misc callbacks that aren't associated with a protocol also turn up only
@@ -226,39 +280,6 @@ int callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user,
}
#endif
- if (!strncmp(in, "/postresults", 12)) {
- m = sprintf(buf, "Form results: '%s'
"
- "", pss->post_string);
-
- p = buffer + LWS_PRE;
- start = p;
- end = p + sizeof(buffer) - LWS_PRE;
-
- if (lws_add_http_header_status(wsi, 200, &p, end))
- return 1;
- if (lws_add_http_header_by_token(wsi,
- WSI_TOKEN_HTTP_CONTENT_TYPE,
- (unsigned char *)"text/html",
- 9, &p, end))
- return 1;
- if (lws_add_http_header_content_length(wsi, m, &p,
- end))
- return 1;
- if (lws_finalize_http_header(wsi, &p, end))
- return 1;
-
- n = lws_write(wsi, start, p - start,
- LWS_WRITE_HTTP_HEADERS);
- if (n < 0)
- return 1;
-
- n = lws_write(wsi, (unsigned char *)buf, m, LWS_WRITE_HTTP);
- if (n < 0)
- return 1;
-
- goto try_to_reuse;
- }
-
/* if a legal POST URL, let it continue and accept data */
if (lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI))
return 0;
@@ -404,27 +425,76 @@ int callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user,
break;
case LWS_CALLBACK_HTTP_BODY:
- lwsl_notice("LWS_CALLBACK_HTTP_BODY: len %d\n", (int)len);
- strncpy(pss->post_string, in, sizeof (pss->post_string) -1);
- pss->post_string[sizeof(pss->post_string) - 1] = '\0';
- if (len < sizeof(pss->post_string) - 1)
- pss->post_string[len] = '\0';
+ /* create the POST argument parser if not already existing */
+ if (!pss->spa) {
+ pss->spa = lws_spa_create(wsi, param_names,
+ ARRAY_SIZE(param_names), 1024,
+ file_upload_cb, pss);
+ if (!pss->spa)
+ return -1;
+
+ pss->filename[0] = '\0';
+ pss->file_length = 0;
+ }
+
+ /* let it parse the POST data */
+ if (lws_spa_process(pss->spa, in, len))
+ return -1;
break;
case LWS_CALLBACK_HTTP_BODY_COMPLETION:
- lwsl_notice("LWS_CALLBACK_HTTP_BODY_COMPLETION\n");
+ lwsl_debug("LWS_CALLBACK_HTTP_BODY_COMPLETION\n");
/*
* the whole of the sent body arrived,
* respond to the client with a redirect to show the
* results
*/
- p = (unsigned char *)buf + LWS_PRE;
- n = lws_http_redirect(wsi,
- HTTP_STATUS_SEE_OTHER, /* 303 */
- (unsigned char *)"/postresults", 12, /* location + len */
- &p, /* temp buffer to use */
- p + sizeof(buf) - 1 - LWS_PRE /* buffer len */
- );
+
+ /* call to inform no more payload data coming */
+ lws_spa_finalize(pss->spa);
+
+ p = (unsigned char *)pss->result + LWS_PRE;
+ end = p + sizeof(pss->result) - LWS_PRE - 1;
+ p += sprintf((char *)p,
+ "Form results (after urldecoding)
"
+ "Name | Length | Value |
");
+
+ for (n = 0; n < ARRAY_SIZE(param_names); n++)
+ p += snprintf((char *)p, end - p,
+ "%s | %d | %s |
",
+ param_names[n],
+ lws_spa_get_length(pss->spa, n),
+ lws_spa_get_string(pss->spa, n));
+
+ p += snprintf((char *)p, end - p, "
filename: %s, length %ld",
+ pss->filename, pss->file_length);
+
+ p += snprintf((char *)p, end - p, "");
+ pss->result_len = p - (unsigned char *)(pss->result + LWS_PRE);
+
+ p = buffer + LWS_PRE;
+ start = p;
+ end = p + sizeof(buffer) - LWS_PRE;
+
+ if (lws_add_http_header_status(wsi, 200, &p, end))
+ return 1;
+
+ if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE,
+ (unsigned char *)"text/html", 9, &p, end))
+ return 1;
+ if (lws_add_http_header_content_length(wsi, pss->result_len, &p, end))
+ return 1;
+ if (lws_finalize_http_header(wsi, &p, end))
+ return 1;
+
+ n = lws_write(wsi, start, p - start, LWS_WRITE_HTTP_HEADERS);
+ if (n < 0)
+ return 1;
+
+ n = lws_write(wsi, (unsigned char *)pss->result + LWS_PRE,
+ pss->result_len, LWS_WRITE_HTTP);
+ if (n < 0)
+ return 1;
goto try_to_reuse;
case LWS_CALLBACK_HTTP_FILE_COMPLETION:
diff --git a/test-server/test-server.h b/test-server/test-server.h
index 1118a028..95474029 100644
--- a/test-server/test-server.h
+++ b/test-server/test-server.h
@@ -70,7 +70,6 @@ extern void test_server_unlock(int care);
struct per_session_data__http {
lws_filefd_type fd;
- char post_string[256];
#ifdef LWS_WITH_CGI
struct lws_cgi_args args;
#endif
@@ -78,6 +77,15 @@ struct per_session_data__http {
int reason_bf;
#endif
unsigned int client_finished:1;
+
+
+ struct lws_spa *spa;
+ char result[500 + LWS_PRE];
+ int result_len;
+
+ char filename[256];
+ long file_length;
+ int post_fd;
};
/*
diff --git a/test-server/test.html b/test-server/test.html
index ebf01de7..c7ae0695 100644
--- a/test-server/test.html
+++ b/test-server/test.html
@@ -27,6 +27,16 @@
-moz-border-radius:8px;
border-radius:8px;
color:#404000; }
+ .tdform { vertical-align:middle; width:200px; height:50px;
+ text-align:center;
+ background:#f0f0d0; padding:6px;
+ -webkit-border-radius:8px;
+ -moz-border-radius:8px; margin:10px;
+ border-radius:8px;
+ border: 1px solid black;
+ border-collapse: collapse;font-size:18pt; font: Arial; font-weight:normal; text-align:center; color:#000000;
+ color:#404000; }
+
td.l { vertical-align:middle;
text-align:center;
background:#d0d0b0;
@@ -258,7 +268,7 @@ whenever the information changes server-side.
-
+
POST Form testing
@@ -268,14 +278,53 @@ whenever the information changes server-side.
This tests POST handling in lws.
|
-
+
+ FORM 1: send with urlencoded POST body args
|
|
+
+
+
+
+
+ FORM 2: send with multipart/form-data
+ (can handle file upload, test limited to 100KB)
+
+ |
+
+