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/** \class THttpLongPollEngine
21
22Emulation of websocket with long poll requests
23
24Allows to send data from server to client without explicit request
25
26Created automatically as fallback solution when normal WebSopcket connection cannot be established
27*/
28
29const std::string THttpLongPollEngine::gLongPollNope = "<<nope>>";
30
31//////////////////////////////////////////////////////////////////////////
32/// constructor
33
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
49///
50/// normally called shortly before destructor
51
53{
54 std::shared_ptr<THttpCallArg> poll;
55
56 {
57 std::lock_guard<std::mutex> grd(fMutex);
58 poll = std::move(fPoll);
59 }
60
61 if (poll) {
62 poll->Set404();
63 poll->NotifyCondition();
64 }
65}
66
67//////////////////////////////////////////////////////////////////////////
68/// Create raw buffer which should be send as reply
69///
70/// For the raw mode all information must be send via binary response
71
72std::string THttpLongPollEngine::MakeBuffer(const void *buf, int len, const char *hdr)
73{
74 std::string res;
75
76 if (!fRaw) {
77 res.resize(len);
78 std::copy((const char *)buf, (const char *)buf + len, res.begin());
79 return res;
80 }
81
82 int hdrlen = hdr ? strlen(hdr) : 0;
83 std::string hdrstr = "bin:";
84 hdrstr.append(std::to_string(hdrlen));
85
86 while ((hdrstr.length() + 1 + hdrlen) % 8 != 0)
87 hdrstr.append(" ");
88 hdrstr.append(":");
89 if (hdrlen > 0)
90 hdrstr.append(hdr);
91
92 res.resize(hdrstr.length() + len);
93 std::copy(hdrstr.begin(), hdrstr.begin() + hdrstr.length(), res.begin());
94 std::copy((const char *)buf, (const char *)buf + len, res.begin() + hdrstr.length());
95
96 return res;
97}
98
99//////////////////////////////////////////////////////////////////////////
100/// Send binary data via connection
101
102void THttpLongPollEngine::Send(const void *buf, int len)
103{
104 std::shared_ptr<THttpCallArg> poll;
105
106 std::string sendbuf = MakeBuffer(buf, len);
107
108 {
109 std::lock_guard<std::mutex> grd(fMutex);
110 if (fPoll) {
111 poll = std::move(fPoll);
112 } else if (fBufKind == kNoBuf) {
114 std::swap(fBuf, sendbuf);
115 return;
116 }
117 }
118
119 if(!poll) {
120 Error("Send", "Operation invoked before polling request obtained");
121 return;
122 }
123
124 poll->SetBinaryContent(std::move(sendbuf));
125 poll->NotifyCondition();
126}
127
128//////////////////////////////////////////////////////////////////////////
129/// Send binary data with text header via connection
130
131void THttpLongPollEngine::SendHeader(const char *hdr, const void *buf, int len)
132{
133 std::shared_ptr<THttpCallArg> poll;
134
135 std::string sendbuf = MakeBuffer(buf, len, hdr);
136
137 {
138 std::lock_guard<std::mutex> grd(fMutex);
139 if (fPoll) {
140 poll = std::move(fPoll);
141 } else if (fBufKind == kNoBuf) {
143 if (!fRaw && hdr) fBufHeader = hdr;
144 std::swap(fBuf, sendbuf);
145 return;
146 }
147 }
148
149 if(!poll) {
150 Error("SendHeader", "Operation invoked before polling request obtained");
151 return;
152 }
153
154 poll->SetBinaryContent(std::move(sendbuf));
155 if (!fRaw)
156 poll->SetExtraHeader("LongpollHeader", hdr);
157 poll->NotifyCondition();
158}
159
160//////////////////////////////////////////////////////////////////////////
161/// Send const char data
162///
163/// Either do it immediately or keep in internal buffer
164
166{
167 std::shared_ptr<THttpCallArg> poll;
168
169 std::string sendbuf(fRaw ? "txt:" : "");
170 sendbuf.append(buf);
171
172 {
173 std::lock_guard<std::mutex> grd(fMutex);
174 if (fPoll) {
175 poll = std::move(fPoll);
176 } else if (fBufKind == kNoBuf) {
178 std::swap(fBuf, sendbuf);
179 return;
180 }
181 }
182
183 if(!poll) {
184 Error("SendCharStart", "Operation invoked before polling request obtained");
185 return;
186 }
187
188 if (fRaw) poll->SetBinaryContent(std::move(sendbuf));
189 else poll->SetTextContent(std::move(sendbuf));
190 poll->NotifyCondition();
191}
192
193//////////////////////////////////////////////////////////////////////////////
194/// Preview data for given socket
195///
196/// Method called by WS handler before processing websocket data
197/// Returns kTRUE when user should ignore such http request - it is for internal use
198
199Bool_t THttpLongPollEngine::PreProcess(std::shared_ptr<THttpCallArg> &arg)
200{
201 if (!strstr(arg->GetQuery(), "&dummy"))
202 return kFALSE;
203
204 std::shared_ptr<THttpCallArg> poll;
205
206 // if request cannot be postponed just reply it
207 if (arg->CanPostpone()) {
208 std::lock_guard<std::mutex> grd(fMutex);
209 if (fBufKind != kNoBuf) {
210 // there is data which can be send directly
211 poll = arg;
212 } else {
213 arg->SetPostponed(); // mark http request as pending, http server should wait for notification
214 poll = std::move(fPoll);
215 fPoll = arg; // keep reference on polling request
216 }
217
218 } else {
219 poll = arg;
220 }
221
222 if (poll) {
223 // if there buffered data, it will be provided
224 PostProcess(poll);
225 poll->NotifyCondition(); // inform http server that request is processed
226 }
227
228 // if arguments has "&dummy" string, user should not process it
229 return kTRUE;
230}
231
232//////////////////////////////////////////////////////////////////////////////
233/// Post process http request
234///
235/// Normally requests from client does not replied directly for longpoll socket
236/// Therefore one can use such request to send data, which was submitted before to the queue
237
238void THttpLongPollEngine::PostProcess(std::shared_ptr<THttpCallArg> &arg)
239{
240 EBufKind kind = kNoBuf;
241 std::string sendbuf, sendhdr;
242
243 {
244 std::lock_guard<std::mutex> grd(fMutex);
245 if (fBufKind != kNoBuf) {
246 kind = fBufKind;
248 std::swap(sendbuf, fBuf);
249 std::swap(sendhdr, fBufHeader);
250 }
251 }
252
253 if (kind == kTxtBuf) {
254 arg->SetTextContent(std::move(sendbuf));
255 } else if (kind == kBinBuf) {
256 arg->SetBinaryContent(std::move(sendbuf));
257 if (!sendhdr.empty())
258 arg->SetExtraHeader("LongpollHeader", sendhdr.c_str());
259 } else if (fRaw) {
260 arg->SetBinaryContent(std::string("txt:") + gLongPollNope);
261 } else {
262 arg->SetTextContent(std::string(gLongPollNope));
263 }
264}
265
266//////////////////////////////////////////////////////////////////////////////
267/// Indicate that polling requests is there or buffer empty and can be immediately invoked
268
270{
271 std::lock_guard<std::mutex> grd(fMutex);
272 return fPoll || (fBufKind == kNoBuf) ? kTRUE : kFALSE;
273}
constexpr Bool_t kFALSE
Definition RtypesCore.h:108
constexpr Bool_t kTRUE
Definition RtypesCore.h:107
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
void Error(const char *location, const char *msgfmt,...)
Use this function in case an error occurred.
Definition TError.cxx:208
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h Atom_t Int_t ULong_t ULong_t unsigned char prop_list Atom_t Atom_t Atom_t Time_t UChar_t len
const_iterator begin() const
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.
void SendCharStar(const char *buf) override
Send const char data.
Bool_t PreProcess(std::shared_ptr< THttpCallArg > &arg) override
Preview data for given socket.
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
Post process http request.
std::string fBuf
!< if buffered data available
Internal instance used to exchange WS functionality between THttpServer and THttpWSHandler.
UInt_t Hash(ECaseCompare cmp=kExact) const
Return hash value.
Definition TString.cxx:684