Logo ROOT   6.12/07
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 "TNamed.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 /// constructor
31 
33  : TObject(), fTopName(), fMethod(), fPathName(), fFileName(), fUserName(), fQuery(), fPostData(0),
34  fPostDataLength(0), fWSHandle(0), fWSId(0), fContentType(), fRequestHeader(), fHeader(), fContent(), fZipping(0),
35  fBinData(0), fBinDataLength(0), fNotifyFlag(kFALSE)
36 {
37 }
38 
39 ////////////////////////////////////////////////////////////////////////////////
40 /// destructor
41 
43 {
44  if (fPostData) {
45  free(fPostData);
46  fPostData = 0;
47  }
48 
49  if (fWSHandle) {
50  delete fWSHandle;
51  fWSHandle = 0;
52  }
53 
54  if (fBinData) {
55  free(fBinData);
56  fBinData = 0;
57  }
58 }
59 
60 ////////////////////////////////////////////////////////////////////////////////
61 /// method used to get or set http header in the string buffer
62 /// Header has following format:
63 /// field1 : value1\\r\\n
64 /// field2 : value2\\r\\n
65 /// Such format corresponds to header format in HTTP requests
66 
67 TString THttpCallArg::AccessHeader(TString &buf, const char *name, const char *value, Bool_t doing_set)
68 {
69  if (name == 0)
70  return TString();
71 
72  Int_t curr = 0;
73 
74  while (curr < buf.Length() - 2) {
75 
76  Int_t next = buf.Index("\r\n", curr);
77  if (next == kNPOS)
78  break; // should never happen
79 
80  if (buf.Index(name, curr) != curr) {
81  curr = next + 2;
82  continue;
83  }
84 
85  if ((value == 0) && doing_set) {
86  // special case - empty value means that field must be removed completely
87  buf.Remove(curr, next - curr + 2);
88  return TString();
89  }
90 
91  curr += strlen(name);
92  while ((curr < next) && (buf[curr] != ':')) curr++;
93  curr++;
94  while ((curr < next) && (buf[curr] == ' ')) curr++;
95 
96  if (value == 0)
97  return buf(curr, next - curr);
98  buf.Remove(curr, next - curr);
99  buf.Insert(curr, value);
100  return TString(value);
101  }
102 
103  if (value == 0)
104  return TString();
105 
106  buf.Append(TString::Format("%s: %s\r\n", name, value));
107  return TString(value);
108 }
109 
110 ////////////////////////////////////////////////////////////////////////////////
111 /// method used to counter number of headers or returns name of specified header
112 
114 {
115  Int_t curr(0), cnt(0);
116 
117  while (curr < buf.Length() - 2) {
118 
119  Int_t next = buf.Index("\r\n", curr);
120  if (next == kNPOS)
121  break; // should never happen
122 
123  if (cnt == number) {
124  // we should extract name of header
125  Int_t separ = curr + 1;
126  while ((separ < next) && (buf[separ] != ':')) separ++;
127  return buf(curr, separ - curr);
128  }
129 
130  curr = next + 2;
131  cnt++;
132  }
133 
134  // return total number of headers
135  if (number == -1111)
136  return TString::Format("%d", cnt);
137  return TString();
138 }
139 
140 ////////////////////////////////////////////////////////////////////////////////
141 /// set data, posted with the request
142 /// buffer should be allocated with malloc(length+1) call,
143 /// while last byte will be set to 0
144 /// Than one could use post data as null-terminated string
145 
146 void THttpCallArg::SetPostData(void *data, Long_t length, Bool_t make_copy)
147 {
148  if (fPostData) {
149  free(fPostData);
150  fPostData = 0;
151  fPostDataLength = 0;
152  }
153 
154  if (length <= 0)
155  return;
156 
157  if (make_copy && data && length) {
158  void *newdata = malloc(length + 1);
159  memcpy(newdata, data, length);
160  data = newdata;
161  }
162 
163  if (data != 0)
164  *(((char *)data) + length) = 0;
165 
166  fPostData = data;
167  fPostDataLength = length;
168 }
169 
170 ////////////////////////////////////////////////////////////////////////////////
171 /// assign websocket handle with HTTP call
172 
174 {
175  if (fWSHandle)
176  delete fWSHandle;
177  fWSHandle = handle;
178 }
179 
180 ////////////////////////////////////////////////////////////////////////////////
181 /// takeout websocket handle with HTTP call
182 /// can be done only once
183 
185 {
186  TNamed *res = fWSHandle;
187  fWSHandle = 0;
188  return res;
189 }
190 
191 ////////////////////////////////////////////////////////////////////////////////
192 /// set binary data, which will be returned as reply body
193 
195 {
196  if (fBinData)
197  free(fBinData);
198  fBinData = data;
199  fBinDataLength = length;
200 
201  // string content must be cleared in any case
202  fContent.Clear();
203 }
204 
205 ////////////////////////////////////////////////////////////////////////////////
206 /// set complete path of requested http element
207 /// For instance, it could be "/folder/subfolder/get.bin"
208 /// Here "/folder/subfolder/" is element path and "get.bin" requested file.
209 /// One could set path and file name separately
210 
211 void THttpCallArg::SetPathAndFileName(const char *fullpath)
212 {
213  fPathName.Clear();
214  fFileName.Clear();
215 
216  if (fullpath == 0)
217  return;
218 
219  const char *rslash = strrchr(fullpath, '/');
220  if (rslash == 0) {
221  fFileName = fullpath;
222  } else {
223  while ((fullpath != rslash) && (*fullpath == '/')) fullpath++;
224  fPathName.Append(fullpath, rslash - fullpath);
225  if (fPathName == "/")
226  fPathName.Clear();
227  fFileName = rslash + 1;
228  }
229 }
230 
231 ////////////////////////////////////////////////////////////////////////////////
232 /// return specified header
233 
235 {
236  if ((name == 0) || (*name == 0))
237  return TString();
238 
239  if (strcmp(name, "Content-Type") == 0)
240  return fContentType;
241  if (strcmp(name, "Content-Length") == 0)
242  return TString::Format("%ld", GetContentLength());
243 
244  return AccessHeader(fHeader, name);
245 }
246 
247 ////////////////////////////////////////////////////////////////////////////////
248 /// Set name: value pair to reply header
249 /// Content-Type field handled separately - one should use SetContentType() method
250 /// Content-Length field cannot be set at all;
251 
252 void THttpCallArg::AddHeader(const char *name, const char *value)
253 {
254  if ((name == 0) || (*name == 0) || (strcmp(name, "Content-Length") == 0))
255  return;
256 
257  if (strcmp(name, "Content-Type") == 0)
258  SetContentType(value);
259  else
260  AccessHeader(fHeader, name, value, kTRUE);
261 }
262 
263 ////////////////////////////////////////////////////////////////////////////////
264 /// fill HTTP header
265 
266 void THttpCallArg::FillHttpHeader(TString &hdr, const char *kind)
267 {
268  if (kind == 0)
269  kind = "HTTP/1.1";
270 
271  if ((fContentType.Length() == 0) || Is404()) {
272  hdr.Form("%s 404 Not Found\r\n"
273  "Content-Length: 0\r\n"
274  "Connection: close\r\n\r\n",
275  kind);
276  } else {
277  hdr.Form("%s 200 OK\r\n"
278  "Content-Type: %s\r\n"
279  "Connection: keep-alive\r\n"
280  "Content-Length: %ld\r\n"
281  "%s\r\n",
283  }
284 }
285 
286 ////////////////////////////////////////////////////////////////////////////////
287 /// compress reply data with gzip compression
288 
290 {
291  char *objbuf = (char *)GetContent();
292  Long_t objlen = GetContentLength();
293 
294  unsigned long objcrc = R__crc32(0, NULL, 0);
295  objcrc = R__crc32(objcrc, (const unsigned char *)objbuf, objlen);
296 
297  // 10 bytes (ZIP header), compressed data, 8 bytes (CRC and original length)
298  Int_t buflen = 10 + objlen + 8;
299  if (buflen < 512)
300  buflen = 512;
301 
302  void *buffer = malloc(buflen);
303 
304  char *bufcur = (char *)buffer;
305 
306  *bufcur++ = 0x1f; // first byte of ZIP identifier
307  *bufcur++ = 0x8b; // second byte of ZIP identifier
308  *bufcur++ = 0x08; // compression method
309  *bufcur++ = 0x00; // FLAG - empty, no any file names
310  *bufcur++ = 0; // empty timestamp
311  *bufcur++ = 0; //
312  *bufcur++ = 0; //
313  *bufcur++ = 0; //
314  *bufcur++ = 0; // XFL (eXtra FLags)
315  *bufcur++ = 3; // OS 3 means Unix
316  // strcpy(bufcur, "item.json");
317  // bufcur += strlen("item.json")+1;
318 
319  char dummy[8];
320  memcpy(dummy, bufcur - 6, 6);
321 
322  // R__memcompress fills first 6 bytes with own header, therefore just overwrite them
323  unsigned long ziplen = R__memcompress(bufcur - 6, objlen + 6, objbuf, objlen);
324 
325  memcpy(bufcur - 6, dummy, 6);
326 
327  bufcur += (ziplen - 6); // jump over compressed data (6 byte is extra ROOT header)
328 
329  // write CRC32
330  *bufcur++ = objcrc & 0xff;
331  *bufcur++ = (objcrc >> 8) & 0xff;
332  *bufcur++ = (objcrc >> 16) & 0xff;
333  *bufcur++ = (objcrc >> 24) & 0xff;
334 
335  // write original data length
336  *bufcur++ = objlen & 0xff;
337  *bufcur++ = (objlen >> 8) & 0xff;
338  *bufcur++ = (objlen >> 16) & 0xff;
339  *bufcur++ = (objlen >> 24) & 0xff;
340 
341  SetBinData(buffer, bufcur - (char *)buffer);
342 
343  SetEncoding("gzip");
344 
345  return kTRUE;
346 }
347 
348 ////////////////////////////////////////////////////////////////////////////////
349 /// method used to notify condition which waiting when operation will complete
350 /// Condition notified only if not-postponed state is set
351 
353 {
354  if (!fNotifyFlag && !IsPostponed()) {
355  fNotifyFlag = kTRUE;
356  HttpReplied();
357  }
358 }
359 
360 ////////////////////////////////////////////////////////////////////////////////
361 /// virtual method to inform object that http request is processed
362 /// Normally condition is notified and waiting thread will be awaked
363 /// One could reimplement this method in sub-class
364 
366 {
367  fCond.notify_one();
368 }
void SetWSHandle(TNamed *handle)
assign websocket handle with HTTP call
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:585
Basic string class.
Definition: TString.h:125
int Int_t
Definition: RtypesCore.h:41
bool Bool_t
Definition: RtypesCore.h:59
void * fBinData
! binary data, assigned with http call
Definition: THttpCallArg.h:50
#define malloc
Definition: civetweb.c:818
TString & Insert(Ssiz_t pos, const char *s)
Definition: TString.h:595
void SetContentType(const char *typ)
set content type like "text/xml" or "application/json"
Definition: THttpCallArg.h:147
TString CountHeader(const TString &buf, Int_t number=-1111) const
method used to counter number of headers or returns name of specified header
void SetPostData(void *data, Long_t length, Bool_t make_copy=kFALSE)
set data, posted with the request buffer should be allocated with malloc(length+1) call...
void SetEncoding(const char *typ)
Set Content-Encoding header like gzip.
Definition: THttpCallArg.h:180
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:2365
The TNamed class is the base class for all named ROOT classes.
Definition: TNamed.h:29
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:44
void Clear()
Clear string without changing its capacity.
Definition: TString.cxx:1150
TString fPathName
! item path
Definition: THttpCallArg.h:31
TString & Append(const char *cs)
Definition: TString.h:495
TNamed * fWSHandle
! web-socket handle, derived from TNamed class
Definition: THttpCallArg.h:39
virtual void HttpReplied()
virtual method to inform object that http request is processed Normally condition is notified and wai...
Long_t fPostDataLength
! length of binary data
Definition: THttpCallArg.h:37
Long_t fBinDataLength
! length of binary data
Definition: THttpCallArg.h:51
void SetBinData(void *data, Long_t length)
set binary data, which will be returned as reply body
Bool_t fNotifyFlag
! indicate that notification called
Definition: THttpCallArg.h:53
TString AccessHeader(TString &buf, const char *name, const char *value=0, Bool_t doing_set=kFALSE)
method used to get or set http header in the string buffer Header has following format: field1 : valu...
void Form(const char *fmt,...)
Formats a string using a printf style format descriptor.
Definition: TString.cxx:2343
Ssiz_t Length() const
Definition: TString.h:386
void * fPostData
! binary data received with post request
Definition: THttpCallArg.h:36
Bool_t IsPostponed() const
Definition: THttpCallArg.h:208
const Bool_t kFALSE
Definition: RtypesCore.h:88
const void * GetContent() const
Definition: THttpCallArg.h:215
TString & Remove(Ssiz_t pos)
Definition: TString.h:619
long Long_t
Definition: RtypesCore.h:50
std::condition_variable fCond
! condition used to wait for processing
Definition: THttpCallArg.h:42
#define ClassImp(name)
Definition: Rtypes.h:359
TString fHeader
! response header like ContentEncoding, Cache-Control and so on
Definition: THttpCallArg.h:46
#define free
Definition: civetweb.c:821
static RooMathCoreReg dummy
const char * GetContentType() const
Definition: THttpCallArg.h:209
THttpCallArg()
constructor
void NotifyCondition()
method used to notify condition which waiting when operation will complete Condition notified only if...
TNamed * TakeWSHandle()
takeout websocket handle with HTTP call can be done only once
Mother of all ROOT objects.
Definition: TObject.h:37
TString fContent
! text content (if any)
Definition: THttpCallArg.h:47
void AddHeader(const char *name, const char *value)
Set name: value pair to reply header Content-Type field handled separately - one should use SetConten...
Bool_t Is404() const
Definition: THttpCallArg.h:206
const Bool_t kTRUE
Definition: RtypesCore.h:87
~THttpCallArg()
destructor
TString fFileName
! file name
Definition: THttpCallArg.h:32
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
void FillHttpHeader(TString &buf, const char *header=0)
fill HTTP header
const char * Data() const
Definition: TString.h:345
Long_t GetContentLength() const
Definition: THttpCallArg.h:213