ksgi

Форк
0
/
sample.c 
352 строки · 8.3 Кб
1
/*	$Id$ */
2
/*
3
 * Copyright (c) 2014, 2015, 2017 Kristaps Dzonsons <kristaps@bsd.lv>
4
 *
5
 * Permission to use, copy, modify, and distribute this software for any
6
 * purpose with or without fee is hereby granted, provided that the above
7
 * copyright notice and this permission notice appear in all copies.
8
 *
9
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16
 */
17
#include <sys/types.h> /* size_t, ssize_t */
18
#include <stdarg.h> /* va_list */
19
#include <stddef.h> /* NULL */
20
#include <stdint.h> /* int64_t */
21
#include <stdlib.h>
22
#include <string.h> /* memset */
23

24
#include "kcgi.h"
25
#include "kcgihtml.h"
26

27
/*
28
 * Simple CGI application.
29
 * Compile it with `make samples` (or using gmake) and install it into
30
 * your web server's /cgi-bin.
31
 * The "template.xml" file should be in the /cgi-bin directory as well
32
 * and readable by the server process.
33
 * (Obviously this is just for a sample.)
34
 *
35
 * Assuming localhost/cgi-bin, the script is localhost/cgi-bin/sample.
36
 * The pages recognised are:
37
 *
38
 *   - /cgi-bin/sample/index.html
39
 *   - /cgi-bin/sample/template.html
40
 *   - /cgi-bin/sample/senddata.html
41
 *
42
 * See the sendindex et al. functions for what these do.
43
 */
44

45
/* Recognised page requests.  See pages[]. */
46
enum	page {
47
	PAGE_INDEX,
48
	PAGE_TEMPLATE,
49
	PAGE_SENDDATA,
50
	PAGE__MAX
51
};
52

53
/*
54
 * All of the keys (input field names) we accept. 
55
 * The key names are in the "keys" array.
56
 * See sendindex() for how these are used.
57
 */
58
enum	key {
59
	KEY_INTEGER, 
60
	KEY_FILE,
61
	KEY_PAGECOUNT,
62
	KEY_PAGESIZE,
63
	KEY__MAX
64
};
65

66
/*
67
 * The elements in our template file.
68
 * The element key names are in the "templs" array.
69
 * See sendtemplate() for how this is used.
70
 */
71
enum	templ {
72
	TEMPL_TITLE,
73
	TEMPL_NAME,
74
	TEMPL_REMOTE_ADDR,
75
	TEMPL__MAX
76
};
77

78
/*
79
 * We need a structure because we can't get the "r" from the request.
80
 * This is used by our template callback.
81
 */
82
struct	tstrct {
83
	struct khtmlreq	 req;
84
	struct kreq	*r;
85
};
86

87
/*
88
 * We'll use this to route pages by creating an array indexed by our
89
 * page.
90
 * Then when the page is parsed, we'll route directly into it.
91
 */
92
typedef	void (*disp)(struct kreq *);
93

94
static void senddata(struct kreq *);
95
static void sendindex(struct kreq *);
96
static void sendtemplate(struct kreq *);
97

98
static const disp disps[PAGE__MAX] = {
99
	sendindex, /* PAGE_INDEX */
100
	sendtemplate, /* PAGE_TEMPLATE */
101
	senddata, /* PAGE_SENDDATA */
102
};
103

104
static const struct kvalid keys[KEY__MAX] = {
105
	{ kvalid_int, "integer" }, /* KEY_INTEGER */
106
	{ NULL, "file" }, /* KEY_FILE */
107
	{ kvalid_uint, "count" }, /* KEY_PAGECOUNT */
108
	{ kvalid_uint, "size" }, /* KEY_PAGESIZE */
109
};
110

111
/*
112
 * Template key names (as in @@TITLE@@ in the file).
113
 */
114
static const char *const templs[TEMPL__MAX] = {
115
	"title", /* TEMPL_TITLE */
116
	"name", /* TEMPL_NAME */
117
	"remote_addr", /* TEMPL_REMOTE_ADDR */
118
};
119

120
/* 
121
 * Page names (as in the URL component) mapped from the first name part
122
 * of requests, e.g., /sample.cgi/index.html -> index -> PAGE_INDEX.
123
 */
124
static const char *const pages[PAGE__MAX] = {
125
	"index", /* PAGE_INDEX */
126
	"template", /* PAGE_TEMPLATE */
127
	"senddata" /* PAGE_SENDDATA */
128
};
129

130
/*
131
 * Open an HTTP response with a status code and a particular
132
 * content-type, then open the HTTP content body.
133
 * You can call khttp_head(3) before this: CGI doesn't dictate any
134
 * particular header order.
135
 */
136
static void
137
resp_open(struct kreq *req, enum khttp http)
138
{
139
	enum kmime	 mime;
140

141
	/*
142
	 * If we've been sent an unknown suffix like '.foo', we won't
143
	 * know what it is.
144
	 * Default to an octet-stream response.
145
	 */
146
	if (KMIME__MAX == (mime = req->mime))
147
		mime = KMIME_APP_OCTET_STREAM;
148

149
	khttp_head(req, kresps[KRESP_STATUS], 
150
		"%s", khttps[http]);
151
	khttp_head(req, kresps[KRESP_CONTENT_TYPE], 
152
		"%s", kmimetypes[mime]);
153
	khttp_body(req);
154
}
155

156
/*
157
 * Callback for filling in a particular template part.
158
 * Let's just be simple for simplicity's sake.
159
 */
160
static int
161
template(size_t key, void *arg)
162
{
163
	struct tstrct	*p = arg;
164

165
	switch (key) {
166
	case (TEMPL_TITLE):
167
		khtml_puts(&p->req, "title");
168
		break;
169
	case (TEMPL_NAME):
170
		khtml_puts(&p->req, "name");
171
		break;
172
	case (TEMPL_REMOTE_ADDR):
173
		khtml_puts(&p->req, p->r->remote);
174
		break;
175
	default:
176
		return(0);
177
	}
178

179
	return(1);
180
}
181

182
/*
183
 * Demonstrates how to use templates.
184
 * Returns HTTP 200 and the template content.
185
 */
186
static void
187
sendtemplate(struct kreq *req)
188
{
189
	struct ktemplate t;
190
	struct tstrct	 p;
191

192
	memset(&t, 0, sizeof(struct ktemplate));
193
	memset(&p, 0, sizeof(struct tstrct));
194

195
	p.r = req;
196
	t.key = templs;
197
	t.keysz = TEMPL__MAX;
198
	t.arg = &p;
199
	t.cb = template;
200

201
	resp_open(req, KHTTP_200);
202
	khtml_open(&p.req, req, 0);
203
	khttp_template(req, &t, "template.xml");
204
	khtml_close(&p.req);
205
}
206

207
/*
208
 * Send a random amount of data.
209
 * Requires KEY_PAGECOUNT (optional), KEY_PAGESIZE (optional).
210
 * Page count is the number of times we flush a page (with the given
211
 * size) to the wire.
212
 * Returns HTTP 200 and the random data.
213
 */
214
static void
215
senddata(struct kreq *req)
216
{
217
	int64_t	  i, j, nm, sz;
218
	char	 *buf;
219

220
	nm = 1024 * 1024;
221
	if (NULL != req->fieldmap[KEY_PAGECOUNT])
222
		nm = req->fieldmap[KEY_PAGECOUNT]->parsed.i;
223
	if (0 == nm)
224
		nm = 1;
225

226
	sz = 1;
227
	if (NULL != req->fieldmap[KEY_PAGESIZE])
228
		sz = req->fieldmap[KEY_PAGESIZE]->parsed.i;
229
	if (0 == sz || (uint64_t)sz > SIZE_MAX)
230
		sz = 1;
231
	
232
	buf = kmalloc(sz);
233

234
	resp_open(req, KHTTP_200);
235
	for (i = 0; i < nm; i++) {
236
		for (j = 0; j < sz; j++)
237
#ifndef __linux__
238
			buf[j] = 65 + arc4random_uniform(24);
239
#else
240
			buf[j] = 65 + (random() % 24);
241
#endif
242
		khttp_write(req, buf, sz);
243
	}
244

245
	free(buf);
246
}
247

248
/*
249
 * Demonstrates how to use GET and POST forms and building with the HTML
250
 * builder functions.
251
 * Returns HTTP 200 and HTML content.
252
 */
253
static void
254
sendindex(struct kreq *req)
255
{
256
	char		*page;
257
	struct khtmlreq	 r;
258
	const char	*cp;
259

260
	cp = NULL == req->fieldmap[KEY_INTEGER] ?
261
		"" : req->fieldmap[KEY_INTEGER]->val;
262
	kasprintf(&page, "%s/%s", req->pname, pages[PAGE_INDEX]);
263

264
	resp_open(req, KHTTP_200);
265
	khtml_open(&r, req, 0);
266
	khtml_elem(&r, KELEM_DOCTYPE);
267
	khtml_elem(&r, KELEM_HTML);
268
	khtml_elem(&r, KELEM_HEAD);
269
	khtml_elem(&r, KELEM_TITLE);
270
	khtml_puts(&r, "Welcome!");
271
	khtml_closeelem(&r, 2);
272
	khtml_elem(&r, KELEM_BODY);
273
	khtml_puts(&r, "Welcome!");
274
	khtml_attr(&r, KELEM_FORM,
275
		KATTR_METHOD, "post",
276
		KATTR_ENCTYPE, "multipart/form-data",
277
		KATTR_ACTION, page,
278
		KATTR__MAX);
279
	khtml_elem(&r, KELEM_FIELDSET);
280
	khtml_elem(&r, KELEM_LEGEND);
281
	khtml_puts(&r, "Post (multipart)");
282
	khtml_closeelem(&r, 1);
283
	khtml_elem(&r, KELEM_P);
284
	khtml_attr(&r, KELEM_INPUT,
285
		KATTR_TYPE, "number",
286
		KATTR_NAME, keys[KEY_INTEGER].name,
287
		KATTR_VALUE, cp, KATTR__MAX);
288
	khtml_closeelem(&r, 1);
289
	khtml_elem(&r, KELEM_P);
290
	khtml_attr(&r, KELEM_INPUT,
291
		KATTR_TYPE, "file",
292
		KATTR_MULTIPLE, "",
293
		KATTR_NAME, keys[KEY_FILE].name,
294
		KATTR__MAX);
295
	if (NULL != req->fieldmap[KEY_FILE]) {
296
		if (NULL != req->fieldmap[KEY_FILE]->file) {
297
			khtml_puts(&r, "file: ");
298
			khtml_puts(&r, req->fieldmap[KEY_FILE]->file);
299
			khtml_puts(&r, " ");
300
		} 
301
		if (NULL != req->fieldmap[KEY_FILE]->ctype) {
302
			khtml_puts(&r, "ctype: ");
303
			khtml_puts(&r, req->fieldmap[KEY_FILE]->ctype);
304
		} 
305
	}
306
	khtml_closeelem(&r, 1);
307
	khtml_elem(&r, KELEM_P);
308
	khtml_attr(&r, KELEM_INPUT,
309
		KATTR_TYPE, "submit",
310
		KATTR__MAX);
311
	khtml_closeelem(&r, 0);
312
	khtml_close(&r);
313
	free(page);
314
}
315

316
int
317
main(void)
318
{
319
	struct kreq	 r;
320
	enum kcgi_err	 er;
321

322
	/* Set up our main HTTP context. */
323

324
	er = khttp_parse(&r, keys, KEY__MAX, 
325
		pages, PAGE__MAX, PAGE_INDEX);
326

327
	if (KCGI_OK != er)
328
		return(EXIT_FAILURE);
329

330
	/* 
331
	 * Accept only GET, POST, and OPTIONS.
332
	 * Restrict to text/html and a valid page.
333
	 * If all of our parameters are valid, use a dispatch array to
334
	 * send us to the page handlers.
335
	 */
336

337
	if (KMETHOD_OPTIONS == r.method) {
338
		khttp_head(&r, kresps[KRESP_ALLOW], 
339
			"OPTIONS GET POST");
340
		resp_open(&r, KHTTP_200);
341
	} else if (KMETHOD_GET != r.method && 
342
		   KMETHOD_POST != r.method) {
343
		resp_open(&r, KHTTP_405);
344
	} else if (PAGE__MAX == r.page || 
345
		   KMIME_TEXT_HTML != r.mime) {
346
		resp_open(&r, KHTTP_404);
347
	} else
348
		(*disps[r.page])(&r);
349

350
	khttp_free(&r);
351
	return(EXIT_SUCCESS);
352
}
353

354

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.