aboutsummaryrefslogtreecommitdiffstats
path: root/library/cpp/http/io/headers.cpp
blob: f2baf64021803bde813054e2f7aa0d09886dd95b (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
#include "headers.h"
#include "stream.h"

#include <util/generic/strbuf.h>
#include <util/generic/yexception.h>
#include <util/stream/output.h>
#include <util/string/ascii.h>
#include <util/string/cast.h>
#include <util/string/strip.h>

static inline TStringBuf Trim(const char* b, const char* e) noexcept {
    return StripString(TStringBuf(b, e));
}

static inline bool HeaderNameEqual(TStringBuf headerName, TStringBuf expectedName) noexcept {
    // Most headers names have distinct sizes.
    // Size comparison adds small overhead if all headers have the same size (~4% or lower with size = 4),
    // but significantly speeds up the case where sizes are different (~4.5x for expectedName.size() = 4 and headerName.size() = 5)
    return headerName.size() == expectedName.size() && AsciiCompareIgnoreCase(headerName, expectedName) == 0;
}

THttpInputHeader::THttpInputHeader(const TStringBuf header) {
    size_t pos = header.find(':');

    if (pos == TString::npos) {
        ythrow THttpParseException() << "can not parse http header(" << TString{header}.Quote() << ")";
    }

    Name_ = TString(header.cbegin(), header.cbegin() + pos);
    Value_ = ::ToString(Trim(header.cbegin() + pos + 1, header.cend()));
}

THttpInputHeader::THttpInputHeader(TString name, TString value)
    : Name_(std::move(name))
    , Value_(std::move(value))
{
}

void THttpInputHeader::OutTo(IOutputStream* stream) const {
    typedef IOutputStream::TPart TPart;

    const TPart parts[] = {
        TPart(Name_),
        TPart(": ", 2),
        TPart(Value_),
        TPart::CrLf(),
    };

    stream->Write(parts, sizeof(parts) / sizeof(*parts));
}

THttpHeaders::THttpHeaders(IInputStream* stream) {
    TString header;
    TString line;

    bool rdOk = stream->ReadLine(header);
    while (rdOk && !header.empty()) {
        rdOk = stream->ReadLine(line);

        if (rdOk && ((line[0] == ' ') || (line[0] == '\t'))) {
            header += line;
        } else {
            AddHeader(THttpInputHeader(header));
            header = line;
        }
    }
}

bool THttpHeaders::HasHeader(const TStringBuf header) const {
    return FindHeader(header);
}

const THttpInputHeader* THttpHeaders::FindHeader(const TStringBuf header) const {
    for (const auto& hdr : Headers_) {
        if (HeaderNameEqual(hdr.Name(), header)) {
            return &hdr;
        }
    }
    return nullptr;
}

void THttpHeaders::RemoveHeader(const TStringBuf header) {
    for (auto h = Headers_.begin(); h != Headers_.end(); ++h) {
        if (HeaderNameEqual(h->Name(), header)) {
            Headers_.erase(h);
            return;
        }
    }
}

void THttpHeaders::AddOrReplaceHeader(const THttpInputHeader& header) {
    TStringBuf name = header.Name();
    for (auto& hdr : Headers_) {
        if (HeaderNameEqual(hdr.Name(), name)) {
            hdr = header;
            return;
        }
    }

    AddHeader(header);
}

void THttpHeaders::AddHeader(THttpInputHeader header) {
    Headers_.push_back(std::move(header));
}

void THttpHeaders::OutTo(IOutputStream* stream) const {
    for (TConstIterator header = Begin(); header != End(); ++header) {
        header->OutTo(stream);
    }
}

template <>
void Out<THttpHeaders>(IOutputStream& out, const THttpHeaders& h) {
    h.OutTo(&out);
}