int CWebServer::CreateMemoryDownloadResponse(IHTTPRequestHandler *handler, struct MHD_Response *&response) { if (handler == NULL) return MHD_NO; const HTTPRequest &request = handler->GetRequest(); const HTTPResponseDetails &responseDetails = handler->GetResponseDetails(); HttpResponseRanges responseRanges = handler->GetResponseData(); // check if the response is completely empty if (responseRanges.empty()) return CreateMemoryDownloadResponse(request.connection, NULL, 0, false, false, response); // check if the response contains more ranges than the request asked for if ((request.ranges.IsEmpty() && responseRanges.size() > 1) || (!request.ranges.IsEmpty() && responseRanges.size() > request.ranges.Size())) { CLog::Log(LOGWARNING, "CWebServer: response contains more ranges (%d) than the request asked for (%d)", (int)responseRanges.size(), (int)request.ranges.Size()); return SendErrorResponse(request.connection, MHD_HTTP_INTERNAL_SERVER_ERROR, request.method); } // if the request asked for no or only one range we can simply use MHDs memory download handler // we MUST NOT send a multipart response if (request.ranges.Size() <= 1) { CHttpResponseRange responseRange = responseRanges.front(); // check if the range is valid if (!responseRange.IsValid()) { CLog::Log(LOGWARNING, "CWebServer: invalid response data with range start at %" PRId64 " and end at %" PRId64, responseRange.GetFirstPosition(), responseRange.GetLastPosition()); return SendErrorResponse(request.connection, MHD_HTTP_INTERNAL_SERVER_ERROR, request.method); } const void* responseData = responseRange.GetData(); size_t responseDataLength = static_cast<size_t>(responseRange.GetLength()); switch (responseDetails.type) { case HTTPMemoryDownloadNoFreeNoCopy: return CreateMemoryDownloadResponse(request.connection, responseData, responseDataLength, false, false, response); case HTTPMemoryDownloadNoFreeCopy: return CreateMemoryDownloadResponse(request.connection, responseData, responseDataLength, false, true, response); case HTTPMemoryDownloadFreeNoCopy: return CreateMemoryDownloadResponse(request.connection, responseData, responseDataLength, true, false, response); case HTTPMemoryDownloadFreeCopy: return CreateMemoryDownloadResponse(request.connection, responseData, responseDataLength, true, true, response); default: return SendErrorResponse(request.connection, MHD_HTTP_INTERNAL_SERVER_ERROR, request.method); } } return CreateRangedMemoryDownloadResponse(handler, response); }
TEST(TestHttpResponseRange, SetData) { const uint64_t validFirstPosition = 1; const uint64_t validLastPosition = 2; const uint64_t validLength = validLastPosition - validFirstPosition + 1; const char* validData = "test"; const void* invalidData = DefaultData; const size_t validDataLength = strlen(validData); const size_t invalidDataLength = 1; CHttpResponseRange range; EXPECT_EQ(DefaultData, range.GetData()); EXPECT_FALSE(range.IsValid()); range.SetData(invalidData); EXPECT_EQ(invalidData, range.GetData()); EXPECT_FALSE(range.IsValid()); range.SetData(validData); EXPECT_EQ(validData, range.GetData()); EXPECT_FALSE(range.IsValid()); range.SetData(invalidData, 0); EXPECT_EQ(validData, range.GetData()); EXPECT_FALSE(range.IsValid()); range.SetData(invalidData, invalidDataLength); EXPECT_EQ(invalidData, range.GetData()); EXPECT_FALSE(range.IsValid()); range.SetData(validData, validDataLength); EXPECT_EQ(validData, range.GetData()); EXPECT_EQ(0U, range.GetFirstPosition()); EXPECT_EQ(validDataLength - 1, range.GetLastPosition()); EXPECT_EQ(validDataLength, range.GetLength()); EXPECT_TRUE(range.IsValid()); range.SetData(invalidData, 0, 0); EXPECT_EQ(invalidData, range.GetData()); EXPECT_FALSE(range.IsValid()); range.SetData(validData, validFirstPosition, validLastPosition); EXPECT_EQ(validData, range.GetData()); EXPECT_EQ(validFirstPosition, range.GetFirstPosition()); EXPECT_EQ(validLastPosition, range.GetLastPosition()); EXPECT_EQ(validLength, range.GetLength()); EXPECT_TRUE(range.IsValid()); }