Logo ROOT   6.10/09
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) return TString();
70 
71  Int_t curr = 0;
72 
73  while (curr < buf.Length() - 2) {
74 
75  Int_t next = buf.Index("\r\n", curr);
76  if (next == kNPOS) break; // should never happen
77 
78  if (buf.Index(name, curr) != curr) {
79  curr = next + 2;
80  continue;
81  }
82 
83  if ((value == 0) && doing_set) {
84  // special case - empty value means that field must be removed completely
85  buf.Remove(curr, next - curr + 2);
86  return TString();
87  }
88 
89  curr += strlen(name);
90  while ((curr < next) && (buf[curr] != ':')) curr++;
91  curr++;
92  while ((curr < next) && (buf[curr] == ' ')) curr++;
93 
94  if (value == 0) return buf(curr, next - curr);
95  buf.Remove(curr, next - curr);
96  buf.Insert(curr, value);
97  return TString(value);
98  }
99 
100  if (value == 0) return TString();
101 
102  buf.Append(TString::Format("%s: %s\r\n", name, value));
103  return TString(value);
104 }
105 
106 ////////////////////////////////////////////////////////////////////////////////
107 /// method used to counter number of headers or returns name of specified header
108 
110 {
111  Int_t curr(0), cnt(0);
112 
113  while (curr < buf.Length() - 2) {
114 
115  Int_t next = buf.Index("\r\n", curr);
116  if (next == kNPOS) break; // should never happen
117 
118  if (cnt == number) {
119  // we should extract name of header
120  Int_t separ = curr + 1;
121  while ((separ < next) && (buf[separ] != ':')) separ++;
122  return buf(curr, separ - curr);
123  }
124 
125  curr = next + 2;
126  cnt++;
127  }
128 
129  // return total number of headers
130  if (number == -1111) return TString::Format("%d", cnt);
131  return TString();
132 }
133 
134 ////////////////////////////////////////////////////////////////////////////////
135 /// set data, posted with the request
136 /// buffer should be allocated with malloc(length+1) call,
137 /// while last byte will be set to 0
138 /// Than one could use post data as null-terminated string
139 
141 {
142  if (fPostData) free(fPostData);
143  if (data != 0) *(((char *)data) + length) = 0;
144  fPostData = data;
145  fPostDataLength = length;
146 }
147 
148 ////////////////////////////////////////////////////////////////////////////////
149 /// assign websocket handle with HTTP call
150 
152 {
153  if (fWSHandle) delete fWSHandle;
154  fWSHandle = handle;
155 }
156 
157 ////////////////////////////////////////////////////////////////////////////////
158 /// takeout websocket handle with HTTP call
159 /// can be done only once
160 
162 {
163  TNamed *res = fWSHandle;
164  fWSHandle = 0;
165  return res;
166 }
167 
168 ////////////////////////////////////////////////////////////////////////////////
169 /// set binary data, which will be returned as reply body
170 
172 {
173  if (fBinData) free(fBinData);
174  fBinData = data;
175  fBinDataLength = length;
176 
177  // string content must be cleared in any case
178  fContent.Clear();
179 }
180 
181 ////////////////////////////////////////////////////////////////////////////////
182 /// set complete path of requested http element
183 /// For instance, it could be "/folder/subfolder/get.bin"
184 /// Here "/folder/subfolder/" is element path and "get.bin" requested file.
185 /// One could set path and file name separately
186 
187 void THttpCallArg::SetPathAndFileName(const char *fullpath)
188 {
189  fPathName.Clear();
190  fFileName.Clear();
191 
192  if (fullpath == 0) return;
193 
194  const char *rslash = strrchr(fullpath, '/');
195  if (rslash == 0) {
196  fFileName = fullpath;
197  } else {
198  while ((fullpath != rslash) && (*fullpath == '/')) fullpath++;
199  fPathName.Append(fullpath, rslash - fullpath);
200  if (fPathName == "/") fPathName.Clear();
201  fFileName = rslash + 1;
202  }
203 }
204 
205 ////////////////////////////////////////////////////////////////////////////////
206 /// return specified header
207 
209 {
210  if ((name == 0) || (*name == 0)) return TString();
211 
212  if (strcmp(name, "Content-Type") == 0) return fContentType;
213  if (strcmp(name, "Content-Length") == 0) return TString::Format("%ld", GetContentLength());
214 
215  return AccessHeader(fHeader, name);
216 }
217 
218 ////////////////////////////////////////////////////////////////////////////////
219 /// Set name: value pair to reply header
220 /// Content-Type field handled separately - one should use SetContentType() method
221 /// Content-Length field cannot be set at all;
222 
223 void THttpCallArg::AddHeader(const char *name, const char *value)
224 {
225  if ((name == 0) || (*name == 0) || (strcmp(name, "Content-Length") == 0)) return;
226 
227  if (strcmp(name, "Content-Type") == 0)
228  SetContentType(value);
229  else
230  AccessHeader(fHeader, name, value, kTRUE);
231 }
232 
233 ////////////////////////////////////////////////////////////////////////////////
234 /// fill HTTP header
235 
236 void THttpCallArg::FillHttpHeader(TString &hdr, const char *kind)
237 {
238  if (kind == 0) kind = "HTTP/1.1";
239 
240  if ((fContentType.Length() == 0) || Is404()) {
241  hdr.Form("%s 404 Not Found\r\n"
242  "Content-Length: 0\r\n"
243  "Connection: close\r\n\r\n",
244  kind);
245  } else {
246  hdr.Form("%s 200 OK\r\n"
247  "Content-Type: %s\r\n"
248  "Connection: keep-alive\r\n"
249  "Content-Length: %ld\r\n"
250  "%s\r\n",
252  }
253 }
254 
255 ////////////////////////////////////////////////////////////////////////////////
256 /// compress reply data with gzip compression
257 
259 {
260  char *objbuf = (char *)GetContent();
261  Long_t objlen = GetContentLength();
262 
263  unsigned long objcrc = R__crc32(0, NULL, 0);
264  objcrc = R__crc32(objcrc, (const unsigned char *)objbuf, objlen);
265 
266  // 10 bytes (ZIP header), compressed data, 8 bytes (CRC and original length)
267  Int_t buflen = 10 + objlen + 8;
268  if (buflen < 512) buflen = 512;
269 
270  void *buffer = malloc(buflen);
271 
272  char *bufcur = (char *)buffer;
273 
274  *bufcur++ = 0x1f; // first byte of ZIP identifier
275  *bufcur++ = 0x8b; // second byte of ZIP identifier
276  *bufcur++ = 0x08; // compression method
277  *bufcur++ = 0x00; // FLAG - empty, no any file names
278  *bufcur++ = 0; // empty timestamp
279  *bufcur++ = 0; //
280  *bufcur++ = 0; //
281  *bufcur++ = 0; //
282  *bufcur++ = 0; // XFL (eXtra FLags)
283  *bufcur++ = 3; // OS 3 means Unix
284  // strcpy(bufcur, "item.json");
285  // bufcur += strlen("item.json")+1;
286 
287  char dummy[8];
288  memcpy(dummy, bufcur - 6, 6);
289 
290  // R__memcompress fills first 6 bytes with own header, therefore just overwrite them
291  unsigned long ziplen = R__memcompress(bufcur - 6, objlen + 6, objbuf, objlen);
292 
293  memcpy(bufcur - 6, dummy, 6);
294 
295  bufcur += (ziplen - 6); // jump over compressed data (6 byte is extra ROOT header)
296 
297  // write CRC32
298  *bufcur++ = objcrc & 0xff;
299  *bufcur++ = (objcrc >> 8) & 0xff;
300  *bufcur++ = (objcrc >> 16) & 0xff;
301  *bufcur++ = (objcrc >> 24) & 0xff;
302 
303  // write original data length
304  *bufcur++ = objlen & 0xff;
305  *bufcur++ = (objlen >> 8) & 0xff;
306  *bufcur++ = (objlen >> 16) & 0xff;
307  *bufcur++ = (objlen >> 24) & 0xff;
308 
309  SetBinData(buffer, bufcur - (char *)buffer);
310 
311  SetEncoding("gzip");
312 
313  return kTRUE;
314 }
315 
316 ////////////////////////////////////////////////////////////////////////////////
317 /// method used to notify condition which waiting when operation will complete
318 /// Condition notified only if not-postponed state is set
319 
321 {
322  if (!fNotifyFlag && !IsPostponed()) {
323  fNotifyFlag = kTRUE;
324  HttpReplied();
325  }
326 }
327 
328 ////////////////////////////////////////////////////////////////////////////////
329 /// virtual method to inform object that http request is processed
330 /// Normally condition is notified and waiting thread will be awaked
331 /// One could reimplement this method in sub-class
332 
334 {
335  fCond.notify_one();
336 }
void SetWSHandle(TNamed *handle)
assign websocket handle with HTTP call
const Ssiz_t kNPOS
Definition: RtypesCore.h:115
Ssiz_t Index(const char *pat, Ssiz_t i=0, ECaseCompare cmp=kExact) const
Definition: TString.h:587
Basic string class.
Definition: TString.h:129
int Int_t
Definition: RtypesCore.h:41
bool Bool_t
Definition: RtypesCore.h:59
#define NULL
Definition: RtypesCore.h:88
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:597
void SetContentType(const char *typ)
set content type like "text/xml" or "application/json"
Definition: THttpCallArg.h:141
TString CountHeader(const TString &buf, Int_t number=-1111) const
method used to counter number of headers or returns name of specified header
void SetEncoding(const char *typ)
Set Content-Encoding header like gzip.
Definition: THttpCallArg.h:173
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:2345
void SetPostData(void *data, Long_t length)
set data, posted with the request buffer should be allocated with malloc(length+1) call...
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:497
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:2332
Ssiz_t Length() const
Definition: TString.h:388
void * fPostData
! binary data received with post request
Definition: THttpCallArg.h:36
Bool_t IsPostponed() const
Definition: THttpCallArg.h:201
const Bool_t kFALSE
Definition: RtypesCore.h:92
const void * GetContent() const
Definition: THttpCallArg.h:208
TString & Remove(Ssiz_t pos)
Definition: TString.h:621
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:336
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:202
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:199
const Bool_t kTRUE
Definition: RtypesCore.h:91
~THttpCallArg()
destructor
TString fFileName
! file name
Definition: THttpCallArg.h:32
const char * cnt
Definition: TXMLSetup.cxx:75
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:347
Long_t GetContentLength() const
Definition: THttpCallArg.h:206