diff --git a/src/nhttpd/Makefile.am b/src/nhttpd/Makefile.am index dfd2195f3..4d608b624 100644 --- a/src/nhttpd/Makefile.am +++ b/src/nhttpd/Makefile.am @@ -1,6 +1,6 @@ SUBDIRS = yhttpd_core yhttpd_mods tuxboxapi web -AM_CPPFLAGS = -fno-rtti -fno-exceptions +AM_CPPFLAGS = -fno-rtti -fno-exceptions -D_FILE_OFFSET_BITS=64 AM_CPPFLAGS += \ -I$(srcdir) \ diff --git a/src/nhttpd/tuxboxapi/coolstream/Makefile.am b/src/nhttpd/tuxboxapi/coolstream/Makefile.am index af0e7e604..e42353f59 100644 --- a/src/nhttpd/tuxboxapi/coolstream/Makefile.am +++ b/src/nhttpd/tuxboxapi/coolstream/Makefile.am @@ -1,4 +1,4 @@ -AM_CPPFLAGS = -fno-rtti -fno-exceptions -D__STDC_FORMAT_MACROS +AM_CPPFLAGS = -fno-rtti -fno-exceptions -D__STDC_FORMAT_MACROS -D_FILE_OFFSET_BITS=64 AM_CPPFLAGS += \ -I$(top_builddir) \ diff --git a/src/nhttpd/tuxboxapi/dbox/Makefile.am b/src/nhttpd/tuxboxapi/dbox/Makefile.am index 6c341a62a..9c9e45cea 100644 --- a/src/nhttpd/tuxboxapi/dbox/Makefile.am +++ b/src/nhttpd/tuxboxapi/dbox/Makefile.am @@ -1,4 +1,4 @@ -AM_CPPFLAGS = -fno-rtti -fno-exceptions +AM_CPPFLAGS = -fno-rtti -fno-exceptions -D_FILE_OFFSET_BITS=64 AM_CPPFLAGS += \ -I$(top_srcdir)/lib \ diff --git a/src/nhttpd/yhttpd_core/Makefile.am b/src/nhttpd/yhttpd_core/Makefile.am index e4ffb5e10..948eae513 100644 --- a/src/nhttpd/yhttpd_core/Makefile.am +++ b/src/nhttpd/yhttpd_core/Makefile.am @@ -19,7 +19,7 @@ AM_CPPFLAGS += -I$(top_srcdir)/lib/libcoolstream endif endif -AM_CPPFLAGS += -fno-rtti -fno-exceptions +AM_CPPFLAGS += -fno-rtti -fno-exceptions -D_FILE_OFFSET_BITS=64 noinst_LIBRARIES = libyhttpd.a diff --git a/src/nhttpd/yhttpd_core/yhook.cpp b/src/nhttpd/yhttpd_core/yhook.cpp index 2736998cf..9246edec8 100644 --- a/src/nhttpd/yhttpd_core/yhook.cpp +++ b/src/nhttpd/yhttpd_core/yhook.cpp @@ -249,7 +249,7 @@ std::string CyhookHandler::BuildHeader(bool cache) { } // print Status-line result = string_printf(HTTP_PROTOCOL " %d %s\r\nContent-Type: %s\r\n",httpStatus, responseString, ResponseMimeType.c_str()); - log_level_printf(2, "Respose: HTTP/1.1 %d %s\r\nContent-Type: %s\r\n", + log_level_printf(2, "Response: HTTP/1.1 %d %s\r\nContent-Type: %s\r\n", httpStatus, responseString, ResponseMimeType.c_str()); switch (httpStatus) { @@ -287,15 +287,13 @@ std::string CyhookHandler::BuildHeader(bool cache) { if(keep_alive) result += "Connection: keep-alive\r\n"; else - result += "Connection: close\r\n"; -#else - result += "Connection: close\r\n"; #endif + result += "Connection: close\r\n"; // gzipped ? if (UrlData["fileext"] == "gz") result += "Content-Encoding: gzip\r\n"; // content-len, last-modified - if (httpStatus == HTTP_NOT_MODIFIED || httpStatus == HTTP_NOT_FOUND) + if (httpStatus == HTTP_NOT_MODIFIED || httpStatus == HTTP_NOT_FOUND || httpStatus == HTTP_REQUEST_RANGE_NOT_SATISFIABLE) result += "Content-Length: 0\r\n"; else if (GetContentLength() > 0) { time_t mod_time = time(NULL); @@ -303,9 +301,14 @@ std::string CyhookHandler::BuildHeader(bool cache) { mod_time = LastModified; strftime(timeStr, sizeof(timeStr), RFC1123FMT, gmtime(&mod_time)); - result += string_printf( - "Last-Modified: %s\r\nContent-Length: %ld\r\n", timeStr, - GetContentLength()); + result += string_printf("Last-Modified: %s\r\n", timeStr); + if (status == HANDLED_SENDFILE) { + result += string_printf("Accept-Ranges: bytes\r\n"); + result += string_printf("Content-Length: %lld\r\n", RangeEnd - RangeStart + 1); + if (httpStatus == HTTP_PARTIAL_CONTENT) + result += string_printf("Content-Range: bytes %lld-%lld/%lld\r\n", RangeStart, RangeEnd, ContentLength); + } else + result += string_printf("Content-Length: %lld\r\n", GetContentLength()); } result += "\r\n"; // End of Header break; @@ -320,6 +323,8 @@ std::string CyhookHandler::BuildHeader(bool cache) { case HTTP_ACCEPTED: case HTTP_NO_CONTENT: case HTTP_NOT_FOUND: + case HTTP_PARTIAL_CONTENT: + case HTTP_REQUEST_RANGE_NOT_SATISFIABLE: case HTTP_INTERNAL_SERVER_ERROR: break; diff --git a/src/nhttpd/yhttpd_core/yhook.h b/src/nhttpd/yhttpd_core/yhook.h index b313dadda..a01fa6f4e 100644 --- a/src/nhttpd/yhttpd_core/yhook.h +++ b/src/nhttpd/yhttpd_core/yhook.h @@ -47,6 +47,7 @@ //============================================================================= #ifndef __yhttpd_yhook_h__ #define __yhttpd_yhook_h__ +#include // C++ #include #include @@ -128,7 +129,9 @@ public: HttpResponseType httpStatus; // http-status code for response std::string ResponseMimeType; // mime-type for response std::string NewURL; // new URL for Redirection - long ContentLength; // Length of Response Body + off_t ContentLength; // Length of Response Body + off_t RangeStart; // Start of range, used for sendfile only + off_t RangeEnd; // End of range, used for sendfile only time_t LastModified; // Last Modified Time of Item to send / -1 dynamic content std::string Sendfile; // Path & Name (local os style) of file to send bool keep_alive; @@ -141,7 +144,7 @@ public: CStringList HookVarList; // Variables in Hook-Handling passing to other Hooks THttp_Method Method; // HTTP Method (requested) // constructor & deconstructor - CyhookHandler(){ContentLength = 0; keep_alive = 0; _outIndent = 0;status = HANDLED_NONE;Method = M_UNKNOWN;httpStatus = HTTP_NIL;outType = plain;}; + CyhookHandler(){ContentLength = 0; RangeStart = 0; RangeEnd = -1; keep_alive = 0; _outIndent = 0;status = HANDLED_NONE;Method = M_UNKNOWN;httpStatus = HTTP_NIL;outType = plain;}; virtual ~CyhookHandler(){}; // hook slot handler @@ -173,8 +176,8 @@ public: void SetError(HttpResponseType responseType, THandleStatus _status) {SetError(responseType); status = _status;} // others - long GetContentLength() - {return (status==HANDLED_SENDFILE)?ContentLength : (long)yresult.length();} + off_t GetContentLength() + {return (status==HANDLED_SENDFILE)?ContentLength : (off_t)yresult.length();} // output methods std::string BuildHeader(bool cache = false); void addResult(const std::string& result) {yresult += result;} diff --git a/src/nhttpd/yhttpd_core/yresponse.cpp b/src/nhttpd/yhttpd_core/yresponse.cpp index 74af5515a..1bcecb0c1 100644 --- a/src/nhttpd/yhttpd_core/yresponse.cpp +++ b/src/nhttpd/yhttpd_core/yresponse.cpp @@ -140,9 +140,13 @@ bool CWebserverResponse::SendResponse() { // cache = false; Write(Connection->HookHandler.BuildHeader(cache)); if (Connection->Method != M_HEAD) - Sendfile(Connection->Request.UrlData["url"]); + Sendfile(Connection->Request.UrlData["url"], Connection->HookHandler.RangeStart, Connection->HookHandler.RangeEnd - Connection->HookHandler.RangeStart + 1); return true; } + if (Connection->HookHandler.status == HANDLED_SENDFILE && Connection->HookHandler.httpStatus == HTTP_REQUEST_RANGE_NOT_SATISFIABLE) { + SendError(Connection->HookHandler.httpStatus); + return false; + } // arrived here? = error! SendError( HTTP_NOT_FOUND); @@ -193,13 +197,13 @@ bool CWebserverResponse::WriteLn(char const *text) { } //----------------------------------------------------------------------------- -bool CWebserverResponse::Sendfile(std::string filename) { +bool CWebserverResponse::Sendfile(std::string filename, off_t start, off_t end) { if (Connection->RequestCanceled) return false; int filed = open(filename.c_str(), O_RDONLY); if (filed != -1) //can access file? { - if (!Connection->sock->SendFile(filed)) + if (!Connection->sock->SendFile(filed, start, end)) Connection->RequestCanceled = true; close(filed); } diff --git a/src/nhttpd/yhttpd_core/yresponse.h b/src/nhttpd/yhttpd_core/yresponse.h index 4eb2bf864..f799be51f 100644 --- a/src/nhttpd/yhttpd_core/yresponse.h +++ b/src/nhttpd/yhttpd_core/yresponse.h @@ -24,7 +24,7 @@ private: protected: bool WriteData(char const *data, long length); - bool Sendfile(std::string filename); + bool Sendfile(std::string filename, off_t start = 0, off_t end = -1); std::string redirectURI; // URI for redirection else: empty public: diff --git a/src/nhttpd/yhttpd_core/ysocket.cpp b/src/nhttpd/yhttpd_core/ysocket.cpp index 6c51d3324..3a7a8cc0c 100644 --- a/src/nhttpd/yhttpd_core/ysocket.cpp +++ b/src/nhttpd/yhttpd_core/ysocket.cpp @@ -5,6 +5,7 @@ #include #include +#include // system #include @@ -12,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -310,31 +312,46 @@ bool CySocket::CheckSocketOpen() { // BASIC Send File over Socket for FILE* // fd is an opened FILE-Descriptor //----------------------------------------------------------------------------- -int CySocket::SendFile(int filed) { +bool CySocket::SendFile(int filed, off_t start, off_t size) { if (!isValid) return false; -#ifdef Y_CONFIG_HAVE_SENDFILE // does not work with SSL !!! - off_t start = 0; - off_t end = lseek(filed,0,SEEK_END); - int written = 0; - if((written = ::sendfile(sock,filed,&start,end)) == -1) - { - perror("sendfile failed\n"); + struct stat st; + fstat(filed, &st); + off_t left = st.st_size - start; + off_t written = 0; + if (size > -1 && size < left) + left = size; +#ifdef Y_CONFIG_HAVE_SENDFILE + while (left > 0) { + // split sendfile() transfer to smaller chunks to reduce memory-mapping requirements + if((written = ::sendfile(sock, filed, &start, std::min((off_t) 0x8000000LL, left))) == -1) { + if (errno != EPIPE) + perror("sendfile failed"); + if (errno != EINVAL) return false; + break; + } else { + BytesSend += written; + left -= written; } - else - BytesSend += written; -#else - char sbuf[1024]; - unsigned int r = 0; - while ((r = read(filed, sbuf, 1024)) > 0) { - if (Send(sbuf, r) < 0) { - perror("sendfile failed\n"); - return false; - } } #endif // Y_CONFIG_HAVE_SENDFILE + if (left > 0) { + ::lseek(filed, start, SEEK_SET); + + char sbuf[65536]; + while (left && (written = read(filed, sbuf, std::min((off_t) sizeof(sbuf), left))) > 0) { + if (Send(sbuf, written) < 0) { + if (errno != EPIPE) + perror("send failed"); + return false; + } + BytesSend += written; + left -= written; + } + } + log_level_printf(9, ": Bytes:%ld\n", BytesSend); return true; } diff --git a/src/nhttpd/yhttpd_core/ysocket.h b/src/nhttpd/yhttpd_core/ysocket.h index 84fe6f59b..8cd1b172e 100644 --- a/src/nhttpd/yhttpd_core/ysocket.h +++ b/src/nhttpd/yhttpd_core/ysocket.h @@ -71,7 +71,7 @@ public: bool CheckSocketOpen(); // check if socket was closed by client // send & receive - int SendFile(int filed); // Send a File + bool SendFile(int filed, off_t start = 0, off_t size = -1); // Send a File std::string ReceiveBlock(); // receive a Block. Look at length unsigned int ReceiveFileGivenLength(int filed, unsigned int _length); // Receive File of given length std::string ReceiveLine(); // receive until "\n" diff --git a/src/nhttpd/yhttpd_mods/Makefile.am b/src/nhttpd/yhttpd_mods/Makefile.am index fc85656f3..2e748d983 100644 --- a/src/nhttpd/yhttpd_mods/Makefile.am +++ b/src/nhttpd/yhttpd_mods/Makefile.am @@ -10,7 +10,7 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/lib/libconfigfile \ @FREETYPE_CFLAGS@ -AM_CPPFLAGS += -fno-rtti -fno-exceptions +AM_CPPFLAGS += -fno-rtti -fno-exceptions -D_FILE_OFFSET_BITS=64 noinst_LIBRARIES = libyhttpdmods.a diff --git a/src/nhttpd/yhttpd_mods/mod_sendfile.cpp b/src/nhttpd/yhttpd_mods/mod_sendfile.cpp index 81f3cabb6..1442ad660 100644 --- a/src/nhttpd/yhttpd_mods/mod_sendfile.cpp +++ b/src/nhttpd/yhttpd_mods/mod_sendfile.cpp @@ -113,8 +113,22 @@ THandleStatus CmodSendfile::Hook_PrepareResponse(CyhookHandler *hh) { // Send normal or not-modified header if (modified) { - hh->SendFile(fullfilename); - hh->ResponseMimeType = mime; + hh->RangeStart = 0; + hh->RangeEnd = hh->ContentLength - 1; + const char *range = (hh->HeaderList["Range"] == "") ? NULL : hh->HeaderList["Range"].c_str(); + if ((range && + (2 != sscanf(range, "bytes=%lld-%lld", &hh->RangeStart, &hh->RangeEnd)) && + (1 != sscanf(range, "bytes=%lld-", &hh->RangeStart))) + || (hh->RangeStart > hh->RangeEnd) + || (hh->RangeEnd > hh->ContentLength - 1)) { + hh->SetError(HTTP_REQUEST_RANGE_NOT_SATISFIABLE); + aprintf("mod_sendfile: Client requested range '%s' which is outside of [0,%lld]\n", range, hh->ContentLength - 1); + } else { + hh->SendFile(fullfilename); + hh->ResponseMimeType = mime; + if (hh->RangeStart && (hh->RangeEnd != hh->ContentLength - 1)) + hh->httpStatus = HTTP_PARTIAL_CONTENT; + } } else hh->SetHeader(HTTP_NOT_MODIFIED, mime, HANDLED_READY); } else { diff --git a/src/nhttpd/yhttpd_mods/mod_weblog.cpp b/src/nhttpd/yhttpd_mods/mod_weblog.cpp index 49e88dab4..4810daa07 100644 --- a/src/nhttpd/yhttpd_mods/mod_weblog.cpp +++ b/src/nhttpd/yhttpd_mods/mod_weblog.cpp @@ -74,8 +74,7 @@ bool CmWebLog::OpenLogFile() { if (LogFormat == "ELF") { printf("#Version: 1.0\n"); printf("#Remarks: yhttpd" WEBSERVERNAME "\n"); - printf( - "#Fields: c-ip username date time x-request cs-uri sc-status cs-method bytes time-taken x-time-request x-time-response cached\n"); + printf("#Fields: c-ip username date time x-request cs-uri sc-status cs-method bytes time-taken x-time-request x-time-response cached\n"); } } pthread_mutex_unlock(&WebLog_mutex); @@ -147,7 +146,7 @@ void CmWebLog::AddLogEntry_CLF(CyhookHandler *hh) std::string c_ip = hh->UrlData["clientaddr"].c_str(); std::string request_startline = hh->UrlData["startline"].c_str(); int s_status = hh->httpStatus; - int bytes = hh->GetContentLength(); + off_t bytes = hh->GetContentLength(); struct tm *time_now; time_t now = time(NULL); @@ -156,12 +155,12 @@ void CmWebLog::AddLogEntry_CLF(CyhookHandler *hh) time_now = localtime(&now); strftime(request_time, 80, "[%d/%b/%Y:%H:%M:%S]", time_now); - printf("%s - - %s \"%s\" %d %d\n", + printf("%s - - %s \"%s\" %d %lld\n", c_ip.c_str(), request_time, request_startline.c_str(), s_status, - bytes); + (long long) bytes); } //----------------------------------------------------------------------------- @@ -319,7 +318,7 @@ void CmWebLog::AddLogEntry_ELF(CyhookHandler *hh) std::string request_startline = hh->UrlData["startline"].c_str(); std::string cs_uri = hh->UrlData["fullurl"]; int sc_status = hh->httpStatus; - int bytes = hh->GetContentLength(); + off_t bytes = hh->GetContentLength(); int cached = (hh->HookVarList["CacheCategory"].empty()) ? 0 : 1; struct tm *time_now; @@ -339,7 +338,7 @@ void CmWebLog::AddLogEntry_ELF(CyhookHandler *hh) std::string time_taken_response = hh->HookVarList["enlapsed_response"]; long time_taken = atoi(time_taken_request.c_str()) + atoi(time_taken_response.c_str()); - printf("%s %s %s \"%s\" %s %d %s %d %ld %s %s %d\n", + printf("%s %s %s \"%s\" %s %d %s %d %ld %s %s %lld\n", c_ip.c_str(), _date, _time, @@ -347,7 +346,7 @@ void CmWebLog::AddLogEntry_ELF(CyhookHandler *hh) cs_uri.c_str(), sc_status, cs_method.c_str(), - bytes, + (long long) bytes, time_taken, time_taken_request.c_str(), time_taken_response.c_str(),