Logo ROOT   6.30.04
Reference Guide
 All Namespaces Files Pages
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 
30 const std::string THttpLongPollEngine::gLongPollNope = "<<nope>>";
31 
32 //////////////////////////////////////////////////////////////////////////
33 /// constructor
34 
35 THttpLongPollEngine::THttpLongPollEngine(bool raw) : THttpWSEngine(), fRaw(raw)
36 {
37 }
38 
39 //////////////////////////////////////////////////////////////////////////
40 /// returns ID of the engine, created from this pointer
41 
42 UInt_t THttpLongPollEngine::GetId() const
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 
51 void THttpLongPollEngine::ClearHandle(Bool_t)
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 
70 std::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 
100 void 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 
123 void 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 
149 void THttpLongPollEngine::SendCharStar(const char *buf)
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 
176 Bool_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 
211 void 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 
220 Bool_t THttpLongPollEngine::CanSendDirectly()
221 {
222  std::lock_guard<std::mutex> grd(fMutex);
223  return fPoll ? kTRUE : kFALSE;
224 }