1 #ifndef CONNECT__IMPL__NCBI_UV_NGHTTP2__HPP
2 #define CONNECT__IMPL__NCBI_UV_NGHTTP2__HPP
3 
4 /*  $Id: ncbi_uv_nghttp2.hpp 630217 2021-04-28 18:43:22Z 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: Rafael Sadyrov
30  *
31  */
32 
33 #include "connect_misc.hpp"
34 
35 #include <corelib/ncbistl.hpp>
36 #include <corelib/ncbidbg.hpp>
37 #include <common/ncbi_export.h>
38 
39 #include <uv.h>
40 #include <nghttp2/nghttp2.h>
41 
42 #include <array>
43 #include <functional>
44 #include <forward_list>
45 #include <sstream>
46 #include <type_traits>
47 #include <vector>
48 
49 BEGIN_NCBI_SCOPE
50 
51 struct SUvNgHttp2_Error
52 {
53     struct SMbedTlsStr : private array<char, 256>
54     {
55         template <typename T, enable_if_t<is_signed<T>::value, T> = 0> const char* operator()(T e);
operator <<SUvNgHttp2_Error56         friend ostream& operator<<(ostream& os, const SMbedTlsStr& str) { return os << str.data(); }
57     };
58 
SUvNgHttp2_ErrorSUvNgHttp2_Error59     SUvNgHttp2_Error(const char* m) : m_Value("error: ") { m_Value += m; }
60 
61     template <typename T>
FromNgHttp2SUvNgHttp2_Error62     static SUvNgHttp2_Error FromNgHttp2(T e, const char* w) { return { "nghttp2 error: ", NgHttp2Str<T>, e, w }; }
63 
64     template <typename T>
FromLibuvSUvNgHttp2_Error65     static SUvNgHttp2_Error FromLibuv(T e, const char* w) { return { "libuv error: ", LibuvStr<T>, e, w }; }
66 
67     template <typename T>
FromMbedTlsSUvNgHttp2_Error68     static SUvNgHttp2_Error FromMbedTls(T e, const char* w) { return { "mbed TLS error: ", SMbedTlsStr(), e, w }; }
69 
70     template <typename T, enable_if_t<is_signed<T>::value, T> = 0>
NgHttp2StrSUvNgHttp2_Error71     static const char* NgHttp2Str(T e) { return nghttp2_strerror(static_cast<int>(e)); }
72 
73     template <typename T, enable_if_t<is_unsigned<T>::value, T> = 0>
NgHttp2StrSUvNgHttp2_Error74     static const char* NgHttp2Str(T e) { return nghttp2_http2_strerror(static_cast<uint32_t>(e));; }
75 
76     template <typename T, enable_if_t<is_signed<T>::value, T> = 0>
LibuvStrSUvNgHttp2_Error77     static const char* LibuvStr(T e) { return uv_strerror(static_cast<int>(e)); }
78 
79     template <typename T, enable_if_t<is_signed<T>::value, T> = 0>
MbedTlsStrSUvNgHttp2_Error80     static SMbedTlsStr MbedTlsStr(T e) { SMbedTlsStr str; str(e); return str; }
81 
operator stringSUvNgHttp2_Error82     operator string() const { return m_Value; }
83 
operator <<(ostream & os,const SUvNgHttp2_Error & error)84     friend ostream& operator<<(ostream& os, const SUvNgHttp2_Error& error) { return os << error.m_Value; }
85 
86 private:
87     template <typename TFunc, typename T>
SUvNgHttp2_ErrorSUvNgHttp2_Error88     SUvNgHttp2_Error(const char* t, TFunc f, T e, const char* w)
89     {
90         stringstream os;
91         os << t << f(e) << " (" << e << ") " << w;
92         m_Value = os.str();
93     };
94 
95     string m_Value;
96 };
97 
98 template <typename THandle>
99 struct SUv_Handle : protected THandle
100 {
SUv_HandleSUv_Handle101     SUv_Handle(uv_close_cb cb = nullptr) : m_Cb(cb) {}
102 
CloseSUv_Handle103     void Close()
104     {
105         uv_close(reinterpret_cast<uv_handle_t*>(this), m_Cb);
106     }
107 
108     template <typename TDerived>
GetThatSUv_Handle109     static auto GetThat(THandle* handle)
110     {
111         return static_cast<TDerived*>(handle);
112     }
113 
114 private:
115     SUv_Handle(const SUv_Handle&) = delete;
116     SUv_Handle(SUv_Handle&&) = delete;
117 
118     SUv_Handle& operator=(const SUv_Handle&) = delete;
119     SUv_Handle& operator=(SUv_Handle&&) = delete;
120 
121     uv_close_cb m_Cb;
122 };
123 
124 struct NCBI_XXCONNECT2_EXPORT SUv_Write
125 {
126     SUv_Write(void* user_data, size_t buf_size);
127 
GetBufferSUv_Write128     vector<char>& GetBuffer() { _ASSERT(m_CurrentBuffer); return m_CurrentBuffer->data; }
129     int Write(uv_stream_t* handle, uv_write_cb cb);
130     void OnWrite(uv_write_t* req);
131     void Reset();
132 
133 private:
134     struct SBuffer
135     {
136         uv_write_t request;
137         vector<char> data;
138         bool in_progress = false;
139     };
140 
141     void NewBuffer();
142 
143     void* const m_UserData;
144     const size_t m_BufSize;
145     forward_list<SBuffer> m_Buffers;
146     SBuffer* m_CurrentBuffer = nullptr;
147 };
148 
149 struct NCBI_XXCONNECT2_EXPORT SUv_Connect
150 {
151     SUv_Connect(void* user_data, const SSocketAddress& address);
152 
153     int operator()(uv_tcp_t* handle, uv_connect_cb cb);
154 
155 private:
156     struct sockaddr_in m_Address;
157     uv_connect_t m_Request;
158 };
159 
160 struct NCBI_XXCONNECT2_EXPORT SUv_Tcp : SUv_Handle<uv_tcp_t>
161 {
162     using TConnectCb = function<void(int)>;
163     using TReadCb = function<void(const char*, ssize_t)>;
164     using TWriteCb = function<void(int)>;
165 
166     SUv_Tcp(uv_loop_t *loop, const SSocketAddress& address, size_t rd_buf_size, size_t wr_buf_size,
167             TConnectCb connect_cb, TReadCb read_cb, TWriteCb write_cb);
168 
169     int Write();
170     void Close();
171 
GetWriteBufferSUv_Tcp172     vector<char>& GetWriteBuffer() { return m_Write.GetBuffer(); }
173 
174 private:
175     enum EState {
176         eClosed,
177         eConnecting,
178         eConnected,
179         eClosing,
180     };
181 
182     void OnConnect(uv_connect_t* req, int status);
183     void OnAlloc(uv_handle_t*, size_t suggested_size, uv_buf_t* buf);
184     void OnRead(uv_stream_t*, ssize_t nread, const uv_buf_t* buf);
185     void OnWrite(uv_write_t*, int status);
186     void OnClose(uv_handle_t*);
187 
188     template <class THandle, class ...TArgs1, class ...TArgs2>
OnCallbackSUv_Tcp189     static void OnCallback(void (SUv_Tcp::*member)(THandle*, TArgs1...), THandle* handle, TArgs2&&... args)
190     {
191         auto that = static_cast<SUv_Tcp*>(handle->data);
192         (that->*member)(handle, forward<TArgs2>(args)...);
193     }
194 
s_OnAllocSUv_Tcp195     static void s_OnAlloc(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf) { OnCallback(&SUv_Tcp::OnAlloc, handle, suggested_size, buf); }
s_OnReadSUv_Tcp196     static void s_OnRead(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf) { OnCallback(&SUv_Tcp::OnRead, stream, nread, buf); }
s_OnWriteSUv_Tcp197     static void s_OnWrite(uv_write_t* req, int status) { OnCallback(&SUv_Tcp::OnWrite, req, status); }
s_OnConnectSUv_Tcp198     static void s_OnConnect(uv_connect_t* req, int status) { OnCallback(&SUv_Tcp::OnConnect, req, status); }
s_OnCloseSUv_Tcp199     static void s_OnClose(uv_handle_t* handle) { OnCallback(&SUv_Tcp::OnClose, handle); }
200 
201     uv_loop_t* m_Loop;
202     EState m_State = eClosed;
203     vector<char> m_ReadBuffer;
204     SUv_Connect m_Connect;
205     SUv_Write m_Write;
206     TConnectCb m_ConnectCb;
207     TReadCb m_ReadCb;
208     TWriteCb m_WriteCb;
209 };
210 
211 struct NCBI_XXCONNECT2_EXPORT SUv_Async : SUv_Handle<uv_async_t>
212 {
InitSUv_Async213     void Init(void* d, uv_loop_t* l, uv_async_cb cb)
214     {
215         if (auto rc = uv_async_init(l, this, cb)) {
216             ERR_POST(Fatal << "uv_async_init failed " << SUvNgHttp2_Error::LibuvStr(rc));
217         }
218 
219         data = d;
220     }
221 
SignalSUv_Async222     void Signal()
223     {
224         if (auto rc = uv_async_send(this)) {
225             ERR_POST(Fatal << "uv_async_send failed " << SUvNgHttp2_Error::LibuvStr(rc));
226         }
227     }
228 };
229 
230 struct NCBI_XXCONNECT2_EXPORT SUv_Timer : SUv_Handle<uv_timer_t>
231 {
SUv_TimerSUv_Timer232     SUv_Timer(void* d, uv_timer_cb cb, uint64_t t, uint64_t r) :
233         m_Cb(cb),
234         m_Timeout(t),
235         m_Repeat(r)
236     {
237         data = d;
238     }
239 
InitSUv_Timer240     void Init(uv_loop_t* l)
241     {
242         if (auto rc = uv_timer_init(l, this)) {
243             ERR_POST(Fatal << "uv_timer_init failed " << SUvNgHttp2_Error::LibuvStr(rc));
244         }
245     }
246 
StartSUv_Timer247     void Start()               { Start(m_Timeout, m_Repeat); }
SetRepeatSUv_Timer248     void SetRepeat(uint64_t r) { Start(r, r);                }
ResetRepeatSUv_Timer249     void ResetRepeat()         { if (m_Repeat) Start(m_Repeat, m_Repeat); }
250 
GetDefaultRepeatSUv_Timer251     uint64_t GetDefaultRepeat() const { return m_Repeat; }
252 
CloseSUv_Timer253     void Close()
254     {
255         if (auto rc = uv_timer_stop(this)) {
256             ERR_POST("uv_timer_stop failed " << SUvNgHttp2_Error::LibuvStr(rc));
257         }
258 
259         SUv_Handle<uv_timer_t>::Close();
260     }
261 
262 private:
StartSUv_Timer263     void Start(uint64_t t, uint64_t r)
264     {
265         if (auto rc = uv_timer_start(this, m_Cb, t, r)) {
266             ERR_POST(Fatal << "uv_timer_start failed " << SUvNgHttp2_Error::LibuvStr(rc));
267         }
268     }
269 
270     uv_timer_cb m_Cb;
271     const uint64_t m_Timeout;
272     const uint64_t m_Repeat;
273 };
274 
275 struct NCBI_XXCONNECT2_EXPORT SUv_Barrier
276 {
SUv_BarrierSUv_Barrier277     SUv_Barrier(unsigned count)
278     {
279         if (auto rc = uv_barrier_init(&m_Barrier, count)) {
280             ERR_POST(Fatal << "uv_barrier_init failed " << SUvNgHttp2_Error::LibuvStr(rc));
281         }
282     }
283 
WaitSUv_Barrier284     void Wait()
285     {
286         auto rc = uv_barrier_wait(&m_Barrier);
287 
288         if (rc > 0) {
289             uv_barrier_destroy(&m_Barrier);
290         } else if (rc < 0) {
291             ERR_POST(Fatal << "uv_barrier_wait failed " << SUvNgHttp2_Error::LibuvStr(rc));
292         }
293     }
294 
295 private:
296     uv_barrier_t m_Barrier;
297 };
298 
299 struct NCBI_XXCONNECT2_EXPORT SUv_Loop : uv_loop_t
300 {
SUv_LoopSUv_Loop301     SUv_Loop()
302     {
303         if (auto rc = uv_loop_init(this)) {
304             ERR_POST(Fatal << "uv_loop_init failed " << SUvNgHttp2_Error::LibuvStr(rc));
305         }
306     }
307 
RunSUv_Loop308     void Run(uv_run_mode mode = UV_RUN_DEFAULT)
309     {
310         auto rc = uv_run(this, mode);
311 
312         if (rc < 0) {
313             ERR_POST(Fatal << "uv_run failed " << SUvNgHttp2_Error::LibuvStr(rc));
314         }
315     }
316 
~SUv_LoopSUv_Loop317     ~SUv_Loop()
318     {
319         if (auto rc = uv_loop_close(this)) {
320             ERR_POST("uv_loop_close failed " << SUvNgHttp2_Error::LibuvStr(rc));
321         }
322     }
323 };
324 
325 template <uint8_t DEFAULT>
326 struct SNgHttp2_Header : nghttp2_nv
327 {
328     struct SConvert
329     {
330         uint8_t* str;
331         size_t len;
332         uint8_t flags;
333 
334         template <size_t SIZE>
SConvertSNgHttp2_Header::SConvert335         SConvert(const char (&s)[SIZE]) : SConvert(s, SIZE - 1) {}
SConvertSNgHttp2_Header::SConvert336         SConvert(const char* s) : SConvert(s, strlen(s)) {} // For gcc 5.x.x
SConvertSNgHttp2_Header::SConvert337         SConvert(const string& s) : SConvert(s.c_str(), s.size()) {}
SConvertSNgHttp2_Header::SConvert338         SConvert(uint8_t f) : SConvert(nullptr, 0, f) {}
SConvertSNgHttp2_Header::SConvert339         SConvert(const char* s, size_t l, uint8_t f = DEFAULT) : str((uint8_t*)s), len(l), flags(f) {}
340         SConvert(const string&& v) = delete;
341     };
342 
SNgHttp2_HeaderSNgHttp2_Header343     SNgHttp2_Header(initializer_list<SConvert> l) :
344         SNgHttp2_Header(Get(l, 0), Get(l, 1))
345     {}
346 
SNgHttp2_HeaderSNgHttp2_Header347     SNgHttp2_Header(const SConvert& n, const SConvert& v) :
348         nghttp2_nv{ n.str, v.str, n.len, v.len, uint8_t((n.flags & NGHTTP2_NV_FLAG_NO_COPY_NAME) | (v.flags & NGHTTP2_NV_FLAG_NO_COPY_VALUE)) }
349     {}
350 
operator =SNgHttp2_Header351     void operator=(SConvert v)
352     {
353         value = v.str;
354         valuelen = v.len;
355     }
356 
357 private:
GetSNgHttp2_Header358     static SConvert Get(initializer_list<SConvert> l, size_t i)  { return l.size() <= i ? NGHTTP2_NV_FLAG_NONE : *(l.begin() + i); }
359 };
360 
361 struct NCBI_XXCONNECT2_EXPORT SNgHttp2_Session
362 {
363     SNgHttp2_Session(void* user_data, uint32_t max_streams,
364             nghttp2_on_data_chunk_recv_callback on_data,
365             nghttp2_on_stream_close_callback    on_stream_close,
366             nghttp2_on_header_callback          on_header,
367             nghttp2_error_callback              on_error,
368             nghttp2_on_frame_recv_callback      on_frame_recv = nullptr);
369 
370     void Del();
371 
372     int32_t Submit(const nghttp2_nv *nva, size_t nvlen, nghttp2_data_provider* data_prd = nullptr);
373     int Resume(int32_t stream_id);
374 
375     // Send() returns either an nghttp2 error or one of the special values below
376     enum ESendResult : ssize_t { eOkay, eWantsClose };
377     ssize_t Send(vector<char>& buffer);
378     ssize_t Recv(const uint8_t* buffer, size_t size);
379 
GetMaxStreamsSNgHttp2_Session380     uint32_t GetMaxStreams() const { return m_MaxStreams.first; }
381 
382 private:
383     int Init();
384 
385     template <typename TInt, enable_if_t<is_signed<TInt>::value, TInt> = 0>
386     TInt x_DelOnError(TInt rv)
387     {
388         if (rv < 0) {
389             nghttp2_session_del(m_Session);
390             m_Session = nullptr;
391         }
392 
393         return rv;
394     }
395 
396     nghttp2_session* m_Session = nullptr;
397     void* m_UserData;
398     nghttp2_on_data_chunk_recv_callback m_OnData;
399     nghttp2_on_stream_close_callback    m_OnStreamClose;
400     nghttp2_on_header_callback          m_OnHeader;
401     nghttp2_error_callback              m_OnError;
402     nghttp2_on_frame_recv_callback      m_OnFrameRecv;
403     pair<uint32_t, const uint32_t> m_MaxStreams;
404 };
405 
406 struct NCBI_XXCONNECT2_EXPORT SUvNgHttp2_UserAgent
407 {
GetSUvNgHttp2_UserAgent408     static const string& Get() { static const string user_agent(Init()); return user_agent; }
409 
410 private:
411     static string Init();
412 };
413 
414 struct NCBI_XXCONNECT2_EXPORT SUvNgHttp2_Tls
415 {
~SUvNgHttp2_TlsSUvNgHttp2_Tls416     virtual ~SUvNgHttp2_Tls() {}
417 
418     virtual int Read(const char*& buf, ssize_t& nread) = 0;
419     virtual int Write() = 0;
420     virtual int Close() = 0;
421 
422     virtual const char* GetReadBuffer() = 0;
423     virtual vector<char>& GetWriteBuffer() = 0;
424 
425     using TGetWriteBuf = function<vector<char>&()>;
426     static SUvNgHttp2_Tls* Create(bool https, const SSocketAddress& address, size_t rd_buf_size, size_t wr_buf_size, TGetWriteBuf get_write_buf);
427 };
428 
429 struct NCBI_XXCONNECT2_EXPORT SUvNgHttp2_SessionBase
430 {
431     template <class ...TArgs>
432     SUvNgHttp2_SessionBase(uv_loop_t* loop, const SSocketAddress& address, size_t rd_buf_size, size_t wr_buf_size, bool https, TArgs&&... args);
433 
~SUvNgHttp2_SessionBaseSUvNgHttp2_SessionBase434     virtual ~SUvNgHttp2_SessionBase() {}
435 
436     void Reset(SUvNgHttp2_Error error);
437 
438 protected:
439     bool Send();
440 
441     const string m_Authority;
442     SUv_Tcp m_Tcp;
443     unique_ptr<SUvNgHttp2_Tls> m_Tls;
444     SNgHttp2_Session m_Session;
445 
446 private:
447     template<typename TR, class... TArgs>
BindThisSUvNgHttp2_SessionBase448     function<TR(TArgs...)> BindThis(TR (SUvNgHttp2_SessionBase::*member)(TArgs...))
449     {
450         return [this, member](TArgs&&... args) -> TR { return (this->*member)(forward<TArgs>(args)...); };
451     };
452 
453     void OnConnect(int status);
454     void OnWrite(int status);
455     void OnRead(const char* buf, ssize_t nread);
456 
457     virtual void OnReset(SUvNgHttp2_Error error) = 0;
458 };
459 
460 template <class TImpl>
461 struct SUvNgHttp2_Session : TImpl
462 {
463     template <class... TArgs>
SUvNgHttp2_SessionSUvNgHttp2_Session464     SUvNgHttp2_Session(TArgs&&... args) :
465         TImpl(forward<TArgs>(args)..., s_OnData, s_OnStreamClose, s_OnHeader, s_OnError)
466     {}
467 
468 private:
GetThatSUvNgHttp2_Session469     static SUvNgHttp2_Session* GetThat(void* user_data)
470     {
471         _ASSERT(user_data);
472         return static_cast<SUvNgHttp2_Session*>(user_data);
473     }
474 
s_OnDataSUvNgHttp2_Session475     static int s_OnData(nghttp2_session* session, uint8_t flags, int32_t stream_id, const uint8_t* data, size_t len, void* user_data)
476     {
477         return GetThat(user_data)->OnData(session, flags, stream_id, data, len);
478     }
479 
s_OnStreamCloseSUvNgHttp2_Session480     static int s_OnStreamClose(nghttp2_session* session, int32_t stream_id, uint32_t error_code, void* user_data)
481     {
482         return GetThat(user_data)->OnStreamClose(session, stream_id, error_code);
483     }
484 
s_OnHeaderSUvNgHttp2_Session485     static int s_OnHeader(nghttp2_session* session, const nghttp2_frame* frame, const uint8_t* name, size_t namelen, const uint8_t* value, size_t valuelen, uint8_t flags, void* user_data)
486     {
487         return GetThat(user_data)->OnHeader(session, frame, name, namelen, value, valuelen, flags);
488     }
489 
s_OnErrorSUvNgHttp2_Session490     static int s_OnError(nghttp2_session* session, const char* msg, size_t len, void* user_data)
491     {
492         return GetThat(user_data)->OnError(session, msg, len);
493     }
494 };
495 
496 template <class ...TArgs>
SUvNgHttp2_SessionBase(uv_loop_t * loop,const SSocketAddress & address,size_t rd_buf_size,size_t wr_buf_size,bool https,TArgs &&...args)497 SUvNgHttp2_SessionBase::SUvNgHttp2_SessionBase(uv_loop_t* loop, const SSocketAddress& address, size_t rd_buf_size, size_t wr_buf_size, bool https, TArgs&&... args) :
498     m_Authority(address.AsString()),
499     m_Tcp(
500             loop,
501             address,
502             rd_buf_size,
503             wr_buf_size,
504             BindThis(&SUvNgHttp2_SessionBase::OnConnect),
505             BindThis(&SUvNgHttp2_SessionBase::OnRead),
506             BindThis(&SUvNgHttp2_SessionBase::OnWrite)),
507     m_Tls(SUvNgHttp2_Tls::Create(https, address, rd_buf_size, wr_buf_size, [&]() -> vector<char>& { return m_Tcp.GetWriteBuffer(); })),
508     m_Session(this, forward<TArgs>(args)...)
509 {
510 }
511 
512 END_NCBI_SCOPE
513 
514 #endif
515