1 #ifndef HTTPSERVERTRANSPORT__HPP 2 #define HTTPSERVERTRANSPORT__HPP 3 4 /* $Id: http_server_transport.hpp 629837 2021-04-22 12:47:49Z ivanov $ 5 * =========================================================================== 6 * 7 * PUBLIC DOMAIN NOTICE 8 * National Center for Biotechnology Information 9 * 10 * This software/database is a "United States Government Work" under the 11 * terms of the United States Copyright Act. It was written as part of 12 * the author's official duties as a United States Government employee and 13 * thus cannot be copyrighted. This software/database is freely available 14 * to the public for use. The National Library of Medicine and the U.S. 15 * Government have not placed any restriction on its use or reproduction. 16 * 17 * Although all reasonable efforts have been taken to ensure the accuracy 18 * and reliability of the software and data, the NLM and the U.S. 19 * Government do not and cannot warrant the performance or results that 20 * may be obtained by using this software or data. The NLM and the U.S. 21 * Government disclaim all warranties, express or implied, including 22 * warranties of performance, merchantability or fitness for any particular 23 * purpose. 24 * 25 * Please cite the author in any work or product based on this material. 26 * 27 * =========================================================================== 28 * 29 * Authors: Dmitri Dmitrienko 30 * 31 * File Description: 32 * 33 */ 34 35 #include <string> 36 #include <vector> 37 #include <list> 38 #include <memory> 39 #include <new> 40 #include <atomic> 41 42 #include <uv.h> 43 #include <h2o.h> 44 45 #include <connect/ncbi_ipv6.h> 46 #include <objtools/pubseq_gateway/impl/cassandra/cass_driver.hpp> 47 48 #include "pubseq_gateway_exception.hpp" 49 #include "tcp_daemon.hpp" 50 #include "pubseq_gateway_logging.hpp" 51 #include "pubseq_gateway_types.hpp" 52 #include "psgs_reply.hpp" 53 54 #include "shutdown_data.hpp" 55 extern SShutdownData g_ShutdownData; 56 57 USING_NCBI_SCOPE; 58 USING_IDBLOB_SCOPE; 59 60 #define CONTAINER_OF(ptr, type, member) ({ \ 61 const typeof(((type*)(0))->member) *__mptr = ((const typeof(((type*)(0))->member) *)(ptr)); \ 62 (type*)((char*)(__mptr) - offsetof(type, member)); \ 63 }) 64 65 66 #define MAX_QUERY_PARAMS 64 67 #define QUERY_PARAMS_RAW_BUF_SIZE 2048 68 69 70 static const char * k_ReasonOK = "OK"; 71 static const char * k_ReasonAccepted = "Accepted"; 72 static const char * k_InternalServerError = "Internal Server Error"; 73 static const char * k_BadGateway = "Bad Gateway"; 74 static const char * k_ServiceUnavailable = "Service Unavailable"; 75 static const char * k_Conflict = "Conflict"; 76 static const char * k_NotFound = "Not Found"; 77 static const char * k_Unauthorized = "Unauthorized"; 78 static const char * k_BadRequest = "Bad Request"; 79 80 void GetSSLSettings(bool & enabled, 81 string & cert_file, 82 string & key_file, 83 string & ciphers); 84 85 struct CQueryParam 86 { 87 const char * m_Name; 88 const char * m_Val; 89 size_t m_NameLen; 90 size_t m_ValLen; 91 }; 92 93 template<typename P> 94 class CHttpDaemon; 95 96 template<typename P> 97 class CHttpProto; 98 99 template<typename P> 100 class CHttpConnection; 101 102 template<typename P> 103 class CHttpReply 104 { 105 public: 106 enum EReplyState { 107 eReplyInitialized, 108 eReplyStarted, 109 eReplyFinished 110 }; 111 CHttpReply(h2o_req_t * req,CHttpProto<P> * proto,CHttpConnection<P> * http_conn,const char * cd_uid)112 CHttpReply(h2o_req_t * req, CHttpProto<P> * proto, 113 CHttpConnection<P> * http_conn, 114 const char * cd_uid) : 115 m_Req(req), 116 m_RespGenerator({0}), 117 m_OutputIsReady(true), 118 m_OutputFinished(false), 119 m_Postponed(false), 120 m_Cancelled(false), 121 m_State(eReplyInitialized), 122 m_HttpProto(proto), 123 m_HttpConn(http_conn), 124 m_DataReady(make_shared<CDataTrigger>(proto)), 125 m_ReplyContentType(ePSGS_NotSet), 126 m_CdUid(cd_uid) 127 {} 128 129 CHttpReply(const CHttpReply&) = delete; 130 CHttpReply(CHttpReply&&) = delete; 131 CHttpReply& operator=(const CHttpReply&) = delete; 132 CHttpReply& operator=(CHttpReply&&) = delete; 133 ~CHttpReply()134 ~CHttpReply() 135 { 136 PSG_TRACE("~CHttpReply"); 137 Clear(); 138 } 139 Clear(void)140 void Clear(void) 141 { 142 for (auto req: m_PendingReqs) { 143 req = nullptr; 144 } 145 m_PendingReqs.clear(); 146 147 m_Req = nullptr; 148 m_RespGenerator = {0}; 149 m_OutputIsReady = false; 150 m_OutputFinished = false; 151 m_Postponed = false; 152 m_Cancelled = false; 153 m_State = eReplyInitialized; 154 m_HttpProto = nullptr; 155 m_HttpConn = nullptr; 156 m_ReplyContentType = ePSGS_NotSet; 157 } 158 AssignPendingReq(unique_ptr<P> pending_req)159 void AssignPendingReq(unique_ptr<P> pending_req) 160 { 161 m_PendingReqs.emplace_back(move(pending_req)); 162 } 163 164 // The method is used only when the reply is finished. 165 // See the comments at the point of invocation. ResetPendingRequest(void)166 void ResetPendingRequest(void) 167 { 168 for (auto req: m_PendingReqs) { 169 req = nullptr; 170 } 171 m_PendingReqs.clear(); 172 } 173 SetContentLength(uint64_t content_length)174 void SetContentLength(uint64_t content_length) 175 { 176 if (m_State == eReplyInitialized) { 177 m_Req->res.content_length = content_length; 178 } else { 179 NCBI_THROW(CPubseqGatewayException, eReplyAlreadyStarted, 180 "Reply has already started"); 181 } 182 } 183 SetContentType(EPSGS_ReplyMimeType mime_type)184 void SetContentType(EPSGS_ReplyMimeType mime_type) 185 { 186 m_ReplyContentType = mime_type; 187 } 188 Send(const char * payload,size_t payload_len,bool is_persist,bool is_last)189 void Send(const char * payload, size_t payload_len, 190 bool is_persist, bool is_last) 191 { 192 h2o_iovec_t body; 193 if (payload_len == 0 || (is_persist && !is_last)) { 194 body.base = (char*)payload; 195 body.len = payload_len; 196 } else { 197 body = h2o_strdup(&m_Req->pool, payload, payload_len); 198 } 199 x_DoSend(&body, payload_len > 0 ? 1 : 0, is_last); 200 } 201 Send(std::vector<h2o_iovec_t> & payload,bool is_last)202 void Send(std::vector<h2o_iovec_t> & payload, bool is_last) 203 { 204 size_t payload_size = payload.size(); 205 if (payload_size > 0 || is_last) { 206 if (payload_size > 0) 207 x_DoSend(&payload.front(), payload_size, is_last); 208 else 209 x_DoSend(nullptr, payload_size, is_last); 210 } 211 } 212 SendOk(const char * payload,size_t payload_len,bool is_persist)213 void SendOk(const char * payload, size_t payload_len, bool is_persist) 214 { Send(payload, payload_len, is_persist, true); } 215 Send202(const char * payload,size_t payload_len)216 void Send202(const char * payload, size_t payload_len) 217 { 218 h2o_iovec_t body = h2o_strdup(&m_Req->pool, payload, payload_len); 219 x_DoSend(&body, 1, true, 202, k_ReasonAccepted); 220 } 221 Send400(const char * payload)222 void Send400(const char * payload) 223 { x_GenericSendError(400, k_BadRequest, payload); } 224 Send401(const char * payload)225 void Send401(const char * payload) 226 { x_GenericSendError(401, k_Unauthorized, payload); } 227 Send404(const char * payload)228 void Send404(const char * payload) 229 { x_GenericSendError(404, k_NotFound, payload); } 230 Send409(const char * payload)231 void Send409(const char * payload) 232 { x_GenericSendError(409, k_Conflict, payload); } 233 Send500(const char * payload)234 void Send500(const char * payload) 235 { x_GenericSendError(500, k_InternalServerError, payload); } 236 Send502(const char * payload)237 void Send502(const char * payload) 238 { x_GenericSendError(502, k_BadGateway, payload); } 239 Send503(const char * payload)240 void Send503(const char * payload) 241 { x_GenericSendError(503, k_ServiceUnavailable, payload); } 242 GetHttpConnection(void)243 CHttpConnection<P> * GetHttpConnection(void) 244 { return m_HttpConn; } 245 PeekPending(void)246 void PeekPending(void) 247 { 248 try { 249 if (!m_Postponed) 250 NCBI_THROW(CPubseqGatewayException, eRequestNotPostponed, 251 "Request has not been postponed"); 252 for (auto req: m_PendingReqs) { 253 req->Peek(true); 254 } 255 } catch (const std::exception & e) { 256 Error(e.what()); 257 } catch (...) { 258 Error("unexpected failure"); 259 } 260 } 261 CancelPending(void)262 void CancelPending(void) 263 { 264 if (!m_Postponed) 265 NCBI_THROW(CPubseqGatewayException, eRequestNotPostponed, 266 "Request has not been postponed"); 267 x_DoCancel(); 268 } 269 GetState(void) const270 EReplyState GetState(void) const 271 { return m_State; } 272 IsFinished(void) const273 bool IsFinished(void) const 274 { return m_State >= eReplyFinished; } 275 IsOutputReady(void) const276 bool IsOutputReady(void) const 277 { return m_OutputIsReady; } 278 IsPostponed(void) const279 bool IsPostponed(void) const 280 { return m_Postponed; } 281 SetPostponed(void)282 void SetPostponed(void) 283 { m_Postponed = true; } 284 GetPendingReqs(void)285 list<shared_ptr<P>> GetPendingReqs(void) 286 { 287 if (m_PendingReqs.empty()) 288 NCBI_THROW(CPubseqGatewayException, ePendingReqNotAssigned, 289 "There are no assigned pending requests"); 290 return m_PendingReqs; 291 } 292 PrepareChunk(const unsigned char * data,unsigned int size)293 h2o_iovec_t PrepareChunk(const unsigned char * data, unsigned int size) 294 { 295 if (m_Req) 296 return h2o_strdup(&m_Req->pool, 297 reinterpret_cast<const char*>(data), size); 298 299 NCBI_THROW(CPubseqGatewayException, eRequestPoolNotAvailable, 300 "Request pool is not available"); 301 } 302 CheckResetDataTriggered(void)303 bool CheckResetDataTriggered(void) 304 { return m_DataReady->CheckResetTriggered(); } 305 Error(const char * what)306 void Error(const char * what) 307 { 308 switch (m_State) { 309 case eReplyInitialized: 310 x_SendPsg503(what, ePSGS_UnknownError); 311 break; 312 case eReplyStarted: 313 Send(nullptr, 0, true, true); // break 314 break; 315 default:; 316 } 317 CancelPending(); 318 } 319 GetDataReadyCB(void)320 shared_ptr<CCassDataCallbackReceiver> GetDataReadyCB(void) 321 { return static_pointer_cast<CCassDataCallbackReceiver>(m_DataReady); } 322 323 private: 324 struct CDataTrigger : public CCassDataCallbackReceiver 325 { 326 public: 327 CDataTrigger(const CDataTrigger & from) = delete; 328 CDataTrigger & operator=(const CDataTrigger & from) = delete; 329 CDataTrigger(CDataTrigger && from) = default; 330 CDataTrigger & operator=(CDataTrigger && from) = default; 331 CDataTriggerCHttpReply::CDataTrigger332 CDataTrigger(CHttpProto<P> * proto) : 333 m_Triggered(false), 334 m_Proto(proto) 335 {} 336 OnDataCHttpReply::CDataTrigger337 virtual void OnData() override 338 { 339 bool b = false; 340 if (m_Triggered.compare_exchange_weak(b, true) && m_Proto) 341 m_Proto->WakeWorker(); 342 343 } 344 CheckResetTriggeredCHttpReply::CDataTrigger345 bool CheckResetTriggered(void) 346 { 347 bool b = true; 348 return m_Triggered.compare_exchange_weak(b, false); 349 350 } 351 352 private: 353 std::atomic<bool> m_Triggered; 354 CHttpProto<P> * m_Proto; 355 }; 356 AssignGenerator(void)357 void AssignGenerator(void) 358 { 359 m_RespGenerator.stop = s_StopCB; 360 m_RespGenerator.proceed = s_ProceedCB; 361 } 362 NeedOutput(void)363 void NeedOutput(void) 364 { 365 if (m_State == eReplyFinished) { 366 PSG_TRACE("NeedOutput -> finished -> wake"); 367 m_HttpProto->WakeWorker(); 368 } else { 369 PeekPending(); 370 } 371 } 372 373 // Called by HTTP daemon when there is no way to send any further data 374 // using this connection StopCB(void)375 void StopCB(void) 376 { 377 PSG_TRACE("CHttpReply::Stop"); 378 m_OutputIsReady = true; 379 m_OutputFinished = true; 380 if (m_State != eReplyFinished) { 381 PSG_TRACE("CHttpReply::Stop: need cancel"); 382 x_DoCancel(); 383 NeedOutput(); 384 } 385 386 m_RespGenerator = {0}; 387 m_Req = nullptr; 388 } 389 390 // Called by HTTP daemon after data has already been sent and 391 // it is ready for the next portion ProceedCB(void)392 void ProceedCB(void) 393 { 394 PSG_TRACE("CHttpReply::Proceed"); 395 m_OutputIsReady = true; 396 NeedOutput(); 397 } 398 s_StopCB(h2o_generator_t * _generator,h2o_req_t * req)399 static void s_StopCB(h2o_generator_t * _generator, h2o_req_t * req) 400 { 401 // Note: it is expected to have a warning here. 402 // h2o_req_t structure does not have any user data field so a pointer 403 // to a reply is calculated by a pointer to one of it members. 404 // Unfortunately the reply has a non POD member as well - a list of 405 // pending operations for the request - so there is a warning. 406 CHttpReply<P> * http_reply = CONTAINER_OF(_generator, CHttpReply<P>, 407 m_RespGenerator); 408 http_reply->StopCB(); 409 } 410 s_ProceedCB(h2o_generator_t * _generator,h2o_req_t * req)411 static void s_ProceedCB(h2o_generator_t * _generator, h2o_req_t * req) 412 { 413 // Note: it is expected to have a warning here. 414 // h2o_req_t structure does not have any user data field so a pointer 415 // to a reply is calculated by a pointer to one of it members. 416 // Unfortunately the reply has a non POD member as well - a list of 417 // pending operations for the request - so there is a warning. 418 CHttpReply<P> * http_reply = CONTAINER_OF(_generator, CHttpReply<P>, 419 m_RespGenerator); 420 http_reply->ProceedCB(); 421 } 422 x_DoSend(h2o_iovec_t * vec,size_t count,bool is_last,int status=200,const char * reason=k_ReasonOK)423 void x_DoSend(h2o_iovec_t * vec, size_t count, bool is_last, 424 int status=200, const char * reason=k_ReasonOK) 425 { 426 if (!m_HttpConn) 427 NCBI_THROW(CPubseqGatewayException, eConnectionNotAssigned, 428 "Connection is not assigned"); 429 430 if (m_HttpConn->IsClosed()) { 431 m_OutputFinished = true; 432 if (count > 0) 433 PSG_ERROR("attempt to send " << count << " chunks (is_last=" << 434 is_last << ") to a closed connection"); 435 if (is_last) { 436 m_State = eReplyFinished; 437 } else { 438 x_DoCancel(); 439 } 440 return; 441 } 442 443 if (!m_OutputIsReady) 444 NCBI_THROW(CPubseqGatewayException, eOutputNotInReadyState, 445 "Output is not in ready state"); 446 447 PSG_TRACE("x_DoSend: " << count << " chunks, " 448 "is_last: " << is_last << ", state: " << m_State); 449 450 switch (m_State) { 451 case eReplyInitialized: 452 if (!m_Cancelled) { 453 x_SetContentType(); 454 x_SetCdUid(); 455 m_State = eReplyStarted; 456 m_Req->res.status = status; 457 m_Req->res.reason = reason; 458 AssignGenerator(); 459 m_OutputIsReady = false; 460 h2o_start_response(m_Req, &m_RespGenerator); 461 } 462 break; 463 case eReplyStarted: 464 break; 465 case eReplyFinished: 466 NCBI_THROW(CPubseqGatewayException, eRequestAlreadyFinished, 467 "Request has already been finished"); 468 break; 469 } 470 471 if (m_Cancelled) { 472 if (!m_OutputFinished && m_OutputIsReady) 473 x_SendCancelled(); 474 } else { 475 m_OutputIsReady = false; 476 h2o_send(m_Req, vec, count, 477 is_last ? H2O_SEND_STATE_FINAL : H2O_SEND_STATE_IN_PROGRESS); 478 } 479 480 if (is_last) { 481 m_State = eReplyFinished; 482 m_OutputFinished = true; 483 } 484 } 485 x_SendPsg503(const string & msg,EPSGS_PubseqGatewayErrorCode err_code)486 void x_SendPsg503(const string & msg, 487 EPSGS_PubseqGatewayErrorCode err_code) 488 { 489 CPSGS_Reply high_level_reply(this); 490 high_level_reply.PrepareReplyMessage( 491 msg, CRequestStatus::e503_ServiceUnavailable, 492 err_code, eDiag_Error); 493 high_level_reply.PrepareReplyCompletion(); 494 high_level_reply.Flush(); 495 } 496 x_SendCancelled(void)497 void x_SendCancelled(void) 498 { 499 if (m_Cancelled && m_OutputIsReady && !m_OutputFinished) { 500 x_SendPsg503("Request has been cancelled", ePSGS_RequestCancelled); 501 } 502 } 503 x_DoCancel(void)504 void x_DoCancel(void) 505 { 506 m_Cancelled = true; 507 if (m_HttpConn->IsClosed()) 508 m_OutputFinished = true; 509 510 if (!m_OutputFinished && m_OutputIsReady) 511 x_SendCancelled(); 512 for (auto req: m_PendingReqs) 513 req->ConnectionCancel(); 514 } 515 x_GenericSendError(int status,const char * head,const char * payload)516 void x_GenericSendError(int status, const char * head, 517 const char * payload) 518 { 519 if (!m_OutputIsReady) 520 NCBI_THROW(CPubseqGatewayException, eOutputNotInReadyState, 521 "Output is not in ready state"); 522 523 if (m_State != eReplyFinished) { 524 if (m_HttpConn->IsClosed()) 525 m_OutputFinished = true; 526 if (!m_OutputFinished) { 527 if (m_State == eReplyInitialized) { 528 x_SetContentType(); 529 x_SetCdUid(); 530 switch (status) { 531 case 400: 532 h2o_send_error_400(m_Req, head ? 533 head : "Bad Request", payload, 0); 534 break; 535 case 401: 536 h2o_send_error_generic(m_Req, 401, head ? 537 head : "Unauthorized", payload, 0); 538 break; 539 case 404: 540 h2o_send_error_404(m_Req, head ? 541 head : "Not Found", payload, 0); 542 break; 543 case 409: 544 h2o_send_error_generic(m_Req, 409, head ? 545 head : "Conflict", payload, 0); 546 break; 547 case 500: 548 h2o_send_error_500(m_Req, head ? 549 head : "Internal Server Error", payload, 0); 550 break; 551 case 502: 552 h2o_send_error_502(m_Req, head ? 553 head : "Bad Gateway", payload, 0); 554 break; 555 case 503: 556 h2o_send_error_503(m_Req, head ? 557 head : "Service Unavailable", payload, 0); 558 break; 559 default: 560 NCBI_THROW(CPubseqGatewayException, eLogic, 561 "Unknown HTTP status to send"); 562 } 563 } else { 564 h2o_send(m_Req, nullptr, 0, H2O_SEND_STATE_ERROR); 565 } 566 m_OutputFinished = true; 567 } 568 m_State = eReplyFinished; 569 } 570 571 } 572 x_SetContentType(void)573 void x_SetContentType(void) 574 { 575 if (m_ReplyContentType == ePSGS_NotSet) 576 return; 577 578 if (m_State != eReplyInitialized) 579 NCBI_THROW(CPubseqGatewayException, eReplyAlreadyStarted, 580 "Reply has already started"); 581 582 switch (m_ReplyContentType) { 583 case ePSGS_JsonMime: 584 h2o_add_header(&m_Req->pool, 585 &m_Req->res.headers, 586 H2O_TOKEN_CONTENT_TYPE, NULL, 587 H2O_STRLIT("application/json")); 588 break; 589 case ePSGS_BinaryMime: 590 h2o_add_header(&m_Req->pool, 591 &m_Req->res.headers, 592 H2O_TOKEN_CONTENT_TYPE, NULL, 593 H2O_STRLIT("application/octet-stream")); 594 break; 595 case ePSGS_PlainTextMime: 596 h2o_add_header(&m_Req->pool, 597 &m_Req->res.headers, 598 H2O_TOKEN_CONTENT_TYPE, NULL, 599 H2O_STRLIT("text/plain")); 600 break; 601 case ePSGS_ImageMime: 602 h2o_add_header(&m_Req->pool, 603 &m_Req->res.headers, 604 H2O_TOKEN_CONTENT_TYPE, NULL, 605 H2O_STRLIT("image/x-icon")); 606 break; 607 case ePSGS_PSGMime: 608 h2o_add_header(&m_Req->pool, 609 &m_Req->res.headers, 610 H2O_TOKEN_CONTENT_TYPE, NULL, 611 H2O_STRLIT("application/x-ncbi-psg")); 612 break; 613 default: 614 // Well, it is not good but without the content type everything 615 // will still work. 616 PSG_WARNING("Unknown content type " << m_ReplyContentType); 617 } 618 } 619 x_SetCdUid(void)620 void x_SetCdUid(void) 621 { 622 if (m_CdUid != nullptr) { 623 static int cd_uid_size = strlen(m_CdUid); 624 h2o_add_header_by_str(&m_Req->pool, &m_Req->res.headers, 625 H2O_STRLIT("X-CD-UID"), 0, NULL, 626 m_CdUid, cd_uid_size); 627 } 628 } 629 630 h2o_req_t * m_Req; 631 h2o_generator_t m_RespGenerator; 632 bool m_OutputIsReady; 633 bool m_OutputFinished; 634 bool m_Postponed; 635 bool m_Cancelled; 636 EReplyState m_State; 637 CHttpProto<P> * m_HttpProto; 638 CHttpConnection<P> * m_HttpConn; 639 640 list<shared_ptr<P>> m_PendingReqs; 641 642 shared_ptr<CDataTrigger> m_DataReady; 643 EPSGS_ReplyMimeType m_ReplyContentType; 644 const char * m_CdUid; 645 }; 646 647 648 649 template<typename P> 650 class CHttpConnection 651 { 652 public: CHttpConnection()653 CHttpConnection() : 654 m_HttpMaxBacklog(1024), 655 m_HttpMaxPending(16), 656 m_IsClosed(false) 657 {} 658 IsClosed(void) const659 bool IsClosed(void) const 660 { 661 return m_IsClosed; 662 } 663 Reset(void)664 void Reset(void) 665 { 666 auto cnt = m_Backlog.size(); 667 if (cnt > 0) { 668 m_Backlog.clear(); 669 g_ShutdownData.m_ActiveRequestCount -= cnt; 670 } 671 cnt = m_Pending.size(); 672 if (cnt > 0) { 673 m_Pending.clear(); 674 g_ShutdownData.m_ActiveRequestCount -= cnt; 675 } 676 m_IsClosed = false; 677 } 678 OnClosedConnection(void)679 void OnClosedConnection(void) 680 { 681 m_IsClosed = true; 682 x_CancelAll(); 683 } 684 OnBeforeClosedConnection(void)685 void OnBeforeClosedConnection(void) 686 { 687 PSG_TRACE("OnBeforeClosedConnection:"); 688 m_IsClosed = true; 689 x_CancelAll(); 690 } 691 s_OnBeforeClosedConnection(void * data)692 static void s_OnBeforeClosedConnection(void * data) 693 { 694 CHttpConnection<P> * p = static_cast<CHttpConnection<P>*>(data); 695 p->OnBeforeClosedConnection(); 696 } 697 PeekAsync(bool chk_data_ready)698 void PeekAsync(bool chk_data_ready) 699 { 700 for (auto & it: m_Pending) { 701 if (!chk_data_ready || 702 it->GetHttpReply()->CheckResetDataTriggered()) { 703 it->GetHttpReply()->PeekPending(); 704 } 705 } 706 x_MaintainFinished(); 707 x_MaintainBacklog(); 708 } 709 710 RegisterPending(list<unique_ptr<P>> pending_reqs,shared_ptr<CPSGS_Reply> reply)711 void RegisterPending(list<unique_ptr<P>> pending_reqs, 712 shared_ptr<CPSGS_Reply> reply) 713 { 714 if (m_Pending.size() < m_HttpMaxPending) { 715 auto req_it = x_RegisterPending(reply, m_Pending); 716 for (auto & pending_req: pending_reqs) { 717 reply->GetHttpReply()->AssignPendingReq(move(pending_req)); 718 } 719 PostponedStart(reply); 720 if (reply->IsFinished()) { 721 PSG_TRACE("Postpone self-drained"); 722 x_UnregisterPending(req_it); 723 } 724 } else if (m_Backlog.size() < m_HttpMaxBacklog) { 725 x_RegisterPending(reply, m_Backlog); 726 for (auto & pending_req: pending_reqs) { 727 reply->GetHttpReply()->AssignPendingReq(move(pending_req)); 728 } 729 } else { 730 reply->SetContentType(ePSGS_PSGMime); 731 reply->PrepareReplyMessage("Too many pending requests", 732 CRequestStatus::e503_ServiceUnavailable, 733 ePSGS_TooManyRequests, eDiag_Error); 734 reply->PrepareReplyCompletion(); 735 reply->Flush(); 736 } 737 } 738 PostponedStart(shared_ptr<CPSGS_Reply> reply)739 void PostponedStart(shared_ptr<CPSGS_Reply> reply) 740 { 741 auto http_reply = reply->GetHttpReply(); 742 if (!http_reply->IsPostponed()) 743 NCBI_THROW(CPubseqGatewayException, eRequestNotPostponed, 744 "Request has not been postponed"); 745 if (IsClosed()) 746 NCBI_THROW(CPubseqGatewayException, eConnectionClosed, 747 "Request handling can not be started after connection was closed"); 748 for (auto req: http_reply->GetPendingReqs()) 749 req->Start(); 750 } 751 Postpone(list<unique_ptr<P>> pending_reqs,shared_ptr<CPSGS_Reply> reply)752 void Postpone(list<unique_ptr<P>> pending_reqs, 753 shared_ptr<CPSGS_Reply> reply) 754 { 755 auto http_reply = reply->GetHttpReply(); 756 switch (http_reply->GetState()) { 757 case CHttpReply<P>::eReplyInitialized: 758 if (http_reply->IsPostponed()) 759 NCBI_THROW(CPubseqGatewayException, 760 eRequestAlreadyPostponed, 761 "Request has already been postponed"); 762 break; 763 case CHttpReply<P>::eReplyStarted: 764 NCBI_THROW(CPubseqGatewayException, eRequestCannotBePostponed, 765 "Request that has already started " 766 "can't be postponed"); 767 break; 768 default: 769 NCBI_THROW(CPubseqGatewayException, eRequestAlreadyFinished, 770 "Request has already been finished"); 771 break; 772 } 773 774 http_reply->SetPostponed(); 775 RegisterPending(move(pending_reqs), reply); 776 777 } OnTimer(void)778 void OnTimer(void) 779 { 780 PeekAsync(false); 781 // x_MaintainFinished(); 782 // x_MaintainBacklog(); 783 } 784 785 private: 786 unsigned short m_HttpMaxBacklog; 787 unsigned short m_HttpMaxPending; 788 bool m_IsClosed; 789 790 list<shared_ptr<CPSGS_Reply>> m_Backlog; 791 list<shared_ptr<CPSGS_Reply>> m_Pending; 792 793 using reply_list_iterator_t = typename list<shared_ptr<CPSGS_Reply>>::iterator; x_CancelAll(void)794 void x_CancelAll(void) 795 { 796 x_CancelBacklog(); 797 while (!m_Pending.empty()) { 798 x_MaintainFinished(); 799 for (auto & it: m_Pending) { 800 if (!it->IsFinished()) { 801 auto http_reply = it->GetHttpReply(); 802 http_reply->CancelPending(); 803 http_reply->PeekPending(); 804 } 805 } 806 x_MaintainFinished(); 807 } 808 } 809 x_UnregisterPending(reply_list_iterator_t & it)810 void x_UnregisterPending(reply_list_iterator_t & it) 811 { 812 // Note: without this call there will be memory leaks. 813 // The infrastructure holds a shared_ptr to the reply, the pending 814 // operation instance also holds a shared_ptr to the very same reply 815 // and the reply holds a shared_ptr to the pending operation instance. 816 // All together it forms a loop which needs to be broken for a correct 817 // memory management. 818 // The call below resets a shared_ptr to the pending operation. It is 819 // safe to do it here because this point is reached only when all 820 // activity on processing a request is over. 821 (*it)->GetHttpReply()->ResetPendingRequest(); 822 823 m_Pending.erase(it); 824 --g_ShutdownData.m_ActiveRequestCount; 825 } 826 x_RegisterPending(shared_ptr<CPSGS_Reply> reply,list<shared_ptr<CPSGS_Reply>> & reply_list)827 reply_list_iterator_t x_RegisterPending(shared_ptr<CPSGS_Reply> reply, 828 list<shared_ptr<CPSGS_Reply>> & reply_list) 829 { 830 ++g_ShutdownData.m_ActiveRequestCount; 831 reply_list.push_back(reply); 832 auto it = reply_list.end(); 833 --it; 834 return it; 835 } 836 x_MaintainFinished(void)837 void x_MaintainFinished(void) 838 { 839 auto it = m_Pending.begin(); 840 while (it != m_Pending.end()) { 841 if ((*it)->IsFinished()) { 842 auto next = it; 843 ++next; 844 x_UnregisterPending(it); 845 it = next; 846 } else { 847 ++it; 848 } 849 } 850 } 851 x_MaintainBacklog(void)852 void x_MaintainBacklog(void) 853 { 854 while (m_Pending.size() < m_HttpMaxPending && !m_Backlog.empty()) { 855 auto it = m_Backlog.begin(); 856 m_Pending.splice(m_Pending.cend(), m_Backlog, it); 857 PostponedStart(*it); 858 } 859 } 860 x_CancelBacklog(void)861 void x_CancelBacklog(void) 862 { 863 auto cnt = m_Backlog.size(); 864 if (cnt > 0) { 865 for (auto & it : m_Backlog) { 866 it->GetHttpReply()->CancelPending(); 867 } 868 869 m_Backlog.clear(); 870 g_ShutdownData.m_ActiveRequestCount -= cnt; 871 } 872 } 873 }; 874 875 876 class CHttpRequest; 877 878 class CHttpRequestParser 879 { 880 public: 881 virtual void Parse(CHttpRequest & req, 882 char * data, size_t length) const = 0; 883 }; 884 885 886 class CHttpGetParser: public CHttpRequestParser 887 { 888 void Parse(CHttpRequest & req, 889 char * data, size_t length) const override; 890 }; 891 892 893 class CHttpPostParser: public CHttpRequestParser 894 { 895 public: 896 virtual bool Supports(const char * content_type, 897 size_t content_type_len) const = 0; 898 }; 899 900 901 class CHttpRequest 902 { 903 private: 904 void ParseParams(void); 905 bool ContentTypeIsDdRpc(void); 906 907 public: CHttpRequest(h2o_req_t * req)908 CHttpRequest(h2o_req_t * req) : 909 m_Req(req), 910 m_ParamCount(0), 911 m_PostParser(nullptr), 912 m_GetParser(nullptr), 913 m_ParamParsed(false) 914 {} 915 SetPostParser(CHttpPostParser * parser)916 void SetPostParser(CHttpPostParser * parser) 917 { 918 m_PostParser = parser; 919 } 920 SetGetParser(CHttpRequestParser * parser)921 void SetGetParser(CHttpRequestParser * parser) 922 { 923 m_GetParser = parser; 924 } 925 926 bool GetParam(const char * name, size_t len, bool required, 927 const char ** value, size_t * value_len); 928 bool GetMultipleValuesParam(const char * name, size_t len, 929 vector<string> & values); 930 ParamCount(void) const931 size_t ParamCount(void) const 932 { 933 return m_ParamCount; 934 } 935 AddParam(void)936 CQueryParam * AddParam(void) 937 { 938 if (m_ParamCount < MAX_QUERY_PARAMS) 939 return &m_Params[m_ParamCount++]; 940 return nullptr; 941 } 942 RevokeParam(void)943 void RevokeParam(void) 944 { 945 if (m_ParamCount > 0) 946 m_ParamCount--; 947 } 948 GetRawBuffer(char ** buf,ssize_t * len)949 void GetRawBuffer(char ** buf, ssize_t * len) 950 { 951 *buf = m_RawBuf; 952 *len = sizeof(m_RawBuf); 953 } 954 955 // Used in PrintRequeststart() to have all the incoming parameters logged 956 CDiagContext_Extra & PrintParams(CDiagContext_Extra & extra); 957 void PrintLogFields(const CNcbiLogFields & log_fields); 958 959 string GetPath(void); 960 string GetHeaderValue(const string & name); 961 TNCBI_IPv6Addr GetClientIP(void); 962 string GetPeerIP(void); 963 GetEntity(void)964 CTempString GetEntity(void) 965 { 966 return CTempString(m_Req->entity.base, 967 m_Req->entity.len); 968 } 969 970 private: 971 h2o_req_t * m_Req; 972 CQueryParam m_Params[MAX_QUERY_PARAMS]; 973 char m_RawBuf[QUERY_PARAMS_RAW_BUF_SIZE]; 974 size_t m_ParamCount; 975 CHttpPostParser * m_PostParser; 976 CHttpRequestParser * m_GetParser; 977 bool m_ParamParsed; 978 }; 979 980 981 template<typename P> 982 using HttpHandlerFunction_t = std::function<void(CHttpRequest & req, 983 shared_ptr<CPSGS_Reply> reply)>; 984 985 template<typename P> 986 struct CHttpGateHandler 987 { InitCHttpGateHandler988 void Init(HttpHandlerFunction_t<P> * handler, 989 TSL::CTcpDaemon<CHttpProto<P>, CHttpConnection<P>, 990 CHttpDaemon<P>> * tcpd, CHttpDaemon<P> * httpd, 991 CHttpRequestParser * get_parser, 992 CHttpPostParser * post_parser) 993 { 994 m_Handler = handler; 995 m_Tcpd = tcpd; 996 m_Httpd = httpd; 997 m_GetParser = get_parser; 998 m_PostParser = post_parser; 999 } 1000 1001 struct st_h2o_handler_t m_H2oHandler; // must be first 1002 HttpHandlerFunction_t<P> * m_Handler; 1003 TSL::CTcpDaemon<CHttpProto<P>, 1004 CHttpConnection<P>, 1005 CHttpDaemon<P>> * m_Tcpd; 1006 CHttpDaemon<P> * m_Httpd; 1007 CHttpRequestParser * m_GetParser; 1008 CHttpPostParser * m_PostParser; 1009 }; 1010 1011 1012 1013 template<typename P> 1014 class CHttpProto 1015 { 1016 public: 1017 using worker_t = TSL::CTcpWorker<CHttpProto<P>, 1018 CHttpConnection<P>, CHttpDaemon<P>>; 1019 CHttpProto(CHttpDaemon<P> & daemon)1020 CHttpProto(CHttpDaemon<P> & daemon) : 1021 m_Worker(nullptr), 1022 m_Daemon(daemon), 1023 m_HttpCtx({0}), 1024 m_H2oCtxInitialized(false), 1025 m_HttpAcceptCtx({0}) 1026 { 1027 PSG_TRACE("CHttpProto::CHttpProto"); 1028 x_SetupSSL(); 1029 } 1030 ~CHttpProto()1031 ~CHttpProto() 1032 { 1033 if (m_HttpAcceptCtx.ssl_ctx) { 1034 SSL_CTX_free(m_HttpAcceptCtx.ssl_ctx); 1035 } 1036 1037 PSG_TRACE("~CHttpProto"); 1038 } 1039 BeforeStart(void)1040 void BeforeStart(void) 1041 {} 1042 ThreadStart(uv_loop_t * loop,worker_t * worker)1043 void ThreadStart(uv_loop_t * loop, worker_t * worker) 1044 { 1045 m_HttpAcceptCtx.ctx = &m_HttpCtx; 1046 m_HttpAcceptCtx.hosts = m_Daemon.HttpCfg()->hosts; 1047 h2o_context_init(&m_HttpCtx, loop, m_Daemon.HttpCfg()); 1048 m_Worker = worker; 1049 m_H2oCtxInitialized = true; 1050 } 1051 ThreadStop(void)1052 void ThreadStop(void) 1053 { 1054 m_Worker = nullptr; 1055 if (m_H2oCtxInitialized) { 1056 h2o_context_dispose(&m_HttpCtx); 1057 m_H2oCtxInitialized = false; 1058 } 1059 // h2o_mem_dispose_recycled_allocators(); 1060 } 1061 OnNewConnection(uv_stream_t * conn,CHttpConnection<P> * http_conn,uv_close_cb close_cb)1062 void OnNewConnection(uv_stream_t * conn, CHttpConnection<P> * http_conn, 1063 uv_close_cb close_cb) 1064 { 1065 int fd; 1066 h2o_socket_t * sock = h2o_uv_socket_create(conn, close_cb); 1067 1068 if (!sock) { 1069 PSG_ERROR("h2o layer failed to create socket"); 1070 uv_close((uv_handle_t*)conn, close_cb); 1071 return; 1072 } 1073 1074 h2o_accept(&m_HttpAcceptCtx, sock); 1075 if (uv_fileno(reinterpret_cast<uv_handle_t*>(conn), &fd) == 0) { 1076 int no = -1; 1077 struct linger linger = {0, 0}; 1078 int keepalive = 1; 1079 1080 setsockopt(fd, IPPROTO_TCP, TCP_LINGER2, &no, 1081 sizeof(no)); 1082 setsockopt(fd, SOL_SOCKET, SO_LINGER, &linger, 1083 sizeof(linger)); 1084 setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &keepalive, 1085 sizeof(keepalive)); 1086 } 1087 sock->on_close.cb = CHttpConnection<P>::s_OnBeforeClosedConnection; 1088 sock->on_close.data = http_conn; 1089 } 1090 OnClosedConnection(uv_stream_t * conn,CHttpConnection<P> * http_conn)1091 void OnClosedConnection(uv_stream_t * conn, 1092 CHttpConnection<P> * http_conn) 1093 { 1094 http_conn->OnClosedConnection(); 1095 } 1096 WakeWorker(void)1097 void WakeWorker(void) 1098 { 1099 if (m_Worker) 1100 m_Worker->WakeWorker(); 1101 } 1102 OnTimer(void)1103 void OnTimer(void) 1104 { 1105 auto & lst = m_Worker->GetConnList(); 1106 for (auto & it: lst) 1107 std::get<1>(it).OnTimer(); 1108 } 1109 OnAsyncWork(bool cancel)1110 void OnAsyncWork(bool cancel) 1111 { 1112 auto & lst = m_Worker->GetConnList(); 1113 for (auto & it: lst) { 1114 std::get<1>(it).PeekAsync(true); 1115 } 1116 } 1117 DaemonStarted()1118 static void DaemonStarted() {} DaemonStopped()1119 static void DaemonStopped() {} 1120 OnHttpRequest(CHttpGateHandler<P> * rh,h2o_req_t * req,const char * cd_uid)1121 int OnHttpRequest(CHttpGateHandler<P> * rh, h2o_req_t * req, const char * cd_uid) 1122 { 1123 h2o_conn_t * conn = req->conn; 1124 h2o_socket_t * sock = conn->callbacks->get_socket(conn); 1125 1126 assert(sock->on_close.data != nullptr); 1127 CHttpConnection<P> * http_conn = 1128 static_cast<CHttpConnection<P>*>(sock->on_close.data); 1129 CHttpRequest hreq(req); 1130 unique_ptr<CHttpReply<P>> low_level_reply( 1131 new CHttpReply<P>(req, this, 1132 http_conn, cd_uid)); 1133 shared_ptr<CPSGS_Reply> reply(new CPSGS_Reply(move(low_level_reply))); 1134 1135 try { 1136 if (rh->m_GetParser) 1137 hreq.SetGetParser(rh->m_GetParser); 1138 if (rh->m_PostParser) 1139 hreq.SetPostParser(rh->m_PostParser); 1140 (*rh->m_Handler)(hreq, reply); 1141 switch (reply->GetHttpReply()->GetState()) { 1142 case CHttpReply<P>::eReplyFinished: 1143 return 0; 1144 case CHttpReply<P>::eReplyStarted: 1145 case CHttpReply<P>::eReplyInitialized: 1146 if (!reply->GetHttpReply()->IsPostponed()) 1147 NCBI_THROW(CPubseqGatewayException, 1148 eUnfinishedRequestNotScheduled, 1149 "Unfinished request hasn't " 1150 "been scheduled (postponed)"); 1151 return 0; 1152 default: 1153 assert(false); 1154 return -1; 1155 } 1156 } catch (const std::exception & e) { 1157 if (!reply->IsFinished()) { 1158 reply->SetContentType(ePSGS_PSGMime); 1159 reply->PrepareReplyMessage(e.what(), 1160 CRequestStatus::e503_ServiceUnavailable, 1161 ePSGS_UnknownError, eDiag_Error); 1162 reply->PrepareReplyCompletion(); 1163 reply->Flush(); 1164 return 0; 1165 } 1166 return -1; 1167 } catch (...) { 1168 if (!reply->IsFinished()) { 1169 reply->SetContentType(ePSGS_PSGMime); 1170 reply->PrepareReplyMessage("Unexpected failure", 1171 CRequestStatus::e503_ServiceUnavailable, 1172 ePSGS_UnknownError, eDiag_Error); 1173 reply->PrepareReplyCompletion(); 1174 reply->Flush(); 1175 return 0; 1176 } 1177 return -1; 1178 } 1179 } 1180 1181 private: x_SetupSSL(void)1182 void x_SetupSSL(void) 1183 { 1184 bool enabled; 1185 string cert_file; 1186 string key_file; 1187 string ciphers; 1188 1189 GetSSLSettings(enabled, cert_file, key_file, ciphers); 1190 if (!enabled) 1191 return; 1192 1193 m_HttpAcceptCtx.ssl_ctx = SSL_CTX_new(SSLv23_server_method()); 1194 m_HttpAcceptCtx.expect_proxy_line = 0; 1195 1196 const long ssl_flags = SSL_OP_NO_SSLv2 | 1197 SSL_OP_NO_SSLv3 | 1198 SSL_OP_NO_TLSv1 | 1199 SSL_OP_NO_TLSv1_1; 1200 SSL_CTX_set_options(m_HttpAcceptCtx.ssl_ctx, ssl_flags); 1201 SSL_CTX_set_ecdh_auto(m_HttpAcceptCtx.ssl_ctx, 1); 1202 1203 if (SSL_CTX_use_certificate_chain_file(m_HttpAcceptCtx.ssl_ctx, 1204 cert_file.c_str()) != 1) { 1205 NCBI_THROW(CPubseqGatewayException, eConfigurationError, 1206 "Error loading SSL certificate from " + 1207 cert_file); 1208 } 1209 1210 if (SSL_CTX_use_PrivateKey_file(m_HttpAcceptCtx.ssl_ctx, 1211 key_file.c_str(), 1212 SSL_FILETYPE_PEM) != 1) { 1213 NCBI_THROW(CPubseqGatewayException, eConfigurationError, 1214 "Error loading SSL private key from " + 1215 key_file); 1216 } 1217 1218 if (SSL_CTX_set_cipher_list(m_HttpAcceptCtx.ssl_ctx, 1219 ciphers.c_str()) != 1) { 1220 NCBI_THROW(CPubseqGatewayException, eConfigurationError, 1221 "Error setting SSL ciphers (" + 1222 ciphers + ")"); 1223 } 1224 1225 // Note: 1226 // NPN support could also be added however it does not look necessary 1227 // because it is for SPDY while http/2 tends to use ALPN 1228 // #if H2O_USE_NPN 1229 // h2o_ssl_register_npn_protocols(m_HttpAcceptCtx.ssl_ctx, 1230 // h2o_http2_npn_protocols); 1231 // #endif 1232 1233 #if H2O_USE_ALPN 1234 h2o_ssl_register_alpn_protocols(m_HttpAcceptCtx.ssl_ctx, 1235 h2o_http2_alpn_protocols); 1236 #endif 1237 } 1238 1239 private: 1240 worker_t * m_Worker; 1241 CHttpDaemon<P> & m_Daemon; 1242 h2o_context_t m_HttpCtx; 1243 bool m_H2oCtxInitialized; 1244 h2o_accept_ctx_t m_HttpAcceptCtx; 1245 }; 1246 1247 1248 template<typename P> 1249 struct CHttpHandler 1250 { CHttpHandlerCHttpHandler1251 CHttpHandler(const std::string & path, 1252 HttpHandlerFunction_t<P> && handler, 1253 CHttpRequestParser * get_parser, 1254 CHttpPostParser * post_parser) : 1255 m_Path(path), 1256 m_Handler(std::move(handler)), 1257 m_GetParser(get_parser), 1258 m_PostParser(post_parser) 1259 {} 1260 1261 std::string m_Path; 1262 HttpHandlerFunction_t<P> m_Handler; 1263 CHttpRequestParser * m_GetParser; 1264 CHttpPostParser * m_PostParser; 1265 }; 1266 1267 1268 template<typename P> 1269 class CHttpDaemon 1270 { 1271 public: 1272 const unsigned short kAnyPort = 65535; 1273 CHttpDaemon(const std::vector<CHttpHandler<P>> & handlers,const std::string & tcp_address,unsigned short tcp_port,unsigned short tcp_workers,unsigned short tcp_backlog,unsigned short tcp_max_connections)1274 CHttpDaemon(const std::vector<CHttpHandler<P>> & handlers, 1275 const std::string & tcp_address, unsigned short tcp_port, 1276 unsigned short tcp_workers, unsigned short tcp_backlog, 1277 unsigned short tcp_max_connections) : 1278 m_HttpCfg({0}), 1279 m_HttpCfgInitialized(false), 1280 m_Handlers(handlers) 1281 { 1282 m_TcpDaemon.reset( 1283 new TSL::CTcpDaemon<CHttpProto<P>, CHttpConnection<P>, 1284 CHttpDaemon<P>>(tcp_address, tcp_port, 1285 tcp_workers, tcp_backlog, 1286 tcp_max_connections)); 1287 1288 h2o_config_init(&m_HttpCfg); 1289 m_HttpCfgInitialized = true; 1290 1291 sm_CdUid = getenv("CD_UID"); 1292 } 1293 ~CHttpDaemon()1294 ~CHttpDaemon() 1295 { 1296 if (m_HttpCfgInitialized) { 1297 m_HttpCfgInitialized = false; 1298 h2o_config_dispose(&m_HttpCfg); 1299 } 1300 // h2o_mem_dispose_recycled_allocators(); 1301 } 1302 Run(std::function<void (TSL::CTcpDaemon<CHttpProto<P>,CHttpConnection<P>,CHttpDaemon<P>> &)> on_watch_dog=nullptr)1303 void Run(std::function<void(TSL::CTcpDaemon<CHttpProto<P>, 1304 CHttpConnection<P>, 1305 CHttpDaemon<P>>&)> on_watch_dog = nullptr) 1306 { 1307 h2o_hostconf_t * hostconf = h2o_config_register_host( 1308 &m_HttpCfg, h2o_iovec_init(H2O_STRLIT("default")), kAnyPort); 1309 1310 for (auto & it: m_Handlers) { 1311 h2o_pathconf_t * pathconf = h2o_config_register_path( 1312 hostconf, it.m_Path.c_str(), 0); 1313 h2o_chunked_register(pathconf); 1314 1315 h2o_handler_t * handler = h2o_create_handler( 1316 pathconf, sizeof(CHttpGateHandler<P>)); 1317 CHttpGateHandler<P> * rh = reinterpret_cast<CHttpGateHandler<P>*>(handler); 1318 1319 rh->Init(&it.m_Handler, m_TcpDaemon.get(), this, it.m_GetParser, 1320 it.m_PostParser); 1321 handler->on_req = s_OnHttpRequest; 1322 } 1323 1324 m_TcpDaemon->Run(*this, on_watch_dog); 1325 } 1326 HttpCfg(void)1327 h2o_globalconf_t * HttpCfg(void) 1328 { 1329 return &m_HttpCfg; 1330 } 1331 NumOfConnections(void) const1332 uint16_t NumOfConnections(void) const 1333 { 1334 return m_TcpDaemon->NumOfConnections(); 1335 } 1336 StopListening(void)1337 void StopListening(void) 1338 { 1339 m_TcpDaemon->StopListening(); 1340 } 1341 1342 private: 1343 std::unique_ptr<TSL::CTcpDaemon<CHttpProto<P>, 1344 CHttpConnection<P>, 1345 CHttpDaemon<P>>> m_TcpDaemon; 1346 h2o_globalconf_t m_HttpCfg; 1347 bool m_HttpCfgInitialized; 1348 std::vector<CHttpHandler<P>> m_Handlers; 1349 static const char * sm_CdUid; 1350 s_OnHttpRequest(h2o_handler_t * self,h2o_req_t * req)1351 static int s_OnHttpRequest(h2o_handler_t * self, h2o_req_t * req) 1352 { 1353 try { 1354 CHttpGateHandler<P> *rh = reinterpret_cast<CHttpGateHandler<P>*>(self); 1355 CHttpProto<P> *proto = nullptr; 1356 1357 if (rh->m_Tcpd->OnRequest(&proto)) { 1358 return proto->OnHttpRequest(rh, req, sm_CdUid); 1359 } 1360 } catch (const std::exception & e) { 1361 h2o_send_error_503(req, "Malfunction", e.what(), 0); 1362 return 0; 1363 } catch (...) { 1364 h2o_send_error_503(req, "Malfunction", "unexpected failure", 0); 1365 return 0; 1366 } 1367 return -1; 1368 } 1369 }; 1370 1371 template<typename P> 1372 const char * CHttpDaemon<P>::sm_CdUid = nullptr; 1373 1374 1375 1376 #endif 1377