Older/MediaServer/Http/HttpBody.cpp
amass 9de3af15eb
All checks were successful
Deploy / PullDocker (push) Successful in 12s
Deploy / Build (push) Successful in 1m51s
add ZLMediaKit code for learning.
2024-09-28 23:55:00 +08:00

397 lines
12 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved.
*
* This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit).
*
* Use of this source code is governed by MIT-like license that can be found in the
* LICENSE file in the root of the source tree. All contributing project authors
* may be found in the AUTHORS file in the root of the source tree.
*/
#include <csignal>
#include <tuple>
#ifndef _WIN32
#include <sys/mman.h>
#endif
#if defined(__linux__) || defined(__linux)
#include <sys/sendfile.h>
#endif
#include "Util/File.h"
#include "Util/logger.h"
#include "Util/onceToken.h"
#include "Util/util.h"
#include "Util/uv_errno.h"
#include "HttpBody.h"
#include "HttpClient.h"
#include "Common/macros.h"
using namespace std;
using namespace toolkit;
namespace mediakit {
HttpStringBody::HttpStringBody(string str) {
_str = std::move(str);
}
int64_t HttpStringBody::remainSize() {
return _str.size() - _offset;
}
Buffer::Ptr HttpStringBody::readData(size_t size) {
size = MIN((size_t)remainSize(), size);
if (!size) {
// 没有剩余字节了 [AUTO-TRANSLATED:7bbaa343]
// No remaining bytes
return nullptr;
}
auto ret = std::make_shared<BufferString>(_str, _offset, size);
_offset += size;
return ret;
}
//////////////////////////////////////////////////////////////////
static mutex s_mtx;
static unordered_map<string /*file_path*/, std::tuple<char */*ptr*/, int64_t /*size*/, weak_ptr<char> /*mmap*/ > > s_shared_mmap;
#if defined(_WIN32)
static void mmap_close(HANDLE _hfile, HANDLE _hmapping, void *_addr) {
if (_addr) {
::UnmapViewOfFile(_addr);
}
if (_hmapping) {
::CloseHandle(_hmapping);
}
if (_hfile != INVALID_HANDLE_VALUE) {
::CloseHandle(_hfile);
}
}
#endif
// 删除mmap记录 [AUTO-TRANSLATED:c956201d]
// Delete mmap record
static void delSharedMmap(const string &file_path, char *ptr) {
lock_guard<mutex> lck(s_mtx);
auto it = s_shared_mmap.find(file_path);
if (it != s_shared_mmap.end() && std::get<0>(it->second) == ptr) {
s_shared_mmap.erase(it);
}
}
static std::shared_ptr<char> getSharedMmap(const string &file_path, int64_t &file_size) {
{
lock_guard<mutex> lck(s_mtx);
auto it = s_shared_mmap.find(file_path);
if (it != s_shared_mmap.end()) {
auto ret = std::get<2>(it->second).lock();
if (ret) {
// 命中mmap缓存 [AUTO-TRANSLATED:95131a66]
// Hit mmap cache
file_size = std::get<1>(it->second);
return ret;
}
}
}
// 打开文件 [AUTO-TRANSLATED:55bfe68a]
// Open file
std::shared_ptr<FILE> fp(fopen(file_path.data(), "rb"), [](FILE *fp) {
if (fp) {
fclose(fp);
}
});
if (!fp) {
// 文件不存在 [AUTO-TRANSLATED:ed160bcf]
// File does not exist
file_size = -1;
return nullptr;
}
#if defined(_WIN32)
auto fd = _fileno(fp.get());
#else
// 获取文件大小 [AUTO-TRANSLATED:82974eea]
// Get file size
file_size = File::fileSize(fp.get());
auto fd = fileno(fp.get());
#endif
if (fd < 0) {
WarnL << "fileno failed:" << get_uv_errmsg(false);
return nullptr;
}
#ifndef _WIN32
auto ptr = (char *)mmap(NULL, file_size, PROT_READ, MAP_SHARED, fd, 0);
if (ptr == MAP_FAILED) {
WarnL << "mmap " << file_path << " failed:" << get_uv_errmsg(false);
return nullptr;
}
std::shared_ptr<char> ret(ptr, [file_size, fp, file_path](char *ptr) {
munmap(ptr, file_size);
delSharedMmap(file_path, ptr);
});
#else
auto hfile = ::CreateFileA(file_path.data(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hfile == INVALID_HANDLE_VALUE) {
WarnL << "CreateFileA() " << file_path << " failed:";
return nullptr;
}
LARGE_INTEGER FileSize;
GetFileSizeEx(hfile, &FileSize); //GetFileSize函数的拓展可用于获取大于4G的文件大小
file_size = FileSize.QuadPart;
auto hmapping = ::CreateFileMapping(hfile, NULL, PAGE_READONLY, 0, 0, NULL);
if (hmapping == NULL) {
mmap_close(hfile, NULL, NULL);
WarnL << "CreateFileMapping() " << file_path << " failed:";
return nullptr;
}
auto addr_ = ::MapViewOfFile(hmapping, FILE_MAP_READ, 0, 0, 0);
if (addr_ == nullptr) {
mmap_close(hfile, hmapping, addr_);
WarnL << "MapViewOfFile() " << file_path << " failed:";
return nullptr;
}
std::shared_ptr<char> ret((char *)(addr_), [hfile, hmapping, file_path](char *addr_) {
mmap_close(hfile, hmapping, addr_);
delSharedMmap(file_path, addr_);
});
#endif
#if 0
if (file_size < 10 * 1024 * 1024 && file_path.rfind(".ts") != string::npos) {
// 如果是小ts文件那么尝试先加载到内存 [AUTO-TRANSLATED:0d96c5cd]
// If it is a small ts file, try to load it into memory first
auto buf = BufferRaw::create();
buf->assign(ret.get(), file_size);
ret.reset(buf->data(), [buf, file_path](char *ptr) {
delSharedMmap(file_path, ptr);
});
}
#endif
{
lock_guard<mutex> lck(s_mtx);
s_shared_mmap[file_path] = std::make_tuple(ret.get(), file_size, ret);
}
return ret;
}
HttpFileBody::HttpFileBody(const string &file_path, bool use_mmap) {
if (use_mmap ) {
_map_addr = getSharedMmap(file_path, _read_to);
}
if (!_map_addr && _read_to != -1) {
// mmap失败(且不是由于文件不存在导致的)或未执行mmap时才进入fread逻辑分支 [AUTO-TRANSLATED:8c7efed5]
// Only enter the fread logic branch when mmap fails (and is not due to file not existing) or when mmap is not executed
_fp.reset(fopen(file_path.data(), "rb"), [](FILE *fp) {
if (fp) {
fclose(fp);
}
});
if (!_fp) {
// 文件不存在 [AUTO-TRANSLATED:ed160bcf]
// File does not exist
_read_to = -1;
return;
}
if (!_read_to) {
// _read_to等于0时说明还未尝试获取文件大小 [AUTO-TRANSLATED:4e3ef6ca]
// When _read_to equals 0, it means that the file size has not been attempted to be obtained yet
// 加上该判断逻辑在mmap失败时可以省去一次该操作 [AUTO-TRANSLATED:b9b585de]
// Adding this judgment logic can save one operation when mmap fails
_read_to = File::fileSize(_fp.get());
}
}
}
void HttpFileBody::setRange(uint64_t offset, uint64_t max_size) {
CHECK((int64_t)offset <= _read_to && (int64_t)(max_size + offset) <= _read_to);
_read_to = max_size + offset;
_file_offset = offset;
if (_fp && !_map_addr) {
fseek64(_fp.get(), _file_offset, SEEK_SET);
}
}
int HttpFileBody::sendFile(int fd) {
#if defined(__linux__) || defined(__linux)
if (!_fp) {
return -1;
}
static onceToken s_token([]() { signal(SIGPIPE, SIG_IGN); });
off_t off = _file_offset;
return sendfile(fd, fileno(_fp.get()), &off, _read_to - _file_offset);
#else
return -1;
#endif
}
class BufferMmap : public Buffer {
public:
using Ptr = std::shared_ptr<BufferMmap>;
BufferMmap(const std::shared_ptr<char> &map_addr, size_t offset, size_t size) {
_map_addr = map_addr;
_data = map_addr.get() + offset;
_size = size;
}
// 返回数据长度 [AUTO-TRANSLATED:955f731c]
// Return data length
char *data() const override { return _data; }
size_t size() const override { return _size; }
private:
char *_data;
size_t _size;
std::shared_ptr<char> _map_addr;
};
int64_t HttpFileBody::remainSize() {
return _read_to - _file_offset;
}
Buffer::Ptr HttpFileBody::readData(size_t size) {
size = (size_t)(MIN(remainSize(), (int64_t)size));
if (!size) {
// 没有剩余字节了 [AUTO-TRANSLATED:7bbaa343]
// No remaining bytes
return nullptr;
}
if (!_map_addr) {
// fread模式 [AUTO-TRANSLATED:c4dee2a3]
// fread mode
ssize_t iRead;
auto ret = _pool.obtain2();
ret->setCapacity(size + 1);
do {
iRead = fread(ret->data(), 1, size, _fp.get());
} while (-1 == iRead && UV_EINTR == get_uv_error(false));
if (iRead > 0) {
// 读到数据了 [AUTO-TRANSLATED:7e5ada62]
// Data is read
ret->setSize(iRead);
_file_offset += iRead;
return std::move(ret);
}
// 读取文件异常,文件真实长度小于声明长度 [AUTO-TRANSLATED:89d09f9b]
// File reading exception, the actual length of the file is less than the declared length
_file_offset = _read_to;
WarnL << "read file err:" << get_uv_errmsg();
return nullptr;
}
// mmap模式 [AUTO-TRANSLATED:b8d616f1]
// mmap mode
auto ret = std::make_shared<BufferMmap>(_map_addr, _file_offset, size);
_file_offset += size;
return ret;
}
//////////////////////////////////////////////////////////////////
HttpMultiFormBody::HttpMultiFormBody(const HttpArgs &args, const string &filePath, const string &boundary) {
_fileBody = std::make_shared<HttpFileBody>(filePath);
if (_fileBody->remainSize() < 0) {
throw std::invalid_argument(StrPrinter << "open file failed" << filePath << " " << get_uv_errmsg());
}
auto fileName = filePath;
auto pos = filePath.rfind('/');
if (pos != string::npos) {
fileName = filePath.substr(pos + 1);
}
_bodyPrefix = multiFormBodyPrefix(args, boundary, fileName);
_bodySuffix = multiFormBodySuffix(boundary);
_totalSize = _bodyPrefix.size() + _bodySuffix.size() + _fileBody->remainSize();
}
int64_t HttpMultiFormBody::remainSize() {
return _totalSize - _offset;
}
Buffer::Ptr HttpMultiFormBody::readData(size_t size) {
if (_bodyPrefix.size()) {
auto ret = std::make_shared<BufferString>(_bodyPrefix);
_offset += _bodyPrefix.size();
_bodyPrefix.clear();
return ret;
}
if (_fileBody->remainSize()) {
auto ret = _fileBody->readData(size);
if (!ret) {
// 读取文件出现异常,提前中断 [AUTO-TRANSLATED:5b8052d9]
// An exception occurred while reading the file, and the process was interrupted prematurely
_offset = _totalSize;
} else {
_offset += ret->size();
}
return ret;
}
if (_bodySuffix.size()) {
auto ret = std::make_shared<BufferString>(_bodySuffix);
_offset = _totalSize;
_bodySuffix.clear();
return ret;
}
return nullptr;
}
string HttpMultiFormBody::multiFormBodySuffix(const string &boundary) {
return "\r\n--" + boundary + "--";
}
string HttpMultiFormBody::multiFormContentType(const string &boundary) {
return StrPrinter << "multipart/form-data; boundary=" << boundary;
}
string HttpMultiFormBody::multiFormBodyPrefix(const HttpArgs &args, const string &boundary, const string &fileName) {
string MPboundary = string("--") + boundary;
_StrPrinter body;
for (auto &pr : args) {
body << MPboundary << "\r\n";
body << "Content-Disposition: form-data; name=\"" << pr.first << "\"\r\n\r\n";
body << pr.second << "\r\n";
}
body << MPboundary << "\r\n";
body << "Content-Disposition: form-data; name=\""
<< "file"
<< "\"; filename=\"" << fileName << "\"\r\n";
body << "Content-Type: application/octet-stream\r\n\r\n";
return std::move(body);
}
HttpBufferBody::HttpBufferBody(Buffer::Ptr buffer) {
_buffer = std::move(buffer);
}
int64_t HttpBufferBody::remainSize() {
return _buffer ? _buffer->size() : 0;
}
Buffer::Ptr HttpBufferBody::readData(size_t size) {
return Buffer::Ptr(std::move(_buffer));
}
} // namespace mediakit