Line data Source code
1 : /*
2 : * Copyright (c) 2013 Juniper Networks, Inc. All rights reserved.
3 : */
4 :
5 : #include "http/http_session.h"
6 :
7 : #include <map>
8 : #include <boost/bind/bind.hpp>
9 : #include <cstdio>
10 :
11 : #include "base/logging.h"
12 : #include "base/task.h"
13 : #include "http/http_request.h"
14 : #include "http/http_server.h"
15 : #include "http_parser/http_parser.h"
16 : #include "sandesh/sandesh_http.h"
17 : #include "http/http_log_types.h"
18 :
19 : using namespace std;
20 : using namespace boost::placeholders;
21 :
22 : int HttpSession::req_handler_task_id_ = -1;
23 : HttpSession::map_type * HttpSession::context_map_ = 0;
24 : std::mutex HttpSession::map_mutex_;
25 : std::atomic<long> HttpSession::task_count_;
26 :
27 : // Input processing context
28 : class HttpSession::RequestBuilder {
29 : public:
30 10 : RequestBuilder() : complete_(false) {
31 10 : parser_.data = this;
32 10 : Clear();
33 10 : }
34 :
35 20 : void Clear() {
36 20 : http_parser_init(&parser_, HTTP_REQUEST);
37 20 : request_.reset(new HttpRequest());
38 20 : complete_ = false;
39 20 : header_key_.clear();
40 20 : header_value_.clear();
41 20 : }
42 10 : size_t Parse(const u_int8_t *data, size_t datalen) {
43 : size_t nparsed =
44 10 : http_parser_execute(&parser_, &settings_,
45 : reinterpret_cast<const char *>(data), datalen);
46 10 : return nparsed;
47 : }
48 :
49 10 : bool complete() const { return complete_; }
50 :
51 : // Transfers ownership
52 10 : HttpRequest *GetRequest() {
53 10 : HttpRequest *request = request_.get();
54 10 : request_.release();
55 10 : return request;
56 : }
57 : private:
58 10 : static int OnMessageBegin(struct http_parser *parser) {
59 10 : return 0;
60 : }
61 :
62 10 : static int OnHeadersComplete(struct http_parser *parser) {
63 10 : RequestBuilder *builder =
64 : reinterpret_cast<RequestBuilder *>(parser->data);
65 10 : builder->request_->SetMethod(static_cast<http_method>(parser->method));
66 10 : builder->request_->SetUrl(&builder->tmp_url_);
67 10 : builder->PushHeader();
68 10 : return 0;
69 : }
70 :
71 10 : static int OnMessageComplete(struct http_parser *parser) {
72 10 : RequestBuilder *builder =
73 : reinterpret_cast<RequestBuilder *>(parser->data);
74 10 : builder->complete_ = true;
75 10 : return 0;
76 : }
77 :
78 10 : static int OnUrl(struct http_parser *parser,
79 : const char *loc, size_t length) {
80 10 : RequestBuilder *builder =
81 : reinterpret_cast<RequestBuilder *>(parser->data);
82 10 : builder->tmp_url_.append(loc, length);
83 10 : return 0;
84 : }
85 :
86 0 : static int OnStatusComplete(struct http_parser *parser) {
87 0 : return 0;
88 : }
89 :
90 50 : static int OnHeaderField(struct http_parser *parser,
91 : const char *loc, size_t length) {
92 50 : RequestBuilder *builder =
93 : reinterpret_cast<RequestBuilder *>(parser->data);
94 50 : if (!builder->header_value_.empty()) {
95 40 : builder->PushHeader();
96 : }
97 50 : builder->header_key_.append(loc, length);
98 50 : return 0;
99 : }
100 :
101 50 : static int OnHeaderValue(struct http_parser *parser,
102 : const char *loc, size_t length) {
103 50 : RequestBuilder *builder =
104 : reinterpret_cast<RequestBuilder *>(parser->data);
105 50 : builder->header_value_.append(loc, length);
106 50 : return 0;
107 : }
108 :
109 0 : static int OnBody(struct http_parser *parser,
110 : const char *loc, size_t length) {
111 0 : RequestBuilder *builder =
112 : reinterpret_cast<RequestBuilder *>(parser->data);
113 0 : builder->request_->SetBody(loc, length);
114 0 : return 0;
115 : }
116 :
117 50 : void PushHeader() {
118 50 : request_->PushHeader(header_key_, header_value_);
119 50 : header_key_.clear();
120 50 : header_value_.clear();
121 50 : }
122 :
123 : static struct http_parser_settings settings_;
124 :
125 : struct http_parser parser_;
126 : std::unique_ptr<HttpRequest> request_;
127 :
128 : bool complete_;
129 : string tmp_url_; // temporary: used while parsing
130 : string header_key_;
131 : string header_value_;
132 : };
133 :
134 : struct http_parser_settings HttpSession::RequestBuilder::settings_ = {
135 : OnMessageBegin,
136 : OnUrl,
137 : OnStatusComplete,
138 : OnHeaderField,
139 : OnHeaderValue,
140 : OnHeadersComplete,
141 : OnBody,
142 : OnMessageComplete
143 : };
144 :
145 : #undef HTTP_SYS_LOG
146 : #define HTTP_SYS_LOG(...)
147 :
148 : class HttpSession::RequestHandler : public Task {
149 : public:
150 20 : explicit RequestHandler(HttpSession *session)
151 20 : : Task(req_handler_task_id_, session->GetSessionInstance()),
152 20 : session_(session) {
153 20 : }
154 :
155 40 : ~RequestHandler() {
156 : HTTP_SYS_LOG("HttpSession", SandeshLevel::UT_INFO, "RequestHandler destructor");
157 40 : }
158 :
159 0 : void NotFound(HttpSession *session, const HttpRequest *request) {
160 : static const char no_response[] =
161 : "HTTP/1.1 404 Not Found\r\n"
162 : "Content-Type: text/html; charset=UTF-8\r\n"
163 : "Content-Length: 46\r\n"
164 : "\r\n"
165 : "<html>\n"
166 : "<title>404 Not Found</title>\n"
167 : "</html>\r\n"
168 : ;
169 0 : session->Send(reinterpret_cast<const u_int8_t *>(no_response),
170 : sizeof(no_response), NULL);
171 0 : delete request;
172 0 : }
173 :
174 : // Retrieve a request item from the queue. Return true if the queue
175 : // is empty.
176 40 : bool FromQ(HttpRequest *& r) {
177 40 : return !session_->request_queue_.try_pop(r);
178 : }
179 20 : virtual bool Run() {
180 : HTTP_SYS_LOG("HttpSession", SandeshLevel::UT_INFO,
181 : "RequestHandler execute");
182 20 : HttpRequest *request = NULL;
183 20 : bool del_session = false;
184 20 : HttpServer *server = static_cast<HttpServer *>(session_->server());
185 : while (true) {
186 40 : request = NULL;
187 40 : session_->req_queue_empty_ = FromQ(request);
188 40 : if (!request) break;
189 : HTTP_SYS_LOG("HttpSession", SandeshLevel::UT_INFO,
190 : "URL is " + request->ToString());
191 20 : if (request->ToString().empty()) {
192 10 : if (session_->event_cb_ && !session_->event_cb_.empty()) {
193 0 : session_->event_cb_(session_.get(), request->Event());
194 : }
195 10 : del_session = true;
196 10 : session_->set_observer(NULL);
197 10 : session_->Close();
198 10 : delete request;
199 : } else {
200 : HttpServer::HttpHandlerFn handler =
201 10 : server->GetHandler(request->UrlPath());
202 10 : if (handler == NULL) {
203 : handler = boost::bind(&RequestHandler::NotFound,
204 0 : this, _1, _2);
205 : }
206 10 : handler(session_.get(), request);
207 10 : }
208 20 : }
209 20 : if (del_session) {
210 : HTTP_SYS_LOG("HttpSession", SandeshLevel::UT_INFO, "DeleteSession "
211 : + session_->ToString());
212 10 : session_->set_observer(NULL);
213 10 : server->DeleteSession(session_.get());
214 : }
215 20 : HttpSession::task_count_--;
216 20 : return true;
217 : }
218 0 : std::string Description() const { return "HttpSession::RequestHandler"; }
219 : private:
220 : HttpSessionPtr session_;
221 : };
222 :
223 10 : HttpSession::HttpSession(HttpServer *server, SslSocket *socket,
224 10 : bool async_ready)
225 10 : : SslSession(server, socket, async_ready), event_cb_(NULL) {
226 10 : if (req_handler_task_id_ == -1) {
227 2 : TaskScheduler *scheduler = TaskScheduler::GetInstance();
228 2 : req_handler_task_id_ = scheduler->GetTaskId("http::RequestHandlerTask");
229 : }
230 10 : req_queue_empty_ = true;
231 10 : set_observer(boost::bind(&HttpSession::OnSessionEvent, this, _1, _2));
232 10 : }
233 :
234 20 : HttpSession::~HttpSession() {
235 10 : HttpRequest *request = NULL;
236 10 : while (request_queue_.try_pop(request)) {
237 0 : delete request;
238 : }
239 20 : }
240 :
241 10 : void HttpSession::AcceptSession() {
242 10 : std::scoped_lock lock(map_mutex_);
243 10 : context_str_ = "http%" + ToString();
244 10 : GetMap()->insert(std::make_pair(context_str_, HttpSessionPtr(this)));
245 : HTTP_SYS_LOG("HttpSession", SandeshLevel::UT_INFO,
246 : "Created Session " + context_str_);
247 10 : }
248 :
249 0 : void HttpSession::RegisterEventCb(SessionEventCb cb) {
250 0 : event_cb_ = cb;
251 0 : }
252 :
253 20 : void HttpSession::OnSessionEvent(TcpSession *session,
254 : enum TcpSession::Event event) {
255 20 : HttpSession *h_session = dynamic_cast<HttpSession *>(session);
256 20 : assert(h_session);
257 :
258 20 : switch (event) {
259 10 : case TcpSession::CLOSE:
260 : {
261 : {
262 10 : std::scoped_lock lock(map_mutex_);
263 10 : if (GetMap()->erase(h_session->context_str_)) {
264 : HTTP_SYS_LOG("HttpSession", SandeshLevel::UT_INFO,
265 : "Removed Session " + h_session->context_str_);
266 : } else {
267 : HTTP_SYS_LOG("HttpSession", SandeshLevel::UT_INFO,
268 : "Not Removed Session " + h_session->context_str_);
269 : }
270 10 : }
271 10 : h_session->context_str_ = "";
272 10 : HttpRequest *request = new HttpRequest();
273 10 : string nourl = "";
274 10 : request->SetUrl(&nourl);
275 10 : request->SetEvent(event);
276 10 : request_queue_.push(request);
277 : // Enqueue new RequestHandler task if needed
278 10 : if (req_queue_empty_) {
279 10 : TaskScheduler *scheduler = TaskScheduler::GetInstance();
280 10 : RequestHandler *task = new RequestHandler(this);
281 10 : HttpSession::task_count_++;
282 10 : scheduler->Enqueue(task);
283 : }
284 10 : }
285 10 : break;
286 10 : default:
287 10 : break;
288 : }
289 20 : }
290 :
291 20 : void HttpSession::OnRead(Buffer buffer) {
292 20 : const u_int8_t *data = BufferData(buffer);
293 20 : size_t size = BufferSize(buffer);
294 20 : std::stringstream msg;
295 :
296 20 : msg << "HttpSession::Read " << size << " bytes";
297 : HTTP_SYS_LOG("HttpSession", SandeshLevel::UT_DEBUG, msg.str());
298 :
299 : // No need to proceed if size is 0 which can be the case with ssl
300 20 : if (size == 0 || context_str_.size() == 0) {
301 10 : ReleaseBuffer(buffer);
302 10 : return;
303 : }
304 10 : if (request_builder_.get() == NULL) {
305 10 : request_builder_.reset(new RequestBuilder());
306 : }
307 10 : request_builder_->Parse(data, size);
308 10 : if (request_builder_->complete()) {
309 10 : HttpRequest *request = request_builder_->GetRequest();
310 : HTTP_SYS_LOG("HttpSession", SandeshLevel::UT_DEBUG, request->ToString());
311 10 : request_queue_.push(request);
312 : // Enqueue new RequestHandler task if needed
313 10 : if (req_queue_empty_) {
314 10 : TaskScheduler *scheduler = TaskScheduler::GetInstance();
315 10 : RequestHandler *task = new RequestHandler(this);
316 10 : HttpSession::task_count_++;
317 10 : scheduler->Enqueue(task);
318 : }
319 10 : request_builder_->Clear();
320 : }
321 : // TODO: error handling
322 10 : ReleaseBuffer(buffer);
323 20 : }
|