changeset 3357:c0aa5f1cf2f5

new class: FileBuffer
author Sebastien Jodogne <s.jodogne@gmail.com>
date Thu, 02 May 2019 09:22:36 +0200
parents f744730c294b
children 849c651c1381
files Core/FileBuffer.cpp Core/FileBuffer.h Core/HttpServer/HttpServer.cpp Resources/CMake/OrthancFrameworkConfiguration.cmake UnitTestsSources/UnitTestsMain.cpp
diffstat 5 files changed, 213 insertions(+), 12 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Core/FileBuffer.cpp	Thu May 02 09:22:36 2019 +0200
@@ -0,0 +1,122 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * In addition, as a special exception, the copyright holders of this
+ * program give permission to link the code of its release with the
+ * OpenSSL project's "OpenSSL" library (or with modified versions of it
+ * that use the same license as the "OpenSSL" library), and distribute
+ * the linked executables. You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s) with this exception, you may extend this exception to
+ * your version of the file(s), but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source files
+ * in the program, then also delete it here.
+ * 
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "PrecompiledHeaders.h"
+#include "FileBuffer.h"
+
+#include "TemporaryFile.h"
+#include "OrthancException.h"
+
+#include <boost/filesystem/fstream.hpp>
+
+
+namespace Orthanc
+{
+  class FileBuffer::PImpl
+  {
+  private:
+    TemporaryFile                file_;
+    boost::filesystem::ofstream  stream_;
+    bool                         isWriting_;
+
+  public:
+    PImpl() :
+      isWriting_(true)
+    {
+      stream_.open(file_.GetPath(), std::ofstream::out | std::ofstream::binary);
+      if (!stream_.good())
+      {
+        throw OrthancException(ErrorCode_CannotWriteFile);
+      }
+    }
+
+    ~PImpl()
+    {
+      if (isWriting_)
+      {
+        stream_.close();
+      }
+    }
+
+    void Append(const char* buffer,
+                size_t size)
+    {
+      if (!isWriting_)
+      {
+        throw OrthancException(ErrorCode_BadSequenceOfCalls);
+      }
+
+      if (size > 0)
+      {
+        stream_.write(buffer, size);
+        if (!stream_.good())
+        {
+          stream_.close();
+          throw OrthancException(ErrorCode_FileStorageCannotWrite);
+        }
+      }
+    }
+
+    void Read(std::string& target)
+    {
+      if (isWriting_)
+      {
+        stream_.close();
+        isWriting_ = false;
+      }
+
+      file_.Read(target);
+    }
+  };
+
+    
+  FileBuffer::FileBuffer() :
+    pimpl_(new PImpl)
+  {
+  }
+
+
+  void FileBuffer::Append(const char* buffer,
+                          size_t size)
+  {
+    assert(pimpl_.get() != NULL);
+    pimpl_->Append(buffer, size);
+  }
+
+
+  void FileBuffer::Read(std::string& target)
+  {
+    assert(pimpl_.get() != NULL);
+    pimpl_->Read(target);
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Core/FileBuffer.h	Thu May 02 09:22:36 2019 +0200
@@ -0,0 +1,64 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * In addition, as a special exception, the copyright holders of this
+ * program give permission to link the code of its release with the
+ * OpenSSL project's "OpenSSL" library (or with modified versions of it
+ * that use the same license as the "OpenSSL" library), and distribute
+ * the linked executables. You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s) with this exception, you may extend this exception to
+ * your version of the file(s), but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source files
+ * in the program, then also delete it here.
+ * 
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#if !defined(ORTHANC_SANDBOXED)
+#  error The macro ORTHANC_SANDBOXED must be defined
+#endif
+
+#if ORTHANC_SANDBOXED == 1
+#  error The namespace SystemToolbox cannot be used in sandboxed environments
+#endif
+
+#include <boost/noncopyable.hpp>
+#include <boost/shared_ptr.hpp>
+
+
+namespace Orthanc
+{
+  class FileBuffer : public boost::noncopyable
+  {
+  private:
+    class PImpl;
+    boost::shared_ptr<PImpl> pimpl_;
+
+  public:
+    FileBuffer();
+
+    void Append(const char* buffer,
+                size_t size);
+
+    void Read(std::string& target);
+  };
+}
--- a/Core/HttpServer/HttpServer.cpp	Mon Apr 29 17:24:30 2019 +0200
+++ b/Core/HttpServer/HttpServer.cpp	Thu May 02 09:22:36 2019 +0200
@@ -36,9 +36,11 @@
 #include "../PrecompiledHeaders.h"
 #include "HttpServer.h"
 
+#include "../ChunkedBuffer.h"
+#include "../FileBuffer.h"
 #include "../Logging.h"
-#include "../ChunkedBuffer.h"
 #include "../OrthancException.h"
+#include "../TemporaryFile.h"
 #include "HttpToolbox.h"
 
 #if ORTHANC_ENABLE_MONGOOSE == 1
@@ -308,17 +310,12 @@
     IHttpHandler::Arguments::const_iterator cs = headers.find("content-length");
     if (cs == headers.end())
     {
-      // TODO - Avoid storing this entirely in RAM, use temporary
-      // files instead. The amount of RAM needed to receive one body
-      // of "N" bytes is currently "2*N" bytes (one copy in "buffer",
-      // one copy in "postData"). With a
-      // "ChunkedBufferInTemporaryFiles", one would need "N" bytes (in
-      // "postData" only).
-       
+      // Store all the individual chunks within a temporary file, then
+      // read it back into the memory buffer "postData"
+      FileBuffer buffer;
+
       std::string tmp(1024 * 1024, 0);
       
-      ChunkedBuffer buffer;
-
       for (;;)
       {
         int r = mg_read(connection, &tmp[0], tmp.size());
@@ -332,16 +329,17 @@
         }
         else
         {
-          buffer.AddChunk(tmp.c_str(), r);
+          buffer.Append(tmp.c_str(), r);
         }
       }
 
-      buffer.Flatten(postData);
+      buffer.Read(postData);
 
       return PostDataStatus_Success;
     }
     else
     {
+      // "Content-Length" is available
       int length;      
       try
       {
--- a/Resources/CMake/OrthancFrameworkConfiguration.cmake	Mon Apr 29 17:24:30 2019 +0200
+++ b/Resources/CMake/OrthancFrameworkConfiguration.cmake	Thu May 02 09:22:36 2019 +0200
@@ -541,6 +541,7 @@
 
   list(APPEND ORTHANC_CORE_SOURCES_INTERNAL
     ${ORTHANC_ROOT}/Core/Cache/SharedArchive.cpp
+    ${ORTHANC_ROOT}/Core/FileBuffer.cpp
     ${ORTHANC_ROOT}/Core/FileStorage/FilesystemStorage.cpp
     ${ORTHANC_ROOT}/Core/MetricsRegistry.cpp
     ${ORTHANC_ROOT}/Core/MultiThreading/RunnableWorkersPool.cpp
--- a/UnitTestsSources/UnitTestsMain.cpp	Mon Apr 29 17:24:30 2019 +0200
+++ b/UnitTestsSources/UnitTestsMain.cpp	Thu May 02 09:22:36 2019 +0200
@@ -39,6 +39,7 @@
 #include <ctype.h>
 
 #include "../Core/DicomFormat/DicomTag.h"
+#include "../Core/FileBuffer.h"
 #include "../Core/HttpServer/HttpToolbox.h"
 #include "../Core/Logging.h"
 #include "../Core/MetricsRegistry.h"
@@ -679,6 +680,21 @@
 }
 
 
+TEST(Toolbox, FileBuffer)
+{
+  FileBuffer f;
+  f.Append("a", 1);
+  f.Append("", 0);
+  f.Append("bc", 2);
+
+  std::string s;
+  f.Read(s);
+  ASSERT_EQ("abc", s);
+
+  ASSERT_THROW(f.Append("d", 1), OrthancException);  // File is closed
+}
+
+
 TEST(Toolbox, Wildcard)
 {
   ASSERT_EQ("abcd", Toolbox::WildcardToRegularExpression("abcd"));