# HG changeset patch # User Sebastien Jodogne # Date 1439308238 -7200 # Node ID f9b0169eb6bbbba77c09c37b8dae14b6cd3b613d # Parent 4a0c2eedceb6424b16ed69519a33c2c463c6a5e3 testing diff -r 4a0c2eedceb6 -r f9b0169eb6bb CMakeLists.txt --- a/CMakeLists.txt Tue Aug 11 16:09:24 2015 +0200 +++ b/CMakeLists.txt Tue Aug 11 17:50:38 2015 +0200 @@ -109,6 +109,7 @@ Core/HttpServer/MongooseServer.cpp Core/HttpServer/HttpFileSender.cpp Core/HttpServer/FilesystemHttpSender.cpp + Core/HttpServer/HttpStreamTranscoder.cpp Core/Logging.cpp Core/RestApi/RestApiCall.cpp Core/RestApi/RestApiGetCall.cpp @@ -214,6 +215,7 @@ UnitTestsSources/ImageProcessingTests.cpp UnitTestsSources/JpegLosslessTests.cpp UnitTestsSources/PluginsTests.cpp + UnitTestsSources/StreamTests.cpp ) diff -r 4a0c2eedceb6 -r f9b0169eb6bb Core/HttpServer/BufferHttpSender.cpp --- a/Core/HttpServer/BufferHttpSender.cpp Tue Aug 11 16:09:24 2015 +0200 +++ b/Core/HttpServer/BufferHttpSender.cpp Tue Aug 11 17:50:38 2015 +0200 @@ -34,28 +34,51 @@ #include "../OrthancException.h" +#include + namespace Orthanc { + BufferHttpSender::BufferHttpSender() : + position_(0), + chunkSize_(0), + currentChunkSize_(0) + { + } + + bool BufferHttpSender::ReadNextChunk() { - if (done_) + assert(position_ + currentChunkSize_ <= buffer_.size()); + + position_ += currentChunkSize_; + + if (position_ == buffer_.size()) { return false; } else { - done_ = true; + currentChunkSize_ = buffer_.size() - position_; + + if (chunkSize_ != 0 && + currentChunkSize_ > chunkSize_) + { + currentChunkSize_ = chunkSize_; + } + return true; } } + const char* BufferHttpSender::GetChunkContent() { - return buffer_.c_str(); + return buffer_.c_str() + position_; } + size_t BufferHttpSender::GetChunkSize() { - return buffer_.size(); + return currentChunkSize_; } } diff -r 4a0c2eedceb6 -r f9b0169eb6bb Core/HttpServer/BufferHttpSender.h --- a/Core/HttpServer/BufferHttpSender.h Tue Aug 11 16:09:24 2015 +0200 +++ b/Core/HttpServer/BufferHttpSender.h Tue Aug 11 17:50:38 2015 +0200 @@ -38,13 +38,13 @@ class BufferHttpSender : public HttpFileSender { private: - std::string buffer_; - bool done_; + std::string buffer_; + size_t position_; + size_t chunkSize_; + size_t currentChunkSize_; public: - BufferHttpSender() : done_(false) - { - } + BufferHttpSender(); std::string& GetBuffer() { @@ -56,18 +56,18 @@ return buffer_; } + // This is for test purpose. If "chunkSize" is set to "0" (the + // default), the entire buffer is consumed at once. + void SetChunkSize(size_t chunkSize) + { + chunkSize_ = chunkSize; + } + /** * Implementation of the IHttpStreamAnswer interface. **/ - virtual HttpCompression SetupHttpCompression(bool /*gzipAllowed*/, - bool /*deflateAllowed*/) - { - // No compression is supported - return HttpCompression_None; - } - virtual uint64_t GetContentLength() { return buffer_.size(); diff -r 4a0c2eedceb6 -r f9b0169eb6bb Core/HttpServer/FilesystemHttpSender.cpp --- a/Core/HttpServer/FilesystemHttpSender.cpp Tue Aug 11 16:09:24 2015 +0200 +++ b/Core/HttpServer/FilesystemHttpSender.cpp Tue Aug 11 17:50:38 2015 +0200 @@ -32,12 +32,7 @@ #include "../PrecompiledHeaders.h" #include "FilesystemHttpSender.h" -#include "../Toolbox.h" #include "../OrthancException.h" -#include "../Compression/ZlibCompressor.h" - -#include - static const size_t CHUNK_SIZE = 64 * 1024; // Use 64KB chunks @@ -45,9 +40,6 @@ { void FilesystemHttpSender::Initialize(const boost::filesystem::path& path) { - sourceCompression_ = CompressionType_None; - targetCompression_ = HttpCompression_None; - SetContentFilename(path.filename().string()); file_.open(path.string().c_str(), std::ifstream::binary); @@ -62,129 +54,23 @@ } - HttpCompression FilesystemHttpSender::SetupHttpCompression(bool gzipAllowed, - bool deflateAllowed) - { - switch (sourceCompression_) - { - case CompressionType_None: - { - return HttpCompression_None; - } - - case CompressionType_ZlibWithSize: - { - if (size_ == 0) - { - return HttpCompression_None; - } - - if (size_ < sizeof(uint64_t)) - { - throw OrthancException(ErrorCode_CorruptedFile); - } - - if (deflateAllowed) - { - file_.seekg(sizeof(uint64_t), file_.beg); - size_ -= sizeof(uint64_t); - return HttpCompression_Deflate; - } - else - { - uncompressed_.reset(new BufferHttpSender); - - // TODO Stream-based uncompression - assert(size_ != 0); - std::string compressed; - compressed.resize(size_); - - file_.read(&compressed[0], size_); - if ((file_.flags() & std::istream::failbit) || - !(file_.flags() & std::istream::eofbit) || - file_.gcount() < 0 || - static_cast(file_.gcount()) != size_) - { - throw OrthancException(ErrorCode_CorruptedFile); - } - - ZlibCompressor compressor; - IBufferCompressor::Uncompress(uncompressed_->GetBuffer(), compressor, compressed); - - return HttpCompression_None; - } - - break; - } - - default: - throw OrthancException(ErrorCode_NotImplemented); - } - } - - - uint64_t FilesystemHttpSender::GetContentLength() - { - if (uncompressed_.get() != NULL) - { - return uncompressed_->GetContentLength(); - } - else - { - return size_; - } - } - - bool FilesystemHttpSender::ReadNextChunk() { - if (uncompressed_.get() != NULL) + if (chunk_.size() == 0) { - return uncompressed_->ReadNextChunk(); + chunk_.resize(CHUNK_SIZE); } - else - { - if (chunk_.size() == 0) - { - chunk_.resize(CHUNK_SIZE); - } - file_.read(&chunk_[0], chunk_.size()); - - if (file_.flags() & std::istream::failbit) - { - throw OrthancException(ErrorCode_CorruptedFile); - } - - chunkSize_ = file_.gcount(); - - return chunkSize_ > 0; - } - } + file_.read(&chunk_[0], chunk_.size()); - - const char* FilesystemHttpSender::GetChunkContent() - { - if (uncompressed_.get() != NULL) + if ((file_.flags() & std::istream::failbit) || + file_.gcount() < 0) { - return uncompressed_->GetChunkContent(); - } - else - { - return chunk_.c_str(); + throw OrthancException(ErrorCode_CorruptedFile); } - } - - size_t FilesystemHttpSender::GetChunkSize() - { - if (uncompressed_.get() != NULL) - { - return uncompressed_->GetChunkSize(); - } - else - { - return chunkSize_; - } + chunkSize_ = file_.gcount(); + + return chunkSize_ > 0; } } diff -r 4a0c2eedceb6 -r f9b0169eb6bb Core/HttpServer/FilesystemHttpSender.h --- a/Core/HttpServer/FilesystemHttpSender.h Tue Aug 11 16:09:24 2015 +0200 +++ b/Core/HttpServer/FilesystemHttpSender.h Tue Aug 11 17:50:38 2015 +0200 @@ -46,10 +46,6 @@ uint64_t size_; std::string chunk_; size_t chunkSize_; - CompressionType sourceCompression_; - HttpCompression targetCompression_; - - std::auto_ptr uncompressed_; void Initialize(const boost::filesystem::path& path); @@ -70,25 +66,25 @@ Initialize(storage.GetPath(uuid)); } - void SetSourceCompression(CompressionType compression) - { - sourceCompression_ = compression; - } - - /** * Implementation of the IHttpStreamAnswer interface. **/ - virtual HttpCompression SetupHttpCompression(bool /*gzipAllowed*/, - bool /*deflateAllowed*/); - - virtual uint64_t GetContentLength(); + virtual uint64_t GetContentLength() + { + return size_; + } virtual bool ReadNextChunk(); - virtual const char* GetChunkContent(); + virtual const char* GetChunkContent() + { + return chunk_.c_str(); + } - virtual size_t GetChunkSize(); + virtual size_t GetChunkSize() + { + return chunkSize_; + } }; } diff -r 4a0c2eedceb6 -r f9b0169eb6bb Core/HttpServer/HttpFileSender.h --- a/Core/HttpServer/HttpFileSender.h Tue Aug 11 16:09:24 2015 +0200 +++ b/Core/HttpServer/HttpFileSender.h Tue Aug 11 17:50:38 2015 +0200 @@ -65,6 +65,12 @@ * Implementation of the IHttpStreamAnswer interface. **/ + virtual HttpCompression SetupHttpCompression(bool /*gzipAllowed*/, + bool /*deflateAllowed*/) + { + return HttpCompression_None; + } + virtual bool HasContentFilename(std::string& filename); virtual std::string GetContentType(); diff -r 4a0c2eedceb6 -r f9b0169eb6bb Core/HttpServer/HttpStreamTranscoder.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Core/HttpServer/HttpStreamTranscoder.cpp Tue Aug 11 17:50:38 2015 +0200 @@ -0,0 +1,223 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, 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 . + **/ + + +#include "../PrecompiledHeaders.h" +#include "HttpStreamTranscoder.h" + +#include "../OrthancException.h" +#include "../Compression/ZlibCompressor.h" + +#include // For memcpy() +#include + +#include + +namespace Orthanc +{ + void HttpStreamTranscoder::ReadSource(std::string& buffer) + { + if (source_.SetupHttpCompression(false, false) != HttpCompression_None) + { + throw OrthancException(ErrorCode_InternalError); + } + + uint64_t size = source_.GetContentLength(); + if (static_cast(static_cast(size)) != size) + { + throw OrthancException(ErrorCode_NotEnoughMemory); + } + + buffer.resize(static_cast(size)); + size_t offset = 0; + + while (source_.ReadNextChunk()) + { + size_t chunkSize = static_cast(source_.GetChunkSize()); + memcpy(&buffer[offset], source_.GetChunkContent(), chunkSize); + offset += chunkSize; + } + + if (offset != size) + { + throw OrthancException(ErrorCode_InternalError); + } + } + + + HttpCompression HttpStreamTranscoder::SetupHttpCompression(bool gzipAllowed, + bool deflateAllowed) + { + switch (sourceCompression_) + { + case CompressionType_None: + { + return HttpCompression_None; + } + + case CompressionType_ZlibWithSize: + { + uint64_t size = source_.GetContentLength(); + + if (size == 0) + { + return HttpCompression_None; + } + + if (size < sizeof(uint64_t)) + { + throw OrthancException(ErrorCode_CorruptedFile); + } + + if (deflateAllowed) + { + bytesToSkip_ = sizeof(uint64_t); + return HttpCompression_Deflate; + } + else + { + std::string compressed; + ReadSource(compressed); + + uncompressed_.reset(new BufferHttpSender); + + ZlibCompressor compressor; + IBufferCompressor::Uncompress(uncompressed_->GetBuffer(), compressor, compressed); + + return HttpCompression_None; + } + + break; + } + + default: + throw OrthancException(ErrorCode_NotImplemented); + } + } + + + uint64_t HttpStreamTranscoder::GetContentLength() + { + if (uncompressed_.get() != NULL) + { + return uncompressed_->GetContentLength(); + } + else + { + uint64_t length = source_.GetContentLength(); + if (length < bytesToSkip_) + { + throw OrthancException(ErrorCode_InternalError); + } + + return length - bytesToSkip_; + } + } + + + bool HttpStreamTranscoder::ReadNextChunk() + { + if (uncompressed_.get() != NULL) + { + return uncompressed_->ReadNextChunk(); + } + + assert(skipped_ <= bytesToSkip_); + if (skipped_ == bytesToSkip_) + { + // We have already skipped the first bytes of the stream + currentChunkOffset_ = 0; + return source_.ReadNextChunk(); + } + + // This condition can only be true on the first call to "ReadNextChunk()" + for (;;) + { + assert(skipped_ < bytesToSkip_); + printf("[%d %d] ", skipped_, bytesToSkip_); fflush(stdout); + + bool ok = source_.ReadNextChunk(); + if (!ok) + { + throw OrthancException(ErrorCode_CorruptedFile); + } + + size_t remaining = bytesToSkip_ - skipped_; + size_t s = source_.GetChunkSize(); + + if (s < remaining) + { + skipped_ += s; + } + else if (s == remaining) + { + // We have skipped enough bytes, but we must read a new chunk + currentChunkOffset_ = 0; + skipped_ = bytesToSkip_; + return source_.GetChunkSize(); + } + else + { + assert(s > remaining); + + // We have skipped enough bytes, and we have enough data in the current chunk + currentChunkOffset_ = s - remaining; + skipped_ = bytesToSkip_; + return true; + } + } + } + + + const char* HttpStreamTranscoder::GetChunkContent() + { + if (uncompressed_.get() != NULL) + { + return uncompressed_->GetChunkContent(); + } + else + { + return source_.GetChunkContent() + currentChunkOffset_; + } + } + + size_t HttpStreamTranscoder::GetChunkSize() + { + if (uncompressed_.get() != NULL) + { + return uncompressed_->GetChunkSize(); + } + else + { + return source_.GetChunkSize() - currentChunkOffset_; + } + } +} diff -r 4a0c2eedceb6 -r f9b0169eb6bb Core/HttpServer/HttpStreamTranscoder.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Core/HttpServer/HttpStreamTranscoder.h Tue Aug 11 17:50:38 2015 +0200 @@ -0,0 +1,86 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, 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 . + **/ + + +#pragma once + +#include "BufferHttpSender.h" + +#include // For std::auto_ptr + +namespace Orthanc +{ + class HttpStreamTranscoder : public IHttpStreamAnswer + { + private: + IHttpStreamAnswer& source_; + CompressionType sourceCompression_; + uint64_t bytesToSkip_; + uint64_t skipped_; + uint64_t currentChunkOffset_; + + std::auto_ptr uncompressed_; + + void ReadSource(std::string& buffer); + + public: + HttpStreamTranscoder(IHttpStreamAnswer& source, + CompressionType compression) : + source_(source), + sourceCompression_(compression), + bytesToSkip_(0), + skipped_(0) + { + } + + // This is the first method to be called + virtual HttpCompression SetupHttpCompression(bool gzipAllowed, + bool deflateAllowed); + + virtual bool HasContentFilename(std::string& filename) + { + return source_.HasContentFilename(filename); + } + + virtual std::string GetContentType() + { + return source_.GetContentType(); + } + + virtual uint64_t GetContentLength(); + + virtual bool ReadNextChunk(); + + virtual const char* GetChunkContent(); + + virtual size_t GetChunkSize(); + }; +} diff -r 4a0c2eedceb6 -r f9b0169eb6bb Core/HttpServer/IHttpStreamAnswer.h --- a/Core/HttpServer/IHttpStreamAnswer.h Tue Aug 11 16:09:24 2015 +0200 +++ b/Core/HttpServer/IHttpStreamAnswer.h Tue Aug 11 17:50:38 2015 +0200 @@ -32,8 +32,11 @@ #pragma once +#include "../Enumerations.h" + #include #include +#include namespace Orthanc { diff -r 4a0c2eedceb6 -r f9b0169eb6bb OrthancServer/ServerContext.cpp --- a/OrthancServer/ServerContext.cpp Tue Aug 11 16:09:24 2015 +0200 +++ b/OrthancServer/ServerContext.cpp Tue Aug 11 17:50:38 2015 +0200 @@ -34,6 +34,7 @@ #include "ServerContext.h" #include "../Core/HttpServer/FilesystemHttpSender.h" +#include "../Core/HttpServer/HttpStreamTranscoder.h" #include "../Core/Logging.h" #include "FromDcmtkBridge.h" #include "ServerToolbox.h" @@ -318,16 +319,17 @@ std::auto_ptr sender(accessor_.ConstructHttpFileSender(attachment.GetUuid(), attachment.GetContentType())); sender->SetContentType(GetMimeType(content)); - sender->SetContentFilename(instancePublicId + ".dcm"); // TODO ".dcm" => ToMimeType(content) + sender->SetContentFilename(attachment.GetUuid() + ".dcm"); // TODO ".dcm" => ToMimeType(content) output.AnswerStream(*sender); #else const FilesystemStorage& a = dynamic_cast(accessor_.GetStorageArea()); FilesystemHttpSender sender(a, attachment.GetUuid()); - sender.SetSourceCompression(attachment.GetCompressionType()); sender.SetContentType(GetMimeType(content)); - sender.SetContentFilename(instancePublicId + ".dcm"); - output.AnswerStream(sender); + sender.SetContentFilename(attachment.GetUuid() + ".dcm"); + + HttpStreamTranscoder transcoder(sender, attachment.GetCompressionType()); + output.AnswerStream(transcoder); #endif } diff -r 4a0c2eedceb6 -r f9b0169eb6bb UnitTestsSources/StreamTests.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/UnitTestsSources/StreamTests.cpp Tue Aug 11 17:50:38 2015 +0200 @@ -0,0 +1,340 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, 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 . + **/ + + +#include "PrecompiledHeadersUnitTests.h" +#include "gtest/gtest.h" + +#include "../Core/Toolbox.h" +#include "../Core/OrthancException.h" +#include "../Core/Uuid.h" +#include "../Core/HttpServer/BufferHttpSender.h" +#include "../Core/HttpServer/FilesystemHttpSender.h" +#include "../Core/HttpServer/HttpStreamTranscoder.h" +#include "../Core/Compression/ZlibCompressor.h" +#include "../Core/Compression/GzipCompressor.h" + + +using namespace Orthanc; + + +TEST(Gzip, Basic) +{ + std::string s = "Hello world"; + + std::string compressed; + GzipCompressor c; + ASSERT_FALSE(c.HasPrefixWithUncompressedSize()); + IBufferCompressor::Compress(compressed, c, s); + + std::string uncompressed; + IBufferCompressor::Uncompress(uncompressed, c, compressed); + ASSERT_EQ(s.size(), uncompressed.size()); + ASSERT_EQ(0, memcmp(&s[0], &uncompressed[0], s.size())); +} + + +TEST(Gzip, Empty) +{ + std::string s; + + std::string compressed; + GzipCompressor c; + ASSERT_FALSE(c.HasPrefixWithUncompressedSize()); + c.SetPrefixWithUncompressedSize(false); + IBufferCompressor::Compress(compressed, c, s); + + std::string uncompressed; + IBufferCompressor::Uncompress(uncompressed, c, compressed); + ASSERT_EQ(0, uncompressed.size()); +} + + +TEST(Gzip, BasicWithPrefix) +{ + std::string s = "Hello world"; + + std::string compressed; + GzipCompressor c; + c.SetPrefixWithUncompressedSize(true); + ASSERT_TRUE(c.HasPrefixWithUncompressedSize()); + IBufferCompressor::Compress(compressed, c, s); + + std::string uncompressed; + IBufferCompressor::Uncompress(uncompressed, c, compressed); + ASSERT_EQ(s.size(), uncompressed.size()); + ASSERT_EQ(0, memcmp(&s[0], &uncompressed[0], s.size())); +} + + +TEST(Gzip, EmptyWithPrefix) +{ + std::string s; + + std::string compressed; + GzipCompressor c; + c.SetPrefixWithUncompressedSize(true); + ASSERT_TRUE(c.HasPrefixWithUncompressedSize()); + IBufferCompressor::Compress(compressed, c, s); + + std::string uncompressed; + IBufferCompressor::Uncompress(uncompressed, c, compressed); + ASSERT_EQ(0, uncompressed.size()); +} + + +TEST(Zlib, Basic) +{ + std::string s = Toolbox::GenerateUuid(); + s = s + s + s + s; + + std::string compressed, compressed2; + ZlibCompressor c; + ASSERT_TRUE(c.HasPrefixWithUncompressedSize()); + IBufferCompressor::Compress(compressed, c, s); + + std::string uncompressed; + IBufferCompressor::Uncompress(uncompressed, c, compressed); + ASSERT_EQ(s.size(), uncompressed.size()); + ASSERT_EQ(0, memcmp(&s[0], &uncompressed[0], s.size())); +} + + +TEST(Zlib, Level) +{ + std::string s = Toolbox::GenerateUuid(); + s = s + s + s + s; + + std::string compressed, compressed2; + ZlibCompressor c; + c.SetCompressionLevel(9); + IBufferCompressor::Compress(compressed, c, s); + + c.SetCompressionLevel(0); + IBufferCompressor::Compress(compressed2, c, s); + + ASSERT_TRUE(compressed.size() < compressed2.size()); +} + + +TEST(Zlib, DISABLED_Corrupted) // Disabled because it may result in a crash +{ + std::string s = Toolbox::GenerateUuid(); + s = s + s + s + s; + + std::string compressed; + ZlibCompressor c; + IBufferCompressor::Compress(compressed, c, s); + + compressed[compressed.size() - 1] = 'a'; + std::string u; + + ASSERT_THROW(IBufferCompressor::Uncompress(u, c, compressed), OrthancException); +} + + +TEST(Zlib, Empty) +{ + std::string s = ""; + + std::string compressed, compressed2; + ZlibCompressor c; + IBufferCompressor::Compress(compressed, c, s); + ASSERT_EQ(compressed, compressed2); + + std::string uncompressed; + IBufferCompressor::Uncompress(uncompressed, c, compressed); + ASSERT_EQ(0u, uncompressed.size()); +} + + +static bool ReadAllStream(std::string& result, + IHttpStreamAnswer& stream, + bool allowGzip = false, + bool allowDeflate = false) +{ + result.resize(stream.GetContentLength()); + + stream.SetupHttpCompression(allowGzip, allowDeflate); + + size_t pos = 0; + while (stream.ReadNextChunk()) + { + size_t s = stream.GetChunkSize(); + if (pos + s > result.size()) + { + return false; + } + + memcpy(&result[pos], stream.GetChunkContent(), s); + pos += s; + } + + return pos == result.size(); +} + + +TEST(BufferHttpSender, Basic) +{ + const std::string s = "Hello world"; + std::string t; + + { + BufferHttpSender sender; + sender.SetChunkSize(0); + sender.GetBuffer() = s; + ASSERT_TRUE(ReadAllStream(t, sender)); + ASSERT_EQ(s, t); + } + + { + BufferHttpSender sender; + sender.SetChunkSize(1); + sender.GetBuffer() = s; + ASSERT_TRUE(ReadAllStream(t, sender)); + ASSERT_EQ(s, t); + } + + { + BufferHttpSender sender; + sender.SetChunkSize(1); + ASSERT_TRUE(ReadAllStream(t, sender)); + ASSERT_EQ(0u, t.size()); + } + + { + BufferHttpSender sender; + sender.SetChunkSize(3); + sender.GetBuffer() = s; + ASSERT_TRUE(ReadAllStream(t, sender)); + ASSERT_EQ(s, t); + } + + { + BufferHttpSender sender; + sender.SetChunkSize(300); + sender.GetBuffer() = s; + ASSERT_TRUE(ReadAllStream(t, sender)); + ASSERT_EQ(s, t); + } +} + + +TEST(FilesystemHttpSender, Basic) +{ + const std::string& path = "UnitTestsResults/stream"; + const std::string s = "Hello world"; + std::string t; + + { + Toolbox::WriteFile(s, path); + FilesystemHttpSender sender(path); + ASSERT_TRUE(ReadAllStream(t, sender)); + ASSERT_EQ(s, t); + } + + { + Toolbox::WriteFile("", path); + FilesystemHttpSender sender(path); + ASSERT_TRUE(ReadAllStream(t, sender)); + ASSERT_EQ(0u, t.size()); + } +} + + +TEST(HttpStreamTranscoder, Basic) +{ + ZlibCompressor compressor; + + const std::string s = "Hello world " + Toolbox::GenerateUuid(); + + std::string t; + IBufferCompressor::Compress(t, compressor, s); + + for (int cs = 0; cs < 3; cs++) + { + BufferHttpSender sender; + sender.SetChunkSize(cs); + sender.GetBuffer() = t; + std::string u; + ASSERT_TRUE(ReadAllStream(u, sender)); + + std::string v; + IBufferCompressor::Uncompress(v, compressor, u); + ASSERT_EQ(s, v); + } + + // Pass-through test, no decompression occurs + for (int cs = 0; cs < 3; cs++) + { + BufferHttpSender sender; + sender.SetChunkSize(cs); + sender.GetBuffer() = t; + + HttpStreamTranscoder transcode(sender, CompressionType_None); + + std::string u; + ASSERT_TRUE(ReadAllStream(u, transcode)); + + ASSERT_EQ(t, u); + } + + // Pass-through test, decompression occurs + for (int cs = 0; cs < 3; cs++) + { + BufferHttpSender sender; + sender.SetChunkSize(cs); + sender.GetBuffer() = t; + + HttpStreamTranscoder transcode(sender, CompressionType_ZlibWithSize); + + std::string u; + ASSERT_TRUE(ReadAllStream(u, transcode, false, false)); + + ASSERT_EQ(s, u); + } + + // Pass-through test with zlib, no decompression occurs but deflate is sent + for (int cs = 0; cs < 3; cs++) + { + BufferHttpSender sender; + sender.SetChunkSize(cs); + sender.GetBuffer() = t; + + HttpStreamTranscoder transcode(sender, CompressionType_ZlibWithSize); + + std::string u; + ASSERT_TRUE(ReadAllStream(u, transcode, false, true)); + + ASSERT_EQ(t.size() - sizeof(uint64_t), u.size()); + ASSERT_EQ(t.substr(sizeof(uint64_t)), u); + } +} diff -r 4a0c2eedceb6 -r f9b0169eb6bb UnitTestsSources/UnitTestsMain.cpp --- a/UnitTestsSources/UnitTestsMain.cpp Tue Aug 11 16:09:24 2015 +0200 +++ b/UnitTestsSources/UnitTestsMain.cpp Tue Aug 11 17:50:38 2015 +0200 @@ -37,8 +37,6 @@ #include -#include "../Core/Compression/ZlibCompressor.h" -#include "../Core/Compression/GzipCompressor.h" #include "../Core/DicomFormat/DicomTag.h" #include "../Core/HttpServer/HttpToolbox.h" #include "../Core/Logging.h" @@ -100,136 +98,6 @@ } -TEST(Gzip, Basic) -{ - std::string s = "Hello world"; - - std::string compressed; - GzipCompressor c; - ASSERT_FALSE(c.HasPrefixWithUncompressedSize()); - IBufferCompressor::Compress(compressed, c, s); - - std::string uncompressed; - IBufferCompressor::Uncompress(uncompressed, c, compressed); - ASSERT_EQ(s.size(), uncompressed.size()); - ASSERT_EQ(0, memcmp(&s[0], &uncompressed[0], s.size())); -} - - -TEST(Gzip, Empty) -{ - std::string s; - - std::string compressed; - GzipCompressor c; - ASSERT_FALSE(c.HasPrefixWithUncompressedSize()); - c.SetPrefixWithUncompressedSize(false); - IBufferCompressor::Compress(compressed, c, s); - - std::string uncompressed; - IBufferCompressor::Uncompress(uncompressed, c, compressed); - ASSERT_EQ(0, uncompressed.size()); -} - - -TEST(Gzip, BasicWithPrefix) -{ - std::string s = "Hello world"; - - std::string compressed; - GzipCompressor c; - c.SetPrefixWithUncompressedSize(true); - ASSERT_TRUE(c.HasPrefixWithUncompressedSize()); - IBufferCompressor::Compress(compressed, c, s); - - std::string uncompressed; - IBufferCompressor::Uncompress(uncompressed, c, compressed); - ASSERT_EQ(s.size(), uncompressed.size()); - ASSERT_EQ(0, memcmp(&s[0], &uncompressed[0], s.size())); -} - - -TEST(Gzip, EmptyWithPrefix) -{ - std::string s; - - std::string compressed; - GzipCompressor c; - c.SetPrefixWithUncompressedSize(true); - ASSERT_TRUE(c.HasPrefixWithUncompressedSize()); - IBufferCompressor::Compress(compressed, c, s); - - std::string uncompressed; - IBufferCompressor::Uncompress(uncompressed, c, compressed); - ASSERT_EQ(0, uncompressed.size()); -} - - -TEST(Zlib, Basic) -{ - std::string s = Toolbox::GenerateUuid(); - s = s + s + s + s; - - std::string compressed, compressed2; - ZlibCompressor c; - ASSERT_TRUE(c.HasPrefixWithUncompressedSize()); - IBufferCompressor::Compress(compressed, c, s); - - std::string uncompressed; - IBufferCompressor::Uncompress(uncompressed, c, compressed); - ASSERT_EQ(s.size(), uncompressed.size()); - ASSERT_EQ(0, memcmp(&s[0], &uncompressed[0], s.size())); -} - - -TEST(Zlib, Level) -{ - std::string s = Toolbox::GenerateUuid(); - s = s + s + s + s; - - std::string compressed, compressed2; - ZlibCompressor c; - c.SetCompressionLevel(9); - IBufferCompressor::Compress(compressed, c, s); - - c.SetCompressionLevel(0); - IBufferCompressor::Compress(compressed2, c, s); - - ASSERT_TRUE(compressed.size() < compressed2.size()); -} - - -TEST(Zlib, DISABLED_Corrupted) // Disabled because it may result in a crash -{ - std::string s = Toolbox::GenerateUuid(); - s = s + s + s + s; - - std::string compressed; - ZlibCompressor c; - IBufferCompressor::Compress(compressed, c, s); - - compressed[compressed.size() - 1] = 'a'; - std::string u; - - ASSERT_THROW(IBufferCompressor::Uncompress(u, c, compressed), OrthancException); -} - - -TEST(Zlib, Empty) -{ - std::string s = ""; - - std::string compressed, compressed2; - ZlibCompressor c; - IBufferCompressor::Compress(compressed, c, s); - ASSERT_EQ(compressed, compressed2); - - std::string uncompressed; - IBufferCompressor::Uncompress(uncompressed, c, compressed); - ASSERT_EQ(0u, uncompressed.size()); -} - - TEST(ParseGetArguments, Basic) { IHttpHandler::GetArguments b;