Orthanc/OrthancFramework/Sources/HttpServer/HttpOutput.h
2025-06-23 19:07:37 +05:30

238 lines
6.4 KiB
C++

/**
* Orthanc - A Lightweight, RESTful DICOM Store
* Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
* Copyright (C) 2017-2023 Osimis S.A., Belgium
* Copyright (C) 2024-2025 Orthanc Team SRL, Belgium
* Copyright (C) 2021-2025 Sebastien Jodogne, ICTEAM UCLouvain, Belgium
*
* This program is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program. If not, see
* <http://www.gnu.org/licenses/>.
**/
#pragma once
#include "../Enumerations.h"
#include "IHttpOutputStream.h"
#include "IHttpStreamAnswer.h"
#include <list>
#include <string>
#include <stdint.h>
#include <map>
#include <vector>
namespace Orthanc
{
class ORTHANC_PUBLIC HttpOutput : public boost::noncopyable
{
private:
typedef std::list< std::pair<std::string, std::string> > Header;
class StateMachine : public boost::noncopyable
{
public:
enum State
{
State_WritingHeader,
State_WritingBody,
State_WritingMultipart,
State_Done,
State_WritingStream
};
private:
IHttpOutputStream& stream_;
State state_;
bool isContentCompressible_;
HttpStatus status_;
bool hasContentLength_;
uint64_t contentLength_;
uint64_t contentPosition_;
bool keepAlive_;
unsigned int keepAliveTimeout_;
std::list<std::string> headers_;
bool hasXContentTypeOptions_;
bool hasContentType_;
std::string multipartBoundary_;
std::string multipartContentType_;
void StartStreamInternal(const std::string& contentType);
public:
StateMachine(IHttpOutputStream& stream,
bool isKeepAlive,
unsigned int keepAliveTimeout);
~StateMachine();
void SetHttpStatus(HttpStatus status);
void SetContentLength(uint64_t length);
void SetContentType(const char* contentType);
void SetContentCompressible(bool isCompressible);
void SetContentFilename(const char* filename);
void SetCookie(const std::string& cookie,
const std::string& value);
void AddHeader(const std::string& header,
const std::string& value);
void ClearHeaders();
void SendBody(const void* buffer, size_t length);
void StartMultipart(const std::string& subType,
const std::string& contentType);
void SendMultipartItem(const void* item,
size_t length,
const std::map<std::string, std::string>& headers);
void CloseMultipart();
void CloseBody();
State GetState() const
{
return state_;
}
bool IsContentCompressible() const;
void CheckHeadersCompatibilityWithMultipart() const;
void StartStream(const std::string& contentType);
void SendStreamItem(const void* data,
size_t size);
void CloseStream();
bool HasContentType() const
{
return hasContentType_;
}
};
StateMachine stateMachine_;
bool isDeflateAllowed_;
bool isGzipAllowed_;
HttpCompression GetPreferredCompression(size_t bodySize) const;
public:
HttpOutput(IHttpOutputStream& stream,
bool isKeepAlive,
unsigned int keepAliveTimeout);
void SetDeflateAllowed(bool allowed);
bool IsDeflateAllowed() const;
void SetGzipAllowed(bool allowed);
bool IsGzipAllowed() const;
bool IsContentCompressible() const
{
return stateMachine_.IsContentCompressible();
}
void SendStatus(HttpStatus status,
const char* message,
size_t messageSize);
void SendStatus(HttpStatus status);
void SendStatus(HttpStatus status,
const std::string& message);
void SetContentType(MimeType contentType);
void SetContentType(const std::string& contentType);
void SetContentFilename(const char* filename);
void SetCookie(const std::string& cookie,
const std::string& value);
void AddHeader(const std::string& key,
const std::string& value);
void Answer(const void* buffer,
size_t length);
void Answer(const std::string& str);
void AnswerEmpty();
void SendMethodNotAllowed(const std::string& allowed);
void Redirect(const std::string& path);
void SendUnauthorized(const std::string& realm);
void StartMultipart(const std::string& subType,
const std::string& contentType);
void SendMultipartItem(const void* item,
size_t size,
const std::map<std::string, std::string>& headers);
void CloseMultipart();
bool IsWritingMultipart() const;
void Answer(IHttpStreamAnswer& stream);
/**
* This method is a replacement to the combination
* "StartMultipart()" + "SendMultipartItem()". It generates the
* same answer, but it gives a chance to compress the body if
* "Accept-Encoding: gzip" is provided by the client, which is not
* possible in chunked transfers.
**/
void AnswerMultipartWithoutChunkedTransfer(
const std::string& subType,
const std::string& contentType,
const std::vector<const void*>& parts,
const std::vector<size_t>& sizes,
const std::vector<const std::map<std::string, std::string>*>& headers);
/**
* Contrarily to "Answer()", this method doesn't bufferizes the
* stream before sending it, which reduces memory but cannot be
* used to handle compression using "Content-Encoding".
**/
void AnswerWithoutBuffering(IHttpStreamAnswer& stream);
void StartStream(const std::string& contentType);
void SendStreamItem(const void* data,
size_t size);
void CloseStream();
bool IsWritingStream() const;
};
}