net/http/headers.cpp (167 lines of code) (raw):
/*
Copyright 2022 The Photon Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include "headers.h"
#include <algorithm>
#include <photon/common/utility.h>
#include <photon/common/alog-stdstring.h>
#include <photon/common/iovector.h>
#include "url.h"
#include "parser.h"
using namespace std;
namespace photon {
namespace net {
namespace http {
class HeaderAssistant {
public:
const HeadersBase* _h;
HeaderAssistant(const HeadersBase* h) : _h(h) {}
bool equal_to(rstring_view16 _k, std::string_view key) const {
return (_k | _h->m_buf).icmp(key) == 0;
}
bool less(rstring_view16 _k1, rstring_view16 _k2) const {
return (_k1 | _h->m_buf).icmp(_k2 | _h->m_buf) < 0;
}
bool less(rstring_view16 _k, std::string_view key) const {
return (_k | _h->m_buf).icmp(key) < 0;
}
bool less(std::string_view key, rstring_view16 _k) const {
return estring_view(key).icmp(_k | _h->m_buf) < 0;
}
bool operator()(HeadersBase::KV a, std::string_view b) const { return less(a.first, b); }
bool operator()(std::string_view a, HeadersBase::KV b) const { return less(a, b.first); }
bool operator()(HeadersBase::KV a, HeadersBase::KV b) const { return less(a.first, b.first); }
};
using HA = HeaderAssistant;
HeadersBase::iterator HeadersBase::find(std::string_view key) const {
auto it = std::lower_bound(kv_begin(), kv_end(), key, HA(this));
if (it == kv_end() || !HA(this).equal_to(it->first, key)) return end();
return {this, (uint16_t)(it - kv_begin())};
}
std::pair<HeadersBase::iterator, HeadersBase::iterator>
HeadersBase::equal_range(std::string_view key) const {
auto r = std::equal_range(kv_begin(), kv_end(), key, HA(this));
return {{this, (uint16_t)(r.first - kv_begin())},
{this, (uint16_t)(r.second - kv_begin())}};
}
void buf_append(char*& ptr, uint64_t x) {
auto begin = ptr;
do {
*ptr = '0' + x % 10;
ptr++;
x /= 10;
} while(x);
std::reverse(begin, ptr);
}
int HeadersBase::insert(std::string_view key, std::string_view value, int allow_dup) {
if (!allow_dup) {
auto it = find(key);
if (it != end()) return -EEXIST;
}
uint64_t vbegin = m_buf_size + key.size() + 2;
uint64_t new_size = vbegin + value.size() + 2;
if (new_size + (m_kv_size + 1) * sizeof(KV) > m_buf_capacity)
LOG_ERROR_RETURN(ENOBUFS, -1, "no buffer");
auto ptr = m_buf + m_buf_size;
buf_append(ptr, key);
buf_append(ptr, ": ");
buf_append(ptr, value);
buf_append(ptr, "\r\n");
m_last_kv = kv_add_sort({{m_buf_size, (uint16_t)key.size()},
{(uint16_t)vbegin, (uint16_t)value.size()}}) - kv_begin();
m_buf_size = (uint16_t)new_size;
return 0;
}
bool HeadersBase::value_append(std::string_view value) {
if (m_last_kv >= m_kv_size) return false;
auto append_size = value.size();
uint64_t new_size = m_buf_size + append_size;
if (new_size + m_kv_size * sizeof(KV) > m_buf_capacity)
LOG_ERROR_RETURN(ENOBUFS, false, "no buffer");
auto ptr = m_buf + m_buf_size - 2;
buf_append(ptr, value);
buf_append(ptr, "\r\n");
m_buf_size = (uint16_t)new_size;
kv_begin()[m_last_kv].second.size() += append_size;
return true;
}
int HeadersBase::reset_host(int delta, std::string_view host) {
KV* host_kv = nullptr;
for (auto kv = kv_begin(); kv != kv_end(); kv++)
if (kv->first.offset() == 0) {
host_kv = kv;
break;
}
assert(host_kv != nullptr);
int inner_delta = host.size() - host_kv->second.size();
if ((ssize_t)space_remain() < delta + inner_delta)
LOG_ERROR_RETURN(ENOBUFS, -1, "no buffer");
memmove(m_buf + delta + host.size() + 8,
m_buf + host_kv->second.size() + 8,
size() - host_kv->second.size() - 8);
for (auto kv = kv_begin(); kv != kv_end(); kv++) {
kv->first += inner_delta;
kv->second += inner_delta;
}
*host_kv = {{0, 4}, {6, host.size()}};
m_buf += delta;
m_buf_capacity -= delta;
m_buf_size += inner_delta;
auto ptr = m_buf;
buf_append(ptr, "Host: ");
buf_append(ptr, host);
buf_append(ptr, "\r\n");
return 0;
}
HeadersBase::KV* HeadersBase::kv_add_sort(KV kv) {
auto begin = kv_begin();
if ((char*)(begin - 1) <= m_buf + m_buf_size)
LOG_ERROR_RETURN(ENOBUFS, nullptr, "no buffer");
auto it = std::lower_bound(begin, kv_end(), kv, HA(this));
#ifndef __clang__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wclass-memaccess"
#endif
memmove(begin - 1, begin, sizeof(KV) * (it - begin));
#ifndef __clang__
#pragma GCC diagnostic pop
#endif
m_kv_size++;
*(it - 1) = kv;
return it - 1;
}
HeadersBase::KV* HeadersBase::kv_add(KV kv) {
auto begin = kv_begin();
if ((char*)(begin - 1) <= m_buf + m_buf_size)
LOG_ERROR_RETURN(ENOBUFS, nullptr, "no buffer");
m_kv_size++;
*(begin - 1) = kv;
return begin -1;
}
int HeadersBase::parse() {
Parser p({m_buf, m_buf_size});
while(p[0] != '\r') {
auto k = p.extract_until_char(':');
p.skip_chars(' ', true);
auto v = p.extract_until_char('\r');
p.skip_chars('\n');
if (kv_add({k, v}) == nullptr)
LOG_ERROR_RETURN(0, -1, "add kv failed");
}
std::sort(kv_begin(), kv_end(), HA(this));
return 0;
}
std::pair<ssize_t, ssize_t> Headers::range() const {
auto found = find("Range");
if (found == end()) return {0, -1};
auto range = found.second();
auto eq_pos = range.find("=");
auto dash_pos = range.find("-");
auto start_sv = estring_view(range.substr(eq_pos + 1, dash_pos - eq_pos + 1));
auto end_sv = estring_view(range.substr(dash_pos + 1));
auto start = start_sv.to_uint64();
auto end = end_sv.to_uint64();
if (start_sv.empty()) return {-1, end};
if (end_sv.empty()) return {start, -1};
return {start, end};
}
} // namespace http
} // namespace net
} // namespace photon