Skip to content

Commit 4eec10b

Browse files
committed
- Switched to mapped files for serving static files.
- Added a test script for checking.
1 parent e798225 commit 4eec10b

File tree

5 files changed

+183
-88
lines changed

5 files changed

+183
-88
lines changed

HttpServ.cpp

Lines changed: 85 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#include "CommonLib/UrlCode.h"
3232
#include "SpawnProcess.h"
3333
#include "FastCgi/FastCgi.h"
34+
#include "MappedFile.h"
3435

3536
using namespace std;
3637
using namespace std::placeholders;
@@ -2489,33 +2490,57 @@ void CHttpServ::DoAction(const MetaSocketData soMetaDa, const uint8_t httpVers,
24892490
}
24902491

24912492
// Load file
2492-
fstream fin(FN_CA(strItemPath), ios_base::in | ios_base::binary);
2493-
if (fin.is_open() == true)
2493+
MappedFile map;
2494+
if (map.open(strItemPath) == true)
24942495
{
2495-
uint64_t nFSize = stFileInfo.st_size;
2496+
//std::deque<std::tuple<uint8_t, uint8_t*, uint64_t>> queMem2Send;
2497+
//queMem2Send.emplace_back(0, map.data(), map.size()); // Dummy packet to indicate the start of the data stream
2498+
2499+
uint64_t nFSize = map.size();
24962500
if (vecRanges.size() == 1) // Momentan nur 1 Range
24972501
{
2498-
nFSize = vecRanges[0].second - vecRanges[0].first;
2499-
fin.seekg(vecRanges[0].first, ios_base::beg);
2500-
umPhpHeaders.emplace_back(make_pair("Content-Range", "bytes " + to_string(vecRanges[0].first) + "-" + to_string(vecRanges[0].second) + "/" + to_string(stFileInfo.st_size)));
2501-
iStatus = 206;
2502+
//OutputDebugStringA(std::string("Range requested, nFSize=" + std::to_string(nFSize) + ", Range from: " + std::to_string(vecRanges[0].first) + " bis " + std::to_string(vecRanges[0].second) + "\r\n").c_str());
2503+
if (vecRanges[0].first < nFSize && vecRanges[0].second <= nFSize && vecRanges[0].second >= vecRanges[0].first)
2504+
{
2505+
nFSize = vecRanges[0].second - vecRanges[0].first;
2506+
map.setOffset(vecRanges[0].first);
2507+
umPhpHeaders.emplace_back(make_pair("Content-Range", "bytes " + to_string(vecRanges[0].first) + "-" + to_string(vecRanges[0].second) + "/" + to_string(stFileInfo.st_size)));
2508+
iStatus = 206;
2509+
}
2510+
else
2511+
OutputDebugStringA(std::string("Invalid Range requested, nFSize=" + std::to_string(nFSize) + ", Range from: " + std::to_string(vecRanges[0].first) + " bis " + std::to_string(vecRanges[0].second) + "\r\n").c_str());
25022512
}
25032513
else
25042514
umPhpHeaders.emplace_back(make_pair("Accept-Ranges", "bytes"));
25052515

25062516
const auto acceptencoding = lstHeaderFields.find("accept-encoding");
25072517
if (acceptencoding != end(lstHeaderFields))
25082518
{
2519+
//OutputDebugStringA(string("accept-encoding: " + lstHeaderFields.find("accept-encoding")->second + "\r\n").c_str());
25092520
// http://www.filesignatures.net/index.php?page=all
25102521
if (find_if(begin(m_vHostParam[szHost].m_vDeflateTyps), end(m_vHostParam[szHost].m_vDeflateTyps), [&](const string& strType) noexcept { return strType == strMineType ? true : false; }) != end(m_vHostParam[szHost].m_vDeflateTyps))
25112522
{
25122523
if (acceptencoding->second.find("br") != string::npos) iHeaderFlag |= BROTLICODING;
25132524
else if (acceptencoding->second.find("gzip") != string::npos) iHeaderFlag |= GZIPENCODING;
25142525
else if (acceptencoding->second.find("deflate") != string::npos) iHeaderFlag |= DEFLATEENCODING;
2526+
2527+
const auto xPrefCompAlgo = lstHeaderFields.find("x-prefcompalgo");
2528+
if (xPrefCompAlgo != end(lstHeaderFields))
2529+
{
2530+
//OutputDebugStringA(string("x-prefcompalgo: " + xPrefCompAlgo->second + "\r\n").c_str());
2531+
iHeaderFlag &= ~BROTLICODING;
2532+
iHeaderFlag &= ~DEFLATEENCODING;
2533+
iHeaderFlag &= ~GZIPENCODING;
2534+
if (xPrefCompAlgo->second == "gzip")
2535+
iHeaderFlag |= GZIPENCODING;
2536+
else if (xPrefCompAlgo->second == "deflate")
2537+
iHeaderFlag |= DEFLATEENCODING;
2538+
else if (xPrefCompAlgo->second == "br")
2539+
iHeaderFlag |= BROTLICODING;
2540+
}
25152541
}
25162542
}
25172543

2518-
//iHeaderFlag &= ~(GZIPENCODING | DEFLATEENCODING);
25192544
if (iHeaderFlag & GZIPENCODING || iHeaderFlag & DEFLATEENCODING)
25202545
{
25212546
if (httpVers < 2)
@@ -2534,68 +2559,55 @@ void CHttpServ::DoAction(const MetaSocketData soMetaDa, const uint8_t httpVers,
25342559
GZipPack gzipEncoder;
25352560
if (gzipEncoder.Init((iHeaderFlag & DEFLATEENCODING) ? true : false) == Z_OK)
25362561
{
2537-
unique_ptr<unsigned char[]> srcBuf(new unsigned char[nSizeSendBuf]);
2538-
unique_ptr<unsigned char[]> dstBuf(new unsigned char[nSizeSendBuf]);
2539-
2540-
uint64_t nBytesTransferred = 0;
25412562
int iResult = 0;
2542-
do
2543-
{
2544-
const streamsize nBytesRead = fin.read(reinterpret_cast<char*>(srcBuf.get()), nSizeSendBuf).gcount();
2545-
if (nBytesRead == 0)
2546-
break;
2547-
nBytesTransferred += nBytesRead;
2563+
gzipEncoder.InitBuffer(map.data(), static_cast<uint32_t>(nFSize));
25482564

2549-
gzipEncoder.InitBuffer(srcBuf.get(), static_cast<uint32_t>(nBytesRead));
2550-
const int nFlush = nBytesTransferred == nFSize ? Z_FINISH : Z_NO_FLUSH;
2565+
unique_ptr<unsigned char[]> dstBuf(new unsigned char[nSizeSendBuf]);
2566+
size_t nBytesConverted;
2567+
do
2568+
{ // Get next compressed chunk
2569+
nBytesConverted = nSizeSendBuf - nHttp2Offset;
2570+
iResult = gzipEncoder.Inflate(dstBuf.get() + nHttp2Offset, &nBytesConverted, Z_FINISH);
25512571

2552-
size_t nBytesConverted;
2553-
do
2572+
// Send compressed chunk
2573+
size_t nOffset = 0;
2574+
while ((iResult == Z_OK || iResult == Z_STREAM_END) && ((nSizeSendBuf - nHttp2Offset) - nBytesConverted - nOffset) != 0 && patStop.load() == false && fnIsStreamReset(nStreamId) == false)
25542575
{
2555-
nBytesConverted = nSizeSendBuf - nHttp2Offset;
2556-
iResult = gzipEncoder.Inflate(dstBuf.get() + nHttp2Offset, &nBytesConverted, nFlush);
2557-
2558-
size_t nOffset = 0;
2559-
while ((iResult == Z_OK || iResult == Z_STREAM_END) && ((nSizeSendBuf - nHttp2Offset) - nBytesConverted - nOffset) != 0 && patStop.load() == false && fnIsStreamReset(nStreamId) == false)
2560-
{
2561-
int64_t nStreamWndSize = INT32_MAX;
2562-
if (fnGetStreamWindowSize(nStreamWndSize) == false)
2563-
break; // Stream Item was removed, probably the stream was reset
2564-
2565-
size_t nSendBufLen;
2566-
if (fnSendQueueReady(nStreamWndSize, nSendBufLen, static_cast<uint64_t>(nSizeSendBuf - nHttp2Offset), ((nSizeSendBuf - nHttp2Offset) - nBytesConverted - nOffset)) == false)
2567-
continue;
2568-
2569-
if (httpVers == 2)
2570-
{
2571-
bool bLastPaket = false;
2572-
if (iResult == Z_STREAM_END && ((nSizeSendBuf - nHttp2Offset) - nBytesConverted - nOffset) == nSendBufLen) // Letztes Paket
2573-
bLastPaket = true;
2574-
BuildHttp2Frame(dstBuf.get() + nOffset, nSendBufLen, 0x0, bLastPaket == true ? 0x1 : 0x0, nStreamId);
2575-
}
2576-
else
2577-
{
2578-
stringstream ss;
2579-
ss << hex << ::uppercase << nSendBufLen << "\r\n";
2580-
soMetaDa.fSocketWrite(ss.str().c_str(), ss.str().size());
2581-
}
2582-
soMetaDa.fSocketWrite(dstBuf.get() + nOffset, nSendBufLen + nHttp2Offset);
2583-
if (httpVers < 2)
2584-
soMetaDa.fSocketWrite("\r\n", 2);
2585-
soMetaDa.fResetTimer();
2576+
int64_t nStreamWndSize = INT32_MAX;
2577+
if (fnGetStreamWindowSize(nStreamWndSize) == false)
2578+
break; // Stream Item was removed, probably the stream was reset
25862579

2587-
if (fnUpdateStreamParam(nSendBufLen) == -1)
2588-
break; // Stream Item was removed, probably the stream was reset
2580+
size_t nSendBufLen;
2581+
if (fnSendQueueReady(nStreamWndSize, nSendBufLen, static_cast<uint64_t>(nSizeSendBuf - nHttp2Offset), ((nSizeSendBuf - nHttp2Offset) - nBytesConverted - nOffset)) == false)
2582+
continue;
25892583

2590-
//nBytesConverted += nSendBufLen;
2591-
nOffset += nSendBufLen;
2584+
if (httpVers == 2)
2585+
{
2586+
bool bLastPaket = false;
2587+
if (iResult == Z_STREAM_END && ((nSizeSendBuf - nHttp2Offset) - nBytesConverted - nOffset) == nSendBufLen) // Letztes Paket
2588+
bLastPaket = true;
2589+
BuildHttp2Frame(dstBuf.get() + nOffset, nSendBufLen, 0x0, bLastPaket == true ? 0x1 : 0x0, nStreamId);
2590+
}
2591+
else
2592+
{
2593+
stringstream ss;
2594+
ss << hex << ::uppercase << nSendBufLen << "\r\n";
2595+
soMetaDa.fSocketWrite(ss.str().c_str(), ss.str().size());
25922596
}
2597+
soMetaDa.fSocketWrite(dstBuf.get() + nOffset, nSendBufLen + nHttp2Offset);
2598+
if (httpVers < 2)
2599+
soMetaDa.fSocketWrite("\r\n", 2);
2600+
soMetaDa.fResetTimer();
2601+
2602+
if (fnUpdateStreamParam(nSendBufLen) == -1)
2603+
break; // Stream Item was removed, probably the stream was reset
25932604

2594-
fnResetReservierteWindowSize();
2605+
nOffset += nSendBufLen;
2606+
}
25952607

2596-
} while (iResult == Z_OK && nBytesConverted == 0 && patStop.load() == false && fnIsStreamReset(nStreamId) == false);
2608+
fnResetReservierteWindowSize();
25972609

2598-
} while (iResult == Z_OK && patStop.load() == false && fnIsStreamReset(nStreamId) == false);
2610+
} while (iResult == Z_OK && nBytesConverted == 0 && patStop.load() == false && fnIsStreamReset(nStreamId) == false);
25992611

26002612
if (httpVers < 2 && patStop.load() == false)
26012613
soMetaDa.fSocketWrite("0\r\n\r\n", 5);
@@ -2619,35 +2631,21 @@ void CHttpServ::DoAction(const MetaSocketData soMetaDa, const uint8_t httpVers,
26192631
BrotliEncoderState* s = BrotliEncoderCreateInstance(nullptr, nullptr, nullptr);
26202632
BrotliEncoderSetParameter(s, BROTLI_PARAM_QUALITY, (uint32_t)9);
26212633
BrotliEncoderSetParameter(s, BROTLI_PARAM_LGWIN, (uint32_t)0);
2622-
/* if (dictionary_path != NULL) {
2623-
size_t dictionary_size = 0;
2624-
uint8_t* dictionary = ReadDictionary(dictionary_path, &dictionary_size);
2625-
BrotliEncoderSetCustomDictionary(s, dictionary_size, dictionary);
2626-
free(dictionary);
2627-
}
2628-
*/
2629-
unique_ptr<unsigned char[]> srcBuf(new unsigned char[nSizeSendBuf]);
2634+
26302635
unique_ptr<unsigned char[]> dstBuf(new unsigned char[nSizeSendBuf]);
26312636

2632-
size_t nBytIn = 0;
2633-
const uint8_t* input = nullptr;
2637+
size_t nBytIn = nFSize;
2638+
const uint8_t* input = map.data();
26342639
size_t nBytOut = nSizeSendBuf - nHttp2Offset;
26352640
uint8_t* output = dstBuf.get() + nHttp2Offset;
26362641

2637-
uint64_t nBytesTransferred = 0;
26382642
while (patStop.load() == false && fnIsStreamReset(nStreamId) == false)
26392643
{
2640-
if (nBytIn == 0)
2641-
{
2642-
const streamsize nBytesRead = fin.read(reinterpret_cast<char*>(srcBuf.get()), nSizeSendBuf).gcount();
2643-
nBytIn = static_cast<size_t>(nBytesRead);
2644-
input = srcBuf.get();
2645-
nBytesTransferred += nBytIn;
2646-
}
2647-
2644+
// Nächsten Chunk komprimieren
26482645
if (!BrotliEncoderCompressStream(s, nBytIn == 0 ? BROTLI_OPERATION_FINISH : BROTLI_OPERATION_PROCESS, &nBytIn, &input, &nBytOut, &output, nullptr))
26492646
break;
26502647

2648+
// Senden des komprimierten Chunks
26512649
size_t nOffset = 0;
26522650
while (nBytOut != nSizeSendBuf - nHttp2Offset && patStop.load() == false && fnIsStreamReset(nStreamId) == false)
26532651
{
@@ -2704,9 +2702,6 @@ void CHttpServ::DoAction(const MetaSocketData soMetaDa, const uint8_t httpVers,
27042702
soMetaDa.fSocketWrite(caBuffer, nHeaderLen + nHttp2Offset);
27052703
}
27062704
soMetaDa.fResetTimer();
2707-
2708-
auto apBuf = make_unique<uint8_t[]>(nSizeSendBuf + nHttp2Offset + 2);
2709-
27102705
uint64_t nBytesTransferred = 0;
27112706
while (nBytesTransferred < nFSize && patStop.load() == false && fnIsStreamReset(nStreamId) == false)
27122707
{
@@ -2718,20 +2713,22 @@ void CHttpServ::DoAction(const MetaSocketData soMetaDa, const uint8_t httpVers,
27182713
if (fnSendQueueReady(nStreamWndSize, nSendBufLen, static_cast<uint64_t>(nSizeSendBuf - nHttp2Offset), nFSize - nBytesTransferred) == false)
27192714
continue;
27202715

2721-
nBytesTransferred += nSendBufLen;
2722-
fin.read(reinterpret_cast<char*>(apBuf.get()) + nHttp2Offset, nSendBufLen);
2723-
27242716
if (httpVers == 2)
2725-
BuildHttp2Frame(apBuf.get(), nSendBufLen, 0x0, (nFSize - nBytesTransferred == 0 ? 0x1 : 0x0), nStreamId);
2726-
soMetaDa.fSocketWrite(apBuf.get(), nSendBufLen + nHttp2Offset);
2717+
{
2718+
BuildHttp2Frame(caBuffer, nSendBufLen, 0x0, (nFSize - (nBytesTransferred + nSendBufLen) == 0 ? 0x1 : 0x0), nStreamId);
2719+
soMetaDa.fSocketWrite(caBuffer, nHttp2Offset);
2720+
}
2721+
soMetaDa.fSocketWrite(map.data(), nSendBufLen);
27272722
soMetaDa.fResetTimer();
27282723

27292724
if (fnUpdateStreamParam(nSendBufLen) == -1)
27302725
break; // Stream Item was removed, probably the stream was reset
2726+
2727+
nBytesTransferred += nSendBufLen;
2728+
map.addOffset(nSendBufLen);
27312729
}
27322730
fnResetReservierteWindowSize();
27332731
}
2734-
fin.close();
27352732

27362733
CLogFile::GetInstance(m_vHostParam[szHost].m_strLogFile) << soMetaDa.strIpClient << " - - [" << CLogFile::LOGTYPES::PUTTIME << "] \""
27372734
<< itMethode->second << " " << lstHeaderFields.find(":path")->second

MappedFile.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,3 +110,9 @@ void MappedFile::setOffset(const uint64_t off)
110110
{
111111
offset = off;
112112
}
113+
114+
void MappedFile::addOffset(const uint64_t off)
115+
{
116+
offset += off;
117+
}
118+

MappedFile.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ class MappedFile
2525
const uint8_t* data() const;
2626
uint64_t size() const;
2727
void setOffset(const uint64_t offset);
28+
void addOffset(const uint64_t offset);
29+
uint64_t getOffset() const { return offset; }
2830

2931
private:
3032
std::wstring filePath;

test/test.sh

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
#!/bin/bash
2+
3+
# Generating reference files
4+
echo "Generating reference files..."
5+
if [ ! -f test/ref_499.bin ]; then
6+
dd if=test/output.bin of=test/ref_499.bin bs=1 skip=500 count=499 status=none
7+
fi
8+
if [ ! -f test/ref_73456.bin ]; then
9+
dd if=test/output.bin of=test/ref_73456.bin bs=1 skip=50000 status=none
10+
fi
11+
if [ ! -f test/ref_10000.txt ]; then
12+
dd if=test/output.txt of=test/ref_10000.txt bs=1 skip=50000 count=10000 status=none
13+
fi
14+
if [ ! -f test/ref_73456.txt ]; then
15+
dd if=test/output.txt of=test/ref_73456.txt bs=1 skip=50000 status=none
16+
fi
17+
18+
# Performing tests with curl
19+
echo "Performing tests with curl..."
20+
21+
# http1.1 tests
22+
# Full file
23+
curl --ipv4 --http1.1 --dump-header test/ref_header1.out http://hauck-thomas.de/output.bin -o test/ref_file1.out
24+
diff -q test/output.bin test/ref_file1.out && echo "Http 1.1 Full file test passed." || echo "Http 1.1 Full file test failed!"
25+
26+
curl --ipv4 --http1.1 --compressed --dump-header test/ref_header2.out http://hauck-thomas.de/output.txt -o test/ref_file2.txt
27+
diff -q test/output.txt test/ref_file2.txt && echo "Http 1.1 Full compressed(br) file test passed." || echo "Http 1.1 Full compressed(br) file test failed!"
28+
29+
curl --ipv4 --http1.1 --compressed --dump-header test/ref_header3.out -H "X-PrefCompAlgo: deflate" http://hauck-thomas.de/output.txt -o test/ref_file3.txt
30+
diff -q test/output.txt test/ref_file3.txt && echo "Http 1.1 Full compressed(deflate) file test passed." || echo "Http 1.1 Full compressed(deflate) file test failed!"
31+
32+
33+
# Partial file
34+
curl --ipv4 --http1.1 --range 500-999 --dump-header test/ref_header4.out https://hauck-thomas.de/output.bin -o test/ref_file4.out
35+
diff -q test/ref_499.bin test/ref_file4.out && echo "Http 1.1 partial file test passed." || echo "Http 1.1 partial file test failed!"
36+
37+
curl --ipv4 --http1.1 --range 50000-60000 --compressed --dump-header test/ref_header5.out https://hauck-thomas.de/output.txt -o test/ref_file5.txt
38+
diff -q test/ref_10000.txt test/ref_file5.txt && echo "Http 1.1 partial compressed(br) file test passed." || echo "Http 1.1 partial compressed(br) file test failed!"
39+
40+
curl --ipv4 --http1.1 --range 50000-60000 --compressed --dump-header test/ref_header6.out -H "X-PrefCompAlgo: deflate" https://hauck-thomas.de/output.txt -o test/ref_file6.txt
41+
diff -q test/ref_10000.txt test/ref_file6.txt && echo "Http 1.1 partial compressed(deflate) file test passed." || echo "Http 1.1 partial compressed(deflate) file test failed!"
42+
43+
# http2 tests
44+
# Full file
45+
curl --ipv4 --http2 --dump-header test/ref_header7.out https://hauck-thomas.de/output.bin -o test/ref_file7.out
46+
diff -q test/output.bin test/ref_file7.out && echo "Http2 Full file test passed." || echo "Http2 Full file test failed!"
47+
48+
curl --ipv4 --http2 --compressed --dump-header test/ref_header8.out https://hauck-thomas.de/output.txt -o test/ref_file8.txt
49+
diff -q test/output.txt test/ref_file8.txt && echo "Http2 Full compressed(br) file test passed." || echo "Http2 Full compressed(br) file test failed!"
50+
51+
curl --ipv4 --http2 --compressed --dump-header test/ref_header9.out -H "X-PrefCompAlgo: deflate" https://hauck-thomas.de/output.txt -o test/ref_file9.txt
52+
diff -q test/output.txt test/ref_file9.txt && echo "Http2 Full compressed(deflate) file test passed." || echo "Http2 Full compressed(deflate) file test failed!"
53+
54+
# Partial file
55+
curl --ipv4 --http2 --range 500-999 --dump-header test/ref_header10.out https://hauck-thomas.de/output.bin -o test/ref_file10.out
56+
diff -q test/ref_499.bin test/ref_file10.out && echo "Http2 partial file test passed." || echo "Http2 partial file test failed!"
57+
58+
curl --ipv4 --http2 --range 50000- --compressed --dump-header test/ref_header11.out https://hauck-thomas.de/output.txt -o test/ref_file11.txt
59+
diff -q test/ref_73456.txt test/ref_file11.txt && echo "Http2 partial compressed(br) file test passed." || echo "Http2 partial compressed(br) file test failed!"
60+
61+
curl --ipv4 --http2 --range 50000- --compressed --dump-header test/ref_header12.out -H "X-PrefCompAlgo: deflate" https://hauck-thomas.de/output.txt -o test/ref_file12.txt
62+
diff -q test/ref_73456.txt test/ref_file12.txt && echo "Http2 partial compressed(deflate) file test passed." || echo "Http2 partial compressed(deflate) file test failed!"

0 commit comments

Comments
 (0)