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