Skip to content

Commit

Permalink
Added etag feature
Browse files Browse the repository at this point in the history
  • Loading branch information
getroot committed Dec 23, 2024
1 parent 249122f commit 49d3b77
Show file tree
Hide file tree
Showing 6 changed files with 138 additions and 4 deletions.
33 changes: 33 additions & 0 deletions src/projects/config/items/modules/etag.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
//==============================================================================
//
// OvenMediaEngine
//
// Created by Getroot
// Copyright (c) 2022 AirenSoft. All rights reserved.
//
//==============================================================================
#pragma once

#include "module_template.h"

namespace cfg
{
namespace modules
{
struct ETag : public ModuleTemplate
{
protected:

public:

protected:
void MakeList() override
{
// Experimental feature is disabled by default
SetEnable(false);

ModuleTemplate::MakeList();
}
};
} // namespace modules
} // namespace cfg
4 changes: 4 additions & 0 deletions src/projects/config/items/modules/modules.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include "p2p.h"
#include "recovery.h"
#include "dynamic_app_removal.h"
#include "etag.h"

namespace cfg
{
Expand All @@ -26,13 +27,15 @@ namespace cfg
P2P _p2p;
Recovery _recovery;
DynamicAppRemoval _dynamic_app_removal;
ETag _etag;

public:
CFG_DECLARE_CONST_REF_GETTER_OF(GetHttp2, _http2)
CFG_DECLARE_CONST_REF_GETTER_OF(GetLLHls, _ll_hls)
CFG_DECLARE_CONST_REF_GETTER_OF(GetP2P, _p2p)
CFG_DECLARE_CONST_REF_GETTER_OF(GetRecovery, _recovery)
CFG_DECLARE_CONST_REF_GETTER_OF(GetDynamicAppRemoval, _dynamic_app_removal)
CFG_DECLARE_CONST_REF_GETTER_OF(GetETag, _etag)

protected:
void MakeList() override
Expand All @@ -42,6 +45,7 @@ namespace cfg
Register<Optional>({"P2P", "p2p"}, &_p2p);
Register<Optional>("Recovery", &_recovery);
Register<Optional>("DynamicAppRemoval", &_dynamic_app_removal);
Register<Optional>("ETag", &_etag);
}
};
} // namespace modules
Expand Down
4 changes: 3 additions & 1 deletion src/projects/modules/http/server/http1/http1_response.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,9 @@ namespace http
std::shared_ptr<ov::Data> response = std::make_shared<ov::Data>(65535);
ov::ByteStream stream(response.get());

if (_chunked_transfer == false)
if (_chunked_transfer == false &&
GetStatusCode() != StatusCode::NoContent &&
GetStatusCode() != StatusCode::NotModified)
{
// Calculate the content length
SetHeader("Content-Length", ov::Converter::ToString(GetResponseDataSize()));
Expand Down
15 changes: 13 additions & 2 deletions src/projects/modules/http/server/http_exchange.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@ namespace http
void HttpExchange::Release()
{
// print debug info
if ((static_cast<int>(GetResponse()->GetStatusCode()) / 100) != 2)
// 200 / 300 are not error
if ((static_cast<int>(GetResponse()->GetStatusCode()) / 100) != 2 &&
(static_cast<int>(GetResponse()->GetStatusCode()) / 100) != 3)
{
logte("\n%s", GetDebugInfo().CStr());
}
Expand Down Expand Up @@ -271,7 +273,16 @@ namespace http
return InterceptorResult::Error;
}

GetResponse()->SetMethod(GetRequest()->GetMethod());
auto request = GetRequest();
auto response = GetResponse();

response->SetMethod(request->GetMethod());

auto if_none_match = request->GetHeader("If-None-Match");
if (if_none_match.IsEmpty() == false)
{
response->SetIfNoneMatch(if_none_match);
}

return interceptor->OnRequestCompleted(GetSharedPtr());
}
Expand Down
77 changes: 76 additions & 1 deletion src/projects/modules/http/server/http_response.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include <algorithm>
#include <memory>
#include <utility>
#include "config/config_manager.h"

#include "./http_server_private.h"

Expand All @@ -26,6 +27,9 @@ namespace http
OV_ASSERT2(_client_socket != nullptr);

_created_time = std::chrono::system_clock::now();

auto module_config = cfg::ConfigManager::GetInstance()->GetServer()->GetModules();
_etag_enabled_by_config = module_config.GetETag().IsEnabled();
}

HttpResponse::HttpResponse(const std::shared_ptr<HttpResponse> &http_response)
Expand Down Expand Up @@ -74,6 +78,16 @@ namespace http
return _method;
}

void HttpResponse::SetIfNoneMatch(const ov::String &etag)
{
_if_none_match = etag;
}

const ov::String &HttpResponse::GetIfNoneMatch() const
{
return _if_none_match;
}

StatusCode HttpResponse::GetStatusCode() const
{
return _status_code;
Expand Down Expand Up @@ -156,6 +170,16 @@ namespace http
return true;
}

ov::String HttpResponse::GetEtag()
{
if (_response_hash == nullptr)
{
return "";
}

return ov::String::FormatString("%s-%d", _response_hash->ToHexString().CStr(), _response_data_size);
}

bool HttpResponse::AppendData(const std::shared_ptr<const ov::Data> &data)
{
if (data == nullptr)
Expand All @@ -170,6 +194,33 @@ namespace http
_response_data_list.push_back(cloned_data);
_response_data_size += cloned_data->GetLength();

if (_etag_enabled_by_config == false)
{
return true;
}

auto md5 = ov::MessageDigest::ComputeDigest(ov::CryptoAlgorithm::Md5, cloned_data);
if (md5 == nullptr || md5->GetLength() != 16)
{
// Could not compute MD5
OV_ASSERT2(md5->GetLength() == 16);
return true;
}

if (_response_hash == nullptr)
{
_response_hash = md5;
}
else
{
// Update hash, xor with previous hash
for (size_t i = 0; i < md5->GetLength(); i++)
{
auto ptr = _response_hash->GetWritableDataAs<uint8_t>();
ptr[i] ^= md5->At(i);
}
}

return true;
}

Expand Down Expand Up @@ -237,9 +288,33 @@ namespace http
_response_time = std::chrono::system_clock::now();

uint32_t sent_size = 0;
bool only_header = false;

if (IsHeaderSent() == false)
{
if (_etag_enabled_by_config == true)
{
// IF-NONE-MATCH check
auto if_none_match = GetIfNoneMatch();
if (if_none_match.IsEmpty() == false)
{
if (if_none_match == GetEtag())
{
SetStatusCode(StatusCode::NotModified);
only_header = true;
}
}

if (GetStatusCode() == StatusCode::OK || GetStatusCode() == StatusCode::NotModified)
{
auto etag_value = GetEtag();
if (etag_value.IsEmpty() == false)
{
SetHeader("ETag", etag_value);
}
}
}

auto sent_header_size = SendHeader();
// Header must be bigger than 0, if header is not sent, it is an error
if (sent_header_size <= 0)
Expand All @@ -254,7 +329,7 @@ namespace http
}
}

if (GetMethod() == Method::Head)
if (GetMethod() == Method::Head || only_header == true)
{
_sent_size += sent_size;
return sent_size;
Expand Down
9 changes: 9 additions & 0 deletions src/projects/modules/http/server/http_response.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ namespace http
void SetMethod(Method method);
Method GetMethod() const;

void SetIfNoneMatch(const ov::String &etag);
const ov::String &GetIfNoneMatch() const;

// reason = default
void SetStatusCode(StatusCode status_code);
// custom reason
Expand Down Expand Up @@ -92,6 +95,8 @@ namespace http
virtual int32_t SendHeader();
virtual int32_t SendPayload();

ov::String GetEtag();

std::shared_ptr<ov::ClientSocket> _client_socket;
std::shared_ptr<ov::TlsServerData> _tls_data;

Expand Down Expand Up @@ -128,6 +133,10 @@ namespace http
uint32_t _sent_size = 0;

Method _method = Method::Unknown;

bool _etag_enabled_by_config = false;
ov::String _if_none_match = "";
std::shared_ptr<ov::Data> _response_hash = nullptr;
};
} // namespace svr
} // namespace http

0 comments on commit 49d3b77

Please sign in to comment.