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); }
int CWebServer::CreateRangedMemoryDownloadResponse(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(); // if there's no or only one range this is not the right place if (responseRanges.size() <= 1) return CreateMemoryDownloadResponse(handler, response); // extract all the valid ranges and calculate their total length uint64_t firstRangePosition = 0; HttpResponseRanges ranges; for (HttpResponseRanges::const_iterator range = responseRanges.begin(); range != responseRanges.end(); ++range) { // ignore invalid ranges if (!range->IsValid()) continue; // determine the first range position if (ranges.empty()) firstRangePosition = range->GetFirstPosition(); ranges.push_back(*range); } if (ranges.empty()) return CreateMemoryDownloadResponse(request.connection, NULL, 0, false, false, response); // determine the last range position uint64_t lastRangePosition = ranges.back().GetLastPosition(); // adjust the HTTP status of the response handler->SetResponseStatus(MHD_HTTP_PARTIAL_CONTENT); // add Content-Range header handler->AddResponseHeader(MHD_HTTP_HEADER_CONTENT_RANGE, HttpRangeUtils::GenerateContentRangeHeaderValue(firstRangePosition, lastRangePosition, responseDetails.totalLength)); // generate a multipart boundary std::string multipartBoundary = HttpRangeUtils::GenerateMultipartBoundary(); // and the content-type std::string contentType = HttpRangeUtils::GenerateMultipartBoundaryContentType(multipartBoundary); // add Content-Type header handler->AddResponseHeader(MHD_HTTP_HEADER_CONTENT_TYPE, contentType); // generate the multipart boundary with the Content-Type header field std::string multipartBoundaryWithHeader = HttpRangeUtils::GenerateMultipartBoundaryWithHeader(multipartBoundary, contentType); std::string result; // add all the ranges to the result for (HttpResponseRanges::const_iterator range = ranges.begin(); range != ranges.end(); ++range) { // add a newline before any new multipart boundary if (range != ranges.begin()) result += HEADER_NEWLINE; // generate and append the multipart boundary with the full header (Content-Type and Content-Length) result += HttpRangeUtils::GenerateMultipartBoundaryWithHeader(multipartBoundaryWithHeader, &*range); // and append the data of the range result.append(static_cast<const char*>(range->GetData()), static_cast<size_t>(range->GetLength())); // check if we need to free the range data if (responseDetails.type == HTTPMemoryDownloadFreeNoCopy || responseDetails.type == HTTPMemoryDownloadFreeCopy) free(const_cast<void*>(range->GetData())); } result += HttpRangeUtils::GenerateMultipartBoundaryEnd(multipartBoundary); // add Content-Length header handler->AddResponseHeader(MHD_HTTP_HEADER_CONTENT_LENGTH, StringUtils::Format("%" PRIu64, static_cast<uint64_t>(result.size()))); // finally create the response return CreateMemoryDownloadResponse(request.connection, result.c_str(), result.size(), false, true, response); }