refactored httpd file handling
This commit is contained in:
		| @ -55,8 +55,10 @@ struct qse_httpd_cbs_t | |||||||
| { | { | ||||||
| 	struct | 	struct | ||||||
| 	{ | 	{ | ||||||
| 		int (*readable) (qse_httpd_t* httpd, qse_ubi_t handle, qse_ntoff_t timeout); | 		int (*readable) ( | ||||||
| 		int (*writable) (qse_httpd_t* httpd, qse_ubi_t handle, qse_ntoff_t timeout); | 			qse_httpd_t* httpd, qse_ubi_t handle, qse_ntoff_t timeout); | ||||||
|  | 		int (*writable) ( | ||||||
|  | 			qse_httpd_t* httpd, qse_ubi_t handle, qse_ntoff_t timeout); | ||||||
| 	} mux; | 	} mux; | ||||||
|  |  | ||||||
| 	struct | 	struct | ||||||
| @ -64,6 +66,24 @@ struct qse_httpd_cbs_t | |||||||
| 		int (*executable) (qse_httpd_t* httpd, const qse_mchar_t* path); | 		int (*executable) (qse_httpd_t* httpd, const qse_mchar_t* path); | ||||||
| 	} path; | 	} path; | ||||||
|  |  | ||||||
|  | 	struct | ||||||
|  | 	{ | ||||||
|  | 		int (*ropen) ( | ||||||
|  | 			qse_httpd_t* httpd, const qse_mchar_t* path,  | ||||||
|  | 			qse_ubi_t* handle, qse_foff_t* size); | ||||||
|  | 		int (*wopen) ( | ||||||
|  | 			qse_httpd_t* httpd, const qse_mchar_t* path,  | ||||||
|  | 			qse_ubi_t* handle); | ||||||
|  | 		void (*close) (qse_httpd_t* httpd, qse_ubi_t handle); | ||||||
|  |  | ||||||
|  | 		qse_ssize_t (*read) ( | ||||||
|  | 			qse_httpd_t* httpd, qse_ubi_t handle, | ||||||
|  | 			qse_mchar_t* buf, qse_size_t len); | ||||||
|  | 		qse_ssize_t (*write) ( | ||||||
|  | 			qse_httpd_t* httpd, qse_ubi_t handle, | ||||||
|  | 			const qse_mchar_t* buf, qse_size_t len); | ||||||
|  | 	} file; | ||||||
|  |  | ||||||
| 	struct | 	struct | ||||||
| 	{ | 	{ | ||||||
| 		/* action */ | 		/* action */ | ||||||
| @ -251,6 +271,7 @@ qse_httpd_task_t* qse_httpd_entaskformat ( | |||||||
|  |  | ||||||
| /* -------------------------------------------- */ | /* -------------------------------------------- */ | ||||||
|  |  | ||||||
|  | #if 0 | ||||||
| qse_httpd_task_t* qse_httpd_entaskfile ( | qse_httpd_task_t* qse_httpd_entaskfile ( | ||||||
| 	qse_httpd_t*            httpd, | 	qse_httpd_t*            httpd, | ||||||
| 	qse_httpd_client_t*     client, | 	qse_httpd_client_t*     client, | ||||||
| @ -267,6 +288,7 @@ qse_httpd_task_t* qse_httpd_entaskdir ( | |||||||
| 	qse_ubi_t               handle, | 	qse_ubi_t               handle, | ||||||
| 	int                     chunked | 	int                     chunked | ||||||
| ); | ); | ||||||
|  | #endif | ||||||
|  |  | ||||||
| /* -------------------------------------------- */ | /* -------------------------------------------- */ | ||||||
|  |  | ||||||
| @ -285,7 +307,15 @@ qse_httpd_task_t* qse_httpd_entaskcontinue ( | |||||||
| 	qse_htre_t*               req | 	qse_htre_t*               req | ||||||
| ); | ); | ||||||
|  |  | ||||||
| qse_httpd_task_t* qse_httpd_entaskpath ( | qse_httpd_task_t* qse_httpd_entaskdir ( | ||||||
|  | 	qse_httpd_t*              httpd, | ||||||
|  | 	qse_httpd_client_t*       client, | ||||||
|  | 	const qse_httpd_task_t*   pred, | ||||||
|  | 	const qse_mchar_t*        name, | ||||||
|  | 	qse_htre_t*               req | ||||||
|  | ); | ||||||
|  |  | ||||||
|  | qse_httpd_task_t* qse_httpd_entaskfile ( | ||||||
| 	qse_httpd_t*              httpd, | 	qse_httpd_t*              httpd, | ||||||
| 	qse_httpd_client_t*       client, | 	qse_httpd_client_t*       client, | ||||||
| 	const qse_httpd_task_t*   pred, | 	const qse_httpd_task_t*   pred, | ||||||
|  | |||||||
| @ -25,13 +25,11 @@ | |||||||
|  |  | ||||||
| #include "httpd.h" | #include "httpd.h" | ||||||
| #include "../cmn/mem.h" | #include "../cmn/mem.h" | ||||||
| #include "../cmn/syscall.h" |  | ||||||
| #include <qse/cmn/str.h> | #include <qse/cmn/str.h> | ||||||
| #include <qse/cmn/chr.h> | #include <qse/cmn/chr.h> | ||||||
| #include <qse/cmn/pio.h> | #include <qse/cmn/pio.h> | ||||||
| #include <qse/cmn/fmt.h> | #include <qse/cmn/fmt.h> | ||||||
|  |  | ||||||
| #include <fcntl.h> |  | ||||||
| #include <stdarg.h> | #include <stdarg.h> | ||||||
| #include <stdio.h> | #include <stdio.h> | ||||||
| #include <dirent.h> | #include <dirent.h> | ||||||
| @ -242,7 +240,8 @@ qse_httpd_task_t* qse_httpd_entaskformat ( | |||||||
| 	{ | 	{ | ||||||
| 		qse_size_t capa = 256; | 		qse_size_t capa = 256; | ||||||
|  |  | ||||||
| 		buf = (qse_mchar_t*) qse_httpd_allocmem (httpd, (capa + 1) * QSE_SIZEOF(*buf)); | 		buf = (qse_mchar_t*) qse_httpd_allocmem ( | ||||||
|  | 			httpd, (capa + 1) * QSE_SIZEOF(*buf)); | ||||||
| 		if (buf == QSE_NULL) return QSE_NULL; | 		if (buf == QSE_NULL) return QSE_NULL; | ||||||
|  |  | ||||||
| 		/* an old vsnprintf behaves differently from C99 standard. | 		/* an old vsnprintf behaves differently from C99 standard. | ||||||
| @ -393,7 +392,9 @@ qse_httpd_task_t* qse_httpd_entaskerror ( | |||||||
| 	qse_httpd_t* httpd, qse_httpd_client_t* client,  | 	qse_httpd_t* httpd, qse_httpd_client_t* client,  | ||||||
| 	const qse_httpd_task_t* task, int code, qse_htre_t* req) | 	const qse_httpd_task_t* task, int code, qse_htre_t* req) | ||||||
| { | { | ||||||
| 	return entask_error (httpd, client, task, code, qse_htre_getversion(req), req->attr.keepalive); | 	return entask_error ( | ||||||
|  | 		httpd, client, task, code, | ||||||
|  | 		qse_htre_getversion(req), req->attr.keepalive); | ||||||
| } | } | ||||||
|  |  | ||||||
| /*------------------------------------------------------------------------*/ | /*------------------------------------------------------------------------*/ | ||||||
| @ -410,6 +411,7 @@ qse_httpd_task_t* qse_httpd_entaskcontinue ( | |||||||
|  |  | ||||||
| /*------------------------------------------------------------------------*/ | /*------------------------------------------------------------------------*/ | ||||||
|  |  | ||||||
|  | #if 0 | ||||||
| typedef struct task_file_t task_file_t; | typedef struct task_file_t task_file_t; | ||||||
| struct task_file_t | struct task_file_t | ||||||
| { | { | ||||||
| @ -1143,6 +1145,304 @@ qse_httpd_task_t* qse_httpd_entaskpath ( | |||||||
| 		QSE_SIZEOF(task_path_t) + qse_mbslen(name) + 1); | 		QSE_SIZEOF(task_path_t) + qse_mbslen(name) + 1); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /*------------------------------------------------------------------------*/ | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | typedef struct task_file_t task_file_t; | ||||||
|  | struct task_file_t | ||||||
|  | { | ||||||
|  | 	const qse_mchar_t* path; | ||||||
|  | 	qse_http_range_t   range; | ||||||
|  | 	qse_http_version_t version; | ||||||
|  | 	int                keepalive; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | typedef struct task_fseg_t task_fseg_t; | ||||||
|  | struct task_fseg_t | ||||||
|  | { | ||||||
|  | 	qse_ubi_t handle; | ||||||
|  | 	qse_foff_t left; | ||||||
|  | 	qse_foff_t offset; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | static int task_init_fseg ( | ||||||
|  | 	qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task) | ||||||
|  | { | ||||||
|  | 	task_fseg_t* xtn = qse_httpd_gettaskxtn (httpd, task); | ||||||
|  | 	QSE_MEMCPY (xtn, task->ctx, QSE_SIZEOF(*xtn)); | ||||||
|  | 	task->ctx = xtn; | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void task_fini_fseg ( | ||||||
|  | 	qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task) | ||||||
|  | { | ||||||
|  | 	task_fseg_t* ctx = (task_fseg_t*)task->ctx; | ||||||
|  | 	httpd->cbs->file.close (httpd, ctx->handle); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int task_main_fseg ( | ||||||
|  | 	qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task) | ||||||
|  | { | ||||||
|  | 	qse_ssize_t n; | ||||||
|  | 	qse_size_t count; | ||||||
|  | 	task_fseg_t* ctx = (task_fseg_t*)task->ctx; | ||||||
|  |  | ||||||
|  | 	count = MAX_SEND_SIZE; | ||||||
|  | 	if (count >= ctx->left) count = ctx->left; | ||||||
|  |  | ||||||
|  | /* TODO: more adjustment needed for OS with different sendfile semantics... */ | ||||||
|  | 	n = httpd->cbs->client.sendfile ( | ||||||
|  | 		httpd, client, ctx->handle, &ctx->offset, count); | ||||||
|  | 	if (n <= -1)  | ||||||
|  | 	{ | ||||||
|  | // HANDLE EGAIN specially??? | ||||||
|  | 		return -1; /* TODO: any logging */ | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if (n == 0 && count > 0) | ||||||
|  | 	{ | ||||||
|  | 		/* The file could be truncated when this condition is set. | ||||||
|  | 		 * The content-length sent in the header can't be fulfilled.  | ||||||
|  | 		 * So let's return an error here so that the main loop abort  | ||||||
|  | 		 * the connection. */ | ||||||
|  | /* TODO: any logging....??? */ | ||||||
|  | 		return -1;	 | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	ctx->left -= n; | ||||||
|  | 	if (ctx->left <= 0) return 0; | ||||||
|  |  | ||||||
|  | 	return 1; /* more work to do */ | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static qse_httpd_task_t* entask_file_segment ( | ||||||
|  | 	qse_httpd_t* httpd, qse_httpd_client_t* client,  | ||||||
|  | 	const qse_httpd_task_t* pred, | ||||||
|  | 	qse_ubi_t handle, qse_foff_t offset, qse_foff_t size) | ||||||
|  | { | ||||||
|  | 	qse_httpd_task_t task; | ||||||
|  | 	task_fseg_t data; | ||||||
|  | 	 | ||||||
|  | 	QSE_MEMSET (&data, 0, QSE_SIZEOF(data)); | ||||||
|  | 	data.handle = handle; | ||||||
|  | 	data.offset = offset; | ||||||
|  | 	data.left = size; | ||||||
|  |  | ||||||
|  | 	QSE_MEMSET (&task, 0, QSE_SIZEOF(task)); | ||||||
|  | 	task.init = task_init_fseg; | ||||||
|  | 	task.main = task_main_fseg; | ||||||
|  | 	task.fini = task_fini_fseg; | ||||||
|  | 	task.ctx = &data; | ||||||
|  |  | ||||||
|  | qse_printf (QSE_T("Debug: entasking file segment (%d)\n"), client->handle.i); | ||||||
|  | 	return qse_httpd_entask (httpd, client, pred, &task, QSE_SIZEOF(data)); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /*------------------------------------------------------------------------*/ | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static int task_init_file ( | ||||||
|  | 	qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task) | ||||||
|  | { | ||||||
|  | 	task_file_t* xtn = qse_httpd_gettaskxtn (httpd, task); | ||||||
|  | 	QSE_MEMCPY (xtn, task->ctx, QSE_SIZEOF(*xtn)); | ||||||
|  | 	qse_mbscpy ((qse_mchar_t*)(xtn + 1), xtn->path); | ||||||
|  | 	xtn->path = (qse_mchar_t*)(xtn + 1); | ||||||
|  | 	task->ctx = xtn; | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static QSE_INLINE int task_main_file ( | ||||||
|  | 	qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task) | ||||||
|  | { | ||||||
|  | 	task_file_t* file; | ||||||
|  | 	qse_httpd_task_t* x; | ||||||
|  | 	qse_ubi_t handle; | ||||||
|  | 	qse_foff_t filesize; | ||||||
|  | 	int fileopen = 0; | ||||||
|  |  | ||||||
|  | 	file = (task_file_t*)task->ctx; | ||||||
|  | 	x = task; | ||||||
|  |  | ||||||
|  | /* TODO: if you should deal with files on a network-mounted drive, | ||||||
|  |          setting a trigger or non-blocking I/O are needed. */ | ||||||
|  |  | ||||||
|  | 	/* when it comes to the file size, using fstat after opening  | ||||||
|  | 	 * can be more accurate. but this function uses information | ||||||
|  | 	 * set into the task context before the call to this function */ | ||||||
|  |  | ||||||
|  | qse_printf (QSE_T("opening file %hs\n"), file->path); | ||||||
|  |  | ||||||
|  | 	if (httpd->cbs->file.ropen (httpd, file->path, &handle, &filesize) <= -1) | ||||||
|  | 	{ | ||||||
|  | /* TODO: depending on the error type, either 404 or 403??? */ | ||||||
|  | 		x = entask_error ( | ||||||
|  | 			httpd, client, x, 404, &file->version, file->keepalive); | ||||||
|  | 		goto no_file_send; | ||||||
|  | 	}	 | ||||||
|  | 	fileopen = 1; | ||||||
|  |  | ||||||
|  | 	if (file->range.type != QSE_HTTP_RANGE_NONE) | ||||||
|  | 	{  | ||||||
|  | 		const qse_mchar_t* mime_type = QSE_NULL; | ||||||
|  |  | ||||||
|  | 		if (file->range.type == QSE_HTTP_RANGE_SUFFIX) | ||||||
|  | 		{ | ||||||
|  | 			if (file->range.to > filesize) file->range.to = filesize; | ||||||
|  | 			file->range.from = filesize - file->range.to; | ||||||
|  | 			file->range.to = file->range.to + file->range.from; | ||||||
|  | 			if (filesize > 0) file->range.to--; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		if (file->range.from >= filesize) | ||||||
|  | 		{ | ||||||
|  | 			x = entask_error ( | ||||||
|  | 				httpd, client, x, 416, &file->version, file->keepalive); | ||||||
|  | 			goto no_file_send; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		if (file->range.to >= filesize) file->range.to = filesize - 1; | ||||||
|  |  | ||||||
|  | 		if (httpd->cbs->getmimetype) | ||||||
|  | 		{ | ||||||
|  | 			httpd->errnum = QSE_HTTPD_ENOERR; | ||||||
|  | 			mime_type = httpd->cbs->getmimetype (httpd, file->path); | ||||||
|  | 			/*TODO: how to handle an error... */ | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | #if (QSE_SIZEOF_LONG_LONG > 0) | ||||||
|  | 		x = qse_httpd_entaskformat ( | ||||||
|  | 			httpd, client, x, | ||||||
|  |     			QSE_MT("HTTP/%d.%d 206 Partial Content\r\nConnection: %s\r\n%s%s%sContent-Length: %llu\r\nContent-Range: bytes %llu-%llu/%llu\r\n\r\n"),  | ||||||
|  | 			file->version.major, | ||||||
|  | 			file->version.minor, | ||||||
|  | 			(file->keepalive? QSE_MT("keep-alive"): QSE_MT("close")), | ||||||
|  | 			(mime_type? QSE_MT("Content-Type: "): QSE_MT("")), | ||||||
|  | 			(mime_type? mime_type: QSE_MT("")), | ||||||
|  | 			(mime_type? QSE_MT("\r\n"): QSE_MT("")), | ||||||
|  | 			(unsigned long long)(file->range.to - file->range.from + 1), | ||||||
|  | 			(unsigned long long)file->range.from, | ||||||
|  | 			(unsigned long long)file->range.to, | ||||||
|  | 			(unsigned long long)filesize | ||||||
|  | 		); | ||||||
|  | #else | ||||||
|  | 		x = qse_httpd_entaskformat ( | ||||||
|  | 			httpd, client, x, | ||||||
|  |     			QSE_MT("HTTP/%d.%d 206 Partial Content\r\nConnection: %s\r\n%s%s%sContent-Length: %lu\r\nContent-Range: bytes %lu-%lu/%lu\r\n\r\n"),  | ||||||
|  | 			file->version.major, | ||||||
|  | 			file->version.minor, | ||||||
|  | 			(file->keepalive? QSE_MT("keep-alive"): QSE_MT("close")), | ||||||
|  | 			(mime_type? QSE_MT("Content-Type: "): QSE_MT("")), | ||||||
|  | 			(mime_type? mime_type: QSE_MT("")), | ||||||
|  | 			(mime_type? QSE_MT("\r\n"): QSE_MT("")), | ||||||
|  | 			(unsigned long)(file->range.to - file->range.from + 1), | ||||||
|  | 			(unsigned long)file->range.from, | ||||||
|  | 			(unsigned long)file->range.to, | ||||||
|  | 			(unsigned long)filesize | ||||||
|  | 		); | ||||||
|  | #endif | ||||||
|  | 		if (x) | ||||||
|  | 		{ | ||||||
|  | 			x = entask_file_segment ( | ||||||
|  | 				httpd, client, x, | ||||||
|  | 				handle,  | ||||||
|  | 				file->range.from,  | ||||||
|  | 				(file->range.to - file->range.from + 1) | ||||||
|  | 			); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	else | ||||||
|  | 	{ | ||||||
|  | /* TODO: int64 format.... don't hard code it llu */ | ||||||
|  | 		const qse_mchar_t* mime_type = QSE_NULL; | ||||||
|  |  | ||||||
|  | 		if (httpd->cbs->getmimetype) | ||||||
|  | 		{ | ||||||
|  | 			httpd->errnum = QSE_HTTPD_ENOERR; | ||||||
|  | 			mime_type = httpd->cbs->getmimetype (httpd, file->path); | ||||||
|  | /*TODO: how to handle an error... */ | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		/* wget 1.8.2 set 'Connection: keep-alive' in the http 1.0 header. | ||||||
|  | 		 * if the reply doesn't contain 'Connection: keep-alive', it didn't | ||||||
|  | 		 * close connection.*/ | ||||||
|  | #if (QSE_SIZEOF_LONG_LONG > 0) | ||||||
|  | 		x = qse_httpd_entaskformat ( | ||||||
|  | 			httpd, client, x, | ||||||
|  |     			QSE_MT("HTTP/%d.%d 200 OK\r\nConnection: %s\r\n%s%s%sContent-Length: %llu\r\n\r\n"),  | ||||||
|  | 			file->version.major, file->version.minor, | ||||||
|  | 			(file->keepalive? QSE_MT("keep-alive"): QSE_MT("close")), | ||||||
|  | 			(mime_type? QSE_MT("Content-Type: "): QSE_MT("")), | ||||||
|  | 			(mime_type? mime_type: QSE_MT("")), | ||||||
|  | 			(mime_type? QSE_MT("\r\n"): QSE_MT("")), | ||||||
|  | 			(unsigned long long)filesize | ||||||
|  | 		); | ||||||
|  | #else | ||||||
|  | 		x = qse_httpd_entaskformat ( | ||||||
|  | 			httpd, client, x, | ||||||
|  |     			QSE_MT("HTTP/%d.%d 200 OK\r\nConnection: %s\r\n%s%s%sContent-Length: %lu\r\n\r\n"),  | ||||||
|  | 			file->version.major, | ||||||
|  | 			file->version.minor, | ||||||
|  | 			(file->keepalive? QSE_MT("keep-alive"): QSE_MT("close")), | ||||||
|  | 			(mime_type? QSE_MT("Content-Type: "): QSE_MT("")), | ||||||
|  | 			(mime_type? mime_type: QSE_MT("")), | ||||||
|  | 			(mime_type? QSE_MT("\r\n"): QSE_MT("")), | ||||||
|  | 			(unsigned long)filesize | ||||||
|  | 		); | ||||||
|  | #endif | ||||||
|  | 		if (x) | ||||||
|  | 		{ | ||||||
|  | 			x = entask_file_segment ( | ||||||
|  | 				httpd, client, x, handle, 0, filesize); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return (x == QSE_NULL)? -1: 0; | ||||||
|  |  | ||||||
|  | no_file_send: | ||||||
|  | 	if (fileopen) httpd->cbs->file.close (httpd, handle); | ||||||
|  | 	return (x == QSE_NULL)? -1: 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | qse_httpd_task_t* qse_httpd_entaskfile ( | ||||||
|  | 	qse_httpd_t* httpd, | ||||||
|  | 	qse_httpd_client_t* client,  | ||||||
|  | 	const qse_httpd_task_t* pred, | ||||||
|  | 	const qse_mchar_t* path, | ||||||
|  | 	qse_htre_t* req) | ||||||
|  | { | ||||||
|  | 	qse_httpd_task_t task; | ||||||
|  | 	task_file_t data; | ||||||
|  | 	const qse_mchar_t* range; | ||||||
|  |  | ||||||
|  | 	QSE_MEMSET (&data, 0, QSE_SIZEOF(data)); | ||||||
|  | 	data.path = path; | ||||||
|  | 	data.version = *qse_htre_getversion(req); | ||||||
|  | 	data.keepalive = req->attr.keepalive; | ||||||
|  |  | ||||||
|  | 	range = qse_htre_getheaderval(req, QSE_MT("Range")); | ||||||
|  | 	if (range)  | ||||||
|  | 	{ | ||||||
|  | 		if (qse_parsehttprange (range, &data.range) <= -1) | ||||||
|  | 		{ | ||||||
|  | 			return qse_httpd_entaskerror (httpd, client, pred, 416, req); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	else  | ||||||
|  | 	{ | ||||||
|  | 		data.range.type = QSE_HTTP_RANGE_NONE; | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	QSE_MEMSET (&task, 0, QSE_SIZEOF(task)); | ||||||
|  | 	task.init = task_init_file; | ||||||
|  | 	task.main = task_main_file; | ||||||
|  | 	task.ctx = &data; | ||||||
|  |  | ||||||
|  | 	return qse_httpd_entask (httpd, client, pred, &task,  | ||||||
|  | 		QSE_SIZEOF(task_file_t) + qse_mbslen(path) + 1); | ||||||
|  | } | ||||||
|  |  | ||||||
| /*------------------------------------------------------------------------*/ | /*------------------------------------------------------------------------*/ | ||||||
|  |  | ||||||
| typedef struct task_cgi_arg_t task_cgi_arg_t; | typedef struct task_cgi_arg_t task_cgi_arg_t; | ||||||
| @ -1518,6 +1818,10 @@ else qse_printf (QSE_T("!!!SNATCHING DONE\n")); | |||||||
| 		 * abortion for an error */ | 		 * abortion for an error */ | ||||||
| 		QSE_ASSERT (len == 0); | 		QSE_ASSERT (len == 0); | ||||||
| 		cgi->req = QSE_NULL; | 		cgi->req = QSE_NULL; | ||||||
|  | /* TODO: probably need to add a write trigger.... | ||||||
|  | until cgi->reqcon is emptied... | ||||||
|  | cannot clear triogters since there are jobs depending on readbility or realyableilityy | ||||||
|  | */ | ||||||
| 	} | 	} | ||||||
| 	else if (!cgi->reqfwderr) | 	else if (!cgi->reqfwderr) | ||||||
| 	{ | 	{ | ||||||
| @ -2161,6 +2465,8 @@ static QSE_INLINE qse_httpd_task_t* entask_cgi ( | |||||||
| { | { | ||||||
| 	qse_httpd_task_t task; | 	qse_httpd_task_t task; | ||||||
| 	task_cgi_arg_t arg; | 	task_cgi_arg_t arg; | ||||||
|  |  | ||||||
|  | #if 0 | ||||||
| 	int x; | 	int x; | ||||||
|  |  | ||||||
| /* TODO: NEED TO CHECK IF it's a regular file and executable??  | /* TODO: NEED TO CHECK IF it's a regular file and executable??  | ||||||
| @ -2171,6 +2477,7 @@ directory may be treated as executable??? | |||||||
| 		return qse_httpd_entaskerror (httpd, client, pred, 403, req); | 		return qse_httpd_entaskerror (httpd, client, pred, 403, req); | ||||||
| 	else if (x <= -1) | 	else if (x <= -1) | ||||||
| 		return qse_httpd_entaskerror (httpd, client, pred, 404, req); | 		return qse_httpd_entaskerror (httpd, client, pred, 404, req); | ||||||
|  | #endif | ||||||
|  |  | ||||||
| 	arg.path = path; | 	arg.path = path; | ||||||
| 	arg.req = req; | 	arg.req = req; | ||||||
|  | |||||||
| @ -13,6 +13,8 @@ | |||||||
| #else | #else | ||||||
| #	include <unistd.h> | #	include <unistd.h> | ||||||
| #	include <errno.h> | #	include <errno.h> | ||||||
|  | #	include <fcntl.h> | ||||||
|  | #	include <sys/stat.h> | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| #include <openssl/ssl.h> | #include <openssl/ssl.h> | ||||||
| @ -230,6 +232,87 @@ static int path_executable (qse_httpd_t* httpd, const qse_mchar_t* path) | |||||||
| 	return 1; /* yes */ | 	return 1; /* yes */ | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /* ------------------------------------------------------------------- */ | ||||||
|  |  | ||||||
|  | static int file_ropen ( | ||||||
|  | 	qse_httpd_t* httpd, const qse_mchar_t* path,  | ||||||
|  | 	qse_ubi_t* handle, qse_foff_t* size) | ||||||
|  | { | ||||||
|  | 	int fd; | ||||||
|  | 	int flags; | ||||||
|  | 	struct stat st; | ||||||
|  |  | ||||||
|  | 	flags = O_RDONLY; | ||||||
|  | #if defined(O_LARGEFILE) | ||||||
|  | 	flags |= O_LARGEFILE; | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | qse_printf (QSE_T("opening file [%hs] for reading\n"), path); | ||||||
|  | 	fd = open (path, flags, 0); | ||||||
|  | 	if (fd <= -1) return -1; | ||||||
|  |  | ||||||
|  |      flags = fcntl (fd, F_GETFD); | ||||||
|  |      if (flags >= 0) fcntl (fd, F_SETFD, flags | FD_CLOEXEC); | ||||||
|  |  | ||||||
|  | /* TODO: fstat64??? */ | ||||||
|  | 	if (fstat (fd, &st) <= -1) | ||||||
|  |      { | ||||||
|  | 		close (fd); | ||||||
|  | 		return -1; | ||||||
|  |      }     | ||||||
|  |  | ||||||
|  | 	if (S_ISDIR(st.st_mode)) | ||||||
|  | 	{ | ||||||
|  | 		close (fd); | ||||||
|  | 		return -1; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  |      *size = (st.st_size <= 0)? 0: st.st_size; | ||||||
|  | 	handle->i = fd; | ||||||
|  | qse_printf (QSE_T("opened file %hs\n"), path); | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int file_wopen ( | ||||||
|  | 	qse_httpd_t* httpd, const qse_mchar_t* path,  | ||||||
|  | 	qse_ubi_t* handle) | ||||||
|  | { | ||||||
|  | 	int fd; | ||||||
|  | 	int flags; | ||||||
|  |  | ||||||
|  | 	flags = O_WRONLY | O_CREAT | O_TRUNC; | ||||||
|  | #if defined(O_LARGEFILE) | ||||||
|  | 	flags |= O_LARGEFILE; | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | qse_printf (QSE_T("opening file [%hs] for writing\n"), path); | ||||||
|  | 	fd = open (path, flags, 0644); | ||||||
|  | 	if (fd <= -1) return -1; | ||||||
|  |  | ||||||
|  | 	handle->i = fd; | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void file_close (qse_httpd_t* httpd, qse_ubi_t handle) | ||||||
|  | { | ||||||
|  | qse_printf (QSE_T("closing file %d\n"), handle.i); | ||||||
|  | 	close (handle.i); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static qse_ssize_t file_read ( | ||||||
|  | 	qse_httpd_t* httpd, qse_ubi_t handle,  | ||||||
|  | 	qse_mchar_t* buf, qse_size_t len) | ||||||
|  | { | ||||||
|  | 	return read (handle.i, buf, len); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static qse_ssize_t file_write ( | ||||||
|  | 	qse_httpd_t* httpd, qse_ubi_t handle,  | ||||||
|  | 	const qse_mchar_t* buf, qse_size_t len) | ||||||
|  | { | ||||||
|  | 	return write (handle.i, buf, len); | ||||||
|  | } | ||||||
|  |  | ||||||
| /* ------------------------------------------------------------------- */ | /* ------------------------------------------------------------------- */ | ||||||
| static qse_ssize_t client_recv ( | static qse_ssize_t client_recv ( | ||||||
| 	qse_httpd_t* httpd, qse_httpd_client_t* client, | 	qse_httpd_t* httpd, qse_httpd_client_t* client, | ||||||
| @ -472,7 +555,7 @@ qse_printf (QSE_T("Entasking chunked CGI...\n")); | |||||||
| 			if (!peek) | 			if (!peek) | ||||||
| 			{ | 			{ | ||||||
| 				/* file or directory */ | 				/* file or directory */ | ||||||
| 				task = qse_httpd_entaskpath ( | 				task = qse_httpd_entaskfile ( | ||||||
| 					httpd, client, QSE_NULL, qpath, req); | 					httpd, client, QSE_NULL, qpath, req); | ||||||
| 				if (task == QSE_NULL) goto oops; | 				if (task == QSE_NULL) goto oops; | ||||||
| 			} | 			} | ||||||
| @ -538,6 +621,14 @@ static qse_httpd_cbs_t httpd_cbs = | |||||||
| 	/* path operation */ | 	/* path operation */ | ||||||
| 	{ path_executable }, | 	{ path_executable }, | ||||||
|  |  | ||||||
|  | 	/* file operation */ | ||||||
|  | 	{ file_ropen, | ||||||
|  | 	  file_wopen, | ||||||
|  | 	  file_close, | ||||||
|  | 	  file_read, | ||||||
|  | 	  file_write | ||||||
|  | 	}, | ||||||
|  |  | ||||||
| 	/* client connection */ | 	/* client connection */ | ||||||
| 	{ client_recv,  | 	{ client_recv,  | ||||||
| 	  client_send,  | 	  client_send,  | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user