fastcgi++
A C++ FastCGI/Web API
http.hpp
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 #ifndef FASTCGIPP_HTTP_HPP
30 #define FASTCGIPP_HTTP_HPP
31 
32 #include <string>
33 #include <ostream>
34 #include <istream>
35 #include <iterator>
36 #include <cstring>
37 #include <algorithm>
38 #include <map>
39 #include <vector>
40 #include <memory>
41 #include <array>
42 #include <ctime>
43 #include <cstring>
44 #include <atomic>
45 
46 #include <fastcgi++/protocol.hpp>
47 
49 namespace Fastcgipp
50 {
52  namespace Http
53  {
55 
65  template<class charT> struct File
66  {
68  std::basic_string<charT> filename;
69 
71  std::basic_string<charT> contentType;
72 
74  size_t size;
75 
77  mutable std::unique_ptr<char[]> data;
78 
80  File(File&& x):
81  filename(std::move(x.filename)),
82  contentType(std::move(x.contentType)),
83  size(x.size),
84  data(std::move(x.data))
85  {}
86 
87  File() {}
88  };
89 
91  enum class RequestMethod
92  {
93  ERROR=0,
94  HEAD=1,
95  GET=2,
96  POST=3,
97  PUT=4,
98  DELETE=5,
99  TRACE=6,
100  OPTIONS=7,
101  CONNECT=8
102  };
103 
105  extern const std::array<const char* const, 9> requestMethodLabels;
106 
107  template<class charT, class Traits>
108  inline std::basic_ostream<charT, Traits>& operator<<(
109  std::basic_ostream<charT, Traits>& os,
110  const RequestMethod requestMethod)
111  {
112  return os << requestMethodLabels[static_cast<int>(requestMethod)];
113  }
114 
116 
127  class Address
128  {
129  public:
131  static const size_t size=16;
132 
134  std::array<unsigned char, size> m_data;
135 
137 
140  Address operator=(const unsigned char* data)
141  {
142  std::copy(data, data+m_data.size(), m_data.begin());
143  return *this;
144  }
145 
146  Address operator=(const Address& address)
147  {
148  std::copy(
149  address.m_data.begin(),
150  address.m_data.end(),
151  m_data.begin());
152  return *this;
153  }
154 
155  Address(const Address& address)
156  {
157  std::copy(
158  address.m_data.begin(),
159  address.m_data.end(),
160  m_data.begin());
161  }
162 
165  {
166  zero();
167  }
168 
170 
173  explicit Address(const unsigned char* data)
174  {
175  std::copy(data, data+m_data.size(), m_data.begin());
176  }
177 
179 
188  template<class charT> void assign(
189  const charT* start,
190  const charT* end);
191 
192  bool operator==(const Address& x) const
193  {
194  return std::equal(
195  m_data.cbegin(),
196  m_data.cend(),
197  x.m_data.cbegin());
198  }
199 
200  bool operator<(const Address& x) const
201  {
202  return std::memcmp(m_data.data(), x.m_data.data(), size)<0;
203  }
204 
206  operator bool() const;
207 
208  Address operator&(const Address& x) const;
209 
210  Address& operator&=(const Address& x);
211 
213  void zero()
214  {
215  m_data.fill(0);
216  }
217  };
218 
220 
224  template<class charT, class Traits>
225  std::basic_ostream<charT, Traits>& operator<<(
226  std::basic_ostream<charT, Traits>& os,
227  const Address& address);
228 
230 
235  template<class charT, class Traits>
236  std::basic_istream<charT, Traits>& operator>>(
237  std::basic_istream<charT, Traits>& is,
238  Address& address);
239 
241 
251  template<class charT> struct Environment
252  {
254  std::basic_string<charT> host;
255 
257  std::basic_string<charT> userAgent;
258 
260  std::basic_string<charT> acceptContentTypes;
261 
263  std::vector<std::string> acceptLanguages;
264 
266  std::basic_string<charT> acceptCharsets;
267 
269  std::basic_string<charT> authorization;
270 
272  std::basic_string<charT> referer;
273 
275  std::basic_string<charT> contentType;
276 
278  std::basic_string<charT> root;
279 
281  std::basic_string<charT> scriptName;
282 
285 
287  std::basic_string<charT> requestUri;
288 
290  std::vector<std::basic_string<charT>> pathInfo;
291 
293  unsigned etag;
294 
296  unsigned keepAlive;
297 
299  unsigned contentLength;
300 
303 
306 
308  uint16_t serverPort;
309 
311  uint16_t remotePort;
312 
314  std::time_t ifModifiedSince;
315 
317  std::multimap<
318  std::basic_string<charT>,
319  std::basic_string<charT>> cookies;
320 
322  std::multimap<
323  std::basic_string<charT>,
324  std::basic_string<charT>> gets;
325 
327  std::multimap<
328  std::basic_string<charT>,
329  std::basic_string<charT>> posts;
330 
332  std::multimap<
333  std::basic_string<charT>,
335 
337 
346  void fill(
347  const char* data,
348  const char* dataEnd);
349 
351 
358  void fillPostBuffer(
359  const char* start,
360  const char* end);
361 
363 
370  bool parsePostBuffer();
371 
373  const std::vector<char>& postBuffer() const
374  {
375  return m_postBuffer;
376  }
377 
380  {
381  m_postBuffer.clear();
382  m_postBuffer.shrink_to_fit();
383  }
384 
387  etag(0),
388  keepAlive(0),
389  contentLength(0),
390  serverPort(0),
391  remotePort(0),
392  ifModifiedSince(0)
393  {}
394  private:
396  inline void parsePostsMultipart();
397 
399  inline void parsePostsUrlEncoded();
400 
402  std::vector<char> boundary;
403 
405  std::vector<char> m_postBuffer;
406  };
407 
409 
414  void vecToString(
415  const char* start,
416  const char* end,
417  std::wstring& string);
418 
420 
425  inline void vecToString(
426  const char* start,
427  const char* end,
428  std::string& string)
429  {
430  string.assign(start, end);
431  }
432 
434 
446  template<class charT> int atoi(const charT* start, const charT* end);
447 
449 
461  template<class charT> float atof(const charT* start, const charT* end);
462 
464 
470  template<class charT> void decodeUrlEncoded(
471  const char* data,
472  const char* dataEnd,
473  std::multimap<
474  std::basic_string<charT>,
475  std::basic_string<charT>>& output,
476  const char* const fieldSeparator="&");
477 
479 
494  const char* start,
495  const char* end,
496  char* destination);
497 
499  extern const std::array<const char, 64> base64Characters;
500 
502 
518  template<class In, class Out>
519  Out base64Encode(In start, In end, Out destination);
520 
522 
538  template<class In, class Out>
539  Out base64Decode(In start, In end, Out destination);
540 
542 
546  class SessionId
547  {
548  public:
550  static const size_t size=15;
551 
553  static const size_t stringLength=size*4/3;
554 
555  private:
557  std::array<unsigned char, size> m_data;
558 
560  mutable std::time_t m_timestamp;
561 
563  void refresh() const
564  {
565  m_timestamp = std::time(nullptr);
566  }
567 
568  template<class T> friend class Sessions;
569  public:
571  SessionId();
572 
573  SessionId(const SessionId& x):
575  {
576  std::copy(x.m_data.begin(), x.m_data.end(), m_data.begin());
577  }
578 
580 
586  template<class charT>
587  SessionId(const std::basic_string<charT>& string);
588 
589  template<class charT, class Traits>
590  friend std::basic_ostream<charT, Traits>& operator<<(
591  std::basic_ostream<charT, Traits>& os,
592  const SessionId& x);
593 
594  bool operator<(const SessionId& x) const
595  {
596  return std::memcmp(
597  m_data.data(),
598  x.m_data.data(),
599  SessionId::size)<0;
600  }
601 
602  bool operator==(const SessionId& x) const
603  {
604  return std::memcmp(
605  m_data.data(),
606  x.m_data.data(),
607  SessionId::size)==0;
608  }
609  };
610 
612  template<class charT, class Traits>
613  std::basic_ostream<charT, Traits>& operator<<(
614  std::basic_ostream<charT, Traits>& os,
615  const SessionId& x)
616  {
617  base64Encode(
618  x.m_data.begin(),
619  x.m_data.end(),
620  std::ostream_iterator<charT, charT, Traits>(os));
621  return os;
622  }
623 
625 
641  template<class T> class Sessions
642  {
643  private:
645  const unsigned int m_keepAlive;
646 
648  std::time_t m_cleanupTime;
649 
651  std::map<SessionId, std::shared_ptr<const T>> m_sessions;
652 
655 
657  static const size_t expirationLength = 30;
658 
661 
663  std::atomic<const char*> m_expirationPtr;
664 
666  void setExpiration();
667 
668  public:
670 
674  Sessions(unsigned int keepAlive):
675  m_keepAlive(keepAlive),
676  m_cleanupTime(std::time(nullptr)+keepAlive)
677  {
678  setExpiration();
679  }
680 
682 
687  std::shared_ptr<const T> get(const SessionId& id);
688 
690  size_t size() const
691  {
692  std::lock_guard<std::mutex> lock(m_mutex);
693  return m_sessions.size();
694  }
695 
697 
703  SessionId generate(const std::shared_ptr<const T>& data);
704 
706 
709  void erase(const SessionId& id)
710  {
711  std::lock_guard<std::mutex> lock(m_mutex);
712  m_sessions.erase(id);
713  }
714 
716  const char* expiration() const
717  {
718  return m_expirationPtr;
719  }
720  };
721  }
722 }
723 
724 template<class In, class Out>
725 Out Fastcgipp::Http::base64Decode(In start, In end, Out destination)
726 {
727  Out dest=destination;
728 
729  for(int buffer, bitPos=-8, padStart; start!=end || bitPos>-6; ++dest)
730  {
731  if(bitPos==-8)
732  {
733  bitPos=18;
734  padStart=-9;
735  buffer=0;
736  while(bitPos!=-6)
737  {
738  if(start==end) return destination;
739  int value=*start++;
740  if(value >= 'A' && 'Z' >= value) value -= 'A';
741  else if(value >= 'a' && 'z' >= value) value -= 'a' - 26;
742  else if(value >= '0' && '9' >= value) value -= '0' - 52;
743  else if(value == '+') value = 62;
744  else if(value == '/') value = 63;
745  else if(value == '=') { padStart=bitPos; break; }
746  else return destination;
747 
748  buffer |= value << bitPos;
749  bitPos-=6;
750  }
751  bitPos=16;
752  }
753 
754  *dest = (buffer >> bitPos) & 0xff;
755  bitPos-=8;
756  if(padStart>=bitPos)
757  {
758  if( (padStart-bitPos)/6 )
759  return dest;
760  else
761  return ++dest;
762  }
763  }
764 
765  return dest;
766 }
767 
768 template<class In, class Out>
769 Out Fastcgipp::Http::base64Encode(In start, In end, Out destination)
770 {
771  for(int buffer, bitPos=-6, padded; start!=end || bitPos>-6; ++destination)
772  {
773  if(bitPos==-6)
774  {
775  bitPos=16;
776  buffer=0;
777  padded=-6;
778  while(bitPos!=-8)
779  {
780  if(start!=end)
781  buffer |= static_cast<int>(
782  *reinterpret_cast<const unsigned char*>(
783  start++)) << bitPos;
784  else padded+=6;
785  bitPos-=8;
786  }
787  bitPos=18;
788  }
789 
790  if(padded == bitPos)
791  {
792  *destination='=';
793  padded-=6;
794  }
795  else *destination=base64Characters[ (buffer >> bitPos)&0x3f ];
796  bitPos -= 6;
797  }
798 
799  return destination;
800 }
801 
802 template<class T> Fastcgipp::Http::SessionId
803 Fastcgipp::Http::Sessions<T>::generate(const std::shared_ptr<const T>& data)
804 {
805  std::lock_guard<std::mutex> lock(m_mutex);
806  std::pair<
807  typename std::map<SessionId, std::shared_ptr<const T>>::iterator,
808  bool>
809  retVal;
810  retVal.second=false;
811  while(!retVal.second)
812  retVal=m_sessions.insert(std::pair<SessionId, std::shared_ptr<const T>>(
813  SessionId(),
814  data));
815  return retVal.first->first;
816 }
817 
818 template<class T> std::shared_ptr<const T>
820 {
821  std::lock_guard<std::mutex> lock(m_mutex);
822  const std::time_t now = std::time(nullptr);
823  const std::time_t oldest(now-m_keepAlive);
824 
825  if(now >= m_cleanupTime)
826  {
827  auto session = m_sessions.begin();
828  while(session != m_sessions.end())
829  {
830  if(session->first.m_timestamp < oldest)
831  session = m_sessions.erase(session);
832  else
833  ++session;
834  }
835  m_cleanupTime = std::time(nullptr)+m_keepAlive;
836  setExpiration();
837  }
838 
839  const auto session = m_sessions.find(id);
840  if(session != m_sessions.cend())
841  {
842  if(session->first.m_timestamp < oldest)
843  m_sessions.erase(session);
844  else
845  {
846  session->first.refresh();
847  return session->second;
848  }
849  }
850 
851  return std::shared_ptr<const T>();
852 }
853 
855 {
856  char* const newExpiration(
857  m_expirationPtr==m_expiration[0]?m_expiration[1]:m_expiration[0]);
858  const std::time_t expirationTime = m_cleanupTime + m_keepAlive;
859  const auto count = std::strftime(
860  newExpiration,
861  expirationLength,
862  "%a, %d %b %Y %H:%M:%S GMT",
863  std::gmtime(&expirationTime));
864  std::fill(newExpiration+count, newExpiration+expirationLength, 0);
865  m_expirationPtr = newExpiration;
866 }
867 
868 #endif
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
Topmost namespace for the fastcgi++ library.
RequestMethod
The HTTP request method as an enumeration.
Definition: http.hpp:91
std::vector< char > m_postBuffer
Buffer for processing post data.
Definition: http.hpp:405
uint16_t remotePort
TCP port used by the client.
Definition: http.hpp:311
std::basic_string< charT > filename
Filename.
Definition: http.hpp:68
Address operator &(const Address &x) const
unsigned etag
The etag the client assumes this document should have.
Definition: http.hpp:293
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
void refresh() const
Resets the last access timestamp to the current time.
Definition: http.hpp:563
std::multimap< std::basic_string< charT >, File< charT > > files
Container of file POST data.
Definition: http.hpp:334
bool parsePostBuffer()
Attempts to parse the POST buffer.
Definition: http.cpp:408
std::basic_string< charT > host
Hostname of the server.
Definition: http.hpp:254
bool operator<(const Address &x) const
Definition: http.hpp:200
std::atomic< const char * > m_expirationPtr
Point for the current expiration string.
Definition: http.hpp:663
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
size_t size() const
How many active sessions are there?
Definition: http.hpp:690
std::shared_ptr< const T > get(const SessionId &id)
Get session data from session ID.
Definition: http.hpp:819
std::mutex m_mutex
Thread safe all operations.
Definition: http.hpp:654
STL namespace.
void parsePostsUrlEncoded()
Parses "application/x-www-form-urlencoded" post data.
Definition: http.cpp:618
Address serverAddress
IP address of the server.
Definition: http.hpp:302
std::basic_string< charT > root
HTTP root directory.
Definition: http.hpp:278
std::multimap< std::basic_string< charT >, std::basic_string< charT > > gets
Container with all url-encoded GET data.
Definition: http.hpp:324
std::time_t m_timestamp
Contains the time this session was last used.
Definition: http.hpp:560
std::time_t ifModifiedSince
Timestamp the client has for this document.
Definition: http.hpp:314
std::basic_string< charT > requestUri
REQUEST_URI.
Definition: http.hpp:287
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::vector< char > boundary
Raw string of characters representing the post boundary.
Definition: http.hpp:402
File(File &&x)
Move constructor.
Definition: http.hpp:80
friend std::basic_ostream< charT, Traits > & operator<<(std::basic_ostream< charT, Traits > &os, const SessionId &x)
Output the ID data in base64 encoding.
Definition: http.hpp:613
std::array< unsigned char, size > m_data
ID data.
Definition: http.hpp:557
Declares everything for relating to the FastCGI protocol itself.
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
Sessions(unsigned int keepAlive)
Constructor takes session keep alive times.
Definition: http.hpp:674
std::basic_string< charT > contentType
Content type of data sent from client.
Definition: http.hpp:275
std::unique_ptr< char[]> data
File data.
Definition: http.hpp:77
void zero()
Set all bits to zero in IP address.
Definition: http.hpp:213
Defines ID values for HTTP sessions.
Definition: http.hpp:546
std::mutex mutex
Thread safe the logging mechanism.
Definition: log.cpp:102
unsigned contentLength
Length of content to be received from the client (post data)
Definition: http.hpp:299
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
bool operator==(const Address &x) const
Definition: http.hpp:192
uint16_t serverPort
TCP port used by the server.
Definition: http.hpp:308
std::basic_string< charT > authorization
Http authorization string.
Definition: http.hpp:269
Data structure of HTTP environment data.
Definition: http.hpp:251
std::vector< std::basic_string< charT > > pathInfo
Path information.
Definition: http.hpp:290
std::multimap< std::basic_string< charT >, std::basic_string< charT > > posts
Container of none-file POST data.
Definition: http.hpp:329
std::basic_string< charT > userAgent
User agent string.
Definition: http.hpp:257
SessionId generate(const std::shared_ptr< const T > &data)
Generates a new session.
Definition: http.hpp:803
Out base64Decode(In start, In end, Out destination)
Convert a Base64 encoded container to a binary container.
Definition: http.hpp:725
void setExpiration()
Internal helper for building the m_expiration string.
Definition: http.hpp:854
Address operator=(const unsigned char *data)
Assign the IPv6 address from a data array.
Definition: http.hpp:140
void clearPostBuffer()
Clear the post buffer.
Definition: http.hpp:379
static const size_t expirationLength
Length of expiration string (with null terminator)
Definition: http.hpp:657
const unsigned int m_keepAlive
Amount of seconds to keep sessions around for.
Definition: http.hpp:645
void fill(const char *data, const char *dataEnd)
Parses FastCGI parameter data into the data structure.
Definition: http.cpp:166
std::time_t m_cleanupTime
The time that the next session cleanup should be done.
Definition: http.hpp:648
static const size_t stringLength
Size in characters of string representation.
Definition: http.hpp:553
std::basic_string< charT > acceptCharsets
Character sets the clients accepts.
Definition: http.hpp:266
Address operator=(const Address &address)
Definition: http.hpp:146
const char * expiration() const
Expiration string for setting cookies.
Definition: http.hpp:716
std::multimap< std::basic_string< charT >, std::basic_string< charT > > cookies
Container with all url-encoded cookie data.
Definition: http.hpp:319
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::map< SessionId, std::shared_ptr< const T > > m_sessions
Actual container of sessions.
Definition: http.hpp:651
SessionId(const SessionId &x)
Definition: http.hpp:573
std::basic_ostream< charT, Traits > & operator<<(std::basic_ostream< charT, Traits > &os, const RequestMethod requestMethod)
Definition: http.hpp:108
bool operator<(const SessionId &x) const
Definition: http.hpp:594
std::basic_string< charT > referer
Referral URL.
Definition: http.hpp:272
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)
std::basic_string< charT > acceptContentTypes
Content types the client accepts.
Definition: http.hpp:260
std::basic_string< charT > scriptName
Filename of script relative to the HTTP root directory.
Definition: http.hpp:281
unsigned keepAlive
How many seconds the connection should be kept alive.
Definition: http.hpp:296
Address(const unsigned char *data)
Construct the IPv6 address from a data array.
Definition: http.hpp:173
const std::vector< char > & postBuffer() const
Get the post buffer.
Definition: http.hpp:373
const std::array< const char, 64 > base64Characters
List of characters in order for Base64 encoding.
Definition: http.cpp:751
Container for HTTP sessions.
Definition: http.hpp:641
char m_expiration[2][expirationLength]
Internal string for cookie expirations.
Definition: http.hpp:660
Efficiently stores IPv6 addresses.
Definition: http.hpp:127
void erase(const SessionId &id)
Erase a session.
Definition: http.hpp:709
Out base64Encode(In start, In end, Out destination)
Convert a binary container of data to a Base64 encoded container.
Definition: http.hpp:769
SessionId()
This constructor initializes the ID data to a random value.
Definition: http.cpp:629
Address()
Initializes an all zero address.
Definition: http.hpp:164
std::vector< std::string > acceptLanguages
Languages the client accepts.
Definition: http.hpp:263
Address remoteAddress
IP address of the client.
Definition: http.hpp:305
bool operator==(const SessionId &x) const
Definition: http.hpp:602
Address(const Address &address)
Definition: http.hpp:155
RequestMethod requestMethod
REQUEST_METHOD.
Definition: http.hpp:284