1d4afb5ceSopenharmony_ci/*
2d4afb5ceSopenharmony_ci * ws protocol handler plugin for "lws-minimal"
3d4afb5ceSopenharmony_ci *
4d4afb5ceSopenharmony_ci * Written in 2010-2019 by Andy Green <andy@warmcat.com>
5d4afb5ceSopenharmony_ci *
6d4afb5ceSopenharmony_ci * This file is made available under the Creative Commons CC0 1.0
7d4afb5ceSopenharmony_ci * Universal Public Domain Dedication.
8d4afb5ceSopenharmony_ci *
9d4afb5ceSopenharmony_ci * This version uses an lws_ring ringbuffer to cache up to 8 messages at a time,
10d4afb5ceSopenharmony_ci * so it's not so easy to lose messages.
11d4afb5ceSopenharmony_ci */
12d4afb5ceSopenharmony_ci
13d4afb5ceSopenharmony_ci#if !defined (LWS_PLUGIN_STATIC)
14d4afb5ceSopenharmony_ci#define LWS_DLL
15d4afb5ceSopenharmony_ci#define LWS_INTERNAL
16d4afb5ceSopenharmony_ci#include <libwebsockets.h>
17d4afb5ceSopenharmony_ci#endif
18d4afb5ceSopenharmony_ci
19d4afb5ceSopenharmony_ci#include <string.h>
20d4afb5ceSopenharmony_ci#include <stdlib.h>
21d4afb5ceSopenharmony_ci
22d4afb5ceSopenharmony_ci/* one of these created for each message */
23d4afb5ceSopenharmony_ci
24d4afb5ceSopenharmony_cistruct msg {
25d4afb5ceSopenharmony_ci	void *payload; /* is malloc'd */
26d4afb5ceSopenharmony_ci	size_t len;
27d4afb5ceSopenharmony_ci};
28d4afb5ceSopenharmony_ci
29d4afb5ceSopenharmony_ci/* one of these is created for each client connecting to us */
30d4afb5ceSopenharmony_ci
31d4afb5ceSopenharmony_cistruct per_session_data__minimal {
32d4afb5ceSopenharmony_ci	struct per_session_data__minimal *pss_list;
33d4afb5ceSopenharmony_ci	struct lws *wsi;
34d4afb5ceSopenharmony_ci	uint32_t tail;
35d4afb5ceSopenharmony_ci};
36d4afb5ceSopenharmony_ci
37d4afb5ceSopenharmony_ci/* one of these is created for each vhost our protocol is used with */
38d4afb5ceSopenharmony_ci
39d4afb5ceSopenharmony_cistruct per_vhost_data__minimal {
40d4afb5ceSopenharmony_ci	struct lws_context *context;
41d4afb5ceSopenharmony_ci	struct lws_vhost *vhost;
42d4afb5ceSopenharmony_ci	const struct lws_protocols *protocol;
43d4afb5ceSopenharmony_ci
44d4afb5ceSopenharmony_ci	lws_sorted_usec_list_t sul;
45d4afb5ceSopenharmony_ci
46d4afb5ceSopenharmony_ci	struct per_session_data__minimal *pss_list; /* linked-list of live pss*/
47d4afb5ceSopenharmony_ci
48d4afb5ceSopenharmony_ci	struct lws_ring *ring; /* ringbuffer holding unsent messages */
49d4afb5ceSopenharmony_ci	struct lws_client_connect_info i;
50d4afb5ceSopenharmony_ci	struct lws *client_wsi;
51d4afb5ceSopenharmony_ci};
52d4afb5ceSopenharmony_ci
53d4afb5ceSopenharmony_ci/* destroys the message when everyone has had a copy of it */
54d4afb5ceSopenharmony_ci
55d4afb5ceSopenharmony_cistatic void
56d4afb5ceSopenharmony_ci__minimal_destroy_message(void *_msg)
57d4afb5ceSopenharmony_ci{
58d4afb5ceSopenharmony_ci	struct msg *msg = _msg;
59d4afb5ceSopenharmony_ci
60d4afb5ceSopenharmony_ci	free(msg->payload);
61d4afb5ceSopenharmony_ci	msg->payload = NULL;
62d4afb5ceSopenharmony_ci	msg->len = 0;
63d4afb5ceSopenharmony_ci}
64d4afb5ceSopenharmony_ci
65d4afb5ceSopenharmony_cistatic void
66d4afb5ceSopenharmony_cisul_connect_attempt(struct lws_sorted_usec_list *sul)
67d4afb5ceSopenharmony_ci{
68d4afb5ceSopenharmony_ci	struct per_vhost_data__minimal *vhd =
69d4afb5ceSopenharmony_ci		lws_container_of(sul, struct per_vhost_data__minimal, sul);
70d4afb5ceSopenharmony_ci
71d4afb5ceSopenharmony_ci	vhd->i.context = vhd->context;
72d4afb5ceSopenharmony_ci	vhd->i.port = 443;
73d4afb5ceSopenharmony_ci	vhd->i.address = "libwebsockets.org";
74d4afb5ceSopenharmony_ci	vhd->i.path = "/";
75d4afb5ceSopenharmony_ci	vhd->i.host = vhd->i.address;
76d4afb5ceSopenharmony_ci	vhd->i.origin = vhd->i.address;
77d4afb5ceSopenharmony_ci	vhd->i.ssl_connection = 1;
78d4afb5ceSopenharmony_ci
79d4afb5ceSopenharmony_ci	vhd->i.protocol = "dumb-increment-protocol";
80d4afb5ceSopenharmony_ci	vhd->i.local_protocol_name = "lws-minimal-proxy";
81d4afb5ceSopenharmony_ci	vhd->i.pwsi = &vhd->client_wsi;
82d4afb5ceSopenharmony_ci
83d4afb5ceSopenharmony_ci	if (!lws_client_connect_via_info(&vhd->i))
84d4afb5ceSopenharmony_ci		lws_sul_schedule(vhd->context, 0, &vhd->sul,
85d4afb5ceSopenharmony_ci				 sul_connect_attempt, 10 * LWS_US_PER_SEC);
86d4afb5ceSopenharmony_ci}
87d4afb5ceSopenharmony_ci
88d4afb5ceSopenharmony_cistatic int
89d4afb5ceSopenharmony_cicallback_minimal(struct lws *wsi, enum lws_callback_reasons reason,
90d4afb5ceSopenharmony_ci			void *user, void *in, size_t len)
91d4afb5ceSopenharmony_ci{
92d4afb5ceSopenharmony_ci	struct per_session_data__minimal *pss =
93d4afb5ceSopenharmony_ci			(struct per_session_data__minimal *)user;
94d4afb5ceSopenharmony_ci	struct per_vhost_data__minimal *vhd =
95d4afb5ceSopenharmony_ci			(struct per_vhost_data__minimal *)
96d4afb5ceSopenharmony_ci			lws_protocol_vh_priv_get(lws_get_vhost(wsi),
97d4afb5ceSopenharmony_ci					lws_get_protocol(wsi));
98d4afb5ceSopenharmony_ci	const struct msg *pmsg;
99d4afb5ceSopenharmony_ci	struct msg amsg;
100d4afb5ceSopenharmony_ci	int m;
101d4afb5ceSopenharmony_ci
102d4afb5ceSopenharmony_ci	switch (reason) {
103d4afb5ceSopenharmony_ci
104d4afb5ceSopenharmony_ci	/* --- protocol lifecycle callbacks --- */
105d4afb5ceSopenharmony_ci
106d4afb5ceSopenharmony_ci	case LWS_CALLBACK_PROTOCOL_INIT:
107d4afb5ceSopenharmony_ci		vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi),
108d4afb5ceSopenharmony_ci				lws_get_protocol(wsi),
109d4afb5ceSopenharmony_ci				sizeof(struct per_vhost_data__minimal));
110d4afb5ceSopenharmony_ci		vhd->context = lws_get_context(wsi);
111d4afb5ceSopenharmony_ci		vhd->protocol = lws_get_protocol(wsi);
112d4afb5ceSopenharmony_ci		vhd->vhost = lws_get_vhost(wsi);
113d4afb5ceSopenharmony_ci
114d4afb5ceSopenharmony_ci		vhd->ring = lws_ring_create(sizeof(struct msg), 8,
115d4afb5ceSopenharmony_ci					    __minimal_destroy_message);
116d4afb5ceSopenharmony_ci		if (!vhd->ring)
117d4afb5ceSopenharmony_ci			return 1;
118d4afb5ceSopenharmony_ci
119d4afb5ceSopenharmony_ci		sul_connect_attempt(&vhd->sul);
120d4afb5ceSopenharmony_ci		break;
121d4afb5ceSopenharmony_ci
122d4afb5ceSopenharmony_ci	case LWS_CALLBACK_PROTOCOL_DESTROY:
123d4afb5ceSopenharmony_ci		lws_ring_destroy(vhd->ring);
124d4afb5ceSopenharmony_ci		lws_sul_cancel(&vhd->sul);
125d4afb5ceSopenharmony_ci		break;
126d4afb5ceSopenharmony_ci
127d4afb5ceSopenharmony_ci	/* --- serving callbacks --- */
128d4afb5ceSopenharmony_ci
129d4afb5ceSopenharmony_ci	case LWS_CALLBACK_ESTABLISHED:
130d4afb5ceSopenharmony_ci		/* add ourselves to the list of live pss held in the vhd */
131d4afb5ceSopenharmony_ci		lws_ll_fwd_insert(pss, pss_list, vhd->pss_list);
132d4afb5ceSopenharmony_ci		pss->tail = lws_ring_get_oldest_tail(vhd->ring);
133d4afb5ceSopenharmony_ci		pss->wsi = wsi;
134d4afb5ceSopenharmony_ci		break;
135d4afb5ceSopenharmony_ci
136d4afb5ceSopenharmony_ci	case LWS_CALLBACK_CLOSED:
137d4afb5ceSopenharmony_ci		/* remove our closing pss from the list of live pss */
138d4afb5ceSopenharmony_ci		lws_ll_fwd_remove(struct per_session_data__minimal, pss_list,
139d4afb5ceSopenharmony_ci				  pss, vhd->pss_list);
140d4afb5ceSopenharmony_ci		break;
141d4afb5ceSopenharmony_ci
142d4afb5ceSopenharmony_ci	case LWS_CALLBACK_SERVER_WRITEABLE:
143d4afb5ceSopenharmony_ci		pmsg = lws_ring_get_element(vhd->ring, &pss->tail);
144d4afb5ceSopenharmony_ci		if (!pmsg)
145d4afb5ceSopenharmony_ci			break;
146d4afb5ceSopenharmony_ci
147d4afb5ceSopenharmony_ci		/* notice we allowed for LWS_PRE in the payload already */
148d4afb5ceSopenharmony_ci		m = lws_write(wsi, ((unsigned char *)pmsg->payload) + LWS_PRE,
149d4afb5ceSopenharmony_ci			      pmsg->len, LWS_WRITE_TEXT);
150d4afb5ceSopenharmony_ci		if (m < (int)pmsg->len) {
151d4afb5ceSopenharmony_ci			lwsl_err("ERROR %d writing to ws socket\n", m);
152d4afb5ceSopenharmony_ci			return -1;
153d4afb5ceSopenharmony_ci		}
154d4afb5ceSopenharmony_ci
155d4afb5ceSopenharmony_ci		lws_ring_consume_and_update_oldest_tail(
156d4afb5ceSopenharmony_ci			vhd->ring,	/* lws_ring object */
157d4afb5ceSopenharmony_ci			struct per_session_data__minimal, /* type of objects with tails */
158d4afb5ceSopenharmony_ci			&pss->tail,	/* tail of guy doing the consuming */
159d4afb5ceSopenharmony_ci			1,		/* number of payload objects being consumed */
160d4afb5ceSopenharmony_ci			vhd->pss_list,	/* head of list of objects with tails */
161d4afb5ceSopenharmony_ci			tail,		/* member name of tail in objects with tails */
162d4afb5ceSopenharmony_ci			pss_list	/* member name of next object in objects with tails */
163d4afb5ceSopenharmony_ci		);
164d4afb5ceSopenharmony_ci
165d4afb5ceSopenharmony_ci		/* more to do? */
166d4afb5ceSopenharmony_ci		if (lws_ring_get_element(vhd->ring, &pss->tail))
167d4afb5ceSopenharmony_ci			/* come back as soon as we can write more */
168d4afb5ceSopenharmony_ci			lws_callback_on_writable(pss->wsi);
169d4afb5ceSopenharmony_ci		break;
170d4afb5ceSopenharmony_ci
171d4afb5ceSopenharmony_ci	/* --- client callbacks --- */
172d4afb5ceSopenharmony_ci
173d4afb5ceSopenharmony_ci	case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
174d4afb5ceSopenharmony_ci		lwsl_err("CLIENT_CONNECTION_ERROR: %s\n",
175d4afb5ceSopenharmony_ci			 in ? (char *)in : "(null)");
176d4afb5ceSopenharmony_ci		vhd->client_wsi = NULL;
177d4afb5ceSopenharmony_ci		lws_sul_schedule(vhd->context, 0, &vhd->sul,
178d4afb5ceSopenharmony_ci				 sul_connect_attempt, LWS_US_PER_SEC);
179d4afb5ceSopenharmony_ci		break;
180d4afb5ceSopenharmony_ci
181d4afb5ceSopenharmony_ci	case LWS_CALLBACK_CLIENT_ESTABLISHED:
182d4afb5ceSopenharmony_ci		lwsl_user("%s: established\n", __func__);
183d4afb5ceSopenharmony_ci		break;
184d4afb5ceSopenharmony_ci
185d4afb5ceSopenharmony_ci	case LWS_CALLBACK_CLIENT_RECEIVE:
186d4afb5ceSopenharmony_ci		/* if no clients, just drop incoming */
187d4afb5ceSopenharmony_ci		if (!vhd->pss_list)
188d4afb5ceSopenharmony_ci			break;
189d4afb5ceSopenharmony_ci
190d4afb5ceSopenharmony_ci		if (!lws_ring_get_count_free_elements(vhd->ring)) {
191d4afb5ceSopenharmony_ci			lwsl_user("dropping!\n");
192d4afb5ceSopenharmony_ci			break;
193d4afb5ceSopenharmony_ci		}
194d4afb5ceSopenharmony_ci
195d4afb5ceSopenharmony_ci		amsg.len = len;
196d4afb5ceSopenharmony_ci		/* notice we over-allocate by LWS_PRE */
197d4afb5ceSopenharmony_ci		amsg.payload = malloc(LWS_PRE + len);
198d4afb5ceSopenharmony_ci		if (!amsg.payload) {
199d4afb5ceSopenharmony_ci			lwsl_user("OOM: dropping\n");
200d4afb5ceSopenharmony_ci			break;
201d4afb5ceSopenharmony_ci		}
202d4afb5ceSopenharmony_ci
203d4afb5ceSopenharmony_ci		memcpy((char *)amsg.payload + LWS_PRE, in, len);
204d4afb5ceSopenharmony_ci		if (!lws_ring_insert(vhd->ring, &amsg, 1)) {
205d4afb5ceSopenharmony_ci			__minimal_destroy_message(&amsg);
206d4afb5ceSopenharmony_ci			lwsl_user("dropping!\n");
207d4afb5ceSopenharmony_ci			break;
208d4afb5ceSopenharmony_ci		}
209d4afb5ceSopenharmony_ci
210d4afb5ceSopenharmony_ci		/*
211d4afb5ceSopenharmony_ci		 * let everybody know we want to write something on them
212d4afb5ceSopenharmony_ci		 * as soon as they are ready
213d4afb5ceSopenharmony_ci		 */
214d4afb5ceSopenharmony_ci		lws_start_foreach_llp(struct per_session_data__minimal **,
215d4afb5ceSopenharmony_ci				      ppss, vhd->pss_list) {
216d4afb5ceSopenharmony_ci			lws_callback_on_writable((*ppss)->wsi);
217d4afb5ceSopenharmony_ci		} lws_end_foreach_llp(ppss, pss_list);
218d4afb5ceSopenharmony_ci		break;
219d4afb5ceSopenharmony_ci
220d4afb5ceSopenharmony_ci	case LWS_CALLBACK_CLIENT_CLOSED:
221d4afb5ceSopenharmony_ci		vhd->client_wsi = NULL;
222d4afb5ceSopenharmony_ci		lws_sul_schedule(vhd->context, 0, &vhd->sul,
223d4afb5ceSopenharmony_ci				 sul_connect_attempt, LWS_US_PER_SEC);
224d4afb5ceSopenharmony_ci		break;
225d4afb5ceSopenharmony_ci
226d4afb5ceSopenharmony_ci	default:
227d4afb5ceSopenharmony_ci		break;
228d4afb5ceSopenharmony_ci	}
229d4afb5ceSopenharmony_ci
230d4afb5ceSopenharmony_ci	return 0;
231d4afb5ceSopenharmony_ci}
232d4afb5ceSopenharmony_ci
233d4afb5ceSopenharmony_ci#define LWS_PLUGIN_PROTOCOL_MINIMAL \
234d4afb5ceSopenharmony_ci	{  \
235d4afb5ceSopenharmony_ci		"lws-minimal-proxy", \
236d4afb5ceSopenharmony_ci		callback_minimal, \
237d4afb5ceSopenharmony_ci		sizeof(struct per_session_data__minimal), \
238d4afb5ceSopenharmony_ci		128, \
239d4afb5ceSopenharmony_ci		0, NULL, 0 \
240d4afb5ceSopenharmony_ci	}
241