diff --git a/qse/lib/net/httpd_task.c b/qse/lib/net/httpd_task.c index e439fe86..ad618ae7 100644 --- a/qse/lib/net/httpd_task.c +++ b/qse/lib/net/httpd_task.c @@ -113,6 +113,50 @@ static qse_ssize_t xsendfile ( } #endif +static qse_httpd_task_t* entask_error ( + qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task, + int code, qse_http_version_t* version, int keepalive) +{ + const qse_mchar_t* smsg; + const qse_mchar_t* lmsg; + + switch (code) + { + case 403: + smsg = QSE_MT("Forbidden"); + lmsg = QSE_MT("Directory Listing ForbiddenDIRECTORY LISTING FORBIDDEN"); + break; + + case 404: + smsg = QSE_MT("Not Found"); + lmsg = QSE_MT("Not FoundREQUESTED PATH NOT FOUND"); + break; + + case 416: + smsg = QSE_MT("Requested Range Not Satisfiable"); + lmsg = QSE_MT("Requested Range Not SatsfiableREQUESTED RANGE NOT SATISFIABLE"); + break; + + case 500: + smsg = QSE_MT("Internal Server Error"); + lmsg = QSE_MT("Internal Server ErrorINTERNAL SERVER ERROR"); + + default: + smsg = QSE_MT("Unknown"); + lmsg = QSE_MT("Unknown ErrorUNKNOWN ERROR"); + break; + } + + return qse_httpd_entaskformat ( + httpd, client, task, + QSE_MT("HTTP/%d.%d %d %s\r\nConnection: %s\r\nContent-Type: text/html;charset=utf-8\r\nContent-Length: %d\r\n\r\n%s\r\n\r\n"), + version->major, version->minor, code, smsg, + (keepalive? QSE_MT("keep-alive"): QSE_MT("close")), + (int)qse_mbslen(lmsg) + 4, lmsg + ); +} + + /*------------------------------------------------------------------------*/ static int task_main_disconnect ( @@ -844,150 +888,45 @@ static int task_init_path ( return 0; } -static qse_httpd_task_t* entask_path_error ( - qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task, int code) +static QSE_INLINE int task_main_path_file ( + qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task, qse_foff_t filesize) { task_path_t* data = (task_path_t*)task->ctx; - - const qse_mchar_t* smsg; - const qse_mchar_t* lmsg; - - switch (code) - { - case 403: - smsg = QSE_MT("Forbidden"); - lmsg = QSE_MT("Directory Listing ForbiddenDIRECTORY LISTING FORBIDDEN"); - break; - - case 404: - smsg = QSE_MT("Not Found"); - lmsg = QSE_MT("Not FoundREQUESTED PATH NOT FOUND"); - break; - - case 416: - smsg = QSE_MT("Requested Range Not Satisfiable"); - lmsg = QSE_MT("Requested Range Not SatsfiableREQUESTED RANGE NOT SATISFIABLE"); - break; - - default: - smsg = QSE_MT("Unknown"); - lmsg = QSE_MT("Unknown ErrorUNKNOWN ERROR"); - break; - } - - return qse_httpd_entaskformat ( - httpd, client, task, - QSE_MT("HTTP/%d.%d %d %s\r\nConnection: %s\r\nContent-Type: text/html;charset=utf-8\r\nContent-Length: %d\r\n\r\n%s\r\n\r\n"), - data->version.major, data->version.minor, code, smsg, - (data->keepalive? QSE_MT("keep-alive"): QSE_MT("close")), - (int)qse_mbslen(lmsg) + 4, lmsg - ); -} - -static int task_main_path ( - qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task) -{ - task_path_t* data = (task_path_t*)task->ctx; - qse_ubi_t handle; - struct stat st; qse_httpd_task_t* x = task; + qse_ubi_t handle; + + /* 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"), data->name); handle.i = open (data->name, O_RDONLY); if (handle.i <= -1) { - x = entask_path_error (httpd, client, x, 404); + x = entask_error (httpd, client, x, 404, &data->version, data->keepalive); goto no_file_send; } fcntl (handle.i, F_SETFD, FD_CLOEXEC); - if (fstat (handle.i, &st) <= -1) - { - x = entask_path_error (httpd, client, x, 404); - goto no_file_send; - } - - if (S_ISDIR(st.st_mode)) - { - if (qse_mbsend (data->name, QSE_MT("/"))) - { - qse_ubi_t dir; - - /*#if defined(HAVE_FDOPENDIR) - dir.ptr = fdopendir (handle.i); - #else */ - dir.ptr = opendir (data->name); - /*#endif */ - if (dir.ptr) - { - if (data->version.major < 1 || - (data->version.major == 1 && data->version.minor == 0)) - { - data->keepalive = 0; - } - - if (data->keepalive) - { - x = qse_httpd_entaskformat ( - httpd, client, x, - QSE_MT("HTTP/%d.%d 200 OK\r\nConnection: keep-alive\r\nContent-Type: text/html;charset=utf-8\r\nTransfer-Encoding: chunked\r\n\r\n"), - data->version.major, data->version.minor - ); - if (x) x = qse_httpd_entaskdir (httpd, client, x, dir, data->keepalive); - } - else - { - x = qse_httpd_entaskformat ( - httpd, client, x, - QSE_MT("HTTP/%d.%d 200 OK\r\nConnection: close\r\nContent-Type: text/html;charset=utf-8\r\n\r\n"), - data->version.major, data->version.minor - ); - - if (x) x = qse_httpd_entaskdir (httpd, client, x, dir, data->keepalive); - if (x) x = qse_httpd_entaskdisconnect (httpd, client, x); - } - - if (!x) closedir (dir.ptr); - } - else - { - x = entask_path_error (httpd, client, x, 403); - } - } - else - { - x = qse_httpd_entaskformat ( - httpd, client, x, - QSE_MT("HTTP/%d.%d 301 Moved Permanently\r\nContent-Length: 0\r\nConnection: %s\r\nLocation: %s/\r\n\r\n"), - data->version.major, data->version.minor, - (data->keepalive? QSE_MT("keep-alive"): QSE_MT("close")), - data->name - ); - } - goto no_file_send; - } - - if (st.st_size < 0) st.st_size = 0; /* can this happen? */ - if (data->range.type != QSE_HTTP_RANGE_NONE) { const qse_mchar_t* mime_type = QSE_NULL; if (data->range.type == QSE_HTTP_RANGE_SUFFIX) { - if (data->range.to > st.st_size) data->range.to = st.st_size; - data->range.from = st.st_size - data->range.to; + if (data->range.to > filesize) data->range.to = filesize; + data->range.from = filesize - data->range.to; data->range.to = data->range.to + data->range.from; - if (st.st_size > 0) data->range.to--; + if (filesize > 0) data->range.to--; } - if (data->range.from >= st.st_size) + if (data->range.from >= filesize) { - x = entask_path_error (httpd, client, x, 416); + x = entask_error (httpd, client, x, 416, &data->version, data->keepalive); goto no_file_send; } - if (data->range.to >= st.st_size) data->range.to = st.st_size - 1; + if (data->range.to >= filesize) data->range.to = filesize - 1; if (httpd->cbs->getmimetype) { @@ -1009,7 +948,7 @@ qse_printf (QSE_T("opening file %hs\n"), data->name); (unsigned long long)(data->range.to - data->range.from + 1), (unsigned long long)data->range.from, (unsigned long long)data->range.to, - (unsigned long long)st.st_size + (unsigned long long)filesize ); #else x = qse_httpd_entaskformat ( @@ -1024,7 +963,7 @@ qse_printf (QSE_T("opening file %hs\n"), data->name); (unsigned long)(data->range.to - data->range.from + 1), (unsigned long)data->range.from, (unsigned long)data->range.to, - (unsigned long)st.st_size + (unsigned long)filesize ); #endif if (x) @@ -1061,7 +1000,7 @@ qse_printf (QSE_T("opening file %hs\n"), data->name); (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)st.st_size + (unsigned long long)filesize ); #else x = qse_httpd_entaskformat ( @@ -1073,13 +1012,13 @@ qse_printf (QSE_T("opening file %hs\n"), data->name); (mime_type? QSE_MT("Content-Type: "): QSE_MT("")), (mime_type? mime_type: QSE_MT("")), (mime_type? QSE_MT("\r\n"): QSE_MT("")), - (unsigned long)st.st_size + (unsigned long)filesize ); #endif if (x) { x = qse_httpd_entaskfile ( - httpd, client, x, handle, 0, st.st_size); + httpd, client, x, handle, 0, filesize); } } @@ -1090,6 +1029,91 @@ no_file_send: return (x == QSE_NULL)? -1: 0; } +static QSE_INLINE int task_main_path_dir ( + qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task) +{ + task_path_t* data = (task_path_t*)task->ctx; + qse_httpd_task_t* x = task; + qse_ubi_t handle; + + if (qse_mbsend (data->name, QSE_MT("/"))) + { + handle.ptr = opendir (data->name); + if (handle.ptr) + { + if (data->version.major < 1 || + (data->version.major == 1 && data->version.minor == 0)) + { + data->keepalive = 0; + } + + if (data->keepalive) + { + x = qse_httpd_entaskformat ( + httpd, client, x, + QSE_MT("HTTP/%d.%d 200 OK\r\nConnection: keep-alive\r\nContent-Type: text/html;charset=utf-8\r\nTransfer-Encoding: chunked\r\n\r\n"), + data->version.major, data->version.minor + ); + if (x) x = qse_httpd_entaskdir (httpd, client, x, handle, data->keepalive); + } + else + { + x = qse_httpd_entaskformat ( + httpd, client, x, + QSE_MT("HTTP/%d.%d 200 OK\r\nConnection: close\r\nContent-Type: text/html;charset=utf-8\r\n\r\n"), + data->version.major, data->version.minor + ); + + if (x) + { + x = qse_httpd_entaskdir (httpd, client, x, handle, data->keepalive); + if (x) x = qse_httpd_entaskdisconnect (httpd, client, x); + } + } + + if (!x) closedir (handle.ptr); + } + else + { + x = entask_error (httpd, client, x, 403, &data->version, data->keepalive); + } + } + else + { + x = qse_httpd_entaskformat ( + httpd, client, x, + QSE_MT("HTTP/%d.%d 301 Moved Permanently\r\nContent-Length: 0\r\nConnection: %s\r\nLocation: %s/\r\n\r\n"), + data->version.major, data->version.minor, + (data->keepalive? QSE_MT("keep-alive"): QSE_MT("close")), + data->name + ); + } + + return (x == QSE_NULL)? -1: 0; +} + +static int task_main_path ( + qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task) +{ + task_path_t* data = (task_path_t*)task->ctx; + struct stat st; + + if (lstat (data->name, &st) <= -1) + { + return (entask_error (httpd, client, task, 404, &data->version, data->keepalive) == QSE_NULL)? -1: 0; + } + + if (S_ISDIR(st.st_mode)) + { + return task_main_path_dir (httpd, client, task); + } + else + { + if (st.st_size < 0) st.st_size = 0; /* can this happen? */ + return task_main_path_file (httpd, client, task, st.st_size); + } +} + qse_httpd_task_t* qse_httpd_entaskpath ( qse_httpd_t* httpd, qse_httpd_client_t* client, @@ -1592,8 +1616,6 @@ static int task_main_cgi ( { task_cgi_t* cgi = (task_cgi_t*)task->ctx; cgi_htrd_xtn_t* xtn; -/* TODO: get the message using callback */ - const qse_mchar_t* msg = "Internal server error has occurred"; if (cgi->init_failed) goto oops; @@ -1625,17 +1647,8 @@ oops: if (cgi->res) qse_mbs_close (cgi->res); if (cgi->htrd) qse_htrd_close (cgi->htrd); - qse_httpd_entaskformat ( - httpd, client, task, - QSE_MT("HTTP/%d.%d 500 Internal Server Error\r\nContent-Type: text/plain\r\nContent-Length: %lu\r\n\r\n%s"), - cgi->version.major, - cgi->version.minor, - (unsigned long)qse_mbslen(msg), - msg - ); -/* TODO: can i return something else if this fails... */ - - return 0; +/* TODO: keep alive.... cgi->disconnect???? */ + return (entask_error (httpd, client, task, 500, &cgi->version, 0) == QSE_NULL)? -1: 0; } qse_httpd_task_t* qse_httpd_entaskcgi (