Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
THttpLongPollEngine.cxx
Go to the documentation of this file.
1// $Id$
2// Author: Sergey Linev 8/01/2018
3
4/*************************************************************************
5 * Copyright (C) 1995-2013, Rene Brun and Fons Rademakers. *
6 * All rights reserved. *
7 * *
8 * For the licensing terms see $ROOTSYS/LICENSE. *
9 * For the list of contributors see $ROOTSYS/README/CREDITS. *
10 *************************************************************************/
11
12#include "THttpLongPollEngine.h"
13
14#include "TError.h"
15#include "THttpCallArg.h"
16
17#include <cstring>
18#include <cstdlib>
19
20//////////////////////////////////////////////////////////////////////////
21// //
22// THttpLongPollEngine //
23// //
24// Emulation of websocket with long poll requests //
25// Allows to send data from server to client without explicit request //
26// //
27//////////////////////////////////////////////////////////////////////////
28
29const std::string THttpLongPollEngine::gLongPollNope = "<<nope>>";
30
31//////////////////////////////////////////////////////////////////////////
32/// constructor
33
35{
36}
37
38//////////////////////////////////////////////////////////////////////////
39/// returns ID of the engine, created from this pointer
40
42{
43 const void *ptr = (const void *)this;
44 return TString::Hash((void *)&ptr, sizeof(void *));
45}
46
47//////////////////////////////////////////////////////////////////////////
48/// clear request, normally called shortly before destructor
49
51{
52 std::shared_ptr<THttpCallArg> poll;
53
54 {
55 std::lock_guard<std::mutex> grd(fMutex);
56 poll = std::move(fPoll);
57 }
58
59 if (poll) {
60 poll->Set404();
61 poll->NotifyCondition();
62 }
63}
64
65//////////////////////////////////////////////////////////////////////////
66/// Create raw buffer which should be send as reply
67/// For the raw mode all information must be send via binary response
68
69std::string THttpLongPollEngine::MakeBuffer(const void *buf, int len, const char *hdr)
70{
71 std::string res;
72
73 if (!fRaw) {
74 res.resize(len);
75 std::copy((const char *)buf, (const char *)buf + len, res.begin());
76 return res;
77 }
78
79 int hdrlen = hdr ? strlen(hdr) : 0;
80 std::string hdrstr = "bin:";
81 hdrstr.append(std::to_string(hdrlen));
82
83 while ((hdrstr.length() + 1 + hdrlen) % 8 != 0)
84 hdrstr.append(" ");
85 hdrstr.append(":");
86 if (hdrlen > 0)
87 hdrstr.append(hdr);
88
89 res.resize(hdrstr.length() + len);
90 std::copy(hdrstr.begin(), hdrstr.begin() + hdrstr.length(), res.begin());
91 std::copy((const char *)buf, (const char *)buf + len, res.begin() + hdrstr.length());
92
93 return res;
94}
95
96//////////////////////////////////////////////////////////////////////////
97/// Send binary data via connection
98
99void THttpLongPollEngine::Send(const void *buf, int len)
100{
101 std::shared_ptr<THttpCallArg> poll;
102
103 std::string sendbuf = MakeBuffer(buf, len);
104
105 {
106 std::lock_guard<std::mutex> grd(fMutex);
107 if (fPoll) {
108 poll = std::move(fPoll);
109 } else if (fBufKind == kNoBuf) {
111 std::swap(fBuf, sendbuf);
112 return;
113 }
114 }
115
116 if(!poll) {
117 Error("Send", "Operation invoked before polling request obtained");
118 return;
119 }
120
121 poll->SetBinaryContent(std::move(sendbuf));
122 poll->NotifyCondition();
123}
124
125//////////////////////////////////////////////////////////////////////////
126/// Send binary data with text header via connection
127
128void THttpLongPollEngine::SendHeader(const char *hdr, const void *buf, int len)
129{
130 std::shared_ptr<THttpCallArg> poll;
131
132 std::string sendbuf = MakeBuffer(buf, len, hdr);
133
134 {
135 std::lock_guard<std::mutex> grd(fMutex);
136 if (fPoll) {
137 poll = std::move(fPoll);
138 } else if (fBufKind == kNoBuf) {
140 if (!fRaw && hdr) fBufHeader = hdr;
141 std::swap(fBuf, sendbuf);
142 return;
143 }
144 }
145
146 if(!poll) {
147 Error("SendHeader", "Operation invoked before polling request obtained");
148 return;
149 }
150
151 poll->SetBinaryContent(std::move(sendbuf));
152 if (!fRaw)
153 poll->SetExtraHeader("LongpollHeader", hdr);
154 poll->NotifyCondition();
155}
156
157//////////////////////////////////////////////////////////////////////////
158/// Send const char data
159/// Either do it immediately or keep in internal buffer
160
162{
163 std::shared_ptr<THttpCallArg> poll;
164
165 std::string sendbuf(fRaw ? "txt:" : "");
166 sendbuf.append(buf);
167
168 {
169 std::lock_guard<std::mutex> grd(fMutex);
170 if (fPoll) {
171 poll = std::move(fPoll);
172 } else if (fBufKind == kNoBuf) {
174 std::swap(fBuf, sendbuf);
175 return;
176 }
177 }
178
179 if(!poll) {
180 Error("SendCharStart", "Operation invoked before polling request obtained");
181 return;
182 }
183
184 if (fRaw) poll->SetBinaryContent(std::move(sendbuf));
185 else poll->SetTextContent(std::move(sendbuf));
186 poll->NotifyCondition();
187}
188
189//////////////////////////////////////////////////////////////////////////////
190/// Preview data for given socket
191/// Method called by WS handler before processing websocket data
192/// Returns kTRUE when user should ignore such http request - it is for internal use
193
194Bool_t THttpLongPollEngine::PreProcess(std::shared_ptr<THttpCallArg> &arg)
195{
196 if (!strstr(arg->GetQuery(), "&dummy"))
197 return kFALSE;
198
199 std::shared_ptr<THttpCallArg> poll;
200
201 // if request cannot be postponed just reply it
202 if (arg->CanPostpone()) {
203 std::lock_guard<std::mutex> grd(fMutex);
204 if (fBufKind != kNoBuf) {
205 // there is data which can be send directly
206 poll = arg;
207 } else {
208 arg->SetPostponed(); // mark http request as pending, http server should wait for notification
209 poll = std::move(fPoll);
210 fPoll = arg; // keep reference on polling request
211 }
212
213 } else {
214 poll = arg;
215 }
216
217 if (poll) {
218 // if there buffered data, it will be provided
219 PostProcess(poll);
220 poll->NotifyCondition(); // inform http server that request is processed
221 }
222
223 // if arguments has "&dummy" string, user should not process it
224 return kTRUE;
225}
226
227//////////////////////////////////////////////////////////////////////////////
228/// Normally requests from client does not replied directly for longpoll socket
229/// Therefore one can use such request to send data, which was submitted before to the queue
230
231void THttpLongPollEngine::PostProcess(std::shared_ptr<THttpCallArg> &arg)
232{
233 EBufKind kind = kNoBuf;
234 std::string sendbuf, sendhdr;
235
236 {
237 std::lock_guard<std::mutex> grd(fMutex);
238 if (fBufKind != kNoBuf) {
239 kind = fBufKind;
241 std::swap(sendbuf, fBuf);
242 std::swap(sendhdr, fBufHeader);
243 }
244 }
245
246 if (kind == kTxtBuf) {
247 arg->SetTextContent(std::move(sendbuf));
248 } else if (kind == kBinBuf) {
249 arg->SetBinaryContent(std::move(sendbuf));
250 if (!sendhdr.empty())
251 arg->SetExtraHeader("LongpollHeader", sendhdr.c_str());
252 } else if (fRaw) {
253 arg->SetBinaryContent(std::string("txt:") + gLongPollNope);
254 } else {
255 arg->SetTextContent(std::string(gLongPollNope));
256 }
257}
258
259//////////////////////////////////////////////////////////////////////////////
260/// Indicate that polling requests is there or buffer empty and can be immediately invoked
261
263{
264 std::lock_guard<std::mutex> grd(fMutex);
265 return fPoll || (fBufKind == kNoBuf) ? kTRUE : kFALSE;
266}
const Bool_t kFALSE
Definition RtypesCore.h:101
const Bool_t kTRUE
Definition RtypesCore.h:100
void Error(const char *location, const char *msgfmt,...)
Use this function in case an error occurred.
Definition TError.cxx:187
std::string fBufHeader
!< buffered data
std::string MakeBuffer(const void *buf, int len, const char *hdr=nullptr)
!< default reply on the longpoll request
std::shared_ptr< THttpCallArg > fPoll
!< protect polling request to use it from different threads
void Send(const void *buf, int len) override
Send binary data via connection.
void SendHeader(const char *hdr, const void *buf, int len) override
Send binary data with text header via connection.
std::mutex fMutex
!< if true, only content can be used for data transfer
virtual Bool_t CanSendDirectly() override
Indicate that polling requests is there or buffer empty and can be immediately invoked.
THttpLongPollEngine(bool raw=false)
constructor
static const std::string gLongPollNope
!< buffered header
void ClearHandle(Bool_t) override
clear request, normally called shortly before destructor
void SendCharStar(const char *buf) override
Send const char data Either do it immediately or keep in internal buffer.
Bool_t PreProcess(std::shared_ptr< THttpCallArg > &arg) override
Preview data for given socket Method called by WS handler before processing websocket data Returns kT...
EBufKind fBufKind
!< hold polling request, which can be immediately used for the next sending
UInt_t GetId() const override
returns ID of the engine, created from this pointer
void PostProcess(std::shared_ptr< THttpCallArg > &arg) override
Normally requests from client does not replied directly for longpoll socket Therefore one can use suc...
std::string fBuf
!< if buffered data available
UInt_t Hash(ECaseCompare cmp=kExact) const
Return hash value.
Definition TString.cxx:662