refactor output.c
Signed-off-by: Andy Green <andy.green@linaro.org>
diff --git a/lib/Makefile.am b/lib/Makefile.am
index ad4592b..07500ad 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -5,6 +5,7 @@
parsers.c \
client.c \
client-parser.c \
+ output.c \
libwebsockets.h \
base64-decode.c \
client-handshake.c \
diff --git a/lib/output.c b/lib/output.c
new file mode 100644
index 0000000..994d87d
--- /dev/null
+++ b/lib/output.c
@@ -0,0 +1,748 @@
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010-2013 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 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.
+ *
+ * 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
+ */
+
+#include "private-libwebsockets.h"
+
+#ifdef WIN32
+#include <io.h>
+#endif
+
+static int
+libwebsocket_0405_frame_mask_generate(struct libwebsocket *wsi)
+{
+ char buf[4 + 20];
+ int n;
+
+ /* fetch the per-frame nonce */
+
+ n = libwebsockets_get_random(wsi->protocol->owning_server,
+ wsi->frame_masking_nonce_04, 4);
+ if (n != 4) {
+ lwsl_parser("Unable to read from random device %s %d\n",
+ SYSTEM_RANDOM_FILEPATH, n);
+ return 1;
+ }
+
+ /* start masking from first byte of masking key buffer */
+ wsi->frame_mask_index = 0;
+
+ if (wsi->ietf_spec_revision != 4)
+ return 0;
+
+ /* 04 only does SHA-1 more complex key */
+
+ /*
+ * the frame key is the frame nonce (4 bytes) followed by the
+ * connection masking key, hashed by SHA1
+ */
+
+ memcpy(buf, wsi->frame_masking_nonce_04, 4);
+
+ memcpy(buf + 4, wsi->masking_key_04, 20);
+
+ /* concatenate the nonce with the connection key then hash it */
+
+ SHA1((unsigned char *)buf, 4 + 20, wsi->frame_mask_04);
+
+ return 0;
+}
+
+
+void lws_stderr_hexdump(unsigned char *buf, size_t len)
+{
+ int n;
+ int m;
+ int start;
+
+ lwsl_parser("\n");
+
+ for (n = 0; n < len;) {
+ start = n;
+
+ lwsl_debug("%04X: ", start);
+
+ for (m = 0; m < 16 && n < len; m++)
+ lwsl_debug("%02X ", buf[n++]);
+ while (m++ < 16)
+ lwsl_debug(" ");
+
+ lwsl_debug(" ");
+
+ for (m = 0; m < 16 && (start + m) < len; m++) {
+ if (buf[start + m] >= ' ' && buf[start + m] <= 127)
+ lwsl_debug("%c", buf[start + m]);
+ else
+ lwsl_debug(".");
+ }
+ while (m++ < 16)
+ lwsl_debug(" ");
+
+ lwsl_debug("\n");
+ }
+ lwsl_debug("\n");
+}
+
+int lws_issue_raw(struct libwebsocket *wsi, unsigned char *buf, size_t len)
+{
+ int n;
+ int m;
+
+ /*
+ * one of the extensions is carrying our data itself? Like mux?
+ */
+
+ for (n = 0; n < wsi->count_active_extensions; n++) {
+ /*
+ * there can only be active extensions after handshake completed
+ * so we can rely on protocol being set already in here
+ */
+ m = wsi->active_extensions[n]->callback(
+ wsi->protocol->owning_server,
+ wsi->active_extensions[n], wsi,
+ LWS_EXT_CALLBACK_PACKET_TX_DO_SEND,
+ wsi->active_extensions_user[n], &buf, len);
+ if (m < 0) {
+ lwsl_ext("Extension reports fatal error\n");
+ return -1;
+ }
+ if (m) /* handled */ {
+/* lwsl_ext("ext sent it\n"); */
+ return 0;
+ }
+ }
+
+ if (!wsi->sock)
+ lwsl_warn("** error 0 sock but expected to send\n");
+
+ /*
+ * nope, send it on the socket directly
+ */
+
+#if 0
+ lwsl_debug(" TX: ");
+ lws_stderr_hexdump(buf, len);
+#endif
+
+#ifdef LWS_OPENSSL_SUPPORT
+ if (wsi->ssl) {
+ n = SSL_write(wsi->ssl, buf, len);
+ if (n < 0) {
+ lwsl_debug("ERROR writing to socket\n");
+ return -1;
+ }
+ } else {
+#endif
+ n = send(wsi->sock, buf, len, MSG_NOSIGNAL);
+ if (n < 0) {
+ lwsl_debug("ERROR writing to socket\n");
+ return -1;
+ }
+#ifdef LWS_OPENSSL_SUPPORT
+ }
+#endif
+ return 0;
+}
+
+int
+lws_issue_raw_ext_access(struct libwebsocket *wsi,
+ unsigned char *buf, size_t len)
+{
+ int ret;
+ struct lws_tokens eff_buf;
+ int m;
+ int n;
+
+ eff_buf.token = (char *)buf;
+ eff_buf.token_len = len;
+
+ /*
+ * while we have original buf to spill ourselves, or extensions report
+ * more in their pipeline
+ */
+
+ ret = 1;
+ while (ret == 1) {
+
+ /* default to nobody has more to spill */
+
+ ret = 0;
+
+ /* show every extension the new incoming data */
+
+ for (n = 0; n < wsi->count_active_extensions; n++) {
+ m = wsi->active_extensions[n]->callback(
+ wsi->protocol->owning_server,
+ wsi->active_extensions[n], wsi,
+ LWS_EXT_CALLBACK_PACKET_TX_PRESEND,
+ wsi->active_extensions_user[n], &eff_buf, 0);
+ if (m < 0) {
+ lwsl_ext("Extension: fatal error\n");
+ return -1;
+ }
+ if (m)
+ /*
+ * at least one extension told us he has more
+ * to spill, so we will go around again after
+ */
+ ret = 1;
+ }
+
+ /* assuming they left us something to send, send it */
+
+ if (eff_buf.token_len)
+ if (lws_issue_raw(wsi, (unsigned char *)eff_buf.token,
+ eff_buf.token_len))
+ return -1;
+
+ lwsl_parser("written %d bytes to client\n", eff_buf.token_len);
+
+ /* no extension has more to spill */
+
+ if (!ret)
+ break;
+
+ /* we used up what we had */
+
+ eff_buf.token = NULL;
+ eff_buf.token_len = 0;
+
+ /*
+ * Did that leave the pipe choked?
+ */
+
+ if (!lws_send_pipe_choked(wsi))
+ /* no we could add more */
+ continue;
+
+ lwsl_debug("choked\n");
+
+ /*
+ * Yes, he's choked. Don't spill the rest now get a callback
+ * when he is ready to send and take care of it there
+ */
+ libwebsocket_callback_on_writable(
+ wsi->protocol->owning_server, wsi);
+ wsi->extension_data_pending = 1;
+ ret = 0;
+ }
+
+ return 0;
+}
+
+/**
+ * libwebsocket_write() - Apply protocol then write data to client
+ * @wsi: Websocket instance (available from user callback)
+ * @buf: The data to send. For data being sent on a websocket
+ * connection (ie, not default http), this buffer MUST have
+ * LWS_SEND_BUFFER_PRE_PADDING bytes valid BEFORE the pointer
+ * and an additional LWS_SEND_BUFFER_POST_PADDING bytes valid
+ * in the buffer after (buf + len). This is so the protocol
+ * header and trailer data can be added in-situ.
+ * @len: Count of the data bytes in the payload starting from buf
+ * @protocol: Use LWS_WRITE_HTTP to reply to an http connection, and one
+ * of LWS_WRITE_BINARY or LWS_WRITE_TEXT to send appropriate
+ * data on a websockets connection. Remember to allow the extra
+ * bytes before and after buf if LWS_WRITE_BINARY or LWS_WRITE_TEXT
+ * are used.
+ *
+ * This function provides the way to issue data back to the client
+ * for both http and websocket protocols.
+ *
+ * In the case of sending using websocket protocol, be sure to allocate
+ * valid storage before and after buf as explained above. This scheme
+ * allows maximum efficiency of sending data and protocol in a single
+ * packet while not burdening the user code with any protocol knowledge.
+ */
+
+int libwebsocket_write(struct libwebsocket *wsi, unsigned char *buf,
+ size_t len, enum libwebsocket_write_protocol protocol)
+{
+ int n;
+ int pre = 0;
+ int post = 0;
+ int shift = 7;
+ int masked7 = wsi->mode == LWS_CONNMODE_WS_CLIENT &&
+ wsi->xor_mask != xor_no_mask;
+ unsigned char *dropmask = NULL;
+ unsigned char is_masked_bit = 0;
+ struct lws_tokens eff_buf;
+ int m;
+
+ if (len == 0 && protocol != LWS_WRITE_CLOSE) {
+ lwsl_warn("zero length libwebsocket_write attempt\n");
+ return 0;
+ }
+
+ if (protocol == LWS_WRITE_HTTP)
+ goto send_raw;
+
+ /* websocket protocol, either binary or text */
+
+ if (wsi->state != WSI_STATE_ESTABLISHED)
+ return -1;
+
+ /* give a change to the extensions to modify payload */
+ eff_buf.token = (char *)buf;
+ eff_buf.token_len = len;
+
+ for (n = 0; n < wsi->count_active_extensions; n++) {
+ m = wsi->active_extensions[n]->callback(
+ wsi->protocol->owning_server,
+ wsi->active_extensions[n], wsi,
+ LWS_EXT_CALLBACK_PAYLOAD_TX,
+ wsi->active_extensions_user[n], &eff_buf, 0);
+ if (m < 0)
+ return -1;
+ }
+
+ buf = (unsigned char *)eff_buf.token;
+ len = eff_buf.token_len;
+
+ switch (wsi->ietf_spec_revision) {
+ /* chrome likes this as of 30 Oct 2010 */
+ /* Firefox 4.0b6 likes this as of 30 Oct 2010 */
+ case 0:
+ if ((protocol & 0xf) == LWS_WRITE_BINARY) {
+ /* in binary mode we send 7-bit used length blocks */
+ pre = 1;
+ while (len & (127 << shift)) {
+ pre++;
+ shift += 7;
+ }
+ n = 0;
+ shift -= 7;
+ while (shift >= 0) {
+ if (shift)
+ buf[0 - pre + n] =
+ ((len >> shift) & 127) | 0x80;
+ else
+ buf[0 - pre + n] =
+ ((len >> shift) & 127);
+ n++;
+ shift -= 7;
+ }
+ break;
+ }
+
+ /* frame type = text, length-free spam mode */
+
+ pre = 1;
+ buf[-pre] = 0;
+ buf[len] = 0xff; /* EOT marker */
+ post = 1;
+ break;
+
+ case 7:
+ case 8:
+ case 13:
+ if (masked7) {
+ pre += 4;
+ dropmask = &buf[0 - pre];
+ is_masked_bit = 0x80;
+ }
+ /* fallthru */
+ case 4:
+ case 5:
+ case 6:
+ switch (protocol & 0xf) {
+ case LWS_WRITE_TEXT:
+ if (wsi->ietf_spec_revision < 7)
+ n = LWS_WS_OPCODE_04__TEXT_FRAME;
+ else
+ n = LWS_WS_OPCODE_07__TEXT_FRAME;
+ break;
+ case LWS_WRITE_BINARY:
+ if (wsi->ietf_spec_revision < 7)
+ n = LWS_WS_OPCODE_04__BINARY_FRAME;
+ else
+ n = LWS_WS_OPCODE_07__BINARY_FRAME;
+ break;
+ case LWS_WRITE_CONTINUATION:
+ if (wsi->ietf_spec_revision < 7)
+ n = LWS_WS_OPCODE_04__CONTINUATION;
+ else
+ n = LWS_WS_OPCODE_07__CONTINUATION;
+ break;
+
+ case LWS_WRITE_CLOSE:
+ if (wsi->ietf_spec_revision < 7)
+ n = LWS_WS_OPCODE_04__CLOSE;
+ else
+ n = LWS_WS_OPCODE_07__CLOSE;
+
+ /*
+ * v5 mandates the first byte of close packet
+ * in both client and server directions
+ */
+
+ switch (wsi->ietf_spec_revision) {
+ case 0:
+ case 4:
+ break;
+ case 5:
+ /* we can do this because we demand post-buf */
+
+ if (len < 1)
+ len = 1;
+
+ switch (wsi->mode) {
+ case LWS_CONNMODE_WS_SERVING:
+ /*
+ lwsl_debug("LWS_WRITE_CLOSE S\n");
+ */
+ buf[0] = 'S';
+ break;
+ case LWS_CONNMODE_WS_CLIENT:
+ /*
+ lwsl_debug("LWS_WRITE_CLOSE C\n");
+ */
+ buf[0] = 'C';
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ /*
+ * 06 has a 2-byte status code in network order
+ * we can do this because we demand post-buf
+ */
+
+ if (wsi->close_reason) {
+ /* reason codes count as data bytes */
+ buf -= 2;
+ buf[0] = wsi->close_reason >> 8;
+ buf[1] = wsi->close_reason;
+ len += 2;
+ }
+ break;
+ }
+ break;
+ case LWS_WRITE_PING:
+ if (wsi->ietf_spec_revision < 7)
+ n = LWS_WS_OPCODE_04__PING;
+ else
+ n = LWS_WS_OPCODE_07__PING;
+
+ wsi->pings_vs_pongs++;
+ break;
+ case LWS_WRITE_PONG:
+ if (wsi->ietf_spec_revision < 7)
+ n = LWS_WS_OPCODE_04__PONG;
+ else
+ n = LWS_WS_OPCODE_07__PONG;
+ break;
+ default:
+ lwsl_warn("libwebsocket_write: unknown write "
+ "opcode / protocol\n");
+ return -1;
+ }
+
+ if (!(protocol & LWS_WRITE_NO_FIN))
+ n |= 1 << 7;
+
+ if (len < 126) {
+ pre += 2;
+ buf[-pre] = n;
+ buf[-pre + 1] = len | is_masked_bit;
+ } else {
+ if (len < 65536) {
+ pre += 4;
+ buf[-pre] = n;
+ buf[-pre + 1] = 126 | is_masked_bit;
+ buf[-pre + 2] = len >> 8;
+ buf[-pre + 3] = len;
+ } else {
+ pre += 10;
+ buf[-pre] = n;
+ buf[-pre + 1] = 127 | is_masked_bit;
+#if defined __LP64__
+ buf[-pre + 2] = (len >> 56) & 0x7f;
+ buf[-pre + 3] = len >> 48;
+ buf[-pre + 4] = len >> 40;
+ buf[-pre + 5] = len >> 32;
+#else
+ buf[-pre + 2] = 0;
+ buf[-pre + 3] = 0;
+ buf[-pre + 4] = 0;
+ buf[-pre + 5] = 0;
+#endif
+ buf[-pre + 6] = len >> 24;
+ buf[-pre + 7] = len >> 16;
+ buf[-pre + 8] = len >> 8;
+ buf[-pre + 9] = len;
+ }
+ }
+ break;
+ }
+
+ /*
+ * Deal with masking if we are in client -> server direction and
+ * the protocol demands it
+ */
+
+ if (wsi->mode == LWS_CONNMODE_WS_CLIENT &&
+ wsi->ietf_spec_revision >= 4) {
+
+ /*
+ * this is only useful for security tests where it's required
+ * to control the raw packet payload content
+ */
+
+ if (!(protocol & LWS_WRITE_CLIENT_IGNORE_XOR_MASK) &&
+ wsi->xor_mask != xor_no_mask) {
+
+ if (libwebsocket_0405_frame_mask_generate(wsi)) {
+ lwsl_err("libwebsocket_write: "
+ "frame mask generation failed\n");
+ return 1;
+ }
+
+
+ if (wsi->ietf_spec_revision < 7)
+ /*
+ * use the XOR masking against everything we
+ * send past the frame key
+ */
+ for (n = -pre; n < ((int)len + post); n++)
+ buf[n] = wsi->xor_mask(wsi, buf[n]);
+ else
+ /*
+ * in v7, just mask the payload
+ */
+ for (n = 0; n < (int)len; n++)
+ dropmask[n + 4] =
+ wsi->xor_mask(wsi, dropmask[n + 4]);
+
+
+ if (wsi->ietf_spec_revision < 7) {
+ /* make space for the frame nonce in clear */
+ pre += 4;
+
+ dropmask = &buf[0 - pre];
+ }
+
+ if (dropmask)
+ /* copy the frame nonce into place */
+ memcpy(dropmask,
+ wsi->frame_masking_nonce_04, 4);
+
+ } else {
+ if (wsi->ietf_spec_revision < 7) {
+
+ /* make space for the frame nonce in clear */
+ pre += 4;
+
+ buf[0 - pre] = 0;
+ buf[1 - pre] = 0;
+ buf[2 - pre] = 0;
+ buf[3 - pre] = 0;
+ } else {
+ if (dropmask && wsi->xor_mask != xor_no_mask) {
+ dropmask[0] = 0;
+ dropmask[1] = 0;
+ dropmask[2] = 0;
+ dropmask[3] = 0;
+ }
+ }
+ }
+
+ }
+
+send_raw:
+
+#if 0
+ lwsl_debug("send %ld: ", len + post);
+ for (n = -pre; n < ((int)len + post); n++)
+ lwsl_debug("%02X ", buf[n]);
+
+ lwsl_debug("\n");
+#endif
+
+ if (protocol == LWS_WRITE_HTTP) {
+ if (lws_issue_raw(wsi, (unsigned char *)buf - pre,
+ len + pre + post))
+ return -1;
+
+ return 0;
+ }
+
+ /*
+ * give any active extensions a chance to munge the buffer
+ * before send. We pass in a pointer to an lws_tokens struct
+ * prepared with the default buffer and content length that's in
+ * there. Rather than rewrite the default buffer, extensions
+ * that expect to grow the buffer can adapt .token to
+ * point to their own per-connection buffer in the extension
+ * user allocation. By default with no extensions or no
+ * extension callback handling, just the normal input buffer is
+ * used then so it is efficient.
+ *
+ * callback returns 1 in case it wants to spill more buffers
+ */
+
+ return lws_issue_raw_ext_access(wsi, buf - pre, len + pre + post);
+}
+
+
+/**
+ * libwebsockets_serve_http_file() - Send a file back to the client using http
+ * @context: libwebsockets context
+ * @wsi: Websocket instance (available from user callback)
+ * @file: The file to issue over http
+ * @content_type: The http content type, eg, text/html
+ *
+ * This function is intended to be called from the callback in response
+ * to http requests from the client. It allows the callback to issue
+ * local files down the http link in a single step.
+ */
+
+int libwebsockets_serve_http_file(struct libwebsocket_context *context,
+ struct libwebsocket *wsi, const char *file,
+ const char *content_type)
+{
+ int fd;
+ struct stat stat_buf;
+ char buf[1400];
+ char *p = buf;
+ int n, m;
+
+ strncpy(wsi->filepath, file, sizeof wsi->filepath);
+ wsi->filepath[sizeof(wsi->filepath) - 1] = '\0';
+
+#ifdef WIN32
+ fd = open(wsi->filepath, O_RDONLY | _O_BINARY);
+#else
+ fd = open(wsi->filepath, O_RDONLY);
+#endif
+ if (fd < 1) {
+ p += sprintf(p, "HTTP/1.0 400 Bad\x0d\x0a"
+ "Server: libwebsockets\x0d\x0a"
+ "\x0d\x0a"
+ );
+ libwebsocket_write(wsi, (unsigned char *)buf, p - buf,
+ LWS_WRITE_HTTP);
+
+ return -1;
+ }
+
+ fstat(fd, &stat_buf);
+ wsi->filelen = stat_buf.st_size;
+ p += sprintf(p, "HTTP/1.0 200 OK\x0d\x0a"
+ "Server: libwebsockets\x0d\x0a"
+ "Content-Type: %s\x0d\x0a"
+ "Content-Length: %u\x0d\x0a"
+ "\x0d\x0a", content_type,
+ (unsigned int)stat_buf.st_size);
+
+ n = libwebsocket_write(wsi, (unsigned char *)buf, p - buf, LWS_WRITE_HTTP);
+ if (n) {
+ close(fd);
+ return n;
+ }
+
+ wsi->filepos = 0;
+ wsi->state = WSI_STATE_HTTP_ISSUING_FILE;
+
+ while (!lws_send_pipe_choked(wsi)) {
+
+ n = read(fd, buf, sizeof buf);
+ if (n > 0) {
+ wsi->filepos += n;
+ m = libwebsocket_write(wsi, (unsigned char *)buf, n, LWS_WRITE_HTTP);
+ if (m) {
+ close(fd);
+ return m;
+ }
+ }
+
+ if (n < 0) {
+ close(fd);
+ return -1;
+ }
+
+ if (n < sizeof(buf) || wsi->filepos == wsi->filelen) {
+ /* oh, we were able to finish here! */
+ wsi->state = WSI_STATE_HTTP;
+ close(fd);
+
+ if (wsi->protocol->callback(context, wsi, LWS_CALLBACK_HTTP_FILE_COMPLETION, wsi->user_space,
+ wsi->filepath, wsi->filepos))
+ libwebsocket_close_and_free_session(context, wsi, LWS_CLOSE_STATUS_NOSTATUS);
+
+ return 0;
+ }
+ }
+
+ /* we choked, no worries schedule service for the rest of it */
+
+ libwebsocket_callback_on_writable(context, wsi);
+
+ close(fd);
+
+ return 0;
+}
+
+int libwebsockets_serve_http_file_fragment(struct libwebsocket_context *context,
+ struct libwebsocket *wsi)
+{
+ int fd;
+ int ret = 0;
+ char buf[1400];
+ int n;
+
+#ifdef WIN32
+ fd = open(wsi->filepath, O_RDONLY | _O_BINARY);
+#else
+ fd = open(wsi->filepath, O_RDONLY);
+#endif
+ if (fd < 1)
+ return -1;
+
+ lseek(fd, wsi->filepos, SEEK_SET);
+
+ while (!lws_send_pipe_choked(wsi)) {
+ n = read(fd, buf, sizeof buf);
+ if (n > 0) {
+ libwebsocket_write(wsi, (unsigned char *)buf, n, LWS_WRITE_HTTP);
+ wsi->filepos += n;
+ }
+
+ if (n < 0) {
+ close(fd);
+ return -1;
+ }
+
+ if (n < sizeof(buf) || wsi->filepos == wsi->filelen) {
+ wsi->state = WSI_STATE_HTTP;
+ close(fd);
+ return 0;
+ }
+ }
+
+ libwebsocket_callback_on_writable(context, wsi);
+
+ close(fd);
+
+ return ret;
+}
+
+
diff --git a/lib/parsers.c b/lib/parsers.c
index 4c608e5..98d7124 100644
--- a/lib/parsers.c
+++ b/lib/parsers.c
@@ -911,726 +911,6 @@
}
-static int
-libwebsocket_0405_frame_mask_generate(struct libwebsocket *wsi)
-{
- char buf[4 + 20];
- int n;
-
- /* fetch the per-frame nonce */
-
- n = libwebsockets_get_random(wsi->protocol->owning_server,
- wsi->frame_masking_nonce_04, 4);
- if (n != 4) {
- lwsl_parser("Unable to read from random device %s %d\n",
- SYSTEM_RANDOM_FILEPATH, n);
- return 1;
- }
-
- /* start masking from first byte of masking key buffer */
- wsi->frame_mask_index = 0;
-
- if (wsi->ietf_spec_revision != 4)
- return 0;
-
- /* 04 only does SHA-1 more complex key */
-
- /*
- * the frame key is the frame nonce (4 bytes) followed by the
- * connection masking key, hashed by SHA1
- */
-
- memcpy(buf, wsi->frame_masking_nonce_04, 4);
-
- memcpy(buf + 4, wsi->masking_key_04, 20);
-
- /* concatenate the nonce with the connection key then hash it */
-
- SHA1((unsigned char *)buf, 4 + 20, wsi->frame_mask_04);
-
- return 0;
-}
-
-void lws_stderr_hexdump(unsigned char *buf, size_t len)
-{
- int n;
- int m;
- int start;
-
- lwsl_parser("\n");
-
- for (n = 0; n < len;) {
- start = n;
-
- lwsl_debug("%04X: ", start);
-
- for (m = 0; m < 16 && n < len; m++)
- lwsl_debug("%02X ", buf[n++]);
- while (m++ < 16)
- lwsl_debug(" ");
-
- lwsl_debug(" ");
-
- for (m = 0; m < 16 && (start + m) < len; m++) {
- if (buf[start + m] >= ' ' && buf[start + m] <= 127)
- lwsl_debug("%c", buf[start + m]);
- else
- lwsl_debug(".");
- }
- while (m++ < 16)
- lwsl_debug(" ");
-
- lwsl_debug("\n");
- }
- lwsl_debug("\n");
-}
-
-int lws_issue_raw(struct libwebsocket *wsi, unsigned char *buf, size_t len)
-{
- int n;
- int m;
-
- /*
- * one of the extensions is carrying our data itself? Like mux?
- */
-
- for (n = 0; n < wsi->count_active_extensions; n++) {
- /*
- * there can only be active extensions after handshake completed
- * so we can rely on protocol being set already in here
- */
- m = wsi->active_extensions[n]->callback(
- wsi->protocol->owning_server,
- wsi->active_extensions[n], wsi,
- LWS_EXT_CALLBACK_PACKET_TX_DO_SEND,
- wsi->active_extensions_user[n], &buf, len);
- if (m < 0) {
- lwsl_ext("Extension reports fatal error\n");
- return -1;
- }
- if (m) /* handled */ {
-/* lwsl_ext("ext sent it\n"); */
- return 0;
- }
- }
-
- if (!wsi->sock)
- lwsl_warn("** error 0 sock but expected to send\n");
-
- /*
- * nope, send it on the socket directly
- */
-
-#if 0
- lwsl_debug(" TX: ");
- lws_stderr_hexdump(buf, len);
-#endif
-
-#ifdef LWS_OPENSSL_SUPPORT
- if (wsi->ssl) {
- n = SSL_write(wsi->ssl, buf, len);
- if (n < 0) {
- lwsl_debug("ERROR writing to socket\n");
- return -1;
- }
- } else {
-#endif
- n = send(wsi->sock, buf, len, MSG_NOSIGNAL);
- if (n < 0) {
- lwsl_debug("ERROR writing to socket\n");
- return -1;
- }
-#ifdef LWS_OPENSSL_SUPPORT
- }
-#endif
- return 0;
-}
-
-int
-lws_issue_raw_ext_access(struct libwebsocket *wsi,
- unsigned char *buf, size_t len)
-{
- int ret;
- struct lws_tokens eff_buf;
- int m;
- int n;
-
- eff_buf.token = (char *)buf;
- eff_buf.token_len = len;
-
- /*
- * while we have original buf to spill ourselves, or extensions report
- * more in their pipeline
- */
-
- ret = 1;
- while (ret == 1) {
-
- /* default to nobody has more to spill */
-
- ret = 0;
-
- /* show every extension the new incoming data */
-
- for (n = 0; n < wsi->count_active_extensions; n++) {
- m = wsi->active_extensions[n]->callback(
- wsi->protocol->owning_server,
- wsi->active_extensions[n], wsi,
- LWS_EXT_CALLBACK_PACKET_TX_PRESEND,
- wsi->active_extensions_user[n], &eff_buf, 0);
- if (m < 0) {
- lwsl_ext("Extension: fatal error\n");
- return -1;
- }
- if (m)
- /*
- * at least one extension told us he has more
- * to spill, so we will go around again after
- */
- ret = 1;
- }
-
- /* assuming they left us something to send, send it */
-
- if (eff_buf.token_len)
- if (lws_issue_raw(wsi, (unsigned char *)eff_buf.token,
- eff_buf.token_len))
- return -1;
-
- lwsl_parser("written %d bytes to client\n", eff_buf.token_len);
-
- /* no extension has more to spill */
-
- if (!ret)
- break;
-
- /* we used up what we had */
-
- eff_buf.token = NULL;
- eff_buf.token_len = 0;
-
- /*
- * Did that leave the pipe choked?
- */
-
- if (!lws_send_pipe_choked(wsi))
- /* no we could add more */
- continue;
-
- lwsl_debug("choked\n");
-
- /*
- * Yes, he's choked. Don't spill the rest now get a callback
- * when he is ready to send and take care of it there
- */
- libwebsocket_callback_on_writable(
- wsi->protocol->owning_server, wsi);
- wsi->extension_data_pending = 1;
- ret = 0;
- }
-
- return 0;
-}
-
-/**
- * libwebsocket_write() - Apply protocol then write data to client
- * @wsi: Websocket instance (available from user callback)
- * @buf: The data to send. For data being sent on a websocket
- * connection (ie, not default http), this buffer MUST have
- * LWS_SEND_BUFFER_PRE_PADDING bytes valid BEFORE the pointer
- * and an additional LWS_SEND_BUFFER_POST_PADDING bytes valid
- * in the buffer after (buf + len). This is so the protocol
- * header and trailer data can be added in-situ.
- * @len: Count of the data bytes in the payload starting from buf
- * @protocol: Use LWS_WRITE_HTTP to reply to an http connection, and one
- * of LWS_WRITE_BINARY or LWS_WRITE_TEXT to send appropriate
- * data on a websockets connection. Remember to allow the extra
- * bytes before and after buf if LWS_WRITE_BINARY or LWS_WRITE_TEXT
- * are used.
- *
- * This function provides the way to issue data back to the client
- * for both http and websocket protocols.
- *
- * In the case of sending using websocket protocol, be sure to allocate
- * valid storage before and after buf as explained above. This scheme
- * allows maximum efficiency of sending data and protocol in a single
- * packet while not burdening the user code with any protocol knowledge.
- */
-
-int libwebsocket_write(struct libwebsocket *wsi, unsigned char *buf,
- size_t len, enum libwebsocket_write_protocol protocol)
-{
- int n;
- int pre = 0;
- int post = 0;
- int shift = 7;
- int masked7 = wsi->mode == LWS_CONNMODE_WS_CLIENT &&
- wsi->xor_mask != xor_no_mask;
- unsigned char *dropmask = NULL;
- unsigned char is_masked_bit = 0;
- struct lws_tokens eff_buf;
- int m;
-
- if (len == 0 && protocol != LWS_WRITE_CLOSE) {
- lwsl_warn("zero length libwebsocket_write attempt\n");
- return 0;
- }
-
- if (protocol == LWS_WRITE_HTTP)
- goto send_raw;
-
- /* websocket protocol, either binary or text */
-
- if (wsi->state != WSI_STATE_ESTABLISHED)
- return -1;
-
- /* give a change to the extensions to modify payload */
- eff_buf.token = (char *)buf;
- eff_buf.token_len = len;
-
- for (n = 0; n < wsi->count_active_extensions; n++) {
- m = wsi->active_extensions[n]->callback(
- wsi->protocol->owning_server,
- wsi->active_extensions[n], wsi,
- LWS_EXT_CALLBACK_PAYLOAD_TX,
- wsi->active_extensions_user[n], &eff_buf, 0);
- if (m < 0)
- return -1;
- }
-
- buf = (unsigned char *)eff_buf.token;
- len = eff_buf.token_len;
-
- switch (wsi->ietf_spec_revision) {
- /* chrome likes this as of 30 Oct 2010 */
- /* Firefox 4.0b6 likes this as of 30 Oct 2010 */
- case 0:
- if ((protocol & 0xf) == LWS_WRITE_BINARY) {
- /* in binary mode we send 7-bit used length blocks */
- pre = 1;
- while (len & (127 << shift)) {
- pre++;
- shift += 7;
- }
- n = 0;
- shift -= 7;
- while (shift >= 0) {
- if (shift)
- buf[0 - pre + n] =
- ((len >> shift) & 127) | 0x80;
- else
- buf[0 - pre + n] =
- ((len >> shift) & 127);
- n++;
- shift -= 7;
- }
- break;
- }
-
- /* frame type = text, length-free spam mode */
-
- pre = 1;
- buf[-pre] = 0;
- buf[len] = 0xff; /* EOT marker */
- post = 1;
- break;
-
- case 7:
- case 8:
- case 13:
- if (masked7) {
- pre += 4;
- dropmask = &buf[0 - pre];
- is_masked_bit = 0x80;
- }
- /* fallthru */
- case 4:
- case 5:
- case 6:
- switch (protocol & 0xf) {
- case LWS_WRITE_TEXT:
- if (wsi->ietf_spec_revision < 7)
- n = LWS_WS_OPCODE_04__TEXT_FRAME;
- else
- n = LWS_WS_OPCODE_07__TEXT_FRAME;
- break;
- case LWS_WRITE_BINARY:
- if (wsi->ietf_spec_revision < 7)
- n = LWS_WS_OPCODE_04__BINARY_FRAME;
- else
- n = LWS_WS_OPCODE_07__BINARY_FRAME;
- break;
- case LWS_WRITE_CONTINUATION:
- if (wsi->ietf_spec_revision < 7)
- n = LWS_WS_OPCODE_04__CONTINUATION;
- else
- n = LWS_WS_OPCODE_07__CONTINUATION;
- break;
-
- case LWS_WRITE_CLOSE:
- if (wsi->ietf_spec_revision < 7)
- n = LWS_WS_OPCODE_04__CLOSE;
- else
- n = LWS_WS_OPCODE_07__CLOSE;
-
- /*
- * v5 mandates the first byte of close packet
- * in both client and server directions
- */
-
- switch (wsi->ietf_spec_revision) {
- case 0:
- case 4:
- break;
- case 5:
- /* we can do this because we demand post-buf */
-
- if (len < 1)
- len = 1;
-
- switch (wsi->mode) {
- case LWS_CONNMODE_WS_SERVING:
- /*
- lwsl_debug("LWS_WRITE_CLOSE S\n");
- */
- buf[0] = 'S';
- break;
- case LWS_CONNMODE_WS_CLIENT:
- /*
- lwsl_debug("LWS_WRITE_CLOSE C\n");
- */
- buf[0] = 'C';
- break;
- default:
- break;
- }
- break;
- default:
- /*
- * 06 has a 2-byte status code in network order
- * we can do this because we demand post-buf
- */
-
- if (wsi->close_reason) {
- /* reason codes count as data bytes */
- buf -= 2;
- buf[0] = wsi->close_reason >> 8;
- buf[1] = wsi->close_reason;
- len += 2;
- }
- break;
- }
- break;
- case LWS_WRITE_PING:
- if (wsi->ietf_spec_revision < 7)
- n = LWS_WS_OPCODE_04__PING;
- else
- n = LWS_WS_OPCODE_07__PING;
-
- wsi->pings_vs_pongs++;
- break;
- case LWS_WRITE_PONG:
- if (wsi->ietf_spec_revision < 7)
- n = LWS_WS_OPCODE_04__PONG;
- else
- n = LWS_WS_OPCODE_07__PONG;
- break;
- default:
- lwsl_warn("libwebsocket_write: unknown write "
- "opcode / protocol\n");
- return -1;
- }
-
- if (!(protocol & LWS_WRITE_NO_FIN))
- n |= 1 << 7;
-
- if (len < 126) {
- pre += 2;
- buf[-pre] = n;
- buf[-pre + 1] = len | is_masked_bit;
- } else {
- if (len < 65536) {
- pre += 4;
- buf[-pre] = n;
- buf[-pre + 1] = 126 | is_masked_bit;
- buf[-pre + 2] = len >> 8;
- buf[-pre + 3] = len;
- } else {
- pre += 10;
- buf[-pre] = n;
- buf[-pre + 1] = 127 | is_masked_bit;
-#if defined __LP64__
- buf[-pre + 2] = (len >> 56) & 0x7f;
- buf[-pre + 3] = len >> 48;
- buf[-pre + 4] = len >> 40;
- buf[-pre + 5] = len >> 32;
-#else
- buf[-pre + 2] = 0;
- buf[-pre + 3] = 0;
- buf[-pre + 4] = 0;
- buf[-pre + 5] = 0;
-#endif
- buf[-pre + 6] = len >> 24;
- buf[-pre + 7] = len >> 16;
- buf[-pre + 8] = len >> 8;
- buf[-pre + 9] = len;
- }
- }
- break;
- }
-
- /*
- * Deal with masking if we are in client -> server direction and
- * the protocol demands it
- */
-
- if (wsi->mode == LWS_CONNMODE_WS_CLIENT &&
- wsi->ietf_spec_revision >= 4) {
-
- /*
- * this is only useful for security tests where it's required
- * to control the raw packet payload content
- */
-
- if (!(protocol & LWS_WRITE_CLIENT_IGNORE_XOR_MASK) &&
- wsi->xor_mask != xor_no_mask) {
-
- if (libwebsocket_0405_frame_mask_generate(wsi)) {
- lwsl_err("libwebsocket_write: "
- "frame mask generation failed\n");
- return 1;
- }
-
-
- if (wsi->ietf_spec_revision < 7)
- /*
- * use the XOR masking against everything we
- * send past the frame key
- */
- for (n = -pre; n < ((int)len + post); n++)
- buf[n] = wsi->xor_mask(wsi, buf[n]);
- else
- /*
- * in v7, just mask the payload
- */
- for (n = 0; n < (int)len; n++)
- dropmask[n + 4] =
- wsi->xor_mask(wsi, dropmask[n + 4]);
-
-
- if (wsi->ietf_spec_revision < 7) {
- /* make space for the frame nonce in clear */
- pre += 4;
-
- dropmask = &buf[0 - pre];
- }
-
- if (dropmask)
- /* copy the frame nonce into place */
- memcpy(dropmask,
- wsi->frame_masking_nonce_04, 4);
-
- } else {
- if (wsi->ietf_spec_revision < 7) {
-
- /* make space for the frame nonce in clear */
- pre += 4;
-
- buf[0 - pre] = 0;
- buf[1 - pre] = 0;
- buf[2 - pre] = 0;
- buf[3 - pre] = 0;
- } else {
- if (dropmask && wsi->xor_mask != xor_no_mask) {
- dropmask[0] = 0;
- dropmask[1] = 0;
- dropmask[2] = 0;
- dropmask[3] = 0;
- }
- }
- }
-
- }
-
-send_raw:
-
-#if 0
- lwsl_debug("send %ld: ", len + post);
- for (n = -pre; n < ((int)len + post); n++)
- lwsl_debug("%02X ", buf[n]);
-
- lwsl_debug("\n");
-#endif
-
- if (protocol == LWS_WRITE_HTTP) {
- if (lws_issue_raw(wsi, (unsigned char *)buf - pre,
- len + pre + post))
- return -1;
-
- return 0;
- }
-
- /*
- * give any active extensions a chance to munge the buffer
- * before send. We pass in a pointer to an lws_tokens struct
- * prepared with the default buffer and content length that's in
- * there. Rather than rewrite the default buffer, extensions
- * that expect to grow the buffer can adapt .token to
- * point to their own per-connection buffer in the extension
- * user allocation. By default with no extensions or no
- * extension callback handling, just the normal input buffer is
- * used then so it is efficient.
- *
- * callback returns 1 in case it wants to spill more buffers
- */
-
- return lws_issue_raw_ext_access(wsi, buf - pre, len + pre + post);
-}
-
-
-/**
- * libwebsockets_serve_http_file() - Send a file back to the client using http
- * @context: libwebsockets context
- * @wsi: Websocket instance (available from user callback)
- * @file: The file to issue over http
- * @content_type: The http content type, eg, text/html
- *
- * This function is intended to be called from the callback in response
- * to http requests from the client. It allows the callback to issue
- * local files down the http link in a single step.
- */
-
-int libwebsockets_serve_http_file(struct libwebsocket_context *context,
- struct libwebsocket *wsi, const char *file,
- const char *content_type)
-{
- int fd;
- struct stat stat_buf;
- char buf[1400];
- char *p = buf;
- int n, m;
-
- strncpy(wsi->filepath, file, sizeof wsi->filepath);
- wsi->filepath[sizeof(wsi->filepath) - 1] = '\0';
-
-#ifdef WIN32
- fd = open(wsi->filepath, O_RDONLY | _O_BINARY);
-#else
- fd = open(wsi->filepath, O_RDONLY);
-#endif
- if (fd < 1) {
- p += sprintf(p, "HTTP/1.0 400 Bad\x0d\x0a"
- "Server: libwebsockets\x0d\x0a"
- "\x0d\x0a"
- );
- libwebsocket_write(wsi, (unsigned char *)buf, p - buf,
- LWS_WRITE_HTTP);
-
- return -1;
- }
-
- fstat(fd, &stat_buf);
- wsi->filelen = stat_buf.st_size;
- p += sprintf(p, "HTTP/1.0 200 OK\x0d\x0a"
- "Server: libwebsockets\x0d\x0a"
- "Content-Type: %s\x0d\x0a"
- "Content-Length: %u\x0d\x0a"
- "\x0d\x0a", content_type,
- (unsigned int)stat_buf.st_size);
-
- n = libwebsocket_write(wsi, (unsigned char *)buf, p - buf, LWS_WRITE_HTTP);
- if (n) {
- close(fd);
- return n;
- }
-
- wsi->filepos = 0;
- wsi->state = WSI_STATE_HTTP_ISSUING_FILE;
-
- while (!lws_send_pipe_choked(wsi)) {
-
- n = read(fd, buf, sizeof buf);
- if (n > 0) {
- wsi->filepos += n;
- m = libwebsocket_write(wsi, (unsigned char *)buf, n, LWS_WRITE_HTTP);
- if (m) {
- close(fd);
- return m;
- }
- }
-
- if (n < 0) {
- close(fd);
- return -1;
- }
-
- if (n < sizeof(buf) || wsi->filepos == wsi->filelen) {
- /* oh, we were able to finish here! */
- wsi->state = WSI_STATE_HTTP;
- close(fd);
-
- if (wsi->protocol->callback(context, wsi, LWS_CALLBACK_HTTP_FILE_COMPLETION, wsi->user_space,
- wsi->filepath, wsi->filepos))
- libwebsocket_close_and_free_session(context, wsi, LWS_CLOSE_STATUS_NOSTATUS);
-
- return 0;
- }
- }
-
- /* we choked, no worries schedule service for the rest of it */
-
- libwebsocket_callback_on_writable(context, wsi);
-
- close(fd);
-
- return 0;
-}
-
-int libwebsockets_serve_http_file_fragment(struct libwebsocket_context *context,
- struct libwebsocket *wsi)
-{
- int fd;
- int ret = 0;
- char buf[1400];
- int n;
-
-#ifdef WIN32
- fd = open(wsi->filepath, O_RDONLY | _O_BINARY);
-#else
- fd = open(wsi->filepath, O_RDONLY);
-#endif
- if (fd < 1)
- return -1;
-
- lseek(fd, wsi->filepos, SEEK_SET);
-
- while (!lws_send_pipe_choked(wsi)) {
- n = read(fd, buf, sizeof buf);
- if (n > 0) {
- libwebsocket_write(wsi, (unsigned char *)buf, n, LWS_WRITE_HTTP);
- wsi->filepos += n;
- }
-
- if (n < 0) {
- close(fd);
- return -1;
- }
-
- if (n < sizeof(buf) || wsi->filepos == wsi->filelen) {
- wsi->state = WSI_STATE_HTTP;
- close(fd);
- return 0;
- }
- }
-
- libwebsocket_callback_on_writable(context, wsi);
-
- close(fd);
-
- return ret;
-}
-
-
/**
* libwebsockets_remaining_packet_payload() - Bytes to come before "overall"
* rx packet is complete
diff --git a/libwebsockets-api-doc.html b/libwebsockets-api-doc.html
index 927573a..bd734dc 100644
--- a/libwebsockets-api-doc.html
+++ b/libwebsockets-api-doc.html
@@ -370,69 +370,6 @@
emission on stderr.
</blockquote>
<hr>
-<h2>libwebsocket_write - Apply protocol then write data to client</h2>
-<i>int</i>
-<b>libwebsocket_write</b>
-(<i>struct libwebsocket *</i> <b>wsi</b>,
-<i>unsigned char *</i> <b>buf</b>,
-<i>size_t</i> <b>len</b>,
-<i>enum libwebsocket_write_protocol</i> <b>protocol</b>)
-<h3>Arguments</h3>
-<dl>
-<dt><b>wsi</b>
-<dd>Websocket instance (available from user callback)
-<dt><b>buf</b>
-<dd>The data to send. For data being sent on a websocket
-connection (ie, not default http), this buffer MUST have
-LWS_SEND_BUFFER_PRE_PADDING bytes valid BEFORE the pointer
-and an additional LWS_SEND_BUFFER_POST_PADDING bytes valid
-in the buffer after (buf + len). This is so the protocol
-header and trailer data can be added in-situ.
-<dt><b>len</b>
-<dd>Count of the data bytes in the payload starting from buf
-<dt><b>protocol</b>
-<dd>Use LWS_WRITE_HTTP to reply to an http connection, and one
-of LWS_WRITE_BINARY or LWS_WRITE_TEXT to send appropriate
-data on a websockets connection. Remember to allow the extra
-bytes before and after buf if LWS_WRITE_BINARY or LWS_WRITE_TEXT
-are used.
-</dl>
-<h3>Description</h3>
-<blockquote>
-This function provides the way to issue data back to the client
-for both http and websocket protocols.
-<p>
-In the case of sending using websocket protocol, be sure to allocate
-valid storage before and after buf as explained above. This scheme
-allows maximum efficiency of sending data and protocol in a single
-packet while not burdening the user code with any protocol knowledge.
-</blockquote>
-<hr>
-<h2>libwebsockets_serve_http_file - Send a file back to the client using http</h2>
-<i>int</i>
-<b>libwebsockets_serve_http_file</b>
-(<i>struct libwebsocket_context *</i> <b>context</b>,
-<i>struct libwebsocket *</i> <b>wsi</b>,
-<i>const char *</i> <b>file</b>,
-<i>const char *</i> <b>content_type</b>)
-<h3>Arguments</h3>
-<dl>
-<dt><b>context</b>
-<dd>libwebsockets context
-<dt><b>wsi</b>
-<dd>Websocket instance (available from user callback)
-<dt><b>file</b>
-<dd>The file to issue over http
-<dt><b>content_type</b>
-<dd>The http content type, eg, text/html
-</dl>
-<h3>Description</h3>
-<blockquote>
-This function is intended to be called from the callback in response
-to http requests from the client. It allows the callback to issue
-local files down the http link in a single step.
-</blockquote>
-<hr>
<h2>libwebsockets_remaining_packet_payload - Bytes to come before "overall" rx packet is complete</h2>
<i>size_t</i>
<b>libwebsockets_remaining_packet_payload</b>