Logo ROOT  
Reference Guide
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#include "TSystem.h"
17
18#include <cstring>
19#include <cstdlib>
20
21//////////////////////////////////////////////////////////////////////////
22// //
23// THttpLongPollEngine //
24// //
25// Emulation of websocket with long poll requests //
26// Allows to send data from server to client without explicit request //
27// //
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, normally called shortly before destructor
50
52{
53 std::shared_ptr<THttpCallArg> poll;
54
55 {
56 std::lock_guard<std::mutex> grd(fMutex);
57 poll = std::move(fPoll);
58 }
59
60 if (poll) {
61 poll->Set404();
62 poll->NotifyCondition();
63 }
64}
65
66//////////////////////////////////////////////////////////////////////////
67/// Create raw buffer which should be send as reply
68/// For the raw mode all information must be send via binary response
69
70std::string THttpLongPollEngine::MakeBuffer(const void *buf, int len, const char *hdr)
71{
72 std::string res;
73
74 if (!fRaw) {
75 res.resize(len);
76 std::copy((const char *)buf, (const char *)buf + len, res.begin());
77 return res;
78 }
79
80 int hdrlen = hdr ? strlen(hdr) : 0;
81 std::string hdrstr = "bin:";
82 hdrstr.append(std::to_string(hdrlen));
83
84 while ((hdrstr.length() + 1 + hdrlen) % 8 != 0)
85 hdrstr.append(" ");
86 hdrstr.append(":");
87 if (hdrlen > 0)
88 hdrstr.append(hdr);
89
90 res.resize(hdrstr.length() + len);
91 std::copy(hdrstr.begin(), hdrstr.begin() + hdrstr.length(), res.begin());
92 std::copy((const char *)buf, (const char *)buf + len, res.begin() + hdrstr.length());
93
94 return res;
95}
96
97//////////////////////////////////////////////////////////////////////////
98/// Send binary data via connection
99
100void THttpLongPollEngine::Send(const void *buf, int len)
101{
102 std::shared_ptr<THttpCallArg> poll;
103
104 {
105 std::lock_guard<std::mutex> grd(fMutex);
106 poll = std::move(fPoll);
107 }
108
109 if(!poll) {
110 Error("Send", "Operation invoked before polling request obtained");
111 return;
112 }
113
114 std::string buf2 = MakeBuffer(buf, len);
115
116 poll->SetBinaryContent(std::move(buf2));
117 poll->NotifyCondition();
118}
119
120//////////////////////////////////////////////////////////////////////////
121/// Send binary data with text header via connection
122
123void THttpLongPollEngine::SendHeader(const char *hdr, const void *buf, int len)
124{
125 std::shared_ptr<THttpCallArg> poll;
126
127 {
128 std::lock_guard<std::mutex> grd(fMutex);
129 poll = std::move(fPoll);
130 }
131
132 if(!poll) {
133 Error("SendHeader", "Operation invoked before polling request obtained");
134 return;
135 }
136
137 std::string buf2 = MakeBuffer(buf, len, hdr);
138
139 poll->SetBinaryContent(std::move(buf2));
140 if (!fRaw)
141 poll->SetExtraHeader("LongpollHeader", hdr);
142 poll->NotifyCondition();
143}
144
145//////////////////////////////////////////////////////////////////////////
146/// Send const char data
147/// Either do it immediately or keep in internal buffer
148
150{
151 std::shared_ptr<THttpCallArg> poll;
152
153 {
154 std::lock_guard<std::mutex> grd(fMutex);
155 poll = std::move(fPoll);
156 }
157
158 if(!poll) {
159 Error("SendCharStart", "Operation invoked before polling request obtained");
160 return;
161 }
162
163 std::string sendbuf(fRaw ? "txt:" : "");
164 sendbuf.append(buf);
165
166 if (fRaw) poll->SetBinaryContent(std::move(sendbuf));
167 else poll->SetTextContent(std::move(sendbuf));
168 poll->NotifyCondition();
169}
170
171//////////////////////////////////////////////////////////////////////////////
172/// Preview data for given socket
173/// Method called by WS handler before processing websocket data
174/// Returns kTRUE when user should ignore such http request - it is for internal use
175
176Bool_t THttpLongPollEngine::PreProcess(std::shared_ptr<THttpCallArg> &arg)
177{
178 if (!strstr(arg->GetQuery(), "&dummy"))
179 return kFALSE;
180
181 arg->SetPostponed(); // mark http request as pending, http server should wait for notification
182
183 std::shared_ptr<THttpCallArg> poll;
184
185 {
186 std::lock_guard<std::mutex> grd(fMutex);
187 poll = std::move(fPoll);
188 fPoll = arg; // keep reference on polling request
189 }
190
191 if (arg == poll)
192 Fatal("PreviewData", "Submit same THttpCallArg object once again");
193
194 if (poll) {
195 Error("PreviewData", "Get next dummy request when previous not completed");
196 // if there are pending request, reply it immediately
197 // normally should never happen
198 if (fRaw) poll->SetBinaryContent(std::string("txt:") + gLongPollNope);
199 else poll->SetTextContent(std::string(gLongPollNope));
200 poll->NotifyCondition(); // inform http server that request is processed
201 }
202
203 // if arguments has "&dummy" string, user should not process it
204 return kTRUE;
205}
206
207//////////////////////////////////////////////////////////////////////////////
208/// Normally requests from client does not replied directly for longpoll socket
209/// Therefore one can use such request to send data, which was submitted before to the queue
210
211void THttpLongPollEngine::PostProcess(std::shared_ptr<THttpCallArg> &arg)
212{
213 if (fRaw) arg->SetBinaryContent(std::string("txt:") + gLongPollNope);
214 else arg->SetTextContent(std::string(gLongPollNope));
215}
216
217//////////////////////////////////////////////////////////////////////////////
218/// Indicate that polling requests is there and can be immediately invoked
219
221{
222 std::lock_guard<std::mutex> grd(fMutex);
223 return fPoll ? kTRUE : kFALSE;
224}
const Bool_t kFALSE
Definition: RtypesCore.h:90
const Bool_t kTRUE
Definition: RtypesCore.h:89
void Error(const char *location, const char *msgfmt,...)
void Fatal(const char *location, const char *msgfmt,...)
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 and can be immediately invoked.
THttpLongPollEngine(bool raw=false)
constructor
static const std::string gLongPollNope
!< hold polling request, which can be immediately used for the next sending
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...
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...
UInt_t Hash(ECaseCompare cmp=kExact) const
Return hash value.
Definition: TString.cxx:638