1
#include "source/common/http/http1/legacy_parser_impl.h"
2

            
3
#include <http_parser.h>
4

            
5
#include <cstdint>
6

            
7
#include "source/common/common/assert.h"
8
#include "source/common/http/http1/parser.h"
9

            
10
namespace Envoy {
11
namespace Http {
12
namespace Http1 {
13
namespace {
14

            
15
8280
ParserStatus intToStatus(int rc) {
16
8280
  switch (rc) {
17
8262
  case 0:
18
8262
    return ParserStatus::Ok;
19
  case 31:
20
    return ParserStatus::Paused;
21
18
  default:
22
18
    return ParserStatus::Error;
23
8280
  }
24
8280
}
25

            
26
} // namespace
27

            
28
class LegacyHttpParserImpl::Impl {
29
public:
30
30
  Impl(http_parser_type type) {
31
30
    http_parser_init(&parser_, type);
32
30
    parser_.allow_chunked_length = 1;
33
30
  }
34

            
35
30
  Impl(http_parser_type type, void* data) : Impl(type) {
36
30
    parser_.data = data;
37
30
    settings_ = {
38
30
        [](http_parser* parser) -> int {
39
19
          auto* conn_impl = static_cast<ParserCallbacks*>(parser->data);
40
19
          return static_cast<int>(conn_impl->onMessageBegin());
41
19
        },
42
43
        [](http_parser* parser, const char* at, size_t length) -> int {
43
40
          auto* conn_impl = static_cast<ParserCallbacks*>(parser->data);
44
40
          return static_cast<int>(conn_impl->onUrl(at, length));
45
40
        },
46
30
        [](http_parser* parser, const char* at, size_t length) -> int {
47
          auto* conn_impl = static_cast<ParserCallbacks*>(parser->data);
48
          return static_cast<int>(conn_impl->onStatus(at, length));
49
        },
50
30
        [](http_parser* parser, const char* at, size_t length) -> int {
51
          auto* conn_impl = static_cast<ParserCallbacks*>(parser->data);
52
          return static_cast<int>(conn_impl->onHeaderField(at, length));
53
        },
54
30
        [](http_parser* parser, const char* at, size_t length) -> int {
55
          auto* conn_impl = static_cast<ParserCallbacks*>(parser->data);
56
          return static_cast<int>(conn_impl->onHeaderValue(at, length));
57
        },
58
30
        [](http_parser* parser) -> int {
59
          auto* conn_impl = static_cast<ParserCallbacks*>(parser->data);
60
          return static_cast<int>(conn_impl->onHeadersComplete());
61
        },
62
30
        [](http_parser* parser, const char* at, size_t length) -> int {
63
          static_cast<ParserCallbacks*>(parser->data)->bufferBody(at, length);
64
          return 0;
65
        },
66
30
        [](http_parser* parser) -> int {
67
          auto* conn_impl = static_cast<ParserCallbacks*>(parser->data);
68
          return static_cast<int>(conn_impl->onMessageComplete());
69
        },
70
30
        [](http_parser* parser) -> int {
71
          // A 0-byte chunk header is used to signal the end of the chunked body.
72
          // When this function is called, http-parser holds the size of the chunk in
73
          // parser->content_length. See
74
          // https://github.com/nodejs/http-parser/blob/v2.9.3/http_parser.h#L336
75
          const bool is_final_chunk = (parser->content_length == 0);
76
          static_cast<ParserCallbacks*>(parser->data)->onChunkHeader(is_final_chunk);
77
          return 0;
78
        },
79
30
        nullptr // on_chunk_complete
80
30
    };
81
30
  }
82

            
83
8271
  size_t execute(const char* slice, int len) {
84
8271
    return http_parser_execute(&parser_, &settings_, slice, len);
85
8271
  }
86

            
87
  void resume() { http_parser_pause(&parser_, 0); }
88

            
89
  CallbackResult pause() {
90
    http_parser_pause(&parser_, 1);
91
    return CallbackResult::Success;
92
  }
93

            
94
8280
  int getErrno() { return HTTP_PARSER_ERRNO(&parser_); }
95

            
96
  Envoy::Http::Code statusCode() const { return static_cast<Http::Code>(parser_.status_code); }
97

            
98
11
  bool isHttp11() const { return parser_.http_major == 1 && parser_.http_minor == 1; }
99

            
100
  absl::optional<uint64_t> contentLength() const {
101
    // An unset content length will be have all bits set.
102
    // See
103
    // https://github.com/nodejs/http-parser/blob/ec8b5ee63f0e51191ea43bb0c6eac7bfbff3141d/http_parser.h#L311
104
    if (parser_.content_length == ULLONG_MAX) {
105
      return absl::nullopt;
106
    }
107
    return parser_.content_length;
108
  }
109

            
110
  bool isChunked() const { return parser_.flags & F_CHUNKED; }
111

            
112
  absl::string_view methodName() const {
113
    return http_method_str(static_cast<http_method>(parser_.method));
114
  }
115

            
116
  int hasTransferEncoding() const { return parser_.uses_transfer_encoding; }
117

            
118
private:
119
  http_parser parser_;
120
  http_parser_settings settings_;
121
};
122

            
123
30
LegacyHttpParserImpl::LegacyHttpParserImpl(MessageType type, ParserCallbacks* data) {
124
30
  http_parser_type parser_type;
125
30
  switch (type) {
126
30
  case MessageType::Request:
127
30
    parser_type = HTTP_REQUEST;
128
30
    break;
129
  case MessageType::Response:
130
    parser_type = HTTP_RESPONSE;
131
    break;
132
30
  }
133

            
134
30
  impl_ = std::make_unique<Impl>(parser_type, data);
135
30
}
136

            
137
// Because we have a pointer-to-impl using std::unique_ptr, we must place the destructor in the
138
// same compilation unit so that the destructor has a complete definition of Impl.
139
30
LegacyHttpParserImpl::~LegacyHttpParserImpl() = default;
140

            
141
8271
size_t LegacyHttpParserImpl::execute(const char* slice, int len) {
142
8271
  return impl_->execute(slice, len);
143
8271
}
144

            
145
void LegacyHttpParserImpl::resume() { impl_->resume(); }
146

            
147
CallbackResult LegacyHttpParserImpl::pause() { return impl_->pause(); }
148

            
149
8280
ParserStatus LegacyHttpParserImpl::getStatus() const { return intToStatus(impl_->getErrno()); }
150

            
151
Http::Code LegacyHttpParserImpl::statusCode() const { return impl_->statusCode(); }
152

            
153
11
bool LegacyHttpParserImpl::isHttp11() const { return impl_->isHttp11(); }
154

            
155
absl::optional<uint64_t> LegacyHttpParserImpl::contentLength() const {
156
  return impl_->contentLength();
157
}
158

            
159
bool LegacyHttpParserImpl::isChunked() const { return impl_->isChunked(); }
160

            
161
absl::string_view LegacyHttpParserImpl::methodName() const { return impl_->methodName(); }
162

            
163
absl::string_view LegacyHttpParserImpl::errorMessage() const {
164
  return http_errno_name(static_cast<http_errno>(impl_->getErrno()));
165
}
166

            
167
int LegacyHttpParserImpl::hasTransferEncoding() const { return impl_->hasTransferEncoding(); }
168

            
169
} // namespace Http1
170
} // namespace Http
171
} // namespace Envoy