/*
* libwebsockets-test-server - libwebsockets test implementation
*
- * Copyright (C) 2010-2015 Andy Green <andy@warmcat.com>
+ * Copyright (C) 2010-2016 Andy Green <andy@warmcat.com>
*
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation:
- * version 2.1 of the License.
+ * This file is made available under the Creative Commons CC0 1.0
+ * Universal Public Domain Dedication.
*
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
+ * The person who associated a work with this deed has dedicated
+ * the work to the public domain by waiving all of his or her rights
+ * to the work worldwide under copyright law, including all related
+ * and neighboring rights, to the extent allowed by law. You can copy,
+ * modify, distribute and perform the work, even for commercial purposes,
+ * all without asking permission.
*
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
- * MA 02110-1301 USA
+ * The test apps are intended to be adapted for use in your code, which
+ * may be proprietary. So unlike the library itself, they are licensed
+ * Public Domain.
*/
#include "test-server.h"
* using this protocol, including the sender
*/
+#if defined(LWS_OPENSSL_SUPPORT) && defined(LWS_HAVE_SSL_CTX_set1_param)
+/* location of the certificate revocation list */
+extern char crl_path[1024];
+#endif
+
+extern int debug_level;
+
enum demo_protocols {
/* always first */
PROTOCOL_HTTP = 0,
struct serveable {
const char *urlpath;
const char *mimetype;
-};
+};
/*
* this is just an example of parsing handshake headers, you don't need this
* content
*/
void
-dump_handshake_info(struct libwebsocket *wsi)
+dump_handshake_info(struct lws *wsi)
{
- int n = 0;
+ int n = 0, len;
char buf[256];
const unsigned char *c;
continue;
}
- if (!lws_hdr_total_length(wsi, n)) {
+ len = lws_hdr_total_length(wsi, n);
+ if (!len || len > sizeof(buf) - 1) {
n++;
continue;
}
lws_hdr_copy(wsi, buf, sizeof buf, n);
+ buf[sizeof(buf) - 1] = '\0';
fprintf(stderr, " %s = %s\n", (char *)c, buf);
n++;
if (!strcmp(&file[n - 5], ".html"))
return "text/html";
+ if (!strcmp(&file[n - 4], ".css"))
+ return "text/css";
+
+ if (!strcmp(&file[n - 3], ".js"))
+ return "text/javascript";
+
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;
+
+ (void)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 = (lws_filefd_type)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((int)pss->post_fd, buf, len);
+ lwsl_notice("%s: write %d says %d\n", __func__, len, n);
+ }
+ if (state == LWS_UFS_CONTENT)
+ break;
+ close((int)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
* here on the first protocol server.
*/
-int callback_http(struct libwebsocket_context *context,
- struct libwebsocket *wsi,
- enum libwebsocket_callback_reasons reason, void *user,
- void *in, size_t len)
+int callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user,
+ void *in, size_t len)
{
struct per_session_data__http *pss =
(struct per_session_data__http *)user;
- static unsigned char buffer[4096];
- struct stat stat_buf;
+ unsigned char buffer[4096 + LWS_PRE];
+ lws_filepos_t amount, file_len, sent;
char leaf_path[1024];
const char *mimetype;
char *other_headers;
- unsigned char *end;
+ unsigned char *end, *start;
struct timeval tv;
unsigned char *p;
+#ifndef LWS_NO_CLIENT
+ struct per_session_data__http *pss1;
+ struct lws *wsi1;
+#endif
char buf[256];
char b64[64];
int n, m;
-
#ifdef EXTERNAL_POLL
- struct libwebsocket_pollargs *pa = (struct libwebsocket_pollargs *)in;
+ struct lws_pollargs *pa = (struct lws_pollargs *)in;
#endif
+
switch (reason) {
case LWS_CALLBACK_HTTP:
- dump_handshake_info(wsi);
+ lwsl_info("lws_http_serve: %s\n", (const char *)in);
+
+ if (debug_level & LLL_INFO) {
+ dump_handshake_info(wsi);
+
+ /* dump the individual URI Arg parameters */
+ n = 0;
+ while (lws_hdr_copy_fragment(wsi, buf, sizeof(buf),
+ WSI_TOKEN_HTTP_URI_ARGS, n) > 0) {
+ lwsl_notice("URI Arg %d: %s\n", ++n, buf);
+ }
+ }
+
+ {
+ lws_get_peer_simple(wsi, buf, sizeof(buf));
+ lwsl_info("HTTP connect from %s\n", buf);
+ }
if (len < 1) {
- libwebsockets_return_http_status(context, wsi,
+ lws_return_http_status(wsi,
HTTP_STATUS_BAD_REQUEST, NULL);
goto try_to_reuse;
}
+#if !defined(LWS_NO_CLIENT) && defined(LWS_OPENSSL_SUPPORT)
+ if (!strncmp(in, "/proxytest", 10)) {
+ struct lws_client_connect_info i;
+ char *rootpath = "/git/";
+ const char *p = (const char *)in;
+
+ if (lws_get_child(wsi))
+ break;
+
+ pss->client_finished = 0;
+ memset(&i, 0, sizeof(i));
+ i.context = lws_get_context(wsi);
+ i.address = "libwebsockets.org";
+ i.port = 443;
+ i.ssl_connection = 1;
+ if (p[10])
+ i.path = (char *)in + 10;
+ else
+ i.path = rootpath;
+ i.host = i.address;
+ i.origin = NULL;
+ i.method = "GET";
+ i.parent_wsi = wsi;
+ i.uri_replace_from = "libwebsockets.org/git/";
+ i.uri_replace_to = "/proxytest/";
+
+ if (!lws_client_connect_via_info(&i)) {
+ lwsl_err("proxy connect fail\n");
+ break;
+ }
+
+ break;
+ }
+#endif
+
+#if 1
/* this example server has no concept of directories */
if (strchr((const char *)in + 1, '/')) {
- libwebsockets_return_http_status(context, wsi,
- HTTP_STATUS_FORBIDDEN, NULL);
+ lws_return_http_status(wsi, HTTP_STATUS_NOT_ACCEPTABLE, NULL);
goto try_to_reuse;
}
+#endif
/* if a legal POST URL, let it continue and accept data */
if (lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI))
/* check for the "send a big file by hand" example case */
if (!strcmp((const char *)in, "/leaf.jpg")) {
+ lws_fop_flags_t flags = LWS_O_RDONLY;
+
if (strlen(resource_path) > sizeof(leaf_path) - 10)
return -1;
sprintf(leaf_path, "%s/leaf.jpg", resource_path);
/* well, let's demonstrate how to send the hard way */
- p = buffer + LWS_SEND_BUFFER_PRE_PADDING;
- end = p + sizeof(buffer) - LWS_SEND_BUFFER_PRE_PADDING;
-#ifdef _WIN32
- pss->fd = open(leaf_path, O_RDONLY | _O_BINARY);
-#else
- pss->fd = open(leaf_path, O_RDONLY);
-#endif
-
- if (pss->fd < 0)
- return -1;
+ p = buffer + LWS_PRE;
+ end = p + sizeof(buffer) - LWS_PRE;
- if (fstat(pss->fd, &stat_buf) < 0)
+ pss->fop_fd = lws_vfs_file_open(
+ lws_get_fops(lws_get_context(wsi)),
+ leaf_path, &flags);
+ if (!pss->fop_fd) {
+ lwsl_err("failed to open file %s\n", leaf_path);
return -1;
+ }
+ file_len = lws_vfs_get_length(pss->fop_fd);
/*
* we will send a big jpeg file, but it could be
* anything. Set the Content-Type: appropriately
* so the browser knows what to do with it.
- *
+ *
* Notice we use the APIs to build the header, which
* will do the right thing for HTTP 1/1.1 and HTTP2
* depending on what connection it happens to be working
* on
*/
- if (lws_add_http_header_status(context, wsi, 200, &p, end))
+ if (lws_add_http_header_status(wsi, HTTP_STATUS_OK, &p, end))
return 1;
- if (lws_add_http_header_by_token(context, wsi,
- WSI_TOKEN_HTTP_SERVER,
+ if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_SERVER,
(unsigned char *)"libwebsockets",
13, &p, end))
return 1;
- if (lws_add_http_header_by_token(context, wsi,
+ if (lws_add_http_header_by_token(wsi,
WSI_TOKEN_HTTP_CONTENT_TYPE,
(unsigned char *)"image/jpeg",
10, &p, end))
return 1;
- if (lws_add_http_header_content_length(context, wsi,
- stat_buf.st_size, &p, end))
+ if (lws_add_http_header_content_length(wsi,
+ file_len, &p,
+ end))
return 1;
- if (lws_finalize_http_header(context, wsi, &p, end))
+ if (lws_finalize_http_header(wsi, &p, end))
return 1;
/*
* this won't block since it's the first payload sent
* on the connection since it was established
* (too small for partial)
- *
+ *
* Notice they are sent using LWS_WRITE_HTTP_HEADERS
* which also means you can't send body too in one step,
* this is mandated by changes in HTTP2
*/
- n = libwebsocket_write(wsi,
- buffer + LWS_SEND_BUFFER_PRE_PADDING,
- p - (buffer + LWS_SEND_BUFFER_PRE_PADDING),
- LWS_WRITE_HTTP_HEADERS);
+ *p = '\0';
+ lwsl_info("%s\n", buffer + LWS_PRE);
+ n = lws_write(wsi, buffer + LWS_PRE,
+ p - (buffer + LWS_PRE),
+ LWS_WRITE_HTTP_HEADERS);
if (n < 0) {
- close(pss->fd);
+ lws_vfs_file_close(&pss->fop_fd);
return -1;
}
/*
* book us a LWS_CALLBACK_HTTP_WRITEABLE callback
*/
- libwebsocket_callback_on_writable(context, wsi);
+ lws_callback_on_writable(wsi);
break;
}
/* if not, send a file the easy way */
- strcpy(buf, resource_path);
+ if (!strncmp(in, "/cgit-data/", 11)) {
+ in = (char *)in + 11;
+ strcpy(buf, "/usr/share/cgit");
+ } else
+ strcpy(buf, resource_path);
+
if (strcmp(in, "/")) {
if (*((const char *)in) != '/')
strcat(buf, "/");
- strncat(buf, in, sizeof(buf) - strlen(resource_path));
+ strncat(buf, in, sizeof(buf) - strlen(buf) - 1);
} else /* default file to serve */
strcat(buf, "/test.html");
buf[sizeof(buf) - 1] = '\0';
mimetype = get_mimetype(buf);
if (!mimetype) {
lwsl_err("Unknown mimetype for %s\n", buf);
- libwebsockets_return_http_status(context, wsi,
- HTTP_STATUS_UNSUPPORTED_MEDIA_TYPE, NULL);
+ lws_return_http_status(wsi,
+ HTTP_STATUS_UNSUPPORTED_MEDIA_TYPE, "Unknown Mimetype");
return -1;
}
- /* demostrates how to set a cookie on / */
+ /* demonstrates how to set a cookie on / */
- other_headers = NULL;
- n = 0;
+ other_headers = leaf_path;
+ p = (unsigned char *)leaf_path;
if (!strcmp((const char *)in, "/") &&
!lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_COOKIE)) {
/* this isn't very unguessable but it'll do for us */
(unsigned int)tv.tv_sec,
(unsigned int)tv.tv_usec);
- p = (unsigned char *)leaf_path;
-
- if (lws_add_http_header_by_name(context, wsi,
- (unsigned char *)"set-cookie:",
+ if (lws_add_http_header_by_name(wsi,
+ (unsigned char *)"set-cookie:",
(unsigned char *)b64, n, &p,
(unsigned char *)leaf_path + sizeof(leaf_path)))
return 1;
- n = (char *)p - leaf_path;
- other_headers = leaf_path;
}
+ if (lws_is_ssl(wsi) && lws_add_http_header_by_name(wsi,
+ (unsigned char *)
+ "Strict-Transport-Security:",
+ (unsigned char *)
+ "max-age=15768000 ; "
+ "includeSubDomains", 36, &p,
+ (unsigned char *)leaf_path +
+ sizeof(leaf_path)))
+ return 1;
+ n = (char *)p - leaf_path;
- n = libwebsockets_serve_http_file(context, wsi, buf,
- mimetype, other_headers, n);
- if (n < 0 || ((n > 0) && lws_http_transaction_completed(wsi)))
- return -1; /* error or can't reuse connection: close the socket */
+ n = lws_serve_http_file(wsi, buf, mimetype, other_headers, n);
+ if (n < 0)
+ return -1; /* error*/
/*
* notice that the sending of the file completes asynchronously,
* we'll get a LWS_CALLBACK_HTTP_FILE_COMPLETION callback when
- * it's done
+ * it's done. That's the case even if we just completed the
+ * send, so wait for that.
*/
-
break;
+ case LWS_CALLBACK_CLIENT_RECEIVE:
+ ((char *)in)[len] = '\0';
+ lwsl_info("rx %d '%s'\n", (int)len, (char *)in);
+ break;
+
case LWS_CALLBACK_HTTP_BODY:
- strncpy(buf, in, 20);
- buf[20] = '\0';
- if (len < 20)
- buf[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;
- lwsl_notice("LWS_CALLBACK_HTTP_BODY: %s... len %d\n",
- (const char *)buf, (int)len);
+ 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");
- /* the whole of the sent body arrived, close or reuse the connection */
- libwebsockets_return_http_status(context, wsi,
- HTTP_STATUS_OK, NULL);
+ 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
+ */
+
+ /* 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,
+ "<html><body><h1>Form results (after urldecoding)</h1>"
+ "<table><tr><td>Name</td><td>Length</td><td>Value</td></tr>");
+
+ for (n = 0; n < ARRAY_SIZE(param_names); n++)
+ p += lws_snprintf((char *)p, end - p,
+ "<tr><td><b>%s</b></td><td>%d</td><td>%s</td></tr>",
+ param_names[n],
+ lws_spa_get_length(pss->spa, n),
+ lws_spa_get_string(pss->spa, n));
+
+ p += lws_snprintf((char *)p, end - p, "</table><br><b>filename:</b> %s, <b>length</b> %ld",
+ pss->filename, pss->file_length);
+
+ p += lws_snprintf((char *)p, end - p, "</body></html>");
+ 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, HTTP_STATUS_OK, &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_DROP_PROTOCOL:
+ lwsl_debug("LWS_CALLBACK_HTTP_DROP_PROTOCOL\n");
+ /* called when our wsi user_space is going to be destroyed */
+ if (pss->spa) {
+ lws_spa_destroy(pss->spa);
+ pss->spa = NULL;
+ }
+ break;
case LWS_CALLBACK_HTTP_FILE_COMPLETION:
goto try_to_reuse;
case LWS_CALLBACK_HTTP_WRITEABLE:
+ lwsl_info("LWS_CALLBACK_HTTP_WRITEABLE\n");
+
+ if (pss->client_finished)
+ return -1;
+
+ if (!lws_get_child(wsi) && !pss->fop_fd) {
+ lwsl_notice("fop_fd NULL\n");
+ goto try_to_reuse;
+ }
+
+#ifndef LWS_NO_CLIENT
+ if (pss->reason_bf & 2) {
+ char *px = buf + LWS_PRE;
+ int lenx = sizeof(buf) - LWS_PRE;
+ /*
+ * our sink is writeable and our source has something
+ * to read. So read a lump of source material of
+ * suitable size to send or what's available, whichever
+ * is the smaller.
+ */
+
+
+ pss->reason_bf &= ~2;
+ wsi1 = lws_get_child(wsi);
+ if (!wsi1)
+ break;
+ if (lws_http_client_read(wsi1, &px, &lenx) < 0)
+ return -1;
+
+ if (pss->client_finished)
+ return -1;
+
+ break;
+ }
+
+ if (lws_get_child(wsi))
+ break;
+
+#endif
/*
* we can send more of whatever it is we were sending
*/
+ sent = 0;
do {
/* we'd like the send this much */
- n = sizeof(buffer) - LWS_SEND_BUFFER_PRE_PADDING;
-
+ n = sizeof(buffer) - LWS_PRE;
+
/* but if the peer told us he wants less, we can adapt */
m = lws_get_peer_write_allowance(wsi);
if (m != -1 && m < n)
/* he couldn't handle that much */
n = m;
-
- n = read(pss->fd, buffer + LWS_SEND_BUFFER_PRE_PADDING,
- n);
+
+ n = lws_vfs_file_read(pss->fop_fd,
+ &amount, buffer + LWS_PRE, n);
/* problem reading, close conn */
- if (n < 0)
+ if (n < 0) {
+ lwsl_err("problem reading file\n");
goto bail;
+ }
+ n = (int)amount;
/* sent it all, close conn */
if (n == 0)
- goto flush_bail;
+ goto penultimate;
/*
* To support HTTP2, must take care about preamble space
- *
+ *
* identification of when we send the last payload frame
* is handled by the library itself if you sent a
* content-length header
*/
- m = libwebsocket_write(wsi,
- buffer + LWS_SEND_BUFFER_PRE_PADDING,
- n, LWS_WRITE_HTTP);
- if (m < 0)
+ m = lws_write(wsi, buffer + LWS_PRE, n, LWS_WRITE_HTTP);
+ if (m < 0) {
+ lwsl_err("write failed\n");
/* write failed, close conn */
goto bail;
-
- /*
- * http2 won't do this
- */
- if (m != n)
- /* partial write, adjust */
- if (lseek(pss->fd, m - n, SEEK_CUR) < 0)
- goto bail;
-
+ }
if (m) /* while still active, extend timeout */
- libwebsocket_set_timeout(wsi,
- PENDING_TIMEOUT_HTTP_CONTENT, 5);
-
- /* if we have indigestion, let him clear it before eating more */
- if (lws_partial_buffered(wsi))
- break;
-
- } while (!lws_send_pipe_choked(wsi));
+ lws_set_timeout(wsi, PENDING_TIMEOUT_HTTP_CONTENT, 5);
+ sent += m;
+ } while (!lws_send_pipe_choked(wsi) && (sent < 1024 * 1024));
later:
- libwebsocket_callback_on_writable(context, wsi);
+ lws_callback_on_writable(wsi);
break;
-flush_bail:
- /* true if still partial pending */
- if (lws_partial_buffered(wsi)) {
- libwebsocket_callback_on_writable(context, wsi);
- break;
- }
- close(pss->fd);
+penultimate:
+ lws_vfs_file_close(&pss->fop_fd);
+ pss->fop_fd = NULL;
goto try_to_reuse;
bail:
- close(pss->fd);
+ lws_vfs_file_close(&pss->fop_fd);
+
return -1;
/*
* callback for confirming to continue with client IP appear in
* protocol 0 callback since no websocket protocol has been agreed
* yet. You can just ignore this if you won't filter on client IP
- * since the default uhandled callback return is 0 meaning let the
+ * since the default unhandled callback return is 0 meaning let the
* connection continue.
*/
case LWS_CALLBACK_FILTER_NETWORK_CONNECTION:
-
/* if we returned non-zero from here, we kill the connection */
break;
-#ifdef EXTERNAL_POLL
+#ifndef LWS_NO_CLIENT
+ case LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP: {
+ char ctype[64], ctlen = 0;
+ lwsl_err("LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP\n");
+ p = buffer + LWS_PRE;
+ end = p + sizeof(buffer) - LWS_PRE;
+ if (lws_add_http_header_status(lws_get_parent(wsi), HTTP_STATUS_OK, &p, end))
+ return 1;
+ if (lws_add_http_header_by_token(lws_get_parent(wsi),
+ WSI_TOKEN_HTTP_SERVER,
+ (unsigned char *)"libwebsockets",
+ 13, &p, end))
+ return 1;
+
+ ctlen = lws_hdr_copy(wsi, ctype, sizeof(ctype), WSI_TOKEN_HTTP_CONTENT_TYPE);
+ if (ctlen > 0) {
+ if (lws_add_http_header_by_token(lws_get_parent(wsi),
+ WSI_TOKEN_HTTP_CONTENT_TYPE,
+ (unsigned char *)ctype, ctlen, &p, end))
+ return 1;
+ }
+#if 0
+ if (lws_add_http_header_content_length(lws_get_parent(wsi),
+ file_len, &p, end))
+ return 1;
+#endif
+ if (lws_finalize_http_header(lws_get_parent(wsi), &p, end))
+ return 1;
+
+ *p = '\0';
+ lwsl_info("%s\n", buffer + LWS_PRE);
+
+ n = lws_write(lws_get_parent(wsi), buffer + LWS_PRE,
+ p - (buffer + LWS_PRE),
+ LWS_WRITE_HTTP_HEADERS);
+ if (n < 0)
+ return -1;
+
+ break; }
+ case LWS_CALLBACK_CLOSED_CLIENT_HTTP:
+ //lwsl_err("LWS_CALLBACK_CLOSED_CLIENT_HTTP\n");
+ return -1;
+ break;
+ case LWS_CALLBACK_RECEIVE_CLIENT_HTTP:
+ //lwsl_err("LWS_CALLBACK_RECEIVE_CLIENT_HTTP: wsi %p\n", wsi);
+ assert(lws_get_parent(wsi));
+ if (!lws_get_parent(wsi))
+ break;
+ pss1 = lws_wsi_user(lws_get_parent(wsi));
+ pss1->reason_bf |= 2;
+ lws_callback_on_writable(lws_get_parent(wsi));
+ break;
+ case LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ:
+ //lwsl_err("LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ len %d\n", (int)len);
+ assert(lws_get_parent(wsi));
+ m = lws_write(lws_get_parent(wsi), (unsigned char *)in,
+ len, LWS_WRITE_HTTP);
+ if (m < 0)
+ return -1;
+ break;
+ case LWS_CALLBACK_COMPLETED_CLIENT_HTTP:
+ //lwsl_err("LWS_CALLBACK_COMPLETED_CLIENT_HTTP\n");
+ assert(lws_get_parent(wsi));
+ if (!lws_get_parent(wsi))
+ break;
+ pss1 = lws_wsi_user(lws_get_parent(wsi));
+ pss1->client_finished = 1;
+ break;
+#endif
+
/*
* callbacks for managing the external poll() array appear in
* protocol 0 callback
/*
* lock mutex to protect pollfd state
* called before any other POLL related callback
+ * if protecting wsi lifecycle change, len == 1
*/
+ test_server_lock(len);
break;
case LWS_CALLBACK_UNLOCK_POLL:
/*
* unlock mutex to protect pollfd state when
* called after any other POLL related callback
+ * if protecting wsi lifecycle change, len == 1
*/
+ test_server_unlock(len);
break;
+#ifdef EXTERNAL_POLL
case LWS_CALLBACK_ADD_POLL_FD:
if (count_pollfds >= max_poll_elements) {
case LWS_CALLBACK_GET_THREAD_ID:
/*
- * if you will call "libwebsocket_callback_on_writable"
+ * if you will call "lws_callback_on_writable"
* from a different thread, return the caller thread ID
* here so lws can use this information to work out if it
* should signal the poll() loop to exit and restart early
break;
+#if defined(LWS_OPENSSL_SUPPORT)
+ case LWS_CALLBACK_OPENSSL_PERFORM_CLIENT_CERT_VERIFICATION:
+ /* Verify the client certificate */
+ if (!len || (SSL_get_verify_result((SSL*)in) != X509_V_OK)) {
+ int err = X509_STORE_CTX_get_error((X509_STORE_CTX*)user);
+ int depth = X509_STORE_CTX_get_error_depth((X509_STORE_CTX*)user);
+ const char* msg = X509_verify_cert_error_string(err);
+ lwsl_err("LWS_CALLBACK_OPENSSL_PERFORM_CLIENT_CERT_VERIFICATION: SSL error: %s (%d), depth: %d\n", msg, err, depth);
+ return 1;
+ }
+ break;
+#if defined(LWS_HAVE_SSL_CTX_set1_param)
+ case LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS:
+ if (crl_path[0]) {
+ /* Enable CRL checking */
+ X509_VERIFY_PARAM *param = X509_VERIFY_PARAM_new();
+ X509_VERIFY_PARAM_set_flags(param, X509_V_FLAG_CRL_CHECK);
+ SSL_CTX_set1_param((SSL_CTX*)user, param);
+ X509_STORE *store = SSL_CTX_get_cert_store((SSL_CTX*)user);
+ X509_LOOKUP *lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file());
+ n = X509_load_cert_crl_file(lookup, crl_path, X509_FILETYPE_PEM);
+ X509_VERIFY_PARAM_free(param);
+ if (n != 1) {
+ char errbuf[256];
+ n = ERR_get_error();
+ lwsl_err("LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS: SSL error: %s (%d)\n", ERR_error_string(n, errbuf), n);
+ return 1;
+ }
+ }
+ break;
+#endif
+#endif
+
default:
break;
}