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