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