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