예제 #1
0
static int callback_http(struct libwebsocket_context *context,
		struct libwebsocket *wsi,
		enum libwebsocket_callback_reasons reason, void *user,
							   void *in, size_t len)
{
#if 0
	char client_name[128];
	char client_ip[128];
#endif
	char buf[256];
	char leaf_path[1024];
	char b64[64];
	struct timeval tv;
	int n, m;
	unsigned char *p;
	char *other_headers = 0;
	static unsigned char buffer[4096];
	struct stat stat_buf;
	struct per_session_data__http *pss =
			(struct per_session_data__http *)user;
	const char *mimetype;
#ifdef EXTERNAL_POLL
	struct libwebsocket_pollargs *pa = (struct libwebsocket_pollargs *)in;
#endif

	const string path = WEBUI_PATH;

	const char* resource_path = path.c_str();

	switch (reason) {
	case LWS_CALLBACK_HTTP:

		dump_handshake_info(wsi);

		if (len < 1) {
			libwebsockets_return_http_status(context, wsi,
						HTTP_STATUS_BAD_REQUEST, NULL);
			return -1;
		}

		/* this server has no concept of directories */
		if (strchr((const char *)in + 1, '/')) {
			libwebsockets_return_http_status(context, wsi,
						HTTP_STATUS_FORBIDDEN, NULL);
			return -1;
		}

		/* if a legal POST URL, let it continue and accept data */
		if (lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI))
			return 0;

		/* check for the "send a big file by hand" example case */

		if (!strcmp((const char *)in, "/leaf.jpg")) {
			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;

#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;

			fstat(pss->fd, &stat_buf);

			/*
			 * 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.
			 */

			p += sprintf((char *)p,
				"HTTP/1.0 200 OK\x0d\x0a"
				"Server: libwebsockets\x0d\x0a"
				"Content-Type: image/jpeg\x0d\x0a"
					"Content-Length: %u\x0d\x0a\x0d\x0a",
					(unsigned int)stat_buf.st_size);

			/*
			 * send the http headers...
			 * this won't block since it's the first payload sent
			 * on the connection since it was established
			 * (too small for partial)
			 */

			n = libwebsocket_write(wsi, buffer,
				   p - buffer, LWS_WRITE_HTTP);

			if (n < 0) {
				close(pss->fd);
				return -1;
			}
			/*
			 * book us a LWS_CALLBACK_HTTP_WRITEABLE callback
			 */
			libwebsocket_callback_on_writable(context, wsi);
			break;
		}

		/* if not, send a file the easy way */

		// FIXME: Data loss if buffer is too small
		strncpy(buf, resource_path, sizeof(buf));
		buf[sizeof(buf) - 1] = '\0';

		if (strcmp((const char*)in, "/")) {
			if (*((const char *)in) != '/')
				strcat(buf, "/");
			strncat(buf, (const char*)in, sizeof(buf) - strlen(resource_path));
		} else /* default file to serve */
			strcat(buf, "/debugger.html");
		buf[sizeof(buf) - 1] = '\0';

		/* refuse to serve files we don't understand */
		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);
			return -1;
		}

		if (libwebsockets_serve_http_file(context, wsi, buf,
						mimetype, other_headers))
			return -1; /* through completion or error, close the socket */

		/*
		 * notice that the sending of the file completes asynchronously,
		 * we'll get a LWS_CALLBACK_HTTP_FILE_COMPLETION callback when
		 * it's done
		 */

		break;

	case LWS_CALLBACK_HTTP_BODY:
		strncpy(buf, (const char*)in, 20);
		buf[20] = '\0';
		if (len < 20)
			buf[len] = '\0';

		lwsl_notice("LWS_CALLBACK_HTTP_BODY: %s... len %d\n",
				(const char *)buf, (int)len);

		break;

	case LWS_CALLBACK_HTTP_BODY_COMPLETION:
		lwsl_notice("LWS_CALLBACK_HTTP_BODY_COMPLETION\n");
		/* the whole of the sent body arried, close the connection */
		libwebsockets_return_http_status(context, wsi,
						HTTP_STATUS_OK, NULL);

		return -1;

	case LWS_CALLBACK_HTTP_FILE_COMPLETION:
//		lwsl_info("LWS_CALLBACK_HTTP_FILE_COMPLETION seen\n");
		/* kill the connection after we sent one file */
		return -1;

	case LWS_CALLBACK_HTTP_WRITEABLE:
		/*
		 * we can send more of whatever it is we were sending
		 */

		do {
			n = read(pss->fd, buffer, sizeof buffer);
			/* problem reading, close conn */
			if (n < 0)
				goto bail;
			/* sent it all, close conn */
			if (n == 0)
				goto flush_bail;
			/*
			 * because it's HTTP and not websocket, don't need to take
			 * care about pre and postamble
			 */
			m = libwebsocket_write(wsi, buffer, n, LWS_WRITE_HTTP);
			if (m < 0)
				/* write failed, close conn */
				goto bail;
			if (m != n)
				/* partial write, adjust */
				lseek(pss->fd, m - n, SEEK_CUR);

			if (m) /* while still active, extend timeout */
				libwebsocket_set_timeout(wsi,
					PENDING_TIMEOUT_HTTP_CONTENT, 5);

		} while (!lws_send_pipe_choked(wsi));
		libwebsocket_callback_on_writable(context, wsi);
		break;
flush_bail:
		/* true if still partial pending */
		if (lws_send_pipe_choked(wsi)) {
			libwebsocket_callback_on_writable(context, wsi);
			break;
		}

bail:
		close(pss->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
	 * connection continue.
	 */

	case LWS_CALLBACK_FILTER_NETWORK_CONNECTION:
#if 0
		libwebsockets_get_peer_addresses(context, wsi, (int)(long)in, client_name,
			     sizeof(client_name), client_ip, sizeof(client_ip));

		fprintf(stderr, "Received network connect from %s (%s)\n",
							client_name, client_ip);
#endif
		/* if we returned non-zero from here, we kill the connection */
		break;

#ifdef EXTERNAL_POLL
	/*
	 * callbacks for managing the external poll() array appear in
	 * protocol 0 callback
	 */

	case LWS_CALLBACK_LOCK_POLL:
		/*
		 * lock mutex to protect pollfd state
		 * called before any other POLL related callback
		 */
		break;

	case LWS_CALLBACK_UNLOCK_POLL:
		/*
		 * unlock mutex to protect pollfd state when
		 * called after any other POLL related callback
		 */
		break;

	case LWS_CALLBACK_ADD_POLL_FD:

		if (count_pollfds >= max_poll_elements) {
			lwsl_err("LWS_CALLBACK_ADD_POLL_FD: too many sockets to track\n");
			return 1;
		}

		fd_lookup[pa->fd] = count_pollfds;
		pollfds[count_pollfds].fd = pa->fd;
		pollfds[count_pollfds].events = pa->events;
		pollfds[count_pollfds++].revents = 0;
		break;

	case LWS_CALLBACK_DEL_POLL_FD:
		if (!--count_pollfds)
			break;
		m = fd_lookup[pa->fd];
		/* have the last guy take up the vacant slot */
		pollfds[m] = pollfds[count_pollfds];
		fd_lookup[pollfds[count_pollfds].fd] = m;
		break;

	case LWS_CALLBACK_CHANGE_MODE_POLL_FD:
	        pollfds[fd_lookup[pa->fd]].events = pa->events;
		break;

#endif

	case LWS_CALLBACK_GET_THREAD_ID:
		/*
		 * if you will call "libwebsocket_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
		 */

		/* return pthread_getthreadid_np(); */

		break;

	default:
		break;
	}

	return 0;
}
예제 #2
0
static int
callback_lws_mirror(struct libwebsocket_context *context,
			struct libwebsocket *wsi,
			enum libwebsocket_callback_reasons reason,
					       void *user, void *in, size_t len)
{
	int n;
	struct per_session_data__lws_mirror *pss = (struct per_session_data__lws_mirror *)user;

	switch (reason) {

	case LWS_CALLBACK_ESTABLISHED:
		lwsl_info("callback_lws_mirror: LWS_CALLBACK_ESTABLISHED\n");
		pss->ringbuffer_tail = ringbuffer_head;
		pss->wsi = wsi;
		break;

	case LWS_CALLBACK_PROTOCOL_DESTROY:
		lwsl_notice("mirror protocol cleaning up\n");
		for (n = 0; n < sizeof ringbuffer / sizeof ringbuffer[0]; n++)
			if (ringbuffer[n].payload)
				free(ringbuffer[n].payload);
		break;

	case LWS_CALLBACK_SERVER_WRITEABLE:
		if (close_testing)
			break;
		while (pss->ringbuffer_tail != ringbuffer_head) {

			n = libwebsocket_write(wsi, (unsigned char *)
				   ringbuffer[pss->ringbuffer_tail].payload +
				   LWS_SEND_BUFFER_PRE_PADDING,
				   ringbuffer[pss->ringbuffer_tail].len,
								LWS_WRITE_TEXT);
			if (n < 0) {
				lwsl_err("ERROR %d writing to mirror socket\n", n);
				return -1;
			}
			if (n < ringbuffer[pss->ringbuffer_tail].len)
				lwsl_err("mirror partial write %d vs %d\n",
				       n, ringbuffer[pss->ringbuffer_tail].len);

			if (pss->ringbuffer_tail == (MAX_MESSAGE_QUEUE - 1))
				pss->ringbuffer_tail = 0;
			else
				pss->ringbuffer_tail++;

			if (((ringbuffer_head - pss->ringbuffer_tail) &
				  (MAX_MESSAGE_QUEUE - 1)) == (MAX_MESSAGE_QUEUE - 15))
				libwebsocket_rx_flow_allow_all_protocol(
					       libwebsockets_get_protocol(wsi));

			// lwsl_debug("tx fifo %d\n", (ringbuffer_head - pss->ringbuffer_tail) & (MAX_MESSAGE_QUEUE - 1));

			if (lws_send_pipe_choked(wsi)) {
				libwebsocket_callback_on_writable(context, wsi);
				break;
			}
			/*
			 * for tests with chrome on same machine as client and
			 * server, this is needed to stop chrome choking
			 */
#ifdef _WIN32
			Sleep(1);
#else
			usleep(1);
#endif
		}
		break;

	case LWS_CALLBACK_RECEIVE:

		if (((ringbuffer_head - pss->ringbuffer_tail) &
				  (MAX_MESSAGE_QUEUE - 1)) == (MAX_MESSAGE_QUEUE - 1)) {
			lwsl_err("dropping!\n");
			goto choke;
		}

		if (ringbuffer[ringbuffer_head].payload)
			free(ringbuffer[ringbuffer_head].payload);

		ringbuffer[ringbuffer_head].payload =
				malloc(LWS_SEND_BUFFER_PRE_PADDING + len +
						  LWS_SEND_BUFFER_POST_PADDING);
		ringbuffer[ringbuffer_head].len = len;
		memcpy((char *)ringbuffer[ringbuffer_head].payload +
					  LWS_SEND_BUFFER_PRE_PADDING, in, len);
		if (ringbuffer_head == (MAX_MESSAGE_QUEUE - 1))
			ringbuffer_head = 0;
		else
			ringbuffer_head++;

		if (((ringbuffer_head - pss->ringbuffer_tail) &
				  (MAX_MESSAGE_QUEUE - 1)) != (MAX_MESSAGE_QUEUE - 2))
			goto done;

choke:
		lwsl_debug("LWS_CALLBACK_RECEIVE: throttling %p\n", wsi);
		libwebsocket_rx_flow_control(wsi, 0);

//		lwsl_debug("rx fifo %d\n", (ringbuffer_head - pss->ringbuffer_tail) & (MAX_MESSAGE_QUEUE - 1));
done:
		libwebsocket_callback_on_writable_all_protocol(
					       libwebsockets_get_protocol(wsi));
		break;

	/*
	 * this just demonstrates how to use the protocol filter. If you won't
	 * study and reject connections based on header content, you don't need
	 * to handle this callback
	 */

	case LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION:
		dump_handshake_info(wsi);
		/* you could return non-zero here and kill the connection */
		break;

	default:
		break;
	}

	return 0;
}
예제 #3
0
int
lws_issue_raw_ext_access(struct lws *wsi, unsigned char *buf, size_t len)
{
	struct lws_tokens eff_buf;
	int ret, m, n = 0;

	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 */
		m = lws_ext_cb_active(wsi,
			       LWS_EXT_CB_PACKET_TX_PRESEND, &eff_buf, 0);
		if (m < 0)
			return -1;
		if (m) /* handled */
			ret = 1;

		if ((char *)buf != eff_buf.token)
			/*
			 * extension recreated it:
			 * need to buffer this if not all sent
			 */
			wsi->u.ws.clean_buffer = 0;

		/* assuming they left us something to send, send it */

		if (eff_buf.token_len) {
			n = lws_issue_raw(wsi, (unsigned char *)eff_buf.token,
							    eff_buf.token_len);
			if (n < 0) {
				lwsl_info("closing from ext access\n");
				return -1;
			}

			/* always either sent it all or privately buffered */
			if (wsi->u.ws.clean_buffer)
				len = n;
		}

		lwsl_parser("written %d bytes to client\n", n);

		/* no extension has more to spill?  Then we can go */

		if (!ret)
			break;

		/* we used up what we had */

		eff_buf.token = NULL;
		eff_buf.token_len = 0;

		/*
		 * Did that leave the pipe choked?
		 * Or we had to hold on to some of it?
		 */

		if (!lws_send_pipe_choked(wsi) && !wsi->trunc_len)
			/* no we could add more, lets's do that */
			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
		 */
		lws_callback_on_writable(wsi);
		wsi->extension_data_pending = 1;
		ret = 0;
	}

	return len;
}
예제 #4
0
int callback_jittertrap(struct lws *wsi, enum lws_callback_reasons reason,
                        void *user, void *in,
                        size_t len __attribute__((unused)))
{
	unsigned char buf[LWS_SEND_BUFFER_PRE_PADDING + MAX_JSON_MSG_LEN +
	                  LWS_SEND_BUFFER_POST_PADDING];
	unsigned char *p = &buf[LWS_SEND_BUFFER_PRE_PADDING];
	struct per_session_data__jittertrap *pss =
	    (struct per_session_data__jittertrap *)user;

	int err, cb_err;
	struct cb_data cbd = { wsi, p };

	/* run jt init, stats producer, etc. */
	jt_server_tick();

	switch (reason) {
	case LWS_CALLBACK_CLOSED:
		err = jt_ws_mq_consumer_unsubscribe(pss->consumer_id);
		if (err) {
			lwsl_err("mq consumer unsubscribe failed.\n");
		}
		break;

	case LWS_CALLBACK_ESTABLISHED:
		lwsl_info("callback_jt: "
		          "LWS_CALLBACK_ESTABLISHED\n");
		err = jt_ws_mq_consumer_subscribe(&(pss->consumer_id));
		if (err) {
			lwsl_err("mq consumer subscription failed.\n");
		}
		jt_srv_send_iface_list();
		jt_srv_send_select_iface();
		jt_srv_send_netem_params();
		jt_srv_send_sample_period();
		break;

	case LWS_CALLBACK_SERVER_WRITEABLE:
		do {
			err = jt_ws_mq_consume(pss->consumer_id, lws_writer,
			                       &cbd, &cb_err);
			if (lws_partial_buffered(wsi) ||
			    lws_send_pipe_choked(wsi)) {
				lws_callback_on_writable(wsi);
				break;
			}
		} while (!err);

		if (-JT_WS_MQ_EMPTY != err) {
			return err;
		}
		break;

	case LWS_CALLBACK_RECEIVE:
		jt_server_msg_receive(in);
		break;

	/*
	 * this just demonstrates how to use the protocol filter. If you won't
	 * study and reject connections based on header content, you don't need
	 * to handle this callback
	 */
	case LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION:
		dump_handshake_info(wsi);
		/* you could return non-zero here and kill the connection */
		break;

	default:
		break;
	}

	return 0;
}
예제 #5
0
static int callback_http(struct libwebsocket_context *context,
		struct libwebsocket *wsi,
		enum libwebsocket_callback_reasons reason, void *user,
							   void *in, size_t len)
{
#if 0
	char client_name[128];
	char client_ip[128];
#endif
	char buf[256];
	int n, m;
	unsigned char *p;
	static unsigned char buffer[4096];
	struct stat stat_buf;
	struct per_session_data__http *pss = (struct per_session_data__http *)user;
#ifdef EXTERNAL_POLL
	int fd = (int)(long)in;
#endif

	switch (reason) {
	case LWS_CALLBACK_HTTP:

		/* check for the "send a big file by hand" example case */

		if (!strcmp((const char *)in, "/leaf.jpg")) {

			/* well, let's demonstrate how to send the hard way */

			p = buffer;

			pss->fd = open(LOCAL_RESOURCE_PATH"/leaf.jpg", O_RDONLY);
			if (pss->fd < 0)
				return -1;

			fstat(pss->fd, &stat_buf);

			/*
			 * 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.
			 */

			p += sprintf((char *)p,
				"HTTP/1.0 200 OK\x0d\x0a"
				"Server: libwebsockets\x0d\x0a"
				"Content-Type: image/jpeg\x0d\x0a"
					"Content-Length: %u\x0d\x0a\x0d\x0a",
					(unsigned int)stat_buf.st_size);

			/*
			 * send the http headers...
			 * this won't block since it's the first payload sent
			 * on the connection since it was established
			 * (too small for partial)
			 */

			n = libwebsocket_write(wsi, buffer,
				   p - buffer, LWS_WRITE_HTTP);

			if (n < 0) {
				close(pss->fd);
				return -1;
			}
			/*
			 * book us a LWS_CALLBACK_HTTP_WRITEABLE callback
			 */
			libwebsocket_callback_on_writable(context, wsi);
			break;
		}

		/* if not, send a file the easy way */

		for (n = 0; n < (sizeof(whitelist) / sizeof(whitelist[0]) - 1); n++)
			if (in && strcmp((const char *)in, whitelist[n].urlpath) == 0)
				break;

		sprintf(buf, LOCAL_RESOURCE_PATH"%s", whitelist[n].urlpath);

		if (libwebsockets_serve_http_file(context, wsi, buf, whitelist[n].mimetype))
			return -1; /* through completion or error, close the socket */

		/*
		 * notice that the sending of the file completes asynchronously,
		 * we'll get a LWS_CALLBACK_HTTP_FILE_COMPLETION callback when
		 * it's done
		 */

		break;

	case LWS_CALLBACK_HTTP_FILE_COMPLETION:
//		lwsl_info("LWS_CALLBACK_HTTP_FILE_COMPLETION seen\n");
		/* kill the connection after we sent one file */
		return -1;

	case LWS_CALLBACK_HTTP_WRITEABLE:
		/*
		 * we can send more of whatever it is we were sending
		 */

		do {
			n = read(pss->fd, buffer, sizeof buffer);
			/* problem reading, close conn */
			if (n < 0)
				goto bail;
			/* sent it all, close conn */
			if (n == 0)
				goto bail;
			/*
			 * because it's HTTP and not websocket, don't need to take
			 * care about pre and postamble
			 */
			m = libwebsocket_write(wsi, buffer, n, LWS_WRITE_HTTP);
			if (m < 0)
				/* write failed, close conn */
				goto bail;
			if (m != n)
				/* partial write, adjust */
				lseek(pss->fd, m - n, SEEK_CUR);

		} while (!lws_send_pipe_choked(wsi));
		libwebsocket_callback_on_writable(context, wsi);
		break;

bail:
		close(pss->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
	 * connection continue.
	 */

	case LWS_CALLBACK_FILTER_NETWORK_CONNECTION:
#if 0
		libwebsockets_get_peer_addresses(context, wsi, (int)(long)in, client_name,
			     sizeof(client_name), client_ip, sizeof(client_ip));

		fprintf(stderr, "Received network connect from %s (%s)\n",
							client_name, client_ip);
#endif
		/* if we returned non-zero from here, we kill the connection */
		break;

#ifdef EXTERNAL_POLL
	/*
	 * callbacks for managing the external poll() array appear in
	 * protocol 0 callback
	 */

	case LWS_CALLBACK_ADD_POLL_FD:

		if (count_pollfds >= max_poll_elements) {
			lwsl_err("LWS_CALLBACK_ADD_POLL_FD: too many sockets to track\n");
			return 1;
		}

		fd_lookup[fd] = count_pollfds;
		pollfds[count_pollfds].fd = fd;
		pollfds[count_pollfds].events = (int)(long)len;
		pollfds[count_pollfds++].revents = 0;
		break;

	case LWS_CALLBACK_DEL_POLL_FD:
		if (!--count_pollfds)
			break;
		m = fd_lookup[fd];
		/* have the last guy take up the vacant slot */
		pollfds[m] = pollfds[count_pollfds];
		fd_lookup[pollfds[count_pollfds].fd] = m;
		break;

	case LWS_CALLBACK_SET_MODE_POLL_FD:
		pollfds[fd_lookup[fd]].events |= (int)(long)len;
		break;

	case LWS_CALLBACK_CLEAR_MODE_POLL_FD:
		pollfds[fd_lookup[fd]].events &= ~(int)(long)len;
		break;
#endif

	default:
		break;
	}

	return 0;
}
예제 #6
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;
}
예제 #7
0
int
lws_handle_POLLOUT_event(struct lws *wsi, struct lws_pollfd *pollfd)
{
	int write_type = LWS_WRITE_PONG;
	struct lws_tokens eff_buf;
#ifdef LWS_USE_HTTP2
	struct lws *wsi2;
#endif
	int ret, m, n;

	/*
	 * user callback is lowest priority to get these notifications
	 * actually, since other pending things cannot be disordered
	 */

	/* Priority 1: pending truncated sends are incomplete ws fragments
	 *	       If anything else sent first the protocol would be
	 *	       corrupted.
	 */
	if (wsi->trunc_len) {
		if (lws_issue_raw(wsi, wsi->trunc_alloc + wsi->trunc_offset,
				  wsi->trunc_len) < 0) {
			lwsl_info("%s signalling to close\n", __func__);
			return -1;
		}
		/* leave POLLOUT active either way */
		return 0;
	} else
		if (wsi->state == LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE)
			return -1; /* retry closing now */


#ifdef LWS_USE_HTTP2
	/* Priority 2: protocol packets
	 */
	if (wsi->pps) {
		lwsl_info("servicing pps %d\n", wsi->pps);
		switch (wsi->pps) {
		case LWS_PPS_HTTP2_MY_SETTINGS:
		case LWS_PPS_HTTP2_ACK_SETTINGS:
			lws_http2_do_pps_send(lws_get_context(wsi), wsi);
			break;
		default:
			break;
		}
		wsi->pps = LWS_PPS_NONE;
		lws_rx_flow_control(wsi, 1);

		return 0; /* leave POLLOUT active */
	}
#endif
	/* Priority 3: pending control packets (pong or close)
	 */
	if ((wsi->state == LWSS_ESTABLISHED &&
	     wsi->u.ws.ping_pending_flag) ||
	    (wsi->state == LWSS_RETURNED_CLOSE_ALREADY &&
	     wsi->u.ws.payload_is_close)) {

		if (wsi->u.ws.payload_is_close)
			write_type = LWS_WRITE_CLOSE;

		n = lws_write(wsi, &wsi->u.ws.ping_payload_buf[LWS_PRE],
			      wsi->u.ws.ping_payload_len, write_type);
		if (n < 0)
			return -1;

		/* well he is sent, mark him done */
		wsi->u.ws.ping_pending_flag = 0;
		if (wsi->u.ws.payload_is_close)
			/* oh... a close frame was it... then we are done */
			return -1;

		/* otherwise for PING, leave POLLOUT active either way */
		return 0;
	}

	/* Priority 4: if we are closing, not allowed to send more data frags
	 *	       which means user callback or tx ext flush banned now
	 */
	if (wsi->state == LWSS_RETURNED_CLOSE_ALREADY)
		goto user_service;

	/* Priority 5: Tx path extension with more to send
	 *
	 *	       These are handled as new fragments each time around
	 *	       So while we must block new writeable callback to enforce
	 *	       payload ordering, but since they are always complete
	 *	       fragments control packets can interleave OK.
	 */
	if (wsi->state == LWSS_ESTABLISHED && wsi->u.ws.tx_draining_ext) {
		lwsl_ext("SERVICING TX EXT DRAINING\n");
		if (lws_write(wsi, NULL, 0, LWS_WRITE_CONTINUATION) < 0)
			return -1;
		/* leave POLLOUT active */
		return 0;
	}

	/* Priority 6: user can get the callback
	 */
	m = lws_ext_cb_active(wsi, LWS_EXT_CB_IS_WRITEABLE, NULL, 0);
	if (m)
		return -1;
#ifndef LWS_NO_EXTENSIONS
	if (!wsi->extension_data_pending)
		goto user_service;
#endif
	/*
	 * check in on the active extensions, see if they
	 * had pending stuff to spill... they need to get the
	 * first look-in otherwise sequence will be disordered
	 *
	 * NULL, zero-length eff_buf means just spill pending
	 */

	ret = 1;
	while (ret == 1) {

		/* default to nobody has more to spill */

		ret = 0;
		eff_buf.token = NULL;
		eff_buf.token_len = 0;

		/* give every extension a chance to spill */

		m = lws_ext_cb_active(wsi,
					LWS_EXT_CB_PACKET_TX_PRESEND,
					       &eff_buf, 0);
		if (m < 0) {
			lwsl_err("ext reports 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 gave us something to send, send it */

		if (eff_buf.token_len) {
			n = lws_issue_raw(wsi, (unsigned char *)eff_buf.token,
					  eff_buf.token_len);
			if (n < 0) {
				lwsl_info("closing from POLLOUT spill\n");
				return -1;
			}
			/*
			 * Keep amount spilled small to minimize chance of this
			 */
			if (n != eff_buf.token_len) {
				lwsl_err("Unable to spill ext %d vs %s\n",
							  eff_buf.token_len, n);
				return -1;
			}
		} else
			continue;

		/* no extension has more to spill */

		if (!ret)
			continue;

		/*
		 * There's more to spill from an extension, but we just sent
		 * something... did that leave the pipe choked?
		 */

		if (!lws_send_pipe_choked(wsi))
			/* no we could add more */
			continue;

		lwsl_info("choked in POLLOUT service\n");

		/*
		 * Yes, he's choked.  Leave the POLLOUT masked on so we will
		 * come back here when he is unchoked.  Don't call the user
		 * callback to enforce ordering of spilling, he'll get called
		 * when we come back here and there's nothing more to spill.
		 */

		return 0;
	}
#ifndef LWS_NO_EXTENSIONS
	wsi->extension_data_pending = 0;
#endif
user_service:
	/* one shot */

	if (pollfd)
		if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) {
			lwsl_info("failed at set pollfd\n");
			return 1;
		}

	if (!wsi->hdr_parsing_completed)
		return 0;

#ifdef LWS_USE_HTTP2
	/*
	 * we are the 'network wsi' for potentially many muxed child wsi with
	 * no network connection of their own, who have to use us for all their
	 * network actions.  So we use a round-robin scheme to share out the
	 * POLLOUT notifications to our children.
	 *
	 * But because any child could exhaust the socket's ability to take
	 * writes, we can only let one child get notified each time.
	 *
	 * In addition children may be closed / deleted / added between POLLOUT
	 * notifications, so we can't hold pointers
	 */

	if (wsi->mode != LWSCM_HTTP2_SERVING) {
		lwsl_info("%s: non http2\n", __func__);
		goto notify;
	}

	wsi->u.http2.requested_POLLOUT = 0;
	if (!wsi->u.http2.initialized) {
		lwsl_info("pollout on uninitialized http2 conn\n");
		return 0;
	}

	lwsl_info("%s: doing children\n", __func__);

	wsi2 = wsi;
	do {
		wsi2 = wsi2->u.http2.next_child_wsi;
		lwsl_info("%s: child %p\n", __func__, wsi2);
		if (!wsi2)
			continue;
		if (!wsi2->u.http2.requested_POLLOUT)
			continue;
		wsi2->u.http2.requested_POLLOUT = 0;
		if (lws_calllback_as_writeable(wsi2)) {
			lwsl_debug("Closing POLLOUT child\n");
			lws_close_free_wsi(wsi2, LWS_CLOSE_STATUS_NOSTATUS);
		}
		wsi2 = wsi;
	} while (wsi2 != NULL && !lws_send_pipe_choked(wsi));

	lwsl_info("%s: completed\n", __func__);

	return 0;
notify:
#endif
	return lws_calllback_as_writeable(wsi);
}
예제 #8
0
int
callback_lws_mirror(struct lws *wsi, enum lws_callback_reasons reason,
                    void *user, void *in, size_t len)
{
    struct per_session_data__lws_mirror *pss =
        (struct per_session_data__lws_mirror *)user;
    int n, m;

    switch (reason) {

    case LWS_CALLBACK_ESTABLISHED:
        lwsl_info("%s: LWS_CALLBACK_ESTABLISHED\n", __func__);
        pss->ringbuffer_tail = ringbuffer_head;
        pss->wsi = wsi;
        break;

    case LWS_CALLBACK_PROTOCOL_DESTROY:
        lwsl_notice("%s: mirror protocol cleaning up\n", __func__);
        for (n = 0; n < sizeof ringbuffer / sizeof ringbuffer[0]; n++)
            if (ringbuffer[n].payload)
                free(ringbuffer[n].payload);
        break;

    case LWS_CALLBACK_SERVER_WRITEABLE:
        if (close_testing)
            break;
        while (pss->ringbuffer_tail != ringbuffer_head) {
            m = ringbuffer[pss->ringbuffer_tail].len;
            n = lws_write(wsi, (unsigned char *)
                          ringbuffer[pss->ringbuffer_tail].payload +
                          LWS_PRE, m, LWS_WRITE_TEXT);
            if (n < 0) {
                lwsl_err("ERROR %d writing to mirror socket\n", n);
                return -1;
            }
            if (n < m)
                lwsl_err("mirror partial write %d vs %d\n", n, m);

            if (pss->ringbuffer_tail == (MAX_MESSAGE_QUEUE - 1))
                pss->ringbuffer_tail = 0;
            else
                pss->ringbuffer_tail++;

            if (((ringbuffer_head - pss->ringbuffer_tail) &
                    (MAX_MESSAGE_QUEUE - 1)) == (MAX_MESSAGE_QUEUE - 15))
                lws_rx_flow_allow_all_protocol(lws_get_context(wsi),
                                               lws_get_protocol(wsi));

            if (lws_send_pipe_choked(wsi)) {
                lws_callback_on_writable(wsi);
                break;
            }
        }
        break;

    case LWS_CALLBACK_RECEIVE:
        if (((ringbuffer_head - pss->ringbuffer_tail) &
                (MAX_MESSAGE_QUEUE - 1)) == (MAX_MESSAGE_QUEUE - 1)) {
            lwsl_err("dropping!\n");
            goto choke;
        }

        if (ringbuffer[ringbuffer_head].payload)
            free(ringbuffer[ringbuffer_head].payload);

        ringbuffer[ringbuffer_head].payload = malloc(LWS_PRE + len);
        ringbuffer[ringbuffer_head].len = len;
        memcpy((char *)ringbuffer[ringbuffer_head].payload +
               LWS_PRE, in, len);
        if (ringbuffer_head == (MAX_MESSAGE_QUEUE - 1))
            ringbuffer_head = 0;
        else
            ringbuffer_head++;

        if (((ringbuffer_head - pss->ringbuffer_tail) &
                (MAX_MESSAGE_QUEUE - 1)) != (MAX_MESSAGE_QUEUE - 2))
            goto done;

choke:
        lwsl_debug("LWS_CALLBACK_RECEIVE: throttling %p\n", wsi);
        lws_rx_flow_control(wsi, 0);

done:
        lws_callback_on_writable_all_protocol(lws_get_context(wsi),
                                              lws_get_protocol(wsi));
        break;

    /*
     * this just demonstrates how to use the protocol filter. If you won't
     * study and reject connections based on header content, you don't need
     * to handle this callback
     */

    case LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION:
        dump_handshake_info(wsi);
        /* you could return non-zero here and kill the connection */
        break;

    default:
        break;
    }

    return 0;
}
예제 #9
0
int
lws_handle_POLLOUT_event(struct libwebsocket_context *context,
		   struct libwebsocket *wsi, struct libwebsocket_pollfd *pollfd)
{
	int n;
	struct lws_tokens eff_buf;
#ifdef LWS_USE_HTTP2
	struct libwebsocket *wsi2;
#endif
	int ret;
	int m;
	int write_type = LWS_WRITE_PONG;

	/* pending truncated sends have uber priority */

	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("lws_handle_POLLOUT_event signalling to close\n");
			return -1;
		}
		/* leave POLLOUT active either way */
		return 0;
	} else
		if (wsi->state == WSI_STATE_FLUSHING_STORED_SEND_BEFORE_CLOSE) {
			lwsl_info("***** %x signalling to close in POLLOUT handler\n", wsi);
			return -1; /* retry closing now */
		}
#ifdef LWS_USE_HTTP2
	/* protocol packets are next */
	if (wsi->pps) {
		lwsl_info("servicing pps %d\n", wsi->pps);
		switch (wsi->pps) {
		case LWS_PPS_HTTP2_MY_SETTINGS:
		case LWS_PPS_HTTP2_ACK_SETTINGS:
			lws_http2_do_pps_send(context, wsi);
			break;
		default:
			break;
		}
		wsi->pps = LWS_PPS_NONE;
		libwebsocket_rx_flow_control(wsi, 1);
		
		return 0; /* leave POLLOUT active */
	}
#endif
	/* pending control packets have next priority */
	
	if ((wsi->state == WSI_STATE_ESTABLISHED &&
	     wsi->u.ws.ping_pending_flag) ||
	    (wsi->state == WSI_STATE_RETURNED_CLOSE_ALREADY &&
	     wsi->u.ws.payload_is_close)) {

		if (wsi->u.ws.payload_is_close)
			write_type = LWS_WRITE_CLOSE;

		n = libwebsocket_write(wsi, 
				&wsi->u.ws.ping_payload_buf[
					LWS_SEND_BUFFER_PRE_PADDING],
					wsi->u.ws.ping_payload_len,
							       write_type);
		if (n < 0)
			return -1;

		/* well he is sent, mark him done */
		wsi->u.ws.ping_pending_flag = 0;
		if (wsi->u.ws.payload_is_close)
			/* oh... a close frame was it... then we are done */
			return -1;

		/* otherwise for PING, leave POLLOUT active either way */
		return 0;
	}

	/* if we are closing, don't confuse the user with writeable cb */

	if (wsi->state == WSI_STATE_RETURNED_CLOSE_ALREADY)
		goto user_service;
	
	/* if nothing critical, user can get the callback */
	
	m = lws_ext_callback_for_each_active(wsi, LWS_EXT_CALLBACK_IS_WRITEABLE,
								       NULL, 0);
#ifndef LWS_NO_EXTENSIONS
	if (!wsi->extension_data_pending)
		goto user_service;
#endif
	/*
	 * check in on the active extensions, see if they
	 * had pending stuff to spill... they need to get the
	 * first look-in otherwise sequence will be disordered
	 *
	 * NULL, zero-length eff_buf means just spill pending
	 */

	ret = 1;
	while (ret == 1) {

		/* default to nobody has more to spill */

		ret = 0;
		eff_buf.token = NULL;
		eff_buf.token_len = 0;

		/* give every extension a chance to spill */
		
		m = lws_ext_callback_for_each_active(wsi,
					LWS_EXT_CALLBACK_PACKET_TX_PRESEND,
							           &eff_buf, 0);
		if (m < 0) {
			lwsl_err("ext reports 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 gave us something to send, send it */

		if (eff_buf.token_len) {
			n = lws_issue_raw(wsi, (unsigned char *)eff_buf.token,
							     eff_buf.token_len);
			if (n < 0) {
				lwsl_info("closing from POLLOUT spill\n");
				return -1;
			}
			/*
			 * Keep amount spilled small to minimize chance of this
			 */
			if (n != eff_buf.token_len) {
				lwsl_err("Unable to spill ext %d vs %s\n",
							  eff_buf.token_len, n);
				return -1;
			}
		} else
			continue;

		/* no extension has more to spill */

		if (!ret)
			continue;

		/*
		 * There's more to spill from an extension, but we just sent
		 * something... did that leave the pipe choked?
		 */

		if (!lws_send_pipe_choked(wsi))
			/* no we could add more */
			continue;

		lwsl_info("choked in POLLOUT service\n");

		/*
		 * Yes, he's choked.  Leave the POLLOUT masked on so we will
		 * come back here when he is unchoked.  Don't call the user
		 * callback to enforce ordering of spilling, he'll get called
		 * when we come back here and there's nothing more to spill.
		 */

		return 0;
	}
#ifndef LWS_NO_EXTENSIONS
	wsi->extension_data_pending = 0;
#endif
user_service:
	/* one shot */

	if (pollfd) {
		if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) {
			lwsl_info("failled at set pollfd\n");
			return 1;
		}

		lws_libev_io(context, wsi, LWS_EV_STOP | LWS_EV_WRITE);
	}

#ifdef LWS_USE_HTTP2
	/* 
	 * we are the 'network wsi' for potentially many muxed child wsi with
	 * no network connection of their own, who have to use us for all their
	 * network actions.  So we use a round-robin scheme to share out the
	 * POLLOUT notifications to our children.
	 * 
	 * But because any child could exhaust the socket's ability to take
	 * writes, we can only let one child get notified each time.
	 * 
	 * In addition children may be closed / deleted / added between POLLOUT
	 * notifications, so we can't hold pointers
	 */
	
	if (wsi->mode != LWS_CONNMODE_HTTP2_SERVING) {
		lwsl_info("%s: non http2\n", __func__);
		goto notify;
	}

	wsi->u.http2.requested_POLLOUT = 0;
	if (!wsi->u.http2.initialized) {
		lwsl_info("pollout on uninitialized http2 conn\n");
		return 0;
	}
	
	lwsl_info("%s: doing children\n", __func__);

	wsi2 = wsi;
	do {
		wsi2 = wsi2->u.http2.next_child_wsi;
		lwsl_info("%s: child %p\n", __func__, wsi2);
		if (!wsi2)
			continue;
		if (!wsi2->u.http2.requested_POLLOUT)
			continue;
		wsi2->u.http2.requested_POLLOUT = 0;
		if (lws_calllback_as_writeable(context, wsi2)) {
			lwsl_debug("Closing POLLOUT child\n");
			libwebsocket_close_and_free_session(context, wsi2,
						LWS_CLOSE_STATUS_NOSTATUS);
		}
		wsi2 = wsi;
	} while (wsi2 != NULL && !lws_send_pipe_choked(wsi));
	
	lwsl_info("%s: completed\n", __func__);
	
	return 0;
notify:
#endif
	return lws_calllback_as_writeable(context, wsi);
}
예제 #10
0
static int
callback_maos_monitor(struct libwebsocket_context *context,
		      struct libwebsocket *wsi,
		      enum libwebsocket_callback_reasons reason,
		      void *user, void *in, size_t len)
{
    int n;
    struct per_session_data__maos_monitor *pss = (struct per_session_data__maos_monitor *)user;

    switch (reason) {

    case LWS_CALLBACK_ESTABLISHED:
	lwsl_info("callback_maos_monitor: LWS_CALLBACK_ESTABLISHED\n");
	pss->ringbuffer_tail = ringbuffer_head;
	pss->wsi = wsi;
	pss->head=pss->tail=0;
	html_convert_all(&pss->head, &pss->tail, 
			 LWS_SEND_BUFFER_PRE_PADDING, LWS_SEND_BUFFER_POST_PADDING);
	libwebsocket_callback_on_writable(context, wsi);
	lwsl_notice("head=%p, tail=%p\n", pss->head, pss->tail);
	break;

    case LWS_CALLBACK_PROTOCOL_DESTROY:
	lwsl_notice("mirror protocol cleaning up\n");
	for (n = 0; n < (int)(sizeof ringbuffer / sizeof ringbuffer[0]); n++)
	    if (ringbuffer[n].payload)
		free(ringbuffer[n].payload);
	break;

    case LWS_CALLBACK_SERVER_WRITEABLE:
	while(pss->head){/*initialization*/
	    n = libwebsocket_write(wsi, (unsigned char *)
				   pss->head->payload +
				   LWS_SEND_BUFFER_PRE_PADDING,
				   pss->head->len,
				   LWS_WRITE_TEXT);
	    if(n<0){
		lwsl_err("ERROR %d writing to mirror socket\n", n);
		return -1;
	    }else if(n<(int)pss->head->len){
		lwsl_err("mirror partial write %d vs %d\n", n, pss->head->len);
	    }
	    l_message *tmp=pss->head;
	    pss->head=pss->head->next;
	    free(tmp->payload);
	    free(tmp);
	    if(lws_send_pipe_choked(wsi)){
		libwebsocket_callback_on_writable(context, wsi);
		break;
	    }
#ifdef _WIN32
	    Sleep(1);
#else
	    usleep(1);
#endif
	}
	while (pss->ringbuffer_tail != ringbuffer_head) {
	    n = libwebsocket_write(wsi, (unsigned char *)
				   ringbuffer[pss->ringbuffer_tail].payload +
				   LWS_SEND_BUFFER_PRE_PADDING,
				   ringbuffer[pss->ringbuffer_tail].len,
				   LWS_WRITE_TEXT);
	    if (n < 0) {
		lwsl_err("ERROR %d writing to mirror socket\n", n);
		return -1;
	    }
	    if (n < (int)ringbuffer[pss->ringbuffer_tail].len)
		lwsl_err("mirror partial write %d vs %d\n",
			 n, ringbuffer[pss->ringbuffer_tail].len);

	    if (pss->ringbuffer_tail == (MAX_MESSAGE_QUEUE - 1))
		pss->ringbuffer_tail = 0;
	    else
		pss->ringbuffer_tail++;


	    if (lws_send_pipe_choked(wsi)) {
		libwebsocket_callback_on_writable(context, wsi);
		break;
	    }
	    /*
	     * for tests with chrome on same machine as client and
	     * server, this is needed to stop chrome choking
	     */
#ifdef _WIN32
	    Sleep(1);
#else
	    usleep(1);
#endif
	}
	break;

    case LWS_CALLBACK_RECEIVE:
	scheduler_handle_ws((char*)in, len);
	//ws_push(in, len);
	break;

	/*
	 * this just demonstrates how to use the protocol filter. If you won't
	 * study and reject connections based on header content, you don't need
	 * to handle this callback
	 */

    case LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION:
	/* you could return non-zero here and kill the connection */
	break;

    default:
	break;
    }

    return 0;
}
예제 #11
0
int callback_http(struct libwebsocket_context *context,
		struct libwebsocket *wsi,
		enum libwebsocket_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;
	char leaf_path[1024];
	const char *mimetype;
	char *other_headers;
	unsigned char *end;
	struct timeval tv;
	unsigned char *p;
	char buf[256];
	char b64[64];
	int n, m;

#ifdef EXTERNAL_POLL
	struct libwebsocket_pollargs *pa = (struct libwebsocket_pollargs *)in;
#endif

	switch (reason) {
	case LWS_CALLBACK_HTTP:

		dump_handshake_info(wsi);

		if (len < 1) {
			libwebsockets_return_http_status(context, wsi,
						HTTP_STATUS_BAD_REQUEST, NULL);
			goto try_to_reuse;
		}

		/* this example server has no concept of directories */
		if (strchr((const char *)in + 1, '/')) {
			libwebsockets_return_http_status(context, wsi,
						HTTP_STATUS_FORBIDDEN, NULL);
			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;

		/* check for the "send a big file by hand" example case */

		if (!strcmp((const char *)in, "/leaf.jpg")) {
			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;

			if (fstat(pss->fd, &stat_buf) < 0)
				return -1;

			/*
			 * 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))
				return 1;
			if (lws_add_http_header_by_token(context, wsi,
					WSI_TOKEN_HTTP_SERVER,
				    	(unsigned char *)"libwebsockets",
					13, &p, end))
				return 1;
			if (lws_add_http_header_by_token(context, 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))
				return 1;
			if (lws_finalize_http_header(context, wsi, &p, end))
				return 1;

			/*
			 * send the http headers...
			 * 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);

			if (n < 0) {
				close(pss->fd);
				return -1;
			}
			/*
			 * book us a LWS_CALLBACK_HTTP_WRITEABLE callback
			 */
			libwebsocket_callback_on_writable(context, wsi);
			break;
		}

		/* if not, send a file the easy way */
		strcpy(buf, resource_path);
		if (strcmp(in, "/")) {
			if (*((const char *)in) != '/')
				strcat(buf, "/");
			strncat(buf, in, sizeof(buf) - strlen(resource_path));
		} else /* default file to serve */
			strcat(buf, "/test.html");
		buf[sizeof(buf) - 1] = '\0';

		/* refuse to serve files we don't understand */
		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);
			return -1;
		}

		/* demostrates how to set a cookie on / */

		other_headers = NULL;
		n = 0;
		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 */
			gettimeofday(&tv, NULL);
			n = sprintf(b64, "test=LWS_%u_%u_COOKIE;Max-Age=360000",
				(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:", 
				(unsigned char *)b64, n, &p,
				(unsigned char *)leaf_path + sizeof(leaf_path)))
				return 1;
			n = (char *)p - leaf_path;
			other_headers = 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 */

		/*
		 * notice that the sending of the file completes asynchronously,
		 * we'll get a LWS_CALLBACK_HTTP_FILE_COMPLETION callback when
		 * it's done
		 */

		break;

	case LWS_CALLBACK_HTTP_BODY:
		strncpy(buf, in, 20);
		buf[20] = '\0';
		if (len < 20)
			buf[len] = '\0';

		lwsl_notice("LWS_CALLBACK_HTTP_BODY: %s... len %d\n",
				(const char *)buf, (int)len);

		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);
		goto try_to_reuse;

	case LWS_CALLBACK_HTTP_FILE_COMPLETION:
		goto try_to_reuse;

	case LWS_CALLBACK_HTTP_WRITEABLE:
		/*
		 * we can send more of whatever it is we were sending
		 */
		do {
			/* we'd like the send this much */
			n = sizeof(buffer) - LWS_SEND_BUFFER_PRE_PADDING;
			
			/* but if the peer told us he wants less, we can adapt */
			m = lws_get_peer_write_allowance(wsi);

			/* -1 means not using a protocol that has this info */
			if (m == 0)
				/* right now, peer can't handle anything */
				goto later;

			if (m != -1 && m < n)
				/* he couldn't handle that much */
				n = m;
			
			n = read(pss->fd, buffer + LWS_SEND_BUFFER_PRE_PADDING,
									n);
			/* problem reading, close conn */
			if (n < 0)
				goto bail;
			/* sent it all, close conn */
			if (n == 0)
				goto flush_bail;
			/*
			 * 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)
				/* 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));

later:
		libwebsocket_callback_on_writable(context, wsi);
		break;
flush_bail:
		/* true if still partial pending */
		if (lws_partial_buffered(wsi)) {
			libwebsocket_callback_on_writable(context, wsi);
			break;
		}
		close(pss->fd);
		goto try_to_reuse;

bail:
		close(pss->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
	 * connection continue.
	 */
	case LWS_CALLBACK_FILTER_NETWORK_CONNECTION:

		/* if we returned non-zero from here, we kill the connection */
		break;

	/*
	 * callbacks for managing the external poll() array appear in
	 * protocol 0 callback
	 */

	case LWS_CALLBACK_LOCK_POLL:
		/*
		 * 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) {
			lwsl_err("LWS_CALLBACK_ADD_POLL_FD: too many sockets to track\n");
			return 1;
		}

		fd_lookup[pa->fd] = count_pollfds;
		pollfds[count_pollfds].fd = pa->fd;
		pollfds[count_pollfds].events = pa->events;
		pollfds[count_pollfds++].revents = 0;
		break;

	case LWS_CALLBACK_DEL_POLL_FD:
		if (!--count_pollfds)
			break;
		m = fd_lookup[pa->fd];
		/* have the last guy take up the vacant slot */
		pollfds[m] = pollfds[count_pollfds];
		fd_lookup[pollfds[count_pollfds].fd] = m;
		break;

	case LWS_CALLBACK_CHANGE_MODE_POLL_FD:
	        pollfds[fd_lookup[pa->fd]].events = pa->events;
		break;
#endif

	case LWS_CALLBACK_GET_THREAD_ID:
		/*
		 * if you will call "libwebsocket_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
		 */

		/* return pthread_getthreadid_np(); */

		break;

	default:
		break;
	}

	return 0;

	/* if we're on HTTP1.1 or 2.0, will keep the idle connection alive */
try_to_reuse:
	if (lws_http_transaction_completed(wsi))
		return -1;

	return 0;
}
예제 #12
0
int
lws_handle_POLLOUT_event(struct libwebsocket_context *context,
		   struct libwebsocket *wsi, struct libwebsocket_pollfd *pollfd)
{
	int n;
	struct lws_tokens eff_buf;
	int ret;
	int m;
	int handled = 0;

	/* pending truncated sends have uber priority */

	if (wsi->truncated_send_len) {
		lws_issue_raw(wsi, wsi->truncated_send_malloc +
				wsi->truncated_send_offset,
						wsi->truncated_send_len);
		/* leave POLLOUT active either way */
		return 0;
	}

	m = lws_ext_callback_for_each_active(wsi, LWS_EXT_CALLBACK_IS_WRITEABLE,
								       NULL, 0);
	if (handled == 1)
		goto notify_action;
#ifndef LWS_NO_EXTENSIONS
	if (!wsi->extension_data_pending || handled == 2)
		goto user_service;
#endif
	/*
	 * check in on the active extensions, see if they
	 * had pending stuff to spill... they need to get the
	 * first look-in otherwise sequence will be disordered
	 *
	 * NULL, zero-length eff_buf means just spill pending
	 */

	ret = 1;
	while (ret == 1) {

		/* default to nobody has more to spill */

		ret = 0;
		eff_buf.token = NULL;
		eff_buf.token_len = 0;

		/* give every extension a chance to spill */
		
		m = lws_ext_callback_for_each_active(wsi,
					LWS_EXT_CALLBACK_PACKET_TX_PRESEND,
							           &eff_buf, 0);
		if (m < 0) {
			lwsl_err("ext reports 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 gave us something to send, send it */

		if (eff_buf.token_len) {
			n = lws_issue_raw(wsi, (unsigned char *)eff_buf.token,
							     eff_buf.token_len);
			if (n < 0)
				return -1;
			/*
			 * Keep amount spilled small to minimize chance of this
			 */
			if (n != eff_buf.token_len) {
				lwsl_err("Unable to spill ext %d vs %s\n",
							  eff_buf.token_len, n);
				return -1;
			}
		} else
			continue;

		/* no extension has more to spill */

		if (!ret)
			continue;

		/*
		 * There's more to spill from an extension, but we just sent
		 * something... did that leave the pipe choked?
		 */

		if (!lws_send_pipe_choked(wsi))
			/* no we could add more */
			continue;

		lwsl_info("choked in POLLOUT service\n");

		/*
		 * Yes, he's choked.  Leave the POLLOUT masked on so we will
		 * come back here when he is unchoked.  Don't call the user
		 * callback to enforce ordering of spilling, he'll get called
		 * when we come back here and there's nothing more to spill.
		 */

		return 0;
	}
#ifndef LWS_NO_EXTENSIONS
	wsi->extension_data_pending = 0;

user_service:
#endif
	/* one shot */

	if (pollfd) {
		if (lws_change_pollfd(wsi, LWS_POLLOUT, 0))
			return 1;
#ifdef LWS_USE_LIBEV
		if (LWS_LIBEV_ENABLED(context))
			ev_io_stop(context->io_loop,
				(struct ev_io *)&wsi->w_write);
#endif /* LWS_USE_LIBEV */
	}

notify_action:
	if (wsi->mode == LWS_CONNMODE_WS_CLIENT)
		n = LWS_CALLBACK_CLIENT_WRITEABLE;
	else
		n = LWS_CALLBACK_SERVER_WRITEABLE;

	return user_callback_handle_rxflow(wsi->protocol->callback, context,
			wsi, (enum libwebsocket_callback_reasons) n,
						      wsi->user_space, NULL, 0);
}
예제 #13
0
int callback_http(struct lws *webSocketInstance, enum lws_callback_reasons reason, void *user, void *in, size_t len) {
    struct per_session_data__http *pss = (struct per_session_data__http *) user;
    struct timeval tv;
    unsigned long amount, file_len;
    const char *contentType;
    static unsigned char buffer4096[4096];
    char buffer256[256];
    char buffer64[64];
    char leaf_path[1024];
    char *other_headers;
    unsigned char *end;
    unsigned char *p;
    int n, m, headerIndex = 0;
    
#ifdef EXTERNAL_POLL
    struct lws_pollargs *pa = (struct lws_pollargs *)in;
#endif
    
    switch (reason) {
        case LWS_CALLBACK_HTTP:
            /* SS_Server_DumpHandShake(webSocketInstance); */
            
            /* dump the individual URI Arg parameters */
            while (lws_hdr_copy_fragment(webSocketInstance, buffer256, sizeof(buffer256),
                                         WSI_TOKEN_HTTP_URI_ARGS, headerIndex) > 0) {
                lwsl_info("URI Arg %d: %s\n", ++headerIndex, buffer256);
            }
            
            if (len < 1) {
                lws_return_http_status(webSocketInstance, HTTP_STATUS_BAD_REQUEST, NULL);
                goto try_to_reuse;
            }
            /* if a legal POST URL, let it continue and accept data */
            if (lws_hdr_total_length(webSocketInstance, WSI_TOKEN_POST_URI)) {
                return 0;
            }
            if(strchr((const char*)in + 1, '/')) {
                if (strcmp(in, "/")) {
                    if (*((const char *)in) != '/') {
                        strcat(buffer256, "/");
                    }
                    strncat(buffer256, in, sizeof(buffer256) - strlen(RESOURCE_PATH));
                } 
                else { /* default file to serve */
                    strcat(buffer256, in);
                }

                buffer256[sizeof(buffer256) - 1] = '\0';
                sprintf(leaf_path, "%s%s", RESOURCE_PATH, buffer256);
                p = buffer4096 + LWS_PRE;
                end = p + sizeof(buffer4096) - LWS_PRE;
                pss->fd = lws_plat_file_open(webSocketInstance, leaf_path, &file_len, LWS_O_RDONLY);
                contentType = get_mimetype(buffer256);

                if (!contentType) {
                    lwsl_err("Unknown content-type for %s\n", buffer256);
                    lws_return_http_status(webSocketInstance, HTTP_STATUS_UNSUPPORTED_MEDIA_TYPE, NULL);
                    return -1;
                }
                if (pss->fd == LWS_INVALID_FILE) {
                        return -1;
                }
                if (lws_add_http_header_status(webSocketInstance, 200, &p, end)) {
                    return 1;
                }
                if (lws_add_http_header_by_token(webSocketInstance, WSI_TOKEN_HTTP_SERVER,
                                                 (unsigned char *)"libwebsockets", 13, &p, end)) {
                    return 1;
                }
                if(strncmp(contentType, "*/*", 3) == 0) {
                    if (lws_add_http_header_by_token(webSocketInstance, WSI_TOKEN_HTTP_CONTENT_TYPE,
                                                     (unsigned char *)contentType, 4, &p, end)) {
                        return 1;
                    }
                } else {
                    if(strncmp(contentType, "application/x-javascript", 24) == 0)
                    {
                        if (lws_add_http_header_by_token(webSocketInstance, WSI_TOKEN_HTTP_CONTENT_TYPE,
                                                         (unsigned char *)contentType, 24, &p, end)) {
                            return 1;
                        }
                    }
                    else
                    {
                        if (lws_add_http_header_by_token(webSocketInstance, WSI_TOKEN_HTTP_CONTENT_TYPE,
                                     (unsigned char *)contentType, 9, &p, end)) {
                            return 1;
                        }
                    }
                }
                if (lws_add_http_header_content_length(webSocketInstance, file_len, &p, end)) {
                    return 1;
                }
                if (lws_finalize_http_header(webSocketInstance, &p, end)) {
                    return 1;
                }
                n = lws_write(webSocketInstance, buffer4096 + LWS_PRE, p - (buffer4096 + LWS_PRE), LWS_WRITE_HTTP_HEADERS);
                if (n < 0) {
                    lws_plat_file_close(webSocketInstance, pss->fd);
                    return -1;
                }
                lws_callback_on_writable(webSocketInstance);
                break;
                printf("%s",buffer256);
                other_headers = NULL;
                n = 0;
                if (!strcmp((const char *)in, "/") && !lws_hdr_total_length(webSocketInstance, WSI_TOKEN_HTTP_COOKIE)) {
                    /* this isn't very unguessable but it'll do for us */
                    gettimeofday(&tv, NULL);
                    n = sprintf(buffer64, "test=SOUNDSHIELD_%u_%u_COOKIE;Max-Age=360000",
                                (unsigned int)tv.tv_sec, (unsigned int)tv.tv_usec);
                    p = (unsigned char *)buffer256;
                    if (lws_add_http_header_by_name(webSocketInstance, (unsigned char *)"set-cookie:",
                                                    (unsigned char *)buffer64, n, &p,
                                                    (unsigned char *)buffer256 + sizeof(buffer256))) {
                        return 1;
                    }
                    n = (char *)p - buffer256;
                    other_headers = buffer256;
                }
                n = lws_serve_http_file(webSocketInstance, buffer256, contentType, other_headers, n);
                if (n < 0 || ((n > 0) && lws_http_transaction_completed(webSocketInstance))) {
                    return -1; /* error or can't reuse connection: close the socket */
                }
                break;
            }
            /* if not, send a file the easy way */
            strcpy(buffer256, RESOURCE_PATH);
            if (strcmp(in, "/")) {
                if (*((const char *)in) != '/') {
                    strcat(buffer256, "/");
                }
                strncat(buffer256, in, sizeof(buffer256) - strlen(RESOURCE_PATH));
            } else { /* default file to serve */
                strcat(buffer256, "/index.html");
            }
            buffer256[sizeof(buffer256) - 1] = '\0';
            /* refuse to serve files we don't understand */
            contentType = get_mimetype(buffer256);
            if (!contentType) {
                lwsl_err("Unknown content-type for %s\n", buffer256);
                lws_return_http_status(webSocketInstance, HTTP_STATUS_UNSUPPORTED_MEDIA_TYPE, NULL);
                return -1;
            }
            /* demonstrates how to set a cookie on / */
            other_headers = NULL;
            n = 0;
            if (!strcmp((const char *)in, "/") && !lws_hdr_total_length(webSocketInstance, WSI_TOKEN_HTTP_COOKIE)) {
                /* this isn't very unguessable but it'll do for us */
                gettimeofday(&tv, NULL);
                n = sprintf(buffer64, "test=LWS_%u_%u_COOKIE;Max-Age=360000", (unsigned int)tv.tv_sec, (unsigned int)tv.tv_usec);
                p = (unsigned char *)leaf_path;
                if (lws_add_http_header_by_name(webSocketInstance, (unsigned char *)"set-cookie:",
                                                (unsigned char *)buffer64, n, &p,
                                                (unsigned char *)leaf_path + sizeof(leaf_path))) {
                    return 1;
                }
                n = (char *)p - leaf_path;
                other_headers = leaf_path;
            }
            n = lws_serve_http_file(webSocketInstance, buffer256, contentType, other_headers, n);
            if (n < 0 || ((n > 0) && lws_http_transaction_completed(webSocketInstance))) {
                return -1; /* error or can't reuse connection: close the socket */
            }
            /*
             * notice that the sending of the file completes asynchronously,
             * we'll get a LWS_CALLBACK_HTTP_FILE_COMPLETION callback when
             * it's done
             */
            break;
        case LWS_CALLBACK_HTTP_BODY:
            strncpy(buffer256, in, 20);
            buffer256[20] = '\0';
            if (len < 20) {
                buffer256[len] = '\0';
            }
            lwsl_notice("LWS_CALLBACK_HTTP_BODY: %s... len %d\n", (const char *)buffer256, (int)len);
            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 */
            lws_return_http_status(webSocketInstance, HTTP_STATUS_OK, NULL);
            goto try_to_reuse;
        case LWS_CALLBACK_HTTP_FILE_COMPLETION:
            goto try_to_reuse;
        case LWS_CALLBACK_HTTP_WRITEABLE:
            /*
             * we can send more of whatever it is we were sending
             */
            do {
                /* we'd like the send this much */
                n = sizeof(buffer4096) - LWS_PRE;
                /* but if the peer told us he wants less, we can adapt */
                m = lws_get_peer_write_allowance(webSocketInstance);
                /* -1 means not using a protocol that has this info */
                if (m == 0) {
                /* right now, peer can't handle anything */
                    goto later;
                }
                if (m != -1 && m < n) {
                /* he couldn't handle that much */
                    n = m;
                }
                n = lws_plat_file_read(webSocketInstance, pss->fd, &amount, buffer4096 + LWS_PRE, n);
                /* problem reading, close conn */
                if (n < 0) { 
                    goto bail;
                }
                n = (int)amount;
                /* sent it all, close conn */
                if (n == 0) {
                    goto flush_bail;
                }
                /*
                 * 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 = lws_write(webSocketInstance, buffer4096 + LWS_PRE, n, LWS_WRITE_HTTP);
                if (m < 0) {
                /* write failed, close conn */
                    goto bail;
                }
                /*
                 * http2 won't do this
                 */
                if (m != n) {
                /* partial write, adjust */
                    if (lws_plat_file_seek_cur(webSocketInstance, pss->fd, m - n) == (unsigned long)-1)
                        goto bail;
                }
                if (m) { /* while still active, extend timeout */
                    lws_set_timeout(webSocketInstance, PENDING_TIMEOUT_HTTP_CONTENT, 5);
                }
                /* if we have indigestion, let him clear it
                 * before eating more */
                if (lws_partial_buffered(webSocketInstance)) break;      
            } while (!lws_send_pipe_choked(webSocketInstance));
        later:
            lws_callback_on_writable(webSocketInstance);
            break;
        flush_bail:
            /* true if still partial pending */
            if (lws_partial_buffered(webSocketInstance)) {
                lws_callback_on_writable(webSocketInstance);
                break;
            }
            lws_plat_file_close(webSocketInstance, pss->fd);
            goto try_to_reuse;
        bail:
            lws_plat_file_close(webSocketInstance, pss->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
             * connection continue.
             */
        case LWS_CALLBACK_FILTER_NETWORK_CONNECTION:
            /* if we returned non-zero from here, we kill the connection */
            break;
            /*
             * callbacks for managing the external poll() array appear in
             * protocol 0 callback
             */
            
        case LWS_CALLBACK_LOCK_POLL:
            /*
             * 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) {
                lwsl_err("LWS_CALLBACK_ADD_POLL_FD: too many sockets to track\n");
                return 1;
            }
            fd_lookup[pa->fd] = count_pollfds;
            pollfds[count_pollfds].fd = pa->fd;
            pollfds[count_pollfds].events = pa->events;
            pollfds[count_pollfds++].revents = 0;
            break;
        case LWS_CALLBACK_DEL_POLL_FD:
            if (!--count_pollfds) {
                break;
            }
            m = fd_lookup[pa->fd];
            /* have the last guy take up the vacant slot */
            pollfds[m] = pollfds[count_pollfds];
            fd_lookup[pollfds[count_pollfds].fd] = m;
            break;
        case LWS_CALLBACK_CHANGE_MODE_POLL_FD:
            pollfds[fd_lookup[pa->fd]].events = pa->events;
            break;
#endif
        case LWS_CALLBACK_GET_THREAD_ID:
            /*
             * 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
             */
            /* return pthread_getthreadid_np(); */
            break;
        default:
            break;
    }
    return 0;
    /* if we're on HTTP1.1 or 2.0, will keep the idle connection alive */
try_to_reuse:
    if (lws_http_transaction_completed(webSocketInstance)) {
        return -1;
    }
    return 0;
}