Logo ROOT   6.30.04
Reference Guide
 All Namespaces Files Pages
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 
27 ClassImp(THttpCallArg);
28 
29 ////////////////////////////////////////////////////////////////////////////////
30 /// destructor
31 
32 THttpCallArg::~THttpCallArg()
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 
91 TString THttpCallArg::CountHeader(const TString &buf, Int_t number) const
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 
143 void THttpCallArg::SetText()
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 
161 void THttpCallArg::SetXml()
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 
179 void THttpCallArg::SetJson()
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 
188 void THttpCallArg::SetJsonContent(std::string &&json)
189 {
190  SetJson();
191  fContent = json;
192 }
193 
194 ////////////////////////////////////////////////////////////////////////////////
195 /// Set content type as "application/x-binary"
196 
197 void THttpCallArg::SetBinary()
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 
240 void THttpCallArg::AssignWSId()
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, bool once)
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  if (once) return;
266  start_pos += to.length(); // Handles case where 'to' is a substring of 'from'
267  }
268 }
269 
270 ////////////////////////////////////////////////////////////////////////////////
271 /// set complete path of requested http element
272 /// For instance, it could be "/folder/subfolder/get.bin"
273 /// Here "/folder/subfolder/" is element path and "get.bin" requested file.
274 /// One could set path and file name separately
275 
276 void THttpCallArg::SetPathAndFileName(const char *fullpath)
277 {
278  fPathName.Clear();
279  fFileName.Clear();
280 
281  if (fullpath == 0)
282  return;
283 
284  const char *rslash = strrchr(fullpath, '/');
285  if (rslash == 0) {
286  fFileName = fullpath;
287  } else {
288  while ((fullpath != rslash) && (*fullpath == '/'))
289  fullpath++;
290  fPathName.Append(fullpath, rslash - fullpath);
291  if (fPathName == "/")
292  fPathName.Clear();
293  fFileName = rslash + 1;
294  }
295 }
296 
297 ////////////////////////////////////////////////////////////////////////////////
298 /// return specified header
299 
300 TString THttpCallArg::GetHeader(const char *name)
301 {
302  if ((name == 0) || (*name == 0))
303  return TString();
304 
305  if (strcmp(name, "Content-Type") == 0)
306  return fContentType;
307  if (strcmp(name, "Content-Length") == 0)
308  return TString::Format("%ld", GetContentLength());
309 
310  return AccessHeader(fHeader, name);
311 }
312 
313 ////////////////////////////////////////////////////////////////////////////////
314 /// Set name: value pair to reply header
315 /// Content-Type field handled separately - one should use SetContentType() method
316 /// Content-Length field cannot be set at all;
317 
318 void THttpCallArg::AddHeader(const char *name, const char *value)
319 {
320  if ((name == 0) || (*name == 0) || (strcmp(name, "Content-Length") == 0))
321  return;
322 
323  if (strcmp(name, "Content-Type") == 0)
324  SetContentType(value);
325  else
326  AccessHeader(fHeader, name, value, kTRUE);
327 }
328 
329 ////////////////////////////////////////////////////////////////////////////////
330 /// Set CacheControl http header to disable browser caching
331 
332 void THttpCallArg::AddNoCacheHeader()
333 {
334  AddHeader("Cache-Control", "private, no-cache, no-store, must-revalidate, max-age=0, proxy-revalidate, s-maxage=0");
335 }
336 
337 ////////////////////////////////////////////////////////////////////////////////
338 /// Fills HTTP header, which can be send at the beggining of reply on the http request
339 /// \param name is HTTP protocol name (default "HTTP/1.1")
340 
341 std::string THttpCallArg::FillHttpHeader(const char *name)
342 {
343  std::string hdr(name ? name : "HTTP/1.1");
344 
345  if ((fContentType.Length() == 0) || Is404())
346  hdr.append(" 404 Not Found\r\n"
347  "Content-Length: 0\r\n"
348  "Connection: close\r\n\r\n");
349  else
350  hdr.append(Form(" 200 OK\r\n"
351  "Content-Type: %s\r\n"
352  "Connection: keep-alive\r\n"
353  "Content-Length: %ld\r\n"
354  "%s\r\n",
355  GetContentType(), GetContentLength(), fHeader.Data()));
356 
357  return hdr;
358 }
359 
360 ////////////////////////////////////////////////////////////////////////////////
361 /// compress reply data with gzip compression
362 
363 Bool_t THttpCallArg::CompressWithGzip()
364 {
365  char *objbuf = (char *)GetContent();
366  Long_t objlen = GetContentLength();
367 
368  unsigned long objcrc = R__crc32(0, NULL, 0);
369  objcrc = R__crc32(objcrc, (const unsigned char *)objbuf, objlen);
370 
371  // 10 bytes (ZIP header), compressed data, 8 bytes (CRC and original length)
372  Int_t buflen = 10 + objlen + 8;
373  if (buflen < 512)
374  buflen = 512;
375 
376  std::string buffer;
377  buffer.resize(buflen);
378 
379  char *bufcur = (char *)buffer.data();
380 
381  *bufcur++ = 0x1f; // first byte of ZIP identifier
382  *bufcur++ = 0x8b; // second byte of ZIP identifier
383  *bufcur++ = 0x08; // compression method
384  *bufcur++ = 0x00; // FLAG - empty, no any file names
385  *bufcur++ = 0; // empty timestamp
386  *bufcur++ = 0; //
387  *bufcur++ = 0; //
388  *bufcur++ = 0; //
389  *bufcur++ = 0; // XFL (eXtra FLags)
390  *bufcur++ = 3; // OS 3 means Unix
391  // strcpy(bufcur, "item.json");
392  // bufcur += strlen("item.json")+1;
393 
394  char dummy[8];
395  memcpy(dummy, bufcur - 6, 6);
396 
397  // R__memcompress fills first 6 bytes with own header, therefore just overwrite them
398  unsigned long ziplen = R__memcompress(bufcur - 6, objlen + 6, objbuf, objlen);
399 
400  memcpy(bufcur - 6, dummy, 6);
401 
402  bufcur += (ziplen - 6); // jump over compressed data (6 byte is extra ROOT header)
403 
404  // write CRC32
405  *bufcur++ = objcrc & 0xff;
406  *bufcur++ = (objcrc >> 8) & 0xff;
407  *bufcur++ = (objcrc >> 16) & 0xff;
408  *bufcur++ = (objcrc >> 24) & 0xff;
409 
410  // write original data length
411  *bufcur++ = objlen & 0xff;
412  *bufcur++ = (objlen >> 8) & 0xff;
413  *bufcur++ = (objlen >> 16) & 0xff;
414  *bufcur++ = (objlen >> 24) & 0xff;
415 
416  buffer.resize(bufcur - (char *)buffer.data());
417 
418  SetContent(std::move(buffer));
419 
420  SetEncoding("gzip");
421 
422  return kTRUE;
423 }
424 
425 ////////////////////////////////////////////////////////////////////////////////
426 /// method used to notify condition which waiting when operation will complete
427 /// Condition notified only if not-postponed state is set
428 
429 void THttpCallArg::NotifyCondition()
430 {
431  if (!fNotifyFlag && !IsPostponed()) {
432  fNotifyFlag = kTRUE;
433  HttpReplied();
434  }
435 }
436 
437 ////////////////////////////////////////////////////////////////////////////////
438 /// virtual method to inform object that http request is processed
439 /// Normally condition is notified and waiting thread will be awaked
440 /// One could reimplement this method in sub-class
441 
442 void THttpCallArg::HttpReplied()
443 {
444  fCond.notify_one();
445 }