diff --git a/CMakeLists.txt b/CMakeLists.txt index d9645d47..edeae6a9 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -129,6 +129,7 @@ set(DROGON_SOURCES lib/src/HttpAppFrameworkImpl.cc lib/src/HttpClientImpl.cc lib/src/HttpControllersRouter.cc + lib/src/HttpFileImpl.cc lib/src/HttpFileUploadRequest.cc lib/src/HttpRequestImpl.cc lib/src/HttpRequestParser.cc diff --git a/examples/simple_example/api_Attachment.cc b/examples/simple_example/api_Attachment.cc index b979b1ad..3aa6c292 100644 --- a/examples/simple_example/api_Attachment.cc +++ b/examples/simple_example/api_Attachment.cc @@ -17,7 +17,7 @@ void Attachment::upload(const HttpRequestPtr &req, if (fileUpload.parse(req) == 0) { // LOG_DEBUG << "upload good!"; - auto files = fileUpload.getFiles(); + auto &files = fileUpload.getFiles(); // LOG_DEBUG << "file num=" << files.size(); for (auto const &file : files) { diff --git a/lib/inc/drogon/MultiPart.h b/lib/inc/drogon/MultiPart.h index 6979158a..460d6b3d 100644 --- a/lib/inc/drogon/MultiPart.h +++ b/lib/inc/drogon/MultiPart.h @@ -14,36 +14,32 @@ #pragma once +#include #include #include #include +#include namespace drogon { +class HttpFileImpl; +/** + * @brief This class represents a uploaded file by a HTTP request. + * + */ class HttpFile { public: + HttpFile(std::shared_ptr &&implPtr); /// Return the file name; - const std::string &getFileName() const - { - return fileName_; - }; + const std::string &getFileName() const; - /// Set the file name - void setFileName(const std::string &filename) - { - fileName_ = filename; - }; + /// Set the file name, usually called by the MultiPartParser parser. + void setFileName(const std::string &filename); - /// Set the contents of the file, usually called by the FileUpload parser. - void setFile(const std::string &file) - { - fileContent_ = file; - }; - void setFile(std::string &&file) - { - fileContent_ = std::move(file); - } + /// Set the contents of the file, usually called by the MultiPartParser + /// parser. + void setFile(const char *data, size_t length); /// Save the file to the file system. /** @@ -70,38 +66,16 @@ class HttpFile int saveAs(const std::string &filename) const; /// Return the file length. - int64_t fileLength() const noexcept - { - return fileContent_.length(); - }; - /// Return the file content. - char *fileData() noexcept - { -#if __cplusplus >= 201703L || (defined _MSC_VER && _MSC_VER > 1900) - return fileContent_.data(); -#else - return (char *)(fileContent_.data()); -#endif - } - const char *fileData() const noexcept - { - return fileContent_.data(); - } - std::string &fileContent() noexcept - { - return fileContent_; - } - const std::string &fileContent() const noexcept - { - return fileContent_; - } + size_t fileLength() const noexcept; + + /// Return the file data. + const char *fileData() const noexcept; + /// Return the md5 string of the file std::string getMd5() const; - protected: - int saveTo(const std::string &pathAndFilename) const; - std::string fileName_; - std::string fileContent_; + private: + std::shared_ptr implPtr_; }; /// A parser class which help the user to get the files and the parameters in @@ -113,7 +87,7 @@ class MultiPartParser ~MultiPartParser(){}; /// Get files, This method should be called after calling the parse() /// method. - const std::vector &getFiles(); + const std::vector &getFiles() const; /// Get parameters, This method should be called after calling the parse () /// method. @@ -125,8 +99,11 @@ class MultiPartParser protected: std::vector files_; std::map parameters_; - int parse(const HttpRequestPtr &req, const std::string &boundary); + int parse(const HttpRequestPtr &req, + const char *boundaryData, + size_t boundaryLen); int parseEntity(const char *begin, const char *end); + HttpRequestPtr requestPtr_; }; /// In order to be compatible with old interfaces diff --git a/lib/inc/drogon/utils/Utilities.h b/lib/inc/drogon/utils/Utilities.h index b7075636..26b2f32a 100644 --- a/lib/inc/drogon/utils/Utilities.h +++ b/lib/inc/drogon/utils/Utilities.h @@ -98,7 +98,11 @@ std::string urlEncode(const std::string &); std::string urlEncodeComponent(const std::string &); /// Get the MD5 digest of a string. -std::string getMd5(const std::string &originalString); +std::string getMd5(const char *data, const size_t dataLen); +inline std::string getMd5(const std::string &originalString) +{ + return getMd5(originalString.data(), originalString.length()); +} /// Commpress or decompress data using gzip lib. /** diff --git a/lib/src/HttpFileImpl.cc b/lib/src/HttpFileImpl.cc new file mode 100644 index 00000000..be6b0e26 --- /dev/null +++ b/lib/src/HttpFileImpl.cc @@ -0,0 +1,161 @@ +/** + * + * HttpFileImpl.cc + * An Tao + * + * Copyright 2018, An Tao. All rights reserved. + * https://github.com/an-tao/drogon + * Use of this source code is governed by a MIT license + * that can be found in the License file. + * + * Drogon + * + */ + +#include "HttpFileImpl.h" +#include "HttpAppFrameworkImpl.h" +#include +#include +#include + +using namespace drogon; + +int HttpFileImpl::save(const std::string &path) const +{ + assert(!path.empty()); + if (fileName_ == "") + return -1; + std::string filename; + auto tmpPath = path; + if (path[0] == '/' || + (path.length() >= 2 && path[0] == '.' && path[1] == '/') || + (path.length() >= 3 && path[0] == '.' && path[1] == '.' && + path[2] == '/') || + path == "." || path == "..") + { + // Absolute or relative path + } + else + { + auto &uploadPath = HttpAppFrameworkImpl::instance().getUploadPath(); + if (uploadPath[uploadPath.length() - 1] == '/') + tmpPath = uploadPath + path; + else + tmpPath = uploadPath + "/" + path; + } + + if (utils::createPath(tmpPath) < 0) + return -1; + + if (tmpPath[tmpPath.length() - 1] != '/') + { + filename = tmpPath + "/"; + filename.append(fileName_.data(), fileName_.length()); + } + else + filename = tmpPath.append(fileName_.data(), fileName_.length()); + + return saveTo(filename); +} +int HttpFileImpl::save() const +{ + return save(HttpAppFrameworkImpl::instance().getUploadPath()); +} +int HttpFileImpl::saveAs(const std::string &filename) const +{ + assert(!filename.empty()); + auto pathAndFileName = filename; + if (filename[0] == '/' || + (filename.length() >= 2 && filename[0] == '.' && filename[1] == '/') || + (filename.length() >= 3 && filename[0] == '.' && filename[1] == '.' && + filename[2] == '/')) + { + // Absolute or relative path + } + else + { + auto &uploadPath = HttpAppFrameworkImpl::instance().getUploadPath(); + if (uploadPath[uploadPath.length() - 1] == '/') + pathAndFileName = uploadPath + filename; + else + pathAndFileName = uploadPath + "/" + filename; + } + auto pathPos = pathAndFileName.rfind('/'); + if (pathPos != std::string::npos) + { + std::string path = pathAndFileName.substr(0, pathPos); + if (utils::createPath(path) < 0) + return -1; + } + return saveTo(pathAndFileName); +} +int HttpFileImpl::saveTo(const std::string &pathAndFilename) const +{ + LOG_TRACE << "save uploaded file:" << pathAndFilename; + std::ofstream file(pathAndFilename); + if (file.is_open()) + { + file.write(fileContent_.data(), fileContent_.size()); + file.close(); + return 0; + } + else + { + LOG_ERROR << "save failed!"; + return -1; + } +} +std::string HttpFileImpl::getMd5() const +{ + return utils::getMd5(fileContent_.data(), fileContent_.size()); +} + +const std::string &HttpFile::getFileName() const +{ + return implPtr_->getFileName(); +} + +void HttpFile::setFileName(const std::string &filename) +{ + implPtr_->setFileName(filename); +} + +void HttpFile::setFile(const char *data, size_t length) +{ + implPtr_->setFile(data, length); +} + +int HttpFile::save() const +{ + return implPtr_->save(); +} + +int HttpFile::save(const std::string &path) const +{ + return implPtr_->save(path); +} + +int HttpFile::saveAs(const std::string &filename) const +{ + return implPtr_->saveAs(filename); +} + +size_t HttpFile::fileLength() const noexcept +{ + return implPtr_->fileLength(); +} + +const char *HttpFile::fileData() const noexcept +{ + return implPtr_->fileData(); +} + +std::string HttpFile::getMd5() const +{ + return implPtr_->getMd5(); +} + +HttpFile::HttpFile(std::shared_ptr &&implPtr) + : implPtr_(std::move(implPtr)) +{ +} diff --git a/lib/src/HttpFileImpl.h b/lib/src/HttpFileImpl.h new file mode 100644 index 00000000..8e0cb36e --- /dev/null +++ b/lib/src/HttpFileImpl.h @@ -0,0 +1,100 @@ +/** + * + * HttpFileImpl.h + * An Tao + * + * Copyright 2018, An Tao. All rights reserved. + * https://github.com/an-tao/drogon + * Use of this source code is governed by a MIT license + * that can be found in the License file. + * + * Drogon + * + */ + +#pragma once +#include +#include + +#include +#include +#include +#include +namespace drogon +{ +class HttpFileImpl +{ + public: + /// Return the file name; + const std::string &getFileName() const + { + return fileName_; + }; + + /// Set the file name, usually called by the MultiPartParser parser. + void setFileName(const std::string &filename) + { + fileName_ = filename; + }; + + /// Set the contents of the file, usually called by the MultiPartParser + /// parser. + void setFile(const char *data, size_t length) + { + fileContent_ = string_view{data, length}; + }; + + /// Save the file to the file system. + /** + * The folder saving the file is app().getUploadPath(). + * The full path is app().getUploadPath()+"/"+this->getFileName() + */ + int save() const; + + /// Save the file to @param path + /** + * @param path if the parameter is prefixed with "/", "./" or "../", or is + * "." or "..", the full path is path+"/"+this->getFileName(), + * otherwise the file is saved as + * app().getUploadPath()+"/"+path+"/"+this->getFileName() + */ + int save(const std::string &path) const; + + /// Save the file to file system with a new name + /** + * @param filename if the parameter isn't prefixed with "/", "./" or "../", + * the full path is app().getUploadPath()+"/"+filename, otherwise the file + * is saved as the filename + */ + int saveAs(const std::string &filename) const; + + /// Return the file length. + size_t fileLength() const noexcept + { + return fileContent_.length(); + }; + + const char *fileData() const noexcept + { + return fileContent_.data(); + } + + const string_view &fileContent() const noexcept + { + return fileContent_; + } + + /// Return the md5 string of the file + std::string getMd5() const; + int saveTo(const std::string &pathAndFilename) const; + void setRequest(const HttpRequestPtr &req) + { + requestPtr_ = req; + } + + private: + std::string fileName_; + string_view fileContent_; + HttpRequestPtr requestPtr_; +}; +} // namespace drogon \ No newline at end of file diff --git a/lib/src/MultiPart.cc b/lib/src/MultiPart.cc index ff90e1dc..9d733570 100644 --- a/lib/src/MultiPart.cc +++ b/lib/src/MultiPart.cc @@ -15,6 +15,7 @@ #include "HttpRequestImpl.h" #include "HttpUtils.h" #include "HttpAppFrameworkImpl.h" +#include "HttpFileImpl.h" #include #include #include @@ -29,7 +30,7 @@ using namespace drogon; -const std::vector &MultiPartParser::getFiles() +const std::vector &MultiPartParser::getFiles() const { return files_; } @@ -60,9 +61,9 @@ int MultiPartParser::parse(const HttpRequestPtr &req) pos = contentType.find("boundary="); if (pos == std::string::npos) return -1; - std::string boundary = contentType.substr(pos + 9); - - return parse(req, boundary); + return parse(req, + contentType.data() + (pos + 9), + contentType.size() - (pos + 9)); } int MultiPartParser::parseEntity(const char *begin, const char *end) @@ -95,20 +96,26 @@ int MultiPartParser::parseEntity(const char *begin, const char *end) auto pos1 = std::search(pos, end, quotationMark, quotationMark + 1); if (pos1 == end) return -1; - HttpFile file; - file.setFileName(std::string(pos, pos1)); + auto filePtr = std::make_shared(); + filePtr->setRequest(requestPtr_); + filePtr->setFileName(std::string(pos, pos1)); pos1 = std::search(pos1, end, CRLF, CRLF + 4); if (pos1 == end) return -1; - file.setFile(std::string(pos1 + 4, end)); - files_.push_back(std::move(file)); + filePtr->setFile(pos1 + 4, static_cast(end - pos1 - 4)); + files_.push_back(std::move(HttpFile(std::move(filePtr)))); return 0; } } int MultiPartParser::parse(const HttpRequestPtr &req, - const std::string &boundary) + const char *boundaryData, + size_t boundaryLen) { + string_view boundary{boundaryData, boundaryLen}; + if (boundary.size() > 2 && boundary[0] == '\"') + boundary = boundary.substr(1, boundary.size() - 2); + requestPtr_ = req; string_view::size_type pos1, pos2; pos1 = 0; auto content = static_cast(req.get())->bodyView(); @@ -134,92 +141,3 @@ int MultiPartParser::parse(const HttpRequestPtr &req, } return 0; } - -int HttpFile::save(const std::string &path) const -{ - assert(!path.empty()); - if (fileName_ == "") - return -1; - std::string filename; - auto tmpPath = path; - if (path[0] == '/' || - (path.length() >= 2 && path[0] == '.' && path[1] == '/') || - (path.length() >= 3 && path[0] == '.' && path[1] == '.' && - path[2] == '/') || - path == "." || path == "..") - { - // Absolute or relative path - } - else - { - auto &uploadPath = HttpAppFrameworkImpl::instance().getUploadPath(); - if (uploadPath[uploadPath.length() - 1] == '/') - tmpPath = uploadPath + path; - else - tmpPath = uploadPath + "/" + path; - } - - if (utils::createPath(tmpPath) < 0) - return -1; - - if (tmpPath[tmpPath.length() - 1] != '/') - { - filename = tmpPath + "/" + fileName_; - } - else - filename = tmpPath + fileName_; - - return saveTo(filename); -} -int HttpFile::save() const -{ - return save(HttpAppFrameworkImpl::instance().getUploadPath()); -} -int HttpFile::saveAs(const std::string &filename) const -{ - assert(!filename.empty()); - auto pathAndFileName = filename; - if (filename[0] == '/' || - (filename.length() >= 2 && filename[0] == '.' && filename[1] == '/') || - (filename.length() >= 3 && filename[0] == '.' && filename[1] == '.' && - filename[2] == '/')) - { - // Absolute or relative path - } - else - { - auto &uploadPath = HttpAppFrameworkImpl::instance().getUploadPath(); - if (uploadPath[uploadPath.length() - 1] == '/') - pathAndFileName = uploadPath + filename; - else - pathAndFileName = uploadPath + "/" + filename; - } - auto pathPos = pathAndFileName.rfind('/'); - if (pathPos != std::string::npos) - { - std::string path = pathAndFileName.substr(0, pathPos); - if (utils::createPath(path) < 0) - return -1; - } - return saveTo(pathAndFileName); -} -int HttpFile::saveTo(const std::string &pathAndFilename) const -{ - LOG_TRACE << "save uploaded file:" << pathAndFilename; - std::ofstream file(pathAndFilename); - if (file.is_open()) - { - file << fileContent_; - file.close(); - return 0; - } - else - { - LOG_ERROR << "save failed!"; - return -1; - } -} -std::string HttpFile::getMd5() const -{ - return utils::getMd5(fileContent_); -} diff --git a/lib/src/Utilities.cc b/lib/src/Utilities.cc index e5819ce1..dd72b104 100644 --- a/lib/src/Utilities.cc +++ b/lib/src/Utilities.cc @@ -1144,17 +1144,17 @@ std::string brotliDecompress(const char *data, const size_t ndata) } #endif -std::string getMd5(const std::string &originalString) +std::string getMd5(const char *data, const size_t dataLen) { #ifdef OpenSSL_FOUND MD5_CTX c; unsigned char md5[16] = {0}; MD5_Init(&c); - MD5_Update(&c, originalString.c_str(), originalString.size()); + MD5_Update(&c, data, dataLen); MD5_Final(md5, &c); return utils::binaryStringToHex(md5, 16); #else - return Md5Encode::encode(originalString); + return Md5Encode::encode(data, dataLen); #endif } diff --git a/lib/src/ssl_funcs/Md5.cc b/lib/src/ssl_funcs/Md5.cc index 2330e7ff..411d7ef4 100644 --- a/lib/src/ssl_funcs/Md5.cc +++ b/lib/src/ssl_funcs/Md5.cc @@ -320,7 +320,7 @@ std::string Md5Encode::getHexStr(uint32_t numStr) // function: Encode // @param srcInfo: the string to be encoded. // return : the string after encoding -std::string Md5Encode::encode(const std::string &srcInfo) +std::string Md5Encode::encode(const char *data, const size_t dataLen) { ParamDynamic param; param.ua_ = kA; @@ -334,7 +334,7 @@ std::string Md5Encode::encode(const std::string &srcInfo) std::string result; char *outDataPtr = nullptr; - int totalByte = fillData(srcInfo.c_str(), srcInfo.length(), &outDataPtr); + int totalByte = fillData(data, dataLen, &outDataPtr); for (int i = 0; i < totalByte / (BIT_OF_GROUP / BIT_OF_BYTE); ++i) { diff --git a/lib/src/ssl_funcs/Md5.h b/lib/src/ssl_funcs/Md5.h index 07bee842..e65117e4 100644 --- a/lib/src/ssl_funcs/Md5.h +++ b/lib/src/ssl_funcs/Md5.h @@ -58,7 +58,7 @@ class Md5Encode }; public: - static std::string encode(const std::string &srcInfo); + static std::string encode(const char *data, const size_t dataLen); protected: static uint32_t cycleMoveLeft(uint32_t srcNum, int bitNumToMove);