fastcgi++
A C++ FastCGI/Web API
http.cpp
Go to the documentation of this file.
1 
10 /*******************************************************************************
11 * Copyright (C) 2017 Eddie Carle [eddie@isatec.ca] *
12 * *
13 * This file is part of fastcgi++. *
14 * *
15 * fastcgi++ is free software: you can redistribute it and/or modify it under *
16 * the terms of the GNU Lesser General Public License as published by the Free *
17 * Software Foundation, either version 3 of the License, or (at your option) *
18 * any later version. *
19 * *
20 * fastcgi++ is distributed in the hope that it will be useful, but WITHOUT ANY *
21 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS *
22 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for *
23 * more details. *
24 * *
25 * You should have received a copy of the GNU Lesser General Public License *
26 * along with fastcgi++. If not, see <http://www.gnu.org/licenses/>. *
27 *******************************************************************************/
28 
29 #include <locale>
30 #include <codecvt>
31 #include <utility>
32 #include <sstream>
33 #include <iomanip>
34 #include <random>
35 
36 #include "fastcgi++/log.hpp"
37 #include "fastcgi++/http.hpp"
38 
39 
41  const char* start,
42  const char* end,
43  std::wstring& string)
44 {
45  std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> converter;
46  try
47  {
48  string = converter.from_bytes(&*start, &*end);
49  }
50  catch(const std::range_error& e)
51  {
52  WARNING_LOG("Error in code conversion from utf8")
53  }
54 }
55 
56 template int Fastcgipp::Http::atoi<char>(const char* start, const char* end);
57 template int Fastcgipp::Http::atoi<wchar_t>(
58  const wchar_t* start,
59  const wchar_t* end);
60 template<class charT>
61 int Fastcgipp::Http::atoi(const charT* start, const charT* end)
62 {
63  bool neg=false;
64  if(*start=='-')
65  {
66  neg=true;
67  ++start;
68  }
69  int result=0;
70  for(; 0x30 <= *start && *start <= 0x39 && start<end; ++start)
71  result=result*10+(*start&0x0f);
72 
73  return neg?-result:result;
74 }
75 
76 template float Fastcgipp::Http::atof<char>(const char* start, const char* end);
77 template float Fastcgipp::Http::atof<wchar_t>(
78  const wchar_t* start,
79  const wchar_t* end);
80 template<class charT>
81 float Fastcgipp::Http::atof(const charT* start, const charT* end)
82 {
83  if(end <= start)
84  return 0;
85 
86  bool neg=false;
87  if(*start=='-')
88  {
89  neg=true;
90  ++start;
91  }
92  double result=0;
93  double multiplier=0;
94  while(start<end)
95  {
96  if(0x30 <= *start && *start <= 0x39)
97  {
98  if(multiplier == 0)
99  result = result*10+(*start&0x0f);
100  else
101  {
102  result += (*start&0x0f)*multiplier;
103  multiplier *= 0.1;
104  }
105  }
106  else if(*start == '.')
107  multiplier = 0.1;
108  else
109  break;
110  ++start;
111  }
112 
113  return neg?-result:result;
114 }
115 
117  const char* start,
118  const char* end,
119  char* destination)
120 {
121  enum State
122  {
123  NORMAL,
124  DECODINGFIRST,
125  DECODINGSECOND,
126  } state = NORMAL;
127 
128  while(start != end)
129  {
130  if(state == NORMAL)
131  {
132  if(*start=='%')
133  {
134  *destination=0;
135  state = DECODINGFIRST;
136  }
137  else if(*start=='+')
138  *destination++=' ';
139  else
140  *destination++=*start;
141  }
142  else if(state == DECODINGFIRST)
143  {
144  if((*start|0x20) >= 'a' && (*start|0x20) <= 'f')
145  *destination = ((*start|0x20)-0x57)<<4;
146  else if(*start >= '0' && *start <= '9')
147  *destination = (*start&0x0f)<<4;
148 
149  state = DECODINGSECOND;
150  }
151  else if(state == DECODINGSECOND)
152  {
153  if((*start|0x20) >= 'a' && (*start|0x20) <= 'f')
154  *destination |= (*start|0x20)-0x57;
155  else if(*start >= '0' && *start <= '9')
156  *destination |= *start&0x0f;
157 
158  ++destination;
159  state = NORMAL;
160  }
161  ++start;
162  }
163  return destination;
164 }
165 
166 template<class charT> void Fastcgipp::Http::Environment<charT>::fill(
167  const char* data,
168  const char* const dataEnd)
169 {
170  const char* name;
171  const char* value;
172  const char* end;
173 
175  data,
176  dataEnd,
177  name,
178  value,
179  end))
180  {
181  switch(value-name)
182  {
183  case 9:
184  if(std::equal(name, value, "HTTP_HOST"))
185  vecToString(value, end, host);
186  else if(std::equal(name, value, "PATH_INFO"))
187  {
188  const size_t bufferSize = end-value;
189  std::unique_ptr<char[]> buffer(new char[bufferSize]);
190  int size=-1;
191  for(
192  auto source=value;
193  source<=end;
194  ++source, ++size)
195  {
196  if(*source == '/' || source == end)
197  {
198  if(size > 0)
199  {
200  const auto bufferEnd = percentEscapedToRealBytes(
201  source-size,
202  source,
203  buffer.get());
204  pathInfo.push_back(std::basic_string<charT>());
205  vecToString(
206  buffer.get(),
207  bufferEnd,
208  pathInfo.back());
209  }
210  size=-1;
211  }
212  }
213  }
214  break;
215  case 11:
216  if(std::equal(name, value, "HTTP_ACCEPT"))
217  vecToString(value, end, acceptContentTypes);
218  else if(std::equal(name, value, "HTTP_COOKIE"))
219  decodeUrlEncoded(value, end, cookies, "; ");
220  else if(std::equal(name, value, "SERVER_ADDR"))
221  serverAddress.assign(&*value, &*end);
222  else if(std::equal(name, value, "REMOTE_ADDR"))
223  remoteAddress.assign(&*value, &*end);
224  else if(std::equal(name, value, "SERVER_PORT"))
225  serverPort=atoi(&*value, &*end);
226  else if(std::equal(name, value, "REMOTE_PORT"))
227  remotePort=atoi(&*value, &*end);
228  else if(std::equal(name, value, "SCRIPT_NAME"))
229  vecToString(value, end, scriptName);
230  else if(std::equal(name, value, "REQUEST_URI"))
231  vecToString(value, end, requestUri);
232  break;
233  case 12:
234  if(std::equal(name, value, "HTTP_REFERER"))
235  vecToString(value, end, referer);
236  else if(std::equal(name, value, "CONTENT_TYPE"))
237  {
238  const auto semicolon = std::find(value, end, ';');
239  vecToString(
240  value,
241  semicolon,
242  contentType);
243  if(semicolon != end)
244  {
245  const auto equals = std::find(semicolon, end, '=');
246  if(equals != end)
247  boundary.assign(
248  equals+1,
249  end);
250  }
251  }
252  else if(std::equal(name, value, "QUERY_STRING"))
253  decodeUrlEncoded(value, end, gets);
254  break;
255  case 13:
256  if(std::equal(name, value, "DOCUMENT_ROOT"))
257  vecToString(value, end, root);
258  break;
259  case 14:
260  if(std::equal(name, value, "REQUEST_METHOD"))
261  {
262  requestMethod = RequestMethod::ERROR;
263  switch(end-value)
264  {
265  case 3:
266  if(std::equal(
267  value,
268  end,
269  requestMethodLabels[static_cast<int>(
270  RequestMethod::GET)]))
271  requestMethod = RequestMethod::GET;
272  else if(std::equal(
273  value,
274  end,
275  requestMethodLabels[static_cast<int>(
276  RequestMethod::PUT)]))
277  requestMethod = RequestMethod::PUT;
278  break;
279  case 4:
280  if(std::equal(
281  value,
282  end,
283  requestMethodLabels[static_cast<int>(
284  RequestMethod::HEAD)]))
285  requestMethod = RequestMethod::HEAD;
286  else if(std::equal(
287  value,
288  end,
289  requestMethodLabels[static_cast<int>(
290  RequestMethod::POST)]))
291  requestMethod = RequestMethod::POST;
292  break;
293  case 5:
294  if(std::equal(
295  value,
296  end,
297  requestMethodLabels[static_cast<int>(
298  RequestMethod::TRACE)]))
299  requestMethod = RequestMethod::TRACE;
300  break;
301  case 6:
302  if(std::equal(
303  value,
304  end,
305  requestMethodLabels[static_cast<int>(
306  RequestMethod::DELETE)]))
307  requestMethod = RequestMethod::DELETE;
308  break;
309  case 7:
310  if(std::equal(
311  value,
312  end,
313  requestMethodLabels[static_cast<int>(
314  RequestMethod::OPTIONS)]))
315  requestMethod = RequestMethod::OPTIONS;
316  else if(std::equal(
317  value,
318  end,
319  requestMethodLabels[static_cast<int>(
320  RequestMethod::OPTIONS)]))
321  requestMethod = RequestMethod::CONNECT;
322  break;
323  }
324  }
325  else if(std::equal(name, value, "CONTENT_LENGTH"))
326  contentLength=atoi(&*value, &*end);
327  break;
328  case 15:
329  if(std::equal(name, value, "HTTP_USER_AGENT"))
330  vecToString(value, end, userAgent);
331  else if(std::equal(name, value, "HTTP_KEEP_ALIVE"))
332  keepAlive=atoi(&*value, &*end);
333  break;
334  case 18:
335  if(std::equal(name, value, "HTTP_IF_NONE_MATCH"))
336  etag=atoi(&*value, &*end);
337  else if(std::equal(name, value, "HTTP_AUTHORIZATION"))
338  vecToString(value, end, authorization);
339  break;
340  case 19:
341  if(std::equal(name, value, "HTTP_ACCEPT_CHARSET"))
342  vecToString(value, end, acceptCharsets);
343  break;
344  case 20:
345  if(std::equal(name, value, "HTTP_ACCEPT_LANGUAGE"))
346  {
347  const char* groupStart = value;
348  const char* groupEnd;
349  const char* subStart;
350  const char* subEnd;
351  size_t dash;
352  while(groupStart < end)
353  {
354  acceptLanguages.push_back(std::string());
355  std::string& language = acceptLanguages.back();
356 
357  groupEnd = std::find(groupStart, end, ',');
358 
359  // Setup the locality
360  subEnd = std::find(groupStart, groupEnd, ';');
361  subStart = groupStart;
362  while(subStart != subEnd && *subStart == ' ')
363  ++subStart;
364  while(subEnd != subStart && *(subEnd-1) == ' ')
365  --subEnd;
366  vecToString(subStart, subEnd, language);
367 
368  dash = language.find('-');
369  if(dash != std::string::npos)
370  language[dash] = '_';
371 
372  groupStart = groupEnd+1;
373  }
374  }
375  break;
376  case 22:
377  if(std::equal(name, value, "HTTP_IF_MODIFIED_SINCE"))
378  {
379  std::tm time;
380  std::fill(
381  reinterpret_cast<char*>(&time),
382  reinterpret_cast<char*>(&time)+sizeof(time),
383  0);
384  std::stringstream dateStream;
385  dateStream.write(&*value, end-value);
386  dateStream >> std::get_time(
387  &time,
388  "%a, %d %b %Y %H:%M:%S GMT");
389  ifModifiedSince = std::mktime(&time) - timezone;
390  }
391  break;
392  }
393  data = end;
394  }
395 }
396 
397 template<class charT>
399  const char* const start,
400  const char* const end)
401 {
402  if(m_postBuffer.empty())
403  m_postBuffer.reserve(contentLength);
404  m_postBuffer.insert(m_postBuffer.end(), start, end);
405 }
406 
407 template<class charT>
409 {
410  static const std::string multipartStr("multipart/form-data");
411  static const std::string urlEncodedStr("application/x-www-form-urlencoded");
412 
413  if(!m_postBuffer.size())
414  return true;
415 
416  bool parsed = false;
417 
418  if(std::equal(
419  multipartStr.cbegin(),
420  multipartStr.cend(),
421  contentType.cbegin(),
422  contentType.cend()))
423  {
424  parsePostsMultipart();
425  parsed = true;
426  }
427  else if(std::equal(
428  urlEncodedStr.cbegin(),
429  urlEncodedStr.cend(),
430  contentType.cbegin(),
431  contentType.cend()))
432  {
433  parsePostsUrlEncoded();
434  parsed = true;
435  }
436 
437  return parsed;
438 }
439 
440 template<class charT>
442 {
443  static const std::string cName("name=\"");
444  static const std::string cFilename("filename=\"");
445  static const std::string cContentType("Content-Type: ");
446  static const std::string cBody("\r\n\r\n");
447 
448  const char* const postBufferStart = m_postBuffer.data();
449  const char* const postBufferEnd = m_postBuffer.data() + m_postBuffer.size();
450 
451  auto nameStart(postBufferEnd);
452  auto nameEnd(postBufferEnd);
453  auto filenameStart(postBufferEnd);
454  auto filenameEnd(postBufferEnd);
455  auto contentTypeStart(postBufferEnd);
456  auto contentTypeEnd(postBufferEnd);
457  auto bodyStart(postBufferEnd);
458  auto bodyEnd(postBufferEnd);
459 
460  enum State
461  {
462  HEADER,
463  NAME,
464  FILENAME,
465  CONTENT_TYPE,
466  BODY
467  } state=HEADER;
468 
469  for(auto byte = postBufferStart; byte < postBufferEnd; ++byte)
470  {
471  switch(state)
472  {
473  case HEADER:
474  {
475  const size_t bytesLeft = size_t(postBufferEnd-byte);
476 
477  if(
478  nameEnd == postBufferEnd &&
479  bytesLeft >= cName.size() &&
480  std::equal(cName.begin(), cName.end(), byte))
481  {
482  byte += cName.size()-1;
483  nameStart = byte+1;
484  state = NAME;
485  }
486  else if(
487  filenameEnd == postBufferEnd &&
488  bytesLeft >= cFilename.size() &&
489  std::equal(cFilename.begin(), cFilename.end(), byte))
490  {
491  byte += cFilename.size()-1;
492  filenameStart = byte+1;
493  state = FILENAME;
494  }
495  else if(
496  contentTypeEnd == postBufferEnd &&
497  bytesLeft >= cContentType.size() &&
498  std::equal(cContentType.begin(), cContentType.end(), byte))
499  {
500  byte += cContentType.size()-1;
501  contentTypeStart = byte+1;
502  state = CONTENT_TYPE;
503  }
504  else if(
505  bodyEnd == postBufferEnd &&
506  bytesLeft >= cBody.size() &&
507  std::equal(cBody.begin(), cBody.end(), byte))
508  {
509  byte += cBody.size()-1;
510  bodyStart = byte+1;
511  state = BODY;
512  }
513 
514  break;
515  }
516 
517  case NAME:
518  {
519  if(*byte == '"')
520  {
521  nameEnd=byte;
522  state=HEADER;
523  }
524  break;
525  }
526 
527  case FILENAME:
528  {
529  if(*byte == '"')
530  {
531  filenameEnd=byte;
532  state=HEADER;
533  }
534  break;
535  }
536 
537  case CONTENT_TYPE:
538  {
539  if(*byte == '\r' || *byte == '\n')
540  {
541  contentTypeEnd = byte--;
542  state=HEADER;
543  }
544  break;
545  }
546 
547  case BODY:
548  {
549  const size_t bytesLeft = size_t(postBufferEnd-byte);
550 
551  if(
552  bytesLeft >= boundary.size() &&
553  std::equal(boundary.begin(), boundary.end(), byte))
554  {
555  bodyEnd = byte-2;
556  if(bodyEnd<bodyStart)
557  bodyEnd = bodyStart;
558  else if(
559  bodyEnd-bodyStart>=2
560  && *(bodyEnd-1)=='\n'
561  && *(bodyEnd-2)=='\r')
562  bodyEnd -= 2;
563 
564  if(nameEnd != postBufferEnd)
565  {
566  std::basic_string<charT> name;
567  vecToString(nameStart, nameEnd, name);
568 
569  if(contentTypeEnd != postBufferEnd)
570  {
571  File<charT> file;
572  vecToString(
573  contentTypeStart,
574  contentTypeEnd,
575  file.contentType);
576  if(filenameEnd != postBufferEnd)
577  vecToString(
578  filenameStart,
579  filenameEnd,
580  file.filename);
581 
582  file.size = bodyEnd-bodyStart;
583  file.data.reset(new char[file.size]);
584  std::copy(bodyStart, bodyEnd, file.data.get());
585 
586  files.insert(std::make_pair(
587  std::move(name),
588  std::move(file)));
589  }
590  else
591  {
592  std::basic_string<charT> value;
593  vecToString(bodyStart, bodyEnd, value);
594  posts.insert(std::make_pair(
595  std::move(name),
596  std::move(value)));
597  }
598  }
599 
600  state=HEADER;
601  nameStart = postBufferEnd;
602  nameEnd = postBufferEnd;
603  filenameStart = postBufferEnd;
604  filenameEnd = postBufferEnd;
605  contentTypeStart = postBufferEnd;
606  contentTypeEnd = postBufferEnd;
607  bodyStart = postBufferEnd;
608  bodyEnd = postBufferEnd;
609  }
610 
611  break;
612  }
613  }
614  }
615 }
616 
617 template<class charT>
619 {
621  m_postBuffer.data(),
622  m_postBuffer.data()+m_postBuffer.size(),
623  posts);
624 }
625 
628 
630 {
631  std::random_device device;
632  std::uniform_int_distribution<unsigned short> distribution(0, 255);
633 
634  for(unsigned char& byte: m_data)
635  byte = static_cast<unsigned char>(distribution(device));
636  m_timestamp = std::time(nullptr);
637 }
638 
640  const std::basic_string<char>& string);
642  const std::basic_string<wchar_t>& string);
644  const std::basic_string<charT>& string)
645 {
646  base64Decode(
647  string.begin(),
648  string.begin()+std::min(stringLength, string.size()),
649  m_data.begin());
650  m_timestamp = std::time(nullptr);
651 }
652 
655 
656 template void Fastcgipp::Http::decodeUrlEncoded<char>(
657  const char* data,
658  const char* const dataEnd,
659  std::multimap<
660  std::basic_string<char>,
661  std::basic_string<char>>& output,
662  const char* const fieldSeparator);
663 template void Fastcgipp::Http::decodeUrlEncoded<wchar_t>(
664  const char* data,
665  const char* const dataEnd,
666  std::multimap<
667  std::basic_string<wchar_t>,
668  std::basic_string<wchar_t>>& output,
669  const char* const fieldSeparator);
670 template<class charT> void Fastcgipp::Http::decodeUrlEncoded(
671  const char* data,
672  const char* const dataEnd,
673  std::multimap<
674  std::basic_string<charT>,
675  std::basic_string<charT>>& output,
676  const char* const fieldSeparator)
677 {
678  std::unique_ptr<char[]> buffer(new char[dataEnd-data]);
679  std::basic_string<charT> name;
680  std::basic_string<charT> value;
681 
682  const size_t fieldSeparatorSize = std::strlen(fieldSeparator);
683  const char* const fieldSeparatorEnd = fieldSeparator+fieldSeparatorSize;
684 
685  enum EndState
686  {
687  NONE,
688  END,
689  SEPARATOR
690  };
691  EndState endState;
692 
693  auto nameStart(data);
694  auto nameEnd(dataEnd);
695  auto valueStart(dataEnd);
696  auto valueEnd(dataEnd);
697 
698  while(data < dataEnd)
699  {
700  if(nameEnd != dataEnd)
701  {
702  if(data+1 == dataEnd)
703  {
704  endState = END;
705  valueEnd = data+1;
706  }
707  else if(data+fieldSeparatorSize<=dataEnd
708  && std::equal(fieldSeparator, fieldSeparatorEnd, data))
709  {
710  endState = SEPARATOR;
711  valueEnd = data;
712  }
713  else
714  endState = NONE;
715 
716  if(endState != NONE)
717  {
718  valueEnd=percentEscapedToRealBytes(
719  valueStart,
720  valueEnd,
721  buffer.get());
722  vecToString(buffer.get(), valueEnd, value);
723 
724  output.insert(std::make_pair(
725  std::move(name),
726  std::move(value)));
727 
728  nameStart = data+fieldSeparatorSize;
729  data += fieldSeparatorSize-1;
730  nameEnd = dataEnd;
731  valueStart = dataEnd;
732  valueEnd = dataEnd;
733  }
734  }
735  else
736  {
737  if(*data == '=')
738  {
739  nameEnd = percentEscapedToRealBytes(
740  nameStart,
741  data,
742  buffer.get());
743  vecToString(buffer.get(), nameEnd, name);
744  valueStart=data+1;
745  }
746  }
747  ++data;
748  }
749 }
750 
751 extern const std::array<const char, 64> Fastcgipp::Http::base64Characters =
752 {{
753  'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S',
754  'T','U','V','W','X','Y','Z','a','b','c','d','e','f','g','h','i','j','k','l',
755  'm','n','o','p','q','r','s','t','u','v','w','x','y','z','0','1','2','3','4',
756  '5','6','7','8','9','+','/'
757 }};
758 
759 const std::array<const char* const, 9> Fastcgipp::Http::requestMethodLabels =
760 {{
761  "ERROR",
762  "HEAD",
763  "GET",
764  "POST",
765  "PUT",
766  "DELETE",
767  "TRACE",
768  "OPTIONS",
769  "CONNECT"
770 }};
771 
773  const Address& x)
774 {
775  *reinterpret_cast<uint64_t*>(m_data.data())
776  &= *reinterpret_cast<const uint64_t*>(x.m_data.data());
777  *reinterpret_cast<uint64_t*>(m_data.data()+8)
778  &= *reinterpret_cast<const uint64_t*>(x.m_data.data()+8);
779 
780  return *this;
781 }
782 
784  const Address& x) const
785 {
786  Address address(*this);
787  address &= x;
788 
789  return address;
790 }
791 
792 template void Fastcgipp::Http::Address::assign<char>(
793  const char* start,
794  const char* end);
795 template void Fastcgipp::Http::Address::assign<wchar_t>(
796  const wchar_t* start,
797  const wchar_t* end);
798 template<class charT> void Fastcgipp::Http::Address::assign(
799  const charT* start,
800  const charT* end)
801 {
802  const charT* read=start-1;
803  auto write=m_data.begin();
804  auto pad=m_data.end();
805  unsigned char offset;
806  uint16_t chunk=0;
807  bool error=false;
808 
809  while(1)
810  {
811  ++read;
812  if(read >= end || *read == ':')
813  {
814  if(read == start || *(read-1) == ':')
815  {
816  if(pad!=m_data.end() && pad!=write)
817  {
818  error=true;
819  break;
820  }
821  else
822  pad = write;
823  }
824  else
825  {
826  *write = (chunk&0xff00)>>8;
827  *(write+1) = chunk&0x00ff;
828  chunk = 0;
829  write += 2;
830  if(write>=m_data.end() || read>=end)
831  {
832  if(read>=end && write<m_data.end() && pad==m_data.end())
833  error=true;
834  break;
835  }
836  }
837  continue;
838  }
839  else if('0' <= *read && *read <= '9')
840  offset = '0';
841  else if('A' <= *read && *read <= 'F')
842  offset = 'A'-10;
843  else if('a' <= *read && *read <= 'f')
844  offset = 'a'-10;
845  else if(*read == '.')
846  {
847  if(write == m_data.begin())
848  {
849  // We must be getting a pure ipv4 formatted address. Not an
850  // ::ffff:xxx.xxx.xxx.xxx style ipv4 address.
851  *reinterpret_cast<uint16_t*>(write) = 0xffff;
852  pad = m_data.begin();
853  write += 2;
854  }
855  else if(write - m_data.begin() > 12)
856  {
857  // We don't have enought space for an ipv4 address
858  error=true;
859  break;
860  }
861 
862  // First convert the value stored in chunk to the first part of the
863  // ipv4 address
864  *write = 0;
865  for(int i=0; i<3; ++i)
866  {
867  *write = *write * 10 + ((chunk&0x0f00)>>8);
868  chunk <<= 4;
869  }
870  ++write;
871 
872  // Now we'll get the remaining pieces
873  for(int i=0; i<3 && read<end; ++i)
874  {
875  const charT* point=std::find(read, end, '.');
876  if(point<end-1)
877  read=point;
878  else
879  {
880  error=true;
881  break;
882  }
883  *write++ = atoi(++read, end);
884  }
885  break;
886  }
887  else
888  {
889  error=true;
890  break;
891  }
892  chunk <<= 4;
893  chunk |= *read-offset;
894  }
895 
896  if(error)
897  {
898  m_data.fill(0);
899  WARNING_LOG("Error converting IPv6 address " \
900  << std::wstring(start, end))
901  }
902  else if(pad != m_data.end())
903  {
904  if(pad==write)
905  std::fill(write, m_data.end(), 0);
906  else
907  {
908  auto padEnd=pad+(m_data.end()-write);
909  std::copy(pad, write, padEnd);
910  std::fill(pad, padEnd, 0);
911  }
912  }
913 }
914 
915 template std::basic_ostream<char, std::char_traits<char>>&
916 Fastcgipp::Http::operator<< <char, std::char_traits<char>>(
917  std::basic_ostream<char, std::char_traits<char>>& os,
918  const Address& address);
919 template std::basic_ostream<wchar_t, std::char_traits<wchar_t>>&
920 Fastcgipp::Http::operator<< <wchar_t, std::char_traits<wchar_t>>(
921  std::basic_ostream<wchar_t, std::char_traits<wchar_t>>& os,
922  const Address& address);
923 template<class charT, class Traits> std::basic_ostream<charT, Traits>&
925  std::basic_ostream<charT, Traits>& os,
926  const Address& address)
927 {
928  using namespace std;
929  if(!os.good()) return os;
930 
931  try
932  {
933  typename basic_ostream<charT, Traits>::sentry opfx(os);
934  if(opfx)
935  {
936  streamsize fieldWidth=os.width(0);
937  charT buffer[40];
938  charT* bufPtr=buffer;
939  locale loc(os.getloc(), new num_put<charT, charT*>);
940 
941  const uint16_t* subStart=0;
942  const uint16_t* subEnd=0;
943  {
944  const uint16_t* subStartCandidate;
945  const uint16_t* subEndCandidate;
946  bool inZero = false;
947 
948  for(
949  const uint16_t* it = reinterpret_cast<const uint16_t*>(
950  address.m_data.data());
951  it < reinterpret_cast<const uint16_t*>(
952  address.m_data.data()+Address::size);
953  ++it)
954  {
955  if(*it == 0)
956  {
957  if(!inZero)
958  {
959  subStartCandidate = it;
960  subEndCandidate = it;
961  inZero=true;
962  }
963  ++subEndCandidate;
964  }
965  else if(inZero)
966  {
967  if(subEndCandidate-subStartCandidate > subEnd-subStart)
968  {
969  subStart=subStartCandidate;
970  subEnd=subEndCandidate-1;
971  }
972  inZero=false;
973  }
974  }
975  if(inZero)
976  {
977  if(subEndCandidate-subStartCandidate > subEnd-subStart)
978  {
979  subStart=subStartCandidate;
980  subEnd=subEndCandidate-1;
981  }
982  inZero=false;
983  }
984  }
985 
986  ios_base::fmtflags oldFlags = os.flags();
987  os.setf(ios::hex, ios::basefield);
988 
989  if(
990  subStart==reinterpret_cast<const uint16_t*>(
991  address.m_data.data())
992  && subEnd==reinterpret_cast<const uint16_t*>(
993  address.m_data.data())+4
994  && *(reinterpret_cast<const uint16_t*>(
995  address.m_data.data())+5) == 0xffff)
996  {
997  // It is an ipv4 address
998  *bufPtr++=os.widen(':');
999  *bufPtr++=os.widen(':');
1000  bufPtr=use_facet<num_put<charT, charT*> >(loc).put(
1001  bufPtr,
1002  os,
1003  os.fill(),
1004  static_cast<unsigned long int>(0xffff));
1005  *bufPtr++=os.widen(':');
1006  os.setf(ios::dec, ios::basefield);
1007 
1008  for(
1009  const unsigned char* it = address.m_data.data()+12;
1010  it < address.m_data.data()+Address::size;
1011  ++it)
1012  {
1013  bufPtr=use_facet<num_put<charT, charT*> >(loc).put(
1014  bufPtr,
1015  os,
1016  os.fill(),
1017  static_cast<unsigned long int>(*it));
1018  *bufPtr++=os.widen('.');
1019  }
1020  --bufPtr;
1021  }
1022  else
1023  {
1024  // It is an ipv6 address
1025  for(const uint16_t* it= reinterpret_cast<const uint16_t*>(
1026  address.m_data.data());
1027  it < reinterpret_cast<const uint16_t*>(
1028  address.m_data.data()+Address::size);
1029  ++it)
1030  {
1031  if(subStart <= it && it <= subEnd)
1032  {
1033  if(
1034  it == subStart
1035  && it == reinterpret_cast<const uint16_t*>(
1036  address.m_data.data()))
1037  *bufPtr++=os.widen(':');
1038  if(it == subEnd)
1039  *bufPtr++=os.widen(':');
1040  }
1041  else
1042  {
1043  bufPtr=use_facet<num_put<charT, charT*> >(loc).put(
1044  bufPtr,
1045  os,
1046  os.fill(),
1047  static_cast<unsigned long int>(
1048  *reinterpret_cast<
1049  const Protocol::BigEndian<uint16_t>*>(it)));
1050 
1051  if(it < reinterpret_cast<const uint16_t*>(
1052  address.m_data.data()+Address::size)-1)
1053  *bufPtr++=os.widen(':');
1054  }
1055  }
1056  }
1057 
1058  os.flags(oldFlags);
1059 
1060  charT* ptr=buffer;
1061  ostreambuf_iterator<charT,Traits> sink(os);
1062  if(os.flags() & ios_base::left)
1063  for(int i=max(fieldWidth, bufPtr-buffer); i>0; i--)
1064  {
1065  if(ptr!=bufPtr) *sink++=*ptr++;
1066  else *sink++=os.fill();
1067  }
1068  else
1069  for(int i=fieldWidth-(bufPtr-buffer); ptr!=bufPtr;)
1070  {
1071  if(i>0) { *sink++=os.fill(); --i; }
1072  else *sink++=*ptr++;
1073  }
1074 
1075  if(sink.failed()) os.setstate(ios_base::failbit);
1076  }
1077  }
1078  catch(bad_alloc&)
1079  {
1080  ios_base::iostate exception_mask = os.exceptions();
1081  os.exceptions(ios_base::goodbit);
1082  os.setstate(ios_base::badbit);
1083  os.exceptions(exception_mask);
1084  if(exception_mask & ios_base::badbit) throw;
1085  }
1086  catch(...)
1087  {
1088  ios_base::iostate exception_mask = os.exceptions();
1089  os.exceptions(ios_base::goodbit);
1090  os.setstate(ios_base::failbit);
1091  os.exceptions(exception_mask);
1092  if(exception_mask & ios_base::failbit) throw;
1093  }
1094  return os;
1095 }
1096 
1097 template std::basic_istream<char, std::char_traits<char>>&
1098 Fastcgipp::Http::operator>> <char, std::char_traits<char>>(
1099  std::basic_istream<char, std::char_traits<char>>& is,
1100  Address& address);
1101 template std::basic_istream<wchar_t, std::char_traits<wchar_t>>&
1102 Fastcgipp::Http::operator>> <wchar_t, std::char_traits<wchar_t>>(
1103  std::basic_istream<wchar_t, std::char_traits<wchar_t>>& is,
1104  Address& address);
1105 template<class charT, class Traits> std::basic_istream<charT, Traits>&
1107  std::basic_istream<charT, Traits>& is,
1108  Address& address)
1109 {
1110  using namespace std;
1111  if(!is.good()) return is;
1112 
1113  ios_base::iostate err = ios::goodbit;
1114  try
1115  {
1116  typename basic_istream<charT, Traits>::sentry ipfx(is);
1117  if(ipfx)
1118  {
1119  istreambuf_iterator<charT, Traits> read(is);
1120  unsigned char buffer[Address::size];
1121  unsigned char* write=buffer;
1122  unsigned char* pad=0;
1123  unsigned char offset;
1124  unsigned char count=0;
1125  uint16_t chunk=0;
1126  charT lastChar=0;
1127 
1128  for(;;++read)
1129  {
1130  if(++count>40)
1131  {
1132  err = ios::failbit;
1133  break;
1134  }
1135  else if('0' <= *read && *read <= '9')
1136  offset = '0';
1137  else if('A' <= *read && *read <= 'F')
1138  offset = 'A'-10;
1139  else if('a' <= *read && *read <= 'f')
1140  offset = 'a'-10;
1141  else if(*read == '.')
1142  {
1143  if(write == buffer)
1144  {
1145  // We must be getting a pure ipv4 formatted address. Not an ::ffff:xxx.xxx.xxx.xxx style ipv4 address.
1146  *reinterpret_cast<uint16_t*>(write) = 0xffff;
1147  pad = buffer;
1148  write+=2;
1149  }
1150  else if(write - buffer > 12)
1151  {
1152  // We don't have enought space for an ipv4 address
1153  err = ios::failbit;
1154  break;
1155  }
1156 
1157  // First convert the value stored in chunk to the first part of the ipv4 address
1158  *write = 0;
1159  for(int i=0; i<3; ++i)
1160  {
1161  *write = *write * 10 + ((chunk&0x0f00)>>8);
1162  chunk <<= 4;
1163  }
1164  ++write;
1165 
1166  // Now we'll get the remaining pieces
1167  for(int i=0; i<3; ++i)
1168  {
1169  if(*read != is.widen('.'))
1170  {
1171  err = ios::failbit;
1172  break;
1173  }
1174  unsigned int value;
1175  use_facet<num_get<charT, istreambuf_iterator<charT, Traits> > >(is.getloc()).get(++read, istreambuf_iterator<charT, Traits>(), is, err, value);
1176  *write++ = value;
1177  }
1178  break;
1179  }
1180  else
1181  {
1182  if(*read == ':' && (!lastChar || lastChar == ':'))
1183  {
1184  if(pad && pad != write)
1185  {
1186  err = ios::failbit;
1187  break;
1188  }
1189  else
1190  pad = write;
1191  }
1192  else
1193  {
1194  *write = (chunk&0xff00)>>8;
1195  *(write+1) = chunk&0x00ff;
1196  chunk = 0;
1197  write += 2;
1198  if(write>=buffer+Address::size)
1199  break;
1200  if(*read!=':')
1201  {
1202  if(!pad)
1203  err = ios::failbit;
1204  break;
1205  }
1206  }
1207  lastChar=':';
1208  continue;
1209  }
1210  chunk <<= 4;
1211  chunk |= *read-offset;
1212  lastChar=*read;
1213 
1214  }
1215 
1216  if(err == ios::goodbit)
1217  {
1218  if(pad)
1219  {
1220  if(pad==write)
1221  std::memset(write, 0, Address::size-(write-buffer));
1222  else
1223  {
1224  const size_t padSize=buffer+Address::size-write;
1225  std::memmove(pad+padSize, pad, write-pad);
1226  std::memset(pad, 0, padSize);
1227  }
1228  }
1229  address=buffer;
1230  }
1231  else
1232  is.setstate(err);
1233  }
1234  }
1235  catch(bad_alloc&)
1236  {
1237  ios_base::iostate exception_mask = is.exceptions();
1238  is.exceptions(ios_base::goodbit);
1239  is.setstate(ios_base::badbit);
1240  is.exceptions(exception_mask);
1241  if(exception_mask & ios_base::badbit) throw;
1242  }
1243  catch(...)
1244  {
1245  ios_base::iostate exception_mask = is.exceptions();
1246  is.exceptions(ios_base::goodbit);
1247  is.setstate(ios_base::failbit);
1248  is.exceptions(exception_mask);
1249  if(exception_mask & ios_base::failbit) throw;
1250  }
1251 
1252  return is;
1253 }
1254 
1255 Fastcgipp::Http::Address::operator bool() const
1256 {
1257  static const std::array<const unsigned char, 16> nullString =
1258  {{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}};
1259  if(std::equal(m_data.begin(), m_data.end(), nullString.begin()))
1260  return false;
1261  return true;
1262 }
float atof(const charT *start, const charT *end)
Convert a char string to a float.
Definition: http.cpp:81
std::array< unsigned char, size > m_data
Data representation of the IPv6 address.
Definition: http.hpp:134
std::basic_string< charT > filename
Filename.
Definition: http.hpp:68
Address operator &(const Address &x) const
void assign(const charT *start, const charT *end)
Assign the IP address from a string of characters.
Definition: http.cpp:798
std::basic_istream< charT, Traits > & operator>>(std::basic_istream< charT, Traits > &is, Address &address)
Address stream extractor operation.
Definition: http.cpp:1106
bool processParamHeader(const char *data, const char *const dataEnd, const char *&name, const char *&value, const char *&end)
Process the body of a FastCGI record of type RecordType::PARAMS.
Definition: protocol.cpp:32
bool parsePostBuffer()
Attempts to parse the POST buffer.
Definition: http.cpp:408
void fillPostBuffer(const char *start, const char *end)
Consolidates POST data into a single buffer.
Definition: http.cpp:398
Holds a file uploaded from the client.
Definition: http.hpp:65
STL namespace.
void parsePostsUrlEncoded()
Parses "application/x-www-form-urlencoded" post data.
Definition: http.cpp:618
std::time_t m_timestamp
Contains the time this session was last used.
Definition: http.hpp:560
char * percentEscapedToRealBytes(const char *start, const char *end, char *destination)
Convert a string with percent escaped byte values to their values.
Definition: http.cpp:116
std::array< unsigned char, size > m_data
ID data.
Definition: http.hpp:557
Declares elements of the HTTP protocol.
const std::array< const char *const, 9 > requestMethodLabels
Some textual labels for RequestMethod.
Definition: http.cpp:759
void vecToString(const char *start, const char *end, std::wstring &string)
Convert a char array to a std::wstring.
Definition: http.cpp:40
std::unique_ptr< char[]> data
File data.
Definition: http.hpp:77
size_t size
Size of file.
Definition: http.hpp:74
int atoi(const charT *start, const charT *end)
Convert a char string to an integer.
Definition: http.cpp:61
void decodeUrlEncoded(const char *data, const char *dataEnd, std::multimap< std::basic_string< charT >, std::basic_string< charT >> &output, const char *const fieldSeparator="&")
Decodes a url-encoded string into a multimap container.
Definition: http.cpp:670
Data structure of HTTP environment data.
Definition: http.hpp:251
Out base64Decode(In start, In end, Out destination)
Convert a Base64 encoded container to a binary container.
Definition: http.hpp:725
void fill(const char *data, const char *dataEnd)
Parses FastCGI parameter data into the data structure.
Definition: http.cpp:166
static const size_t stringLength
Size in characters of string representation.
Definition: http.hpp:553
static const size_t size
Size in bytes of the ID data. Make sure it is a multiple of 3.
Definition: http.hpp:550
std::basic_ostream< charT, Traits > & operator<<(std::basic_ostream< charT, Traits > &os, const RequestMethod requestMethod)
Definition: http.hpp:108
static const size_t size
This is the data length of the IPv6 address.
Definition: http.hpp:131
std::basic_string< charT > contentType
Content Type.
Definition: http.hpp:71
void parsePostsMultipart()
Parses "multipart/form-data" http post data.
Definition: http.cpp:441
Address & operator &=(const Address &x)
const std::array< const char, 64 > base64Characters
List of characters in order for Base64 encoding.
Definition: http.cpp:751
Efficiently stores IPv6 addresses.
Definition: http.hpp:127
SessionId()
This constructor initializes the ID data to a random value.
Definition: http.cpp:629
Declares the Fastcgipp debugging/logging facilities.
#define WARNING_LOG(data)
Log any externally caused "errors".
Definition: log.hpp:124