• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * ws protocol handler plugin for "fulltext demo"
3  *
4  * Written in 2010-2019 by Andy Green <andy@warmcat.com>
5  *
6  * This file is made available under the Creative Commons CC0 1.0
7  * Universal Public Domain Dedication.
8  *
9  * The person who associated a work with this deed has dedicated
10  * the work to the public domain by waiving all of his or her rights
11  * to the work worldwide under copyright law, including all related
12  * and neighboring rights, to the extent allowed by law. You can copy,
13  * modify, distribute and perform the work, even for commercial purposes,
14  * all without asking permission.
15  *
16  * These test plugins are intended to be adapted for use in your code, which
17  * may be proprietary.  So unlike the library itself, they are licensed
18  * Public Domain.
19  */
20 
21 #if !defined (LWS_PLUGIN_STATIC)
22 #if !defined(LWS_DLL)
23 #define LWS_DLL
24 #endif
25 #if !defined(LWS_INTERNAL)
26 #define LWS_INTERNAL
27 #endif
28 #include <libwebsockets.h>
29 #endif
30 
31 #include <stdlib.h>
32 #include <string.h>
33 #include <fcntl.h>
34 #include <sys/types.h>
35 #include <sys/stat.h>
36 #ifdef WIN32
37 #include <io.h>
38 #endif
39 #include <stdio.h>
40 
41 struct vhd_fts_demo {
42 	const char *indexpath;
43 };
44 
45 struct pss_fts_demo {
46 	struct lwsac *result;
47 	struct lws_fts_result_autocomplete *ac;
48 	struct lws_fts_result_filepath *fp;
49 
50 	uint32_t *li;
51 	int done;
52 
53 	uint8_t first:1;
54 	uint8_t ac_done:1;
55 
56 	uint8_t fp_init_done:1;
57 };
58 
59 static int
callback_fts(struct lws * wsi,enum lws_callback_reasons reason,void * user,void * in,size_t len)60 callback_fts(struct lws *wsi, enum lws_callback_reasons reason, void *user,
61 	     void *in, size_t len)
62 {
63 	struct vhd_fts_demo *vhd = (struct vhd_fts_demo *)
64 		lws_protocol_vh_priv_get(lws_get_vhost(wsi),
65 					 lws_get_protocol(wsi));
66 	struct pss_fts_demo *pss = (struct pss_fts_demo *)user;
67 	uint8_t buf[LWS_PRE + 2048], *start = &buf[LWS_PRE], *p = start,
68 		*end = &buf[sizeof(buf) - LWS_PRE - 1];
69 	struct lws_fts_search_params params;
70 	const char *ccp = (const char *)in;
71 	struct lws_fts_result *result;
72 	struct lws_fts_file *jtf;
73 	int n;
74 
75 	switch (reason) {
76 
77 	case LWS_CALLBACK_PROTOCOL_INIT:
78 		vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi),
79 			     lws_get_protocol(wsi),sizeof(struct vhd_fts_demo));
80 		if (!vhd)
81 			return 0;
82 		if (lws_pvo_get_str(in, "indexpath",
83 				    (const char **)&vhd->indexpath))
84 			return 1;
85 
86 		return 0;
87 
88 	case LWS_CALLBACK_HTTP:
89 
90 		pss->first = 1;
91 		pss->ac_done = 0;
92 
93 		/*
94 		 * we have a "subdirectory" selecting the task
95 		 *
96 		 * /a/ = autocomplete
97 		 * /r/ = results
98 		 */
99 
100 		if (strncmp(ccp, "/a/", 3) && strncmp(ccp, "/r/", 3))
101 			goto reply_404;
102 
103 		memset(&params, 0, sizeof(params));
104 
105 		params.needle = ccp + 3;
106 		if (*(ccp + 1) == 'a')
107 			params.flags = LWSFTS_F_QUERY_AUTOCOMPLETE;
108 		if (*(ccp + 1) == 'r')
109 			params.flags = LWSFTS_F_QUERY_FILES |
110 				       LWSFTS_F_QUERY_FILE_LINES |
111 				       LWSFTS_F_QUERY_QUOTE_LINE;
112 		params.max_autocomplete = 10;
113 		params.max_files = 10;
114 
115 		jtf = lws_fts_open(vhd->indexpath);
116 		if (!jtf) {
117 			lwsl_err("unable to open %s\n", vhd->indexpath);
118 			/* we'll inform the client in the JSON */
119 			goto reply_200;
120 		}
121 
122 		result = lws_fts_search(jtf, &params);
123 		lws_fts_close(jtf);
124 		if (result) {
125 			pss->result = params.results_head;
126 			pss->ac = result->autocomplete_head;
127 			pss->fp = result->filepath_head;
128 		}
129 		/* NULL result will be told in the json as "indexed": 0 */
130 
131 reply_200:
132 		if (lws_add_http_common_headers(wsi, HTTP_STATUS_OK,
133 						"text/html",
134 					LWS_ILLEGAL_HTTP_CONTENT_LEN, &p, end))
135 			return 1;
136 
137 		if (lws_finalize_write_http_header(wsi, start, &p, end))
138 			return 1;
139 
140 		lws_callback_on_writable(wsi);
141 		return 0;
142 
143 reply_404:
144 		if (lws_add_http_common_headers(wsi, HTTP_STATUS_NOT_FOUND,
145 						"text/html",
146 					LWS_ILLEGAL_HTTP_CONTENT_LEN, &p, end))
147 			return 1;
148 
149 		if (lws_finalize_write_http_header(wsi, start, &p, end))
150 			return 1;
151 		return lws_http_transaction_completed(wsi);
152 
153 	case LWS_CALLBACK_CLOSED_HTTP:
154 		if (pss && pss->result)
155 			lwsac_free(&pss->result);
156 		break;
157 
158 	case LWS_CALLBACK_HTTP_WRITEABLE:
159 
160 		if (!pss)
161 			break;
162 
163 		n = LWS_WRITE_HTTP;
164 		if (pss->first)
165 			p += lws_snprintf((char *)p, lws_ptr_diff_size_t(end, p),
166 				"{\"indexed\": %d, \"ac\": [", !!pss->result);
167 
168 		while (pss->ac && lws_ptr_diff(end, p) > 256) {
169 			p += lws_snprintf((char *)p, lws_ptr_diff_size_t(end, p),
170 				"%c{\"ac\": \"%s\",\"matches\": %d,"
171 				"\"agg\": %d, \"elided\": %d}",
172 				pss->first ? ' ' : ',', (char *)(pss->ac + 1),
173 				pss->ac->instances, pss->ac->agg_instances,
174 				pss->ac->elided);
175 
176 			pss->first = 0;
177 			pss->ac = pss->ac->next;
178 		}
179 
180 		if (!pss->ac_done && !pss->ac && pss->fp) {
181 			pss->ac_done = 1;
182 
183 			p += lws_snprintf((char *)p, lws_ptr_diff_size_t(end, p),
184 					  "], \"fp\": [");
185 		}
186 
187 		while (pss->fp && lws_ptr_diff_size_t(end, p) > 256) {
188 			if (!pss->fp_init_done) {
189 				p += lws_snprintf((char *)p,
190 						lws_ptr_diff_size_t(end, p),
191 					"%c{\"path\": \"%s\",\"matches\": %d,"
192 					"\"origlines\": %d,"
193 					"\"hits\": [", pss->first ? ' ' : ',',
194 					((char *)(pss->fp + 1)) +
195 						pss->fp->matches_length,
196 					pss->fp->matches,
197 					pss->fp->lines_in_file);
198 
199 				pss->li = ((uint32_t *)(pss->fp + 1));
200 				pss->done = 0;
201 				pss->fp_init_done = 1;
202 				pss->first = 0;
203 			} else {
204 				while (pss->done < pss->fp->matches &&
205 				       lws_ptr_diff(end, p) > 256) {
206 
207 					p += lws_snprintf((char *)p,
208 							lws_ptr_diff_size_t(end, p),
209 						"%c\n{\"l\":%d,\"o\":%d,"
210 						"\"s\":\"%s\"}",
211 						!pss->done ? ' ' : ',',
212 						pss->li[0], pss->li[1],
213 						*((const char **)&pss->li[2]));
214 					pss->li += 2 + (sizeof(const char *) /
215 							sizeof(uint32_t));
216 					pss->done++;
217 				}
218 
219 				if (pss->done == pss->fp->matches) {
220 					*p++ = ']';
221 					pss->fp_init_done = 0;
222 					pss->fp = pss->fp->next;
223 					if (!pss->fp)
224 						*p++ = '}';
225 				}
226 			}
227 		}
228 
229 		if (!pss->ac && !pss->fp) {
230 			n = LWS_WRITE_HTTP_FINAL;
231 			p += lws_snprintf((char *)p, lws_ptr_diff_size_t(end, p),
232 						"]}");
233 		}
234 
235 		if (lws_write(wsi, (uint8_t *)start,
236 				lws_ptr_diff_size_t(p, start), (enum lws_write_protocol)n) !=
237 					      lws_ptr_diff(p, start))
238 			return 1;
239 
240 		if (n == LWS_WRITE_HTTP_FINAL) {
241 			if (pss->result)
242 				lwsac_free(&pss->result);
243 			if (lws_http_transaction_completed(wsi))
244 				return -1;
245 		} else
246 			lws_callback_on_writable(wsi);
247 
248 		return 0;
249 
250 	default:
251 		break;
252 	}
253 
254 	return lws_callback_http_dummy(wsi, reason, user, in, len);
255 }
256 
257 
258 #define LWS_PLUGIN_PROTOCOL_FULLTEXT_DEMO \
259 	{ \
260 		"lws-test-fts", \
261 		callback_fts, \
262 		sizeof(struct pss_fts_demo), \
263 		0, \
264 		0, NULL, 0 \
265 	}
266 
267 #if !defined (LWS_PLUGIN_STATIC)
268 
269 LWS_VISIBLE const struct lws_protocols fulltext_demo_protocols[] = {
270 	LWS_PLUGIN_PROTOCOL_FULLTEXT_DEMO
271 };
272 
273 LWS_VISIBLE const lws_plugin_protocol_t fulltext_demo = {
274 	.hdr = {
275 		"fulltext demo",
276 		"lws_protocol_plugin",
277 		LWS_BUILD_HASH,
278 		LWS_PLUGIN_API_MAGIC
279 	},
280 
281 	.protocols = fulltext_demo_protocols,
282 	.count_protocols = LWS_ARRAY_SIZE(fulltext_demo_protocols),
283 	.extensions = NULL,
284 	.count_extensions = 0,
285 };
286 
287 #endif
288