about summary refs log tree commit diff
path: root/src/engine/external/libwebsockets/output.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/engine/external/libwebsockets/output.c')
-rw-r--r--src/engine/external/libwebsockets/output.c606
1 files changed, 606 insertions, 0 deletions
diff --git a/src/engine/external/libwebsockets/output.c b/src/engine/external/libwebsockets/output.c
new file mode 100644
index 00000000..b914f281
--- /dev/null
+++ b/src/engine/external/libwebsockets/output.c
@@ -0,0 +1,606 @@
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010-2014 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"
+
+static int
+libwebsocket_0405_frame_mask_generate(struct libwebsocket *wsi)
+{
+	int n;
+
+	/* fetch the per-frame nonce */
+
+	n = libwebsockets_get_random(wsi->protocol->owning_server,
+					   wsi->u.ws.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->u.ws.frame_mask_index = 0;
+
+	return 0;
+}
+
+#ifdef _DEBUG
+
+LWS_VISIBLE void lwsl_hexdump(void *vbuf, size_t len)
+{
+	int n;
+	int m;
+	int start;
+	unsigned char *buf = (unsigned char *)vbuf;
+	char line[80];
+	char *p;
+
+	lwsl_parser("\n");
+
+	for (n = 0; n < len;) {
+		start = n;
+		p = line;
+
+		p += sprintf(p, "%04X: ", start);
+
+		for (m = 0; m < 16 && n < len; m++)
+			p += sprintf(p, "%02X ", buf[n++]);
+		while (m++ < 16)
+			p += sprintf(p, "   ");
+
+		p += sprintf(p, "   ");
+
+		for (m = 0; m < 16 && (start + m) < len; m++) {
+			if (buf[start + m] >= ' ' && buf[start + m] < 127)
+				*p++ = buf[start + m];
+			else
+				*p++ = '.';
+		}
+		while (m++ < 16)
+			*p++ = ' ';
+
+		*p++ = '\n';
+		*p = '\0';
+		lwsl_debug("%s", line);
+	}
+	lwsl_debug("\n");
+}
+
+#endif
+
+/*
+ * notice this returns number of bytes consumed, or -1
+ */
+
+int lws_issue_raw(struct libwebsocket *wsi, unsigned char *buf, size_t len)
+{
+	struct libwebsocket_context *context = wsi->protocol->owning_server;
+	int n;
+	size_t real_len = len;
+	int m;
+	
+	if (!len)
+		return 0;
+	/* just ignore sends after we cleared the truncation buffer */
+	if (wsi->state == WSI_STATE_FLUSHING_STORED_SEND_BEFORE_CLOSE &&
+						!wsi->truncated_send_len)
+		return len;
+
+	if (wsi->truncated_send_len && (buf < wsi->truncated_send_malloc ||
+			buf > (wsi->truncated_send_malloc +
+				wsi->truncated_send_len +
+				wsi->truncated_send_offset))) {
+		lwsl_err("****** %x Sending new, pending truncated ...\n", wsi);
+		assert(0);
+	}
+
+	m = lws_ext_callback_for_each_active(wsi,
+			LWS_EXT_CALLBACK_PACKET_TX_DO_SEND, &buf, len);
+	if (m < 0)
+		return -1;
+	if (m) /* handled */ {
+		n = m;
+		goto handle_truncated_send;
+	}
+	if (wsi->sock < 0)
+		lwsl_warn("** error invalid sock but expected to send\n");
+
+	/*
+	 * nope, send it on the socket directly
+	 */
+	lws_latency_pre(context, wsi);
+	n = lws_ssl_capable_write(wsi, buf, len);
+	lws_latency(context, wsi, "send lws_issue_raw", n, n == len);
+
+	switch (n) {
+	case LWS_SSL_CAPABLE_ERROR:
+		/* we're going to close, let close know sends aren't possible */
+		wsi->socket_is_permanently_unusable = 1;
+		return -1;
+	case LWS_SSL_CAPABLE_MORE_SERVICE:
+		/* nothing got sent, not fatal, retry the whole thing later */
+		n = 0;
+		break;
+	}
+
+handle_truncated_send:
+	/*
+	 * we were already handling a truncated send?
+	 */
+	if (wsi->truncated_send_len) {
+		lwsl_info("***** %x partial send moved on by %d (vs %d)\n",
+							     wsi, n, real_len);
+		wsi->truncated_send_offset += n;
+		wsi->truncated_send_len -= n;
+
+		if (!wsi->truncated_send_len) {
+			lwsl_info("***** %x partial send completed\n", wsi);
+			/* done with it, but don't free it */
+			n = real_len;
+			if (wsi->state == WSI_STATE_FLUSHING_STORED_SEND_BEFORE_CLOSE) {
+				lwsl_info("***** %x signalling to close now\n", wsi);
+				return -1; /* retry closing now */
+			}
+		}
+		/* always callback on writeable */
+		libwebsocket_callback_on_writable(
+					     wsi->protocol->owning_server, wsi);
+
+		return n;
+	}
+
+	if (n == real_len)
+		/* what we just sent went out cleanly */
+		return n;
+
+	if (n && wsi->u.ws.clean_buffer)
+		/*
+		 * This buffer unaffected by extension rewriting.
+		 * It means the user code is expected to deal with
+		 * partial sends.  (lws knows the header was already
+		 * sent, so on next send will just resume sending
+		 * payload)
+		 */
+		 return n;
+
+	/*
+	 * Newly truncated send.  Buffer the remainder (it will get
+	 * first priority next time the socket is writable)
+	 */
+	lwsl_info("***** %x new partial sent %d from %d total\n",
+						      wsi, n, real_len);
+
+	/*
+	 *  - if we still have a suitable malloc lying around, use it
+	 *  - or, if too small, reallocate it
+	 *  - or, if no buffer, create it
+	 */
+	if (!wsi->truncated_send_malloc ||
+			real_len - n > wsi->truncated_send_allocation) {
+		lws_free(wsi->truncated_send_malloc);
+
+		wsi->truncated_send_allocation = real_len - n;
+		wsi->truncated_send_malloc = lws_malloc(real_len - n);
+		if (!wsi->truncated_send_malloc) {
+			lwsl_err("truncated send: unable to malloc %d\n",
+							  real_len - n);
+			return -1;
+		}
+	}
+	wsi->truncated_send_offset = 0;
+	wsi->truncated_send_len = real_len - n;
+	memcpy(wsi->truncated_send_malloc, buf + n, real_len - n);
+
+	/* since something buffered, force it to get another chance to send */
+	libwebsocket_callback_on_writable(wsi->protocol->owning_server, wsi);
+
+	return real_len;
+}
+
+/**
+ * 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.
+ *
+ *	Return may be -1 for a fatal error needing connection close, or a
+ *	positive number reflecting the amount of bytes actually sent.  This
+ *	can be less than the requested number of bytes due to OS memory
+ *	pressure at any given time.
+ */
+
+LWS_VISIBLE 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 masked7 = wsi->mode == LWS_CONNMODE_WS_CLIENT;
+	unsigned char *dropmask = NULL;
+	unsigned char is_masked_bit = 0;
+	size_t orig_len = len;
+	struct lws_tokens eff_buf;
+
+	if (len == 0 && protocol != LWS_WRITE_CLOSE &&
+		     protocol != LWS_WRITE_PING && protocol != LWS_WRITE_PONG) {
+		lwsl_warn("zero length libwebsocket_write attempt\n");
+		return 0;
+	}
+
+	if (protocol == LWS_WRITE_HTTP ||
+	    protocol == LWS_WRITE_HTTP_FINAL ||
+	    protocol == LWS_WRITE_HTTP_HEADERS)
+		goto send_raw;
+
+	/* websocket protocol, either binary or text */
+
+	if (wsi->state != WSI_STATE_ESTABLISHED)
+		return -1;
+
+	/* if we are continuing a frame that already had its header done */
+
+	if (wsi->u.ws.inside_frame)
+		goto do_more_inside_frame;
+
+	wsi->u.ws.clean_buffer = 1;
+
+	/*
+	 * give a chance to the extensions to modify payload
+	 * pre-TX mangling is not allowed to truncate
+	 */
+	eff_buf.token = (char *)buf;
+	eff_buf.token_len = len;
+
+	switch (protocol) {
+	case LWS_WRITE_PING:
+	case LWS_WRITE_PONG:
+	case LWS_WRITE_CLOSE:
+		break;
+	default:
+		if (lws_ext_callback_for_each_active(wsi,
+			       LWS_EXT_CALLBACK_PAYLOAD_TX, &eff_buf, 0) < 0)
+			return -1;
+	}
+
+	/*
+	 * an extension did something we need to keep... for example, if
+	 * compression extension, it has already updated its state according
+	 * to this being issued
+	 */
+	if ((char *)buf != eff_buf.token)
+		/*
+		 * extension recreated it:
+		 * need to buffer this if not all sent
+		 */
+		wsi->u.ws.clean_buffer = 0;
+
+	buf = (unsigned char *)eff_buf.token;
+	len = eff_buf.token_len;
+
+	switch (wsi->ietf_spec_revision) {
+	case 13:
+
+		if (masked7) {
+			pre += 4;
+			dropmask = &buf[0 - pre];
+			is_masked_bit = 0x80;
+		}
+
+		switch (protocol & 0xf) {
+		case LWS_WRITE_TEXT:
+			n = LWS_WS_OPCODE_07__TEXT_FRAME;
+			break;
+		case LWS_WRITE_BINARY:
+			n = LWS_WS_OPCODE_07__BINARY_FRAME;
+			break;
+		case LWS_WRITE_CONTINUATION:
+			n = LWS_WS_OPCODE_07__CONTINUATION;
+			break;
+
+		case LWS_WRITE_CLOSE:
+			n = LWS_WS_OPCODE_07__CLOSE;
+
+			/*
+			 * 06+ has a 2-byte status code in network order
+			 * we can do this because we demand post-buf
+			 */
+
+			if (wsi->u.ws.close_reason) {
+				/* reason codes count as data bytes */
+				buf -= 2;
+				buf[0] = wsi->u.ws.close_reason >> 8;
+				buf[1] = wsi->u.ws.close_reason;
+				len += 2;
+			}
+			break;
+		case LWS_WRITE_PING:
+			n = LWS_WS_OPCODE_07__PING;
+			break;
+		case LWS_WRITE_PONG:
+			n = LWS_WS_OPCODE_07__PONG;
+			break;
+		default:
+			lwsl_warn("lws_write: unknown write opc / 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;
+	}
+
+do_more_inside_frame:
+
+	/*
+	 * Deal with masking if we are in client -> server direction and
+	 * the protocol demands it
+	 */
+
+	if (wsi->mode == LWS_CONNMODE_WS_CLIENT) {
+
+		if (!wsi->u.ws.inside_frame)
+			if (libwebsocket_0405_frame_mask_generate(wsi)) {
+				lwsl_err("frame mask generation failed\n");
+				return -1;
+			}
+
+		/*
+		 * in v7, just mask the payload
+		 */
+		if (dropmask) { /* never set if already inside frame */
+			for (n = 4; n < (int)len + 4; n++)
+				dropmask[n] = dropmask[n] ^
+				wsi->u.ws.frame_masking_nonce_04[
+					(wsi->u.ws.frame_mask_index++) & 3];
+
+			/* copy the frame nonce into place */
+			memcpy(dropmask, wsi->u.ws.frame_masking_nonce_04, 4);
+		}
+	}
+
+send_raw:
+	switch (protocol) {
+	case LWS_WRITE_CLOSE:
+/*		lwsl_hexdump(&buf[-pre], len + post); */
+	case LWS_WRITE_HTTP:
+	case LWS_WRITE_HTTP_FINAL:
+	case LWS_WRITE_HTTP_HEADERS:
+	case LWS_WRITE_PONG:
+	case LWS_WRITE_PING:
+#ifdef LWS_USE_HTTP2
+		if (wsi->mode == LWS_CONNMODE_HTTP2_SERVING) {
+			unsigned char flags = 0;
+
+			n = LWS_HTTP2_FRAME_TYPE_DATA;
+			if (protocol == LWS_WRITE_HTTP_HEADERS) {
+				n = LWS_HTTP2_FRAME_TYPE_HEADERS;
+				flags = LWS_HTTP2_FLAG_END_HEADERS;
+				if (wsi->u.http2.send_END_STREAM)
+					flags |= LWS_HTTP2_FLAG_END_STREAM;
+			}
+			
+			if ((protocol == LWS_WRITE_HTTP || protocol == LWS_WRITE_HTTP_FINAL) && wsi->u.http.content_length) {
+				wsi->u.http.content_remain -= len;
+				lwsl_info("%s: content_remain = %lu\n", __func__, wsi->u.http.content_remain);
+				if (!wsi->u.http.content_remain) {
+					lwsl_info("%s: selecting final write mode\n", __func__);
+					protocol = LWS_WRITE_HTTP_FINAL;
+				}
+			}
+			
+			if (protocol == LWS_WRITE_HTTP_FINAL && wsi->u.http2.END_STREAM) {
+				lwsl_info("%s: setting END_STREAM\n", __func__);
+				flags |= LWS_HTTP2_FLAG_END_STREAM;
+			}
+
+			return lws_http2_frame_write(wsi, n, flags, wsi->u.http2.my_stream_id, len, buf);
+		}
+#endif
+		return lws_issue_raw(wsi, (unsigned char *)buf - pre,
+							      len + pre + post);
+	default:
+		break;
+	}
+
+	wsi->u.ws.inside_frame = 1;
+
+	/*
+	 * 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
+	 *
+	 * This takes care of holding the buffer if send is incomplete, ie,
+	 * if wsi->u.ws.clean_buffer is 0 (meaning an extension meddled with
+	 * the buffer).  If wsi->u.ws.clean_buffer is 1, it will instead
+	 * return to the user code how much OF THE USER BUFFER was consumed.
+	 */
+
+	n = lws_issue_raw_ext_access(wsi, buf - pre, len + pre + post);
+	if (n <= 0)
+		return n;
+
+	if (n == len + pre + post) {
+		/* everything in the buffer was handled (or rebuffered...) */
+		wsi->u.ws.inside_frame = 0;
+		return orig_len;
+	}
+
+	/*
+	 * it is how many bytes of user buffer got sent... may be < orig_len
+	 * in which case callback when writable has already been arranged
+	 * and user code can call libwebsocket_write() again with the rest
+	 * later.
+	 */
+
+	return n - (pre + post);
+}
+
+LWS_VISIBLE int libwebsockets_serve_http_file_fragment(
+		struct libwebsocket_context *context, struct libwebsocket *wsi)
+{
+	int n;
+	int m;
+
+	while (!lws_send_pipe_choked(wsi)) {
+
+		if (wsi->truncated_send_len) {
+			if (lws_issue_raw(wsi, wsi->truncated_send_malloc +
+					wsi->truncated_send_offset,
+						       wsi->truncated_send_len) < 0) {
+				lwsl_info("closing from libwebsockets_serve_http_file_fragment\n");
+				return -1;
+			}
+			continue;
+		}
+
+		if (wsi->u.http.filepos == wsi->u.http.filelen)
+			goto all_sent;
+
+		compatible_file_read(n, wsi->u.http.fd, context->service_buffer,
+					       sizeof(context->service_buffer));
+		if (n < 0)
+			return -1; /* caller will close */
+		if (n) {
+			wsi->u.http.filepos += n;
+			m = libwebsocket_write(wsi, context->service_buffer, n,
+					       wsi->u.http.filepos == wsi->u.http.filelen ? LWS_WRITE_HTTP_FINAL : LWS_WRITE_HTTP);
+			if (m < 0)
+				return -1;
+
+			if (m != n)
+				/* adjust for what was not sent */
+				if (compatible_file_seek_cur(wsi->u.http.fd, m - n) < 0)
+					return -1;
+		}
+all_sent:
+		if (!wsi->truncated_send_len &&
+				wsi->u.http.filepos == wsi->u.http.filelen) {
+			wsi->state = WSI_STATE_HTTP;
+
+			if (wsi->protocol->callback)
+				/* ignore callback returned value */
+				user_callback_handle_rxflow(
+					wsi->protocol->callback, context, wsi,
+					LWS_CALLBACK_HTTP_FILE_COMPLETION,
+					wsi->user_space, NULL, 0);
+			return 1;  /* >0 indicates completed */
+		}
+	}
+
+	lwsl_info("choked before able to send whole file (post)\n");
+	libwebsocket_callback_on_writable(context, wsi);
+
+	return 0; /* indicates further processing must be done */
+}
+
+LWS_VISIBLE int
+lws_ssl_capable_read_no_ssl(struct libwebsocket_context *context,
+			    struct libwebsocket *wsi, unsigned char *buf, int len)
+{
+	int n;
+
+	n = recv(wsi->sock, buf, len, 0);
+	if (n >= 0)
+		return n;
+
+	lwsl_warn("error on reading from skt\n");
+	return LWS_SSL_CAPABLE_ERROR;
+}
+
+LWS_VISIBLE int
+lws_ssl_capable_write_no_ssl(struct libwebsocket *wsi, unsigned char *buf, int len)
+{
+	int n;
+	
+	n = send(wsi->sock, buf, len, 0);
+	if (n >= 0)
+		return n;
+
+	if (LWS_ERRNO == LWS_EAGAIN ||
+	    LWS_ERRNO == LWS_EWOULDBLOCK ||
+	    LWS_ERRNO == LWS_EINTR) {
+		if (LWS_ERRNO == LWS_EWOULDBLOCK)
+			lws_set_blocking_send(wsi);
+
+		return LWS_SSL_CAPABLE_MORE_SERVICE;
+	}
+	lwsl_debug("ERROR writing len %d to skt %d\n", len, n);
+	return LWS_SSL_CAPABLE_ERROR;
+}