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\ingroup http
22
23Emulation of websocket with long poll requests
24
25Allows to send data from server to client without explicit request
26
27Created automatically as fallback solution when normal WebSopcket connection cannot be established
28*/
29
30const std::string THttpLongPollEngine::gLongPollNope = "<<nope>>";
31
32//////////////////////////////////////////////////////////////////////////
33/// constructor
34
36{
37}
38
39//////////////////////////////////////////////////////////////////////////
40/// Returns ID of the engine, created from this pointer
41
43{
44 const void *ptr = (const void *)this;
45 return TString::Hash((void *)&ptr, sizeof(void *));
46}
47
48//////////////////////////////////////////////////////////////////////////
49/// Clear request
50///
51/// normally called shortly before destructor
52
54{
55 std::shared_ptr<THttpCallArg> poll;
56
57 {
58 std::lock_guard<std::mutex> grd(fMutex);
59 poll = std::move(fPoll);
60 }
61
62 if (poll) {
63 poll->Set404();
64 poll->NotifyCondition();
65 }
66}
67
68//////////////////////////////////////////////////////////////////////////
69/// Create raw buffer which should be send as reply
70///
71/// For the raw mode all information must be send via binary response
72
73std::string THttpLongPollEngine::MakeBuffer(const void *buf, int len, const char *hdr)
74{
75 std::string res;
76
77 if (!fRaw) {
78 res.resize(len);
79 std::copy((const char *)buf, (const char *)buf + len, res.begin());
80 return res;
81 }
82
83 int hdrlen = hdr ? strlen(hdr) : 0;
84 std::string hdrstr = "bin:";
85 hdrstr.append(std::to_string(hdrlen));
86
87 while ((hdrstr.length() + 1 + hdrlen) % 8 != 0)
88 hdrstr.append(" ");
89 hdrstr.append(":");
90 if (hdrlen > 0)
91 hdrstr.append(hdr);
92
93 res.resize(hdrstr.length() + len);
94 std::copy(hdrstr.begin(), hdrstr.begin() + hdrstr.length(), res.begin());
95 std::copy((const char *)buf, (const char *)buf + len, res.begin() + hdrstr.length());
96
97 return res;
98}
99
100//////////////////////////////////////////////////////////////////////////
101/// Send binary data via connection
102
103void THttpLongPollEngine::Send(const void *buf, int len)
104{
105 std::shared_ptr<THttpCallArg> poll;
106
107 std::string sendbuf = MakeBuffer(buf, len);
108
109 {
110 std::lock_guard<std::mutex> grd(fMutex);
111 if (fPoll) {
112 poll = std::move(fPoll);
113 } else if (fBufKind == kNoBuf) {
115 std::swap(fBuf, sendbuf);
116 return;
117 }
118 }
119
120 if(!poll) {
121 Error("Send", "Operation invoked before polling request obtained");
122 return;
123 }
124
125 poll->SetBinaryContent(std::move(sendbuf));
126 poll->NotifyCondition();
127}
128
129//////////////////////////////////////////////////////////////////////////
130/// Send binary data with text header via connection
131
132void THttpLongPollEngine::SendHeader(const char *hdr, const void *buf, int len)
133{
134 std::shared_ptr<THttpCallArg> poll;
135
136 std::string sendbuf = MakeBuffer(buf, len, hdr);
137
138 {
139 std::lock_guard<std::mutex> grd(fMutex);
140 if (fPoll) {
141 poll = std::move(fPoll);
142 } else if (fBufKind == kNoBuf) {
144 if (!fRaw && hdr) fBufHeader = hdr;
145 std::swap(fBuf, sendbuf);
146 return;
147 }
148 }
149
150 if(!poll) {
151 Error("SendHeader", "Operation invoked before polling request obtained");
152 return;
153 }
154
155 poll->SetBinaryContent(std::move(sendbuf));
156 if (!fRaw)
157 poll->SetExtraHeader("LongpollHeader", hdr);
158 poll->NotifyCondition();
159}
160
161//////////////////////////////////////////////////////////////////////////
162/// Send const char data
163///
164/// Either do it immediately or keep in internal buffer
165
167{
168 std::shared_ptr<THttpCallArg> poll;
169
170 std::string sendbuf(fRaw ? "txt:" : "");
171 sendbuf.append(buf);
172
173 {
174 std::lock_guard<std::mutex> grd(fMutex);
175 if (fPoll) {
176 poll = std::move(fPoll);
177 } else if (fBufKind == kNoBuf) {
179 std::swap(fBuf, sendbuf);
180 return;
181 }
182 }
183
184 if(!poll) {
185 Error("SendCharStart", "Operation invoked before polling request obtained");
186 return;
187 }
188
189 if (fRaw) poll->SetBinaryContent(std::move(sendbuf));
190 else poll->SetTextContent(std::move(sendbuf));
191 poll->NotifyCondition();
192}
193
194//////////////////////////////////////////////////////////////////////////////
195/// Preview data for given socket
196///
197/// Method called by WS handler before processing websocket data
198/// Returns kTRUE when user should ignore such http request - it is for internal use
199
200Bool_t THttpLongPollEngine::PreProcess(std::shared_ptr<THttpCallArg> &arg)
201{
202 if (!strstr(arg->GetQuery(), "&dummy"))
203 return kFALSE;
204
205 std::shared_ptr<THttpCallArg> poll;
206
207 // if request cannot be postponed just reply it
208 if (arg->CanPostpone()) {
209 std::lock_guard<std::mutex> grd(fMutex);
210 if (fBufKind != kNoBuf) {
211 // there is data which can be send directly
212 poll = arg;
213 } else {
214 arg->SetPostponed(); // mark http request as pending, http server should wait for notification
215 poll = std::move(fPoll);
216 fPoll = arg; // keep reference on polling request
217 }
218
219 } else {
220 poll = arg;
221 }
222
223 if (poll) {
224 // if there buffered data, it will be provided
225 PostProcess(poll);
226 poll->NotifyCondition(); // inform http server that request is processed
227 }
228
229 // if arguments has "&dummy" string, user should not process it
230 return kTRUE;
231}
232
233//////////////////////////////////////////////////////////////////////////////
234/// Post process http request
235///
236/// Normally requests from client does not replied directly for longpoll socket
237/// Therefore one can use such request to send data, which was submitted before to the queue
238
239void THttpLongPollEngine::PostProcess(std::shared_ptr<THttpCallArg> &arg)
240{
241 EBufKind kind = kNoBuf;
242 std::string sendbuf, sendhdr;
243
244 {
245 std::lock_guard<std::mutex> grd(fMutex);
246 if (fBufKind != kNoBuf) {
247 kind = fBufKind;
249 std::swap(sendbuf, fBuf);
250 std::swap(sendhdr, fBufHeader);
251 }
252 }
253
254 if (kind == kTxtBuf) {
255 arg->SetTextContent(std::move(sendbuf));
256 } else if (kind == kBinBuf) {
257 arg->SetBinaryContent(std::move(sendbuf));
258 if (!sendhdr.empty())
259 arg->SetExtraHeader("LongpollHeader", sendhdr.c_str());
260 } else if (fRaw) {
261 arg->SetBinaryContent(std::string("txt:") + gLongPollNope);
262 } else {
263 arg->SetTextContent(std::string(gLongPollNope));
264 }
265}
266
267//////////////////////////////////////////////////////////////////////////////
268/// Indicate that polling requests is there or buffer empty and can be immediately invoked
269
271{
272 std::lock_guard<std::mutex> grd(fMutex);
273 return fPoll || (fBufKind == kNoBuf) ? kTRUE : kFALSE;
274}
constexpr Bool_t kFALSE
Definition RtypesCore.h:101
constexpr 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:185
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
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:677