Logo ROOT   6.14/05
Reference Guide
THttpCallArg.cxx
Go to the documentation of this file.
1 // $Id$
2 // Author: Sergey Linev 21/05/2015
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 "THttpCallArg.h"
13 
14 #include <string.h>
15 #include "RZip.h"
16 #include "THttpWSEngine.h"
17 
18 //////////////////////////////////////////////////////////////////////////
19 // //
20 // THttpCallArg //
21 // //
22 // Contains arguments for single HTTP call //
23 // Must be used in THttpEngine to process incoming http requests //
24 // //
25 //////////////////////////////////////////////////////////////////////////
26 
28 
29 ////////////////////////////////////////////////////////////////////////////////
30 /// destructor
31 
33 {
34 }
35 
36 ////////////////////////////////////////////////////////////////////////////////
37 /// method used to get or set http header in the string buffer
38 /// Header has following format:
39 /// field1 : value1\\r\\n
40 /// field2 : value2\\r\\n
41 /// Such format corresponds to header format in HTTP requests
42 
43 TString THttpCallArg::AccessHeader(TString &buf, const char *name, const char *value, Bool_t doing_set)
44 {
45  if (name == 0)
46  return TString();
47 
48  Int_t curr = 0;
49 
50  while (curr < buf.Length() - 2) {
51 
52  Int_t next = buf.Index("\r\n", curr);
53  if (next == kNPOS)
54  break; // should never happen
55 
56  if (buf.Index(name, curr) != curr) {
57  curr = next + 2;
58  continue;
59  }
60 
61  if ((value == 0) && doing_set) {
62  // special case - empty value means that field must be removed completely
63  buf.Remove(curr, next - curr + 2);
64  return TString();
65  }
66 
67  curr += strlen(name);
68  while ((curr < next) && (buf[curr] != ':'))
69  curr++;
70  curr++;
71  while ((curr < next) && (buf[curr] == ' '))
72  curr++;
73 
74  if (value == 0)
75  return buf(curr, next - curr);
76  buf.Remove(curr, next - curr);
77  buf.Insert(curr, value);
78  return TString(value);
79  }
80 
81  if (value == 0)
82  return TString();
83 
84  buf.Append(TString::Format("%s: %s\r\n", name, value));
85  return TString(value);
86 }
87 
88 ////////////////////////////////////////////////////////////////////////////////
89 /// method used to counter number of headers or returns name of specified header
90 
92 {
93  Int_t curr(0), cnt(0);
94 
95  while (curr < buf.Length() - 2) {
96 
97  Int_t next = buf.Index("\r\n", curr);
98  if (next == kNPOS)
99  break; // should never happen
100 
101  if (cnt == number) {
102  // we should extract name of header
103  Int_t separ = curr + 1;
104  while ((separ < next) && (buf[separ] != ':'))
105  separ++;
106  return buf(curr, separ - curr);
107  }
108 
109  curr = next + 2;
110  cnt++;
111  }
112 
113  // return total number of headers
114  if (number == -1111)
115  return TString::Format("%d", cnt);
116  return TString();
117 }
118 
119 
120 ////////////////////////////////////////////////////////////////////////////////
121 /// Set content as text.
122 /// Content will be copied by THttpCallArg
123 void THttpCallArg::SetContent(const char *cont)
124 {
125  if (cont)
126  fContent = cont;
127  else
128  fContent.clear();
129 }
130 
131 ////////////////////////////////////////////////////////////////////////////////
132 /// Set text or binary content directly
133 /// After method call argument cont will be in undefined state
134 
135 void THttpCallArg::SetContent(std::string &&cont)
136 {
137  fContent = cont;
138 }
139 
140 ////////////////////////////////////////////////////////////////////////////////
141 /// Set content type as "text/plain"
142 
144 {
145  SetContentType("text/plain");
146 }
147 
148 ////////////////////////////////////////////////////////////////////////////////
149 /// Set content type as "text/plain" and also assigns content
150 /// After method call argument \param txt will be in undefined state
151 
152 void THttpCallArg::SetTextContent(std::string &&txt)
153 {
154  SetText();
155  fContent = txt;
156 }
157 
158 ////////////////////////////////////////////////////////////////////////////////
159 /// Set content type as "text/xml"
160 
162 {
163  SetContentType("text/xml");
164 }
165 
166 ////////////////////////////////////////////////////////////////////////////////
167 /// Set content type as "text/xml" and also assigns content
168 /// After method call argument \param xml will be in undefined state
169 
170 void THttpCallArg::SetXmlContent(std::string &&xml)
171 {
172  SetXml();
173  fContent = xml;
174 }
175 
176 ////////////////////////////////////////////////////////////////////////////////
177 /// Set content type as "application/json"
178 
180 {
181  SetContentType("application/json");
182 }
183 
184 ////////////////////////////////////////////////////////////////////////////////
185 /// Set content type as "application/json" and also assigns content
186 /// After method call argument \param json will be in undefined state
187 
189 {
190  SetJson();
191  fContent = json;
192 }
193 
194 ////////////////////////////////////////////////////////////////////////////////
195 /// Set content type as "application/x-binary"
196 
198 {
199  SetContentType("application/x-binary");
200 }
201 
202 ////////////////////////////////////////////////////////////////////////////////
203 /// Set content type as "application/x-binary" and also assigns content
204 /// After method call argument \param bin will be in undefined state
205 
206 void THttpCallArg::SetBinaryContent(std::string &&bin)
207 {
208  SetBinary();
209  fContent = bin;
210 }
211 
212 ////////////////////////////////////////////////////////////////////////////////
213 /// \deprecated Use signature with std::string
214 /// Set data, posted with the request
215 /// If make_copy==kFALSE, data will be released with free(data) call
216 
217 void THttpCallArg::SetPostData(void *data, Long_t length, Bool_t make_copy)
218 {
219  fPostData.resize(length);
220 
221  if (data && length) {
222  std::copy((const char *)data, (const char *)data + length, fPostData.begin());
223  if (!make_copy) free(data); // it supposed to get ownership over the buffer
224  }
225 }
226 
227 ////////////////////////////////////////////////////////////////////////////////
228 /// set data, which is posted with the request
229 /// Although std::string is used, not only text data can be assigned -
230 /// std::string can contain any sequence of symbols
231 
232 void THttpCallArg::SetPostData(std::string &&data)
233 {
234  fPostData = data;
235 }
236 
237 ////////////////////////////////////////////////////////////////////////////////
238 /// Assign websocket identifier from the engine
239 
241 {
242  SetWSId(fWSEngine->GetId());
243 }
244 
245 ////////////////////////////////////////////////////////////////////////////////
246 /// takeout websocket handle with HTTP call
247 /// can be done only once
248 
249 std::shared_ptr<THttpWSEngine> THttpCallArg::TakeWSEngine()
250 {
251  auto res = fWSEngine;
252  fWSEngine.reset();
253  return res;
254 }
255 
256 ////////////////////////////////////////////////////////////////////////////////
257 /// Replace all occurrences of \param from by \param to in content
258 /// Used only internally
259 
260 void THttpCallArg::ReplaceAllinContent(const std::string &from, const std::string &to)
261 {
262  std::size_t start_pos = 0;
263  while((start_pos = fContent.find(from, start_pos)) != std::string::npos) {
264  fContent.replace(start_pos, from.length(), to);
265  start_pos += to.length(); // Handles case where 'to' is a substring of 'from'
266  }
267 }
268 
269 ////////////////////////////////////////////////////////////////////////////////
270 /// \deprecated use SetContent(std::string &&arg) signature instead
271 /// set binary data, which will be returned as reply body
272 /// Memory should be allocated with std::malloc().
273 /// THttpCallArg take over ownership over specified memory.
274 /// Memory will be released by calling std::free() function.
275 
276 void THttpCallArg::SetBinData(void *data, Long_t length)
277 {
278  // string content must be cleared in any case
279  if (length <= 0) {
280  fContent.clear();
281  } else {
282  fContent.resize(length);
283  if (data) {
284  std::copy((const char *)data, (const char *)data + length, fContent.begin());
285  free(data);
286  }
287  }
288 }
289 
290 ////////////////////////////////////////////////////////////////////////////////
291 /// set complete path of requested http element
292 /// For instance, it could be "/folder/subfolder/get.bin"
293 /// Here "/folder/subfolder/" is element path and "get.bin" requested file.
294 /// One could set path and file name separately
295 
296 void THttpCallArg::SetPathAndFileName(const char *fullpath)
297 {
298  fPathName.Clear();
299  fFileName.Clear();
300 
301  if (fullpath == 0)
302  return;
303 
304  const char *rslash = strrchr(fullpath, '/');
305  if (rslash == 0) {
306  fFileName = fullpath;
307  } else {
308  while ((fullpath != rslash) && (*fullpath == '/'))
309  fullpath++;
310  fPathName.Append(fullpath, rslash - fullpath);
311  if (fPathName == "/")
312  fPathName.Clear();
313  fFileName = rslash + 1;
314  }
315 }
316 
317 ////////////////////////////////////////////////////////////////////////////////
318 /// return specified header
319 
321 {
322  if ((name == 0) || (*name == 0))
323  return TString();
324 
325  if (strcmp(name, "Content-Type") == 0)
326  return fContentType;
327  if (strcmp(name, "Content-Length") == 0)
328  return TString::Format("%ld", GetContentLength());
329 
330  return AccessHeader(fHeader, name);
331 }
332 
333 ////////////////////////////////////////////////////////////////////////////////
334 /// Set name: value pair to reply header
335 /// Content-Type field handled separately - one should use SetContentType() method
336 /// Content-Length field cannot be set at all;
337 
338 void THttpCallArg::AddHeader(const char *name, const char *value)
339 {
340  if ((name == 0) || (*name == 0) || (strcmp(name, "Content-Length") == 0))
341  return;
342 
343  if (strcmp(name, "Content-Type") == 0)
344  SetContentType(value);
345  else
346  AccessHeader(fHeader, name, value, kTRUE);
347 }
348 
349 ////////////////////////////////////////////////////////////////////////////////
350 /// Fills HTTP header, which can be send at the beggining of reply on the http request
351 /// \param name is HTTP protocol name (default "HTTP/1.1")
352 
353 std::string THttpCallArg::FillHttpHeader(const char *name)
354 {
355  std::string hdr(name ? name : "HTTP/1.1");
356 
357  if ((fContentType.Length() == 0) || Is404())
358  hdr.append(" 404 Not Found\r\n"
359  "Content-Length: 0\r\n"
360  "Connection: close\r\n\r\n");
361  else
362  hdr.append(Form(" 200 OK\r\n"
363  "Content-Type: %s\r\n"
364  "Connection: keep-alive\r\n"
365  "Content-Length: %ld\r\n"
366  "%s\r\n",
367  GetContentType(), GetContentLength(), fHeader.Data()));
368 
369  return hdr;
370 }
371 
372 ////////////////////////////////////////////////////////////////////////////////
373 /// \depricated use FillHttpHeader with other signature
374 /// Fills HTTP header, which can be send at the beggining of reply on the http request
375 /// \param name is HTTP protocol name (default "HTTP/1.1")
376 
377 void THttpCallArg::FillHttpHeader(TString &hdr, const char *name)
378 {
379  hdr = FillHttpHeader(name).c_str();
380 }
381 
382 ////////////////////////////////////////////////////////////////////////////////
383 /// compress reply data with gzip compression
384 
386 {
387  char *objbuf = (char *)GetContent();
388  Long_t objlen = GetContentLength();
389 
390  unsigned long objcrc = R__crc32(0, NULL, 0);
391  objcrc = R__crc32(objcrc, (const unsigned char *)objbuf, objlen);
392 
393  // 10 bytes (ZIP header), compressed data, 8 bytes (CRC and original length)
394  Int_t buflen = 10 + objlen + 8;
395  if (buflen < 512)
396  buflen = 512;
397 
398  std::string buffer;
399  buffer.resize(buflen);
400 
401  char *bufcur = (char *)buffer.data();
402 
403  *bufcur++ = 0x1f; // first byte of ZIP identifier
404  *bufcur++ = 0x8b; // second byte of ZIP identifier
405  *bufcur++ = 0x08; // compression method
406  *bufcur++ = 0x00; // FLAG - empty, no any file names
407  *bufcur++ = 0; // empty timestamp
408  *bufcur++ = 0; //
409  *bufcur++ = 0; //
410  *bufcur++ = 0; //
411  *bufcur++ = 0; // XFL (eXtra FLags)
412  *bufcur++ = 3; // OS 3 means Unix
413  // strcpy(bufcur, "item.json");
414  // bufcur += strlen("item.json")+1;
415 
416  char dummy[8];
417  memcpy(dummy, bufcur - 6, 6);
418 
419  // R__memcompress fills first 6 bytes with own header, therefore just overwrite them
420  unsigned long ziplen = R__memcompress(bufcur - 6, objlen + 6, objbuf, objlen);
421 
422  memcpy(bufcur - 6, dummy, 6);
423 
424  bufcur += (ziplen - 6); // jump over compressed data (6 byte is extra ROOT header)
425 
426  // write CRC32
427  *bufcur++ = objcrc & 0xff;
428  *bufcur++ = (objcrc >> 8) & 0xff;
429  *bufcur++ = (objcrc >> 16) & 0xff;
430  *bufcur++ = (objcrc >> 24) & 0xff;
431 
432  // write original data length
433  *bufcur++ = objlen & 0xff;
434  *bufcur++ = (objlen >> 8) & 0xff;
435  *bufcur++ = (objlen >> 16) & 0xff;
436  *bufcur++ = (objlen >> 24) & 0xff;
437 
438  buffer.resize(bufcur - (char *)buffer.data());
439 
440  SetContent(std::move(buffer));
441 
442  SetEncoding("gzip");
443 
444  return kTRUE;
445 }
446 
447 ////////////////////////////////////////////////////////////////////////////////
448 /// method used to notify condition which waiting when operation will complete
449 /// Condition notified only if not-postponed state is set
450 
451 void THttpCallArg::NotifyCondition()
452 {
453  if (!fNotifyFlag && !IsPostponed()) {
454  fNotifyFlag = kTRUE;
455  HttpReplied();
456  }
457 }
458 
459 ////////////////////////////////////////////////////////////////////////////////
460 /// virtual method to inform object that http request is processed
461 /// Normally condition is notified and waiting thread will be awaked
462 /// One could reimplement this method in sub-class
463 
464 void THttpCallArg::HttpReplied()
465 {
466  fCond.notify_one();
467 }
void SetTextContent(std::string &&txt)
Set content type as "text/plain" and also assigns content After method call argument.
std::string fContent
Definition: THttpCallArg.h:66
void SetBinary()
Set content type as "application/x-binary".
void ReplaceAllinContent(const std::string &from, const std::string &to)
Replace all occurrences of.
std::string fPostData
!< content - text or binary
Definition: THttpCallArg.h:67
TString AccessHeader(TString &buf, const char *name, const char *value=nullptr, Bool_t doing_set=kFALSE)
method used to get or set http header in the string buffer Header has following format: field1 : valu...
const Ssiz_t kNPOS
Definition: RtypesCore.h:111
Ssiz_t Index(const char *pat, Ssiz_t i=0, ECaseCompare cmp=kExact) const
Definition: TString.h:634
Basic string class.
Definition: TString.h:131
int Int_t
Definition: RtypesCore.h:41
bool Bool_t
Definition: RtypesCore.h:59
TString & Insert(Ssiz_t pos, const char *s)
Definition: TString.h:644
void SetContentType(const char *typ)
set content type like "text/xml" or "application/json"
Definition: THttpCallArg.h:159
TString CountHeader(const TString &buf, Int_t number=-1111) const
method used to counter number of headers or returns name of specified header
void SetContent(const char *cont)
Set content as text.
void SetPostData(void *data, Long_t length, Bool_t make_copy=kFALSE)
void SetEncoding(const char *typ)
Set Content-Encoding header like gzip.
Definition: THttpCallArg.h:198
static TString Format(const char *fmt,...)
Static method which formats a string using a printf style format descriptor and return a TString...
Definition: TString.cxx:2286
void SetPathAndFileName(const char *fullpath)
set complete path of requested http element For instance, it could be "/folder/subfolder/get.bin" Here "/folder/subfolder/" is element path and "get.bin" requested file.
TString fContentType
! type of content
Definition: THttpCallArg.h:52
std::shared_ptr< THttpWSEngine > fWSEngine
! web-socket engine, which supplied to run created web socket
Definition: THttpCallArg.h:64
void Clear()
Clear string without changing its capacity.
Definition: TString.cxx:1151
void SetBinaryContent(std::string &&bin)
Set content type as "application/x-binary" and also assigns content After method call argument...
TString fPathName
! item path
Definition: THttpCallArg.h:43
TString & Append(const char *cs)
Definition: TString.h:559
void AssignWSId()
Assign websocket identifier from the engine.
void SetJson()
Set content type as "application/json".
Bool_t fNotifyFlag
! indicate that notification called
Definition: THttpCallArg.h:57
void SetXmlContent(std::string &&xml)
Set content type as "text/xml" and also assigns content After method call argument.
char * Form(const char *fmt,...)
Ssiz_t Length() const
Definition: TString.h:405
void SetText()
Set content type as "text/plain".
TString & Remove(Ssiz_t pos)
Definition: TString.h:668
long Long_t
Definition: RtypesCore.h:50
std::condition_variable fCond
! condition used to wait for processing
Definition: THttpCallArg.h:50
#define ClassImp(name)
Definition: Rtypes.h:359
void SetWSId(UInt_t id)
set web-socket id
Definition: THttpCallArg.h:105
void SetXml()
Set content type as "text/xml".
TString fHeader
! response header like ContentEncoding, Cache-Control and so on
Definition: THttpCallArg.h:54
#define free
Definition: civetweb.c:1350
static RooMathCoreReg dummy
void SetJsonContent(std::string &&json)
Set content type as "application/json" and also assigns content After method call argument...
void AddHeader(const char *name, const char *value)
Set name: value pair to reply header Content-Type field handled separately - one should use SetConten...
basic_json<> json
default JSON class
Definition: json.hpp:12912
const Bool_t kTRUE
Definition: RtypesCore.h:87
virtual ~THttpCallArg()
destructor
TString fFileName
! file name
Definition: THttpCallArg.h:44
std::shared_ptr< THttpWSEngine > TakeWSEngine()
takeout websocket handle with HTTP call can be done only once
char name[80]
Definition: TGX11.cxx:109
const char * cnt
Definition: TXMLSetup.cxx:74
Bool_t CompressWithGzip()
compress reply data with gzip compression
TString GetHeader(const char *name)
return specified header
const char * Data() const
Definition: TString.h:364