refactored httpd file handling
This commit is contained in:
		| @ -55,8 +55,10 @@ struct qse_httpd_cbs_t | ||||
| { | ||||
| 	struct | ||||
| 	{ | ||||
| 		int (*readable) (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); | ||||
| 		int (*readable) ( | ||||
| 			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; | ||||
|  | ||||
| 	struct | ||||
| @ -64,6 +66,24 @@ struct qse_httpd_cbs_t | ||||
| 		int (*executable) (qse_httpd_t* httpd, const qse_mchar_t* 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 | ||||
| 	{ | ||||
| 		/* action */ | ||||
| @ -251,6 +271,7 @@ qse_httpd_task_t* qse_httpd_entaskformat ( | ||||
|  | ||||
| /* -------------------------------------------- */ | ||||
|  | ||||
| #if 0 | ||||
| qse_httpd_task_t* qse_httpd_entaskfile ( | ||||
| 	qse_httpd_t*            httpd, | ||||
| 	qse_httpd_client_t*     client, | ||||
| @ -267,6 +288,7 @@ qse_httpd_task_t* qse_httpd_entaskdir ( | ||||
| 	qse_ubi_t               handle, | ||||
| 	int                     chunked | ||||
| ); | ||||
| #endif | ||||
|  | ||||
| /* -------------------------------------------- */ | ||||
|  | ||||
| @ -285,7 +307,15 @@ qse_httpd_task_t* qse_httpd_entaskcontinue ( | ||||
| 	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_client_t*       client, | ||||
| 	const qse_httpd_task_t*   pred, | ||||
|  | ||||
| @ -25,13 +25,11 @@ | ||||
|  | ||||
| #include "httpd.h" | ||||
| #include "../cmn/mem.h" | ||||
| #include "../cmn/syscall.h" | ||||
| #include <qse/cmn/str.h> | ||||
| #include <qse/cmn/chr.h> | ||||
| #include <qse/cmn/pio.h> | ||||
| #include <qse/cmn/fmt.h> | ||||
|  | ||||
| #include <fcntl.h> | ||||
| #include <stdarg.h> | ||||
| #include <stdio.h> | ||||
| #include <dirent.h> | ||||
| @ -242,7 +240,8 @@ qse_httpd_task_t* qse_httpd_entaskformat ( | ||||
| 	{ | ||||
| 		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; | ||||
|  | ||||
| 		/* 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,  | ||||
| 	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; | ||||
| struct task_file_t | ||||
| { | ||||
| @ -1143,6 +1145,304 @@ qse_httpd_task_t* qse_httpd_entaskpath ( | ||||
| 		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; | ||||
| @ -1518,6 +1818,10 @@ else qse_printf (QSE_T("!!!SNATCHING DONE\n")); | ||||
| 		 * abortion for an error */ | ||||
| 		QSE_ASSERT (len == 0); | ||||
| 		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) | ||||
| 	{ | ||||
| @ -2161,6 +2465,8 @@ static QSE_INLINE qse_httpd_task_t* entask_cgi ( | ||||
| { | ||||
| 	qse_httpd_task_t task; | ||||
| 	task_cgi_arg_t arg; | ||||
|  | ||||
| #if 0 | ||||
| 	int x; | ||||
|  | ||||
| /* 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); | ||||
| 	else if (x <= -1) | ||||
| 		return qse_httpd_entaskerror (httpd, client, pred, 404, req); | ||||
| #endif | ||||
|  | ||||
| 	arg.path = path; | ||||
| 	arg.req = req; | ||||
|  | ||||
| @ -13,6 +13,8 @@ | ||||
| #else | ||||
| #	include <unistd.h> | ||||
| #	include <errno.h> | ||||
| #	include <fcntl.h> | ||||
| #	include <sys/stat.h> | ||||
| #endif | ||||
|  | ||||
| #include <openssl/ssl.h> | ||||
| @ -230,6 +232,87 @@ static int path_executable (qse_httpd_t* httpd, const qse_mchar_t* path) | ||||
| 	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 ( | ||||
| 	qse_httpd_t* httpd, qse_httpd_client_t* client, | ||||
| @ -472,7 +555,7 @@ qse_printf (QSE_T("Entasking chunked CGI...\n")); | ||||
| 			if (!peek) | ||||
| 			{ | ||||
| 				/* file or directory */ | ||||
| 				task = qse_httpd_entaskpath ( | ||||
| 				task = qse_httpd_entaskfile ( | ||||
| 					httpd, client, QSE_NULL, qpath, req); | ||||
| 				if (task == QSE_NULL) goto oops; | ||||
| 			} | ||||
| @ -538,6 +621,14 @@ static qse_httpd_cbs_t httpd_cbs = | ||||
| 	/* path operation */ | ||||
| 	{ path_executable }, | ||||
|  | ||||
| 	/* file operation */ | ||||
| 	{ file_ropen, | ||||
| 	  file_wopen, | ||||
| 	  file_close, | ||||
| 	  file_read, | ||||
| 	  file_write | ||||
| 	}, | ||||
|  | ||||
| 	/* client connection */ | ||||
| 	{ client_recv,  | ||||
| 	  client_send,  | ||||
|  | ||||
		Reference in New Issue
	
	Block a user