1 /* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3 * Copyright 2014-2020 Couchbase, Inc.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 #include "lcbht.h"
19 #include "contrib/http_parser/http_parser.h"
20 #include "settings.h"
21
22 using namespace lcb::htparse;
23
Parser(lcb_settings_st *settings_)24 Parser::Parser(lcb_settings_st *settings_) : http_parser(), settings(settings_)
25 {
26 lcb_settings_ref(settings);
27 reset();
28 }
29
~Parser()30 Parser::~Parser()
31 {
32 lcb_settings_unref(settings);
33 }
34
on_hdr_key(http_parser *pb, const char *s, size_t n)35 static int on_hdr_key(http_parser *pb, const char *s, size_t n)
36 {
37 return Parser::from_htp(pb)->on_hdr_key(s, n);
38 }
on_hdr_key(const char *s, size_t n)39 int Parser::on_hdr_key(const char *s, size_t n)
40 {
41 if (lastcall != CB_HDR_KEY) {
42 /* new key */
43 resp.headers.emplace_back(MimeHeader());
44 }
45
46 resp.headers.back().key.append(s, n);
47 lastcall = CB_HDR_KEY;
48 return 0;
49 }
50
on_hdr_value(http_parser *pb, const char *s, size_t n)51 static int on_hdr_value(http_parser *pb, const char *s, size_t n)
52 {
53 return Parser::from_htp(pb)->on_hdr_value(s, n);
54 }
55
on_hdr_value(const char *s, size_t n)56 int Parser::on_hdr_value(const char *s, size_t n)
57 {
58 MimeHeader *header = &resp.headers.back();
59 header->value.append(s, n);
60 lastcall = CB_HDR_VALUE;
61 return 0;
62 }
63
on_hdr_done(http_parser *pb)64 static int on_hdr_done(http_parser *pb)
65 {
66 return Parser::from_htp(pb)->on_hdr_done();
67 }
on_hdr_done()68 int Parser::on_hdr_done()
69 {
70 resp.state |= S_HTSTATUS | S_HEADER;
71
72 /* extract the status */
73 resp.status = http_parser::status_code;
74 lastcall = CB_HDR_DONE;
75 return 0;
76 }
77
on_body(http_parser *pb, const char *s, size_t n)78 static int on_body(http_parser *pb, const char *s, size_t n)
79 {
80 return Parser::from_htp(pb)->on_body(s, n);
81 }
on_body(const char *s, size_t n)82 int Parser::on_body(const char *s, size_t n)
83 {
84 if (is_ex) {
85 last_body = s;
86 last_bodylen = n;
87 paused = true;
88 _lcb_http_parser_pause(this, 1);
89 } else {
90 resp.body.append(s, n);
91 }
92
93 lastcall = CB_BODY;
94 resp.state |= S_BODY;
95 return 0;
96 }
97
on_msg_done(http_parser *pb)98 static int on_msg_done(http_parser *pb)
99 {
100 return Parser::from_htp(pb)->on_msg_done();
101 }
102
on_msg_done()103 int Parser::on_msg_done()
104 {
105 resp.state |= S_DONE;
106 return 0;
107 }
108
109 static const struct http_parser_settings Parser_Settings = {nullptr, /* msg_begin */
110 nullptr, /* on_url */
111 ::on_hdr_key, ::on_hdr_value, ::on_hdr_done,
112 ::on_body, ::on_msg_done};
113
parse(const void *data_, size_t ndata)114 unsigned Parser::parse(const void *data_, size_t ndata)
115 {
116 is_ex = false;
117 size_t nb = _lcb_http_parser_execute(this, &Parser_Settings, reinterpret_cast<const char *>(data_), ndata);
118
119 if (nb != ndata) {
120 resp.state |= S_ERROR;
121 }
122
123 return resp.state;
124 }
125
parse_ex(const void *data_, unsigned ndata, unsigned *nused, unsigned *nbody, const char **pbody)126 unsigned Parser::parse_ex(const void *data_, unsigned ndata, unsigned *nused, unsigned *nbody, const char **pbody)
127 {
128 is_ex = true;
129 size_t nb = _lcb_http_parser_execute(this, &Parser_Settings, reinterpret_cast<const char *>(data_), ndata);
130 if (nb != ndata) {
131 if (paused) {
132 _lcb_http_parser_pause(this, 0);
133 paused = false;
134 } else {
135 resp.state |= S_ERROR;
136 return resp.state;
137 }
138 }
139
140 *nused = nb;
141 *nbody = last_bodylen;
142 *pbody = last_body;
143
144 last_body = nullptr;
145 last_bodylen = 0;
146 return resp.state;
147 }
148
can_keepalive() const149 bool Parser::can_keepalive() const
150 {
151 if (!(resp.state & S_DONE)) {
152 return false;
153 }
154
155 if (resp.state & S_ERROR) {
156 return false;
157 }
158
159 return _lcb_http_should_keep_alive(const_cast<http_parser *>(static_cast<const http_parser *>(this)));
160 }
161
reset()162 void Parser::reset()
163 {
164 resp.clear();
165 _lcb_http_parser_init(this, HTTP_RESPONSE);
166 }
167
get_header(const std::string &key) const168 const MimeHeader *Response::get_header(const std::string &key) const
169 {
170 std::list<MimeHeader>::const_iterator it;
171 for (it = headers.begin(); it != headers.end(); ++it) {
172 if (it->key == key) {
173 return &*it;
174 }
175 }
176 return nullptr;
177 }
178