fastcgi++
http.cpp
Go to the documentation of this file.
1 
2 /***************************************************************************
3 * Copyright (C) 2007 Eddie Carle [eddie@erctech.org] *
4 * *
5 * This file is part of fastcgi++. *
6 * *
7 * fastcgi++ is free software: you can redistribute it and/or modify it *
8 * under the terms of the GNU Lesser General Public License as published *
9 * by the Free Software Foundation, either version 3 of the License, or (at *
10 * your option) any later version. *
11 * *
12 * fastcgi++ is distributed in the hope that it will be useful, but WITHOUT *
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or *
14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public *
15 * License for more details. *
16 * *
17 * You should have received a copy of the GNU Lesser General Public License *
18 * along with fastcgi++. If not, see <http://www.gnu.org/licenses/>. *
19 ****************************************************************************/
20 
21 
22 #include <boost/date_time/posix_time/posix_time.hpp>
23 
24 #include <fastcgi++/http.hpp>
25 #include <fastcgi++/protocol.hpp>
26 
27 #include "utf8_codecvt.hpp"
28 
29 void Fastcgipp::Http::charToString(const char* data, size_t size, std::wstring& string)
30 {
31  const size_t bufferSize=512;
32  wchar_t buffer[bufferSize];
33  using namespace std;
34 
35  if(size)
36  {
37  codecvt_base::result cr=codecvt_base::partial;
38  while(cr==codecvt_base::partial)
39  {{
40  wchar_t* it;
41  const char* tmpData;
42  mbstate_t conversionState = mbstate_t();
43  cr=use_facet<codecvt<wchar_t, char, mbstate_t> >(locale(locale::classic(), new utf8CodeCvt::utf8_codecvt_facet)).in(conversionState, data, data+size, tmpData, buffer, buffer+bufferSize, it);
44  string.append(buffer, it);
45  size-=tmpData-data;
46  data=tmpData;
47  }}
48  if(cr==codecvt_base::error) throw Exceptions::CodeCvt();
49  }
50 }
51 
52 int Fastcgipp::Http::atoi(const char* start, const char* end)
53 {
54  bool neg=false;
55  if(*start=='-')
56  {
57  neg=true;
58  ++start;
59  }
60  int result=0;
61  for(; 0x30 <= *start && *start <= 0x39 && start<end; ++start)
62  result=result*10+(*start&0x0f);
63 
64  return neg?-result:result;
65 }
66 
67 size_t Fastcgipp::Http::percentEscapedToRealBytes(const char* source, char* destination, size_t size)
68 {
69  if (size < 1) return 0;
70 
71  unsigned int i=0;
72  char* start=destination;
73  while(1)
74  {
75  if(*source=='%')
76  {
77  *destination=0;
78  for(int shift=4; shift>=0; shift-=4)
79  {
80  if(++i>=size) break;
81  ++source;
82  if((*source|0x20) >= 'a' && (*source|0x20) <= 'f')
83  *destination|=((*source|0x20)-0x57)<<shift;
84  else if(*source >= '0' && *source <= '9')
85  *destination|=(*source&0x0f)<<shift;
86  }
87  ++source;
88  ++destination;
89  }
90  else if(*source=='+')
91  {
92  *destination++=' ';
93  ++source;
94  }
95  else
96  *destination++=*source++;
97 
98  if(++i>=size) break;
99  }
100  return destination-start;
101 }
102 
103 template void Fastcgipp::Http::Environment<char>::fill(const char* data, size_t size);
104 template void Fastcgipp::Http::Environment<wchar_t>::fill(const char* data, size_t size);
105 template<class charT> void Fastcgipp::Http::Environment<charT>::fill(const char* data, size_t size)
106 {
107  using namespace std;
108  using namespace boost;
109 
110  while(size)
111  {{
112  size_t nameSize;
113  size_t valueSize;
114  const char* name;
115  const char* value;
116  Protocol::processParamHeader(data, size, name, nameSize, value, valueSize);
117  size-=value-data+valueSize;
118  data=value+valueSize;
119 
120  switch(nameSize)
121  {
122  case 9:
123  if(!memcmp(name, "HTTP_HOST", 9))
124  charToString(value, valueSize, host);
125  else if(!memcmp(name, "PATH_INFO", 9))
126  {
127  boost::scoped_array<char> buffer(new char[valueSize]);
128  const char* source=value;
129  int size=-1;
130  for(; source<value+valueSize+1; ++source, ++size)
131  {
132  if(*source == '/' || source == value+valueSize)
133  {
134  if(size > 0)
135  {
136  percentEscapedToRealBytes(source-size, buffer.get(), size);
137  pathInfo.push_back(std::basic_string<charT>());
138  charToString(buffer.get(), size, pathInfo.back());
139  }
140  size=-1;
141  }
142  }
143  }
144  break;
145  case 11:
146  if(!memcmp(name, "HTTP_ACCEPT", 11))
147  charToString(value, valueSize, acceptContentTypes);
148  else if(!memcmp(name, "HTTP_COOKIE", 11))
149  decodeUrlEncoded(value, valueSize, cookies, ';');
150  else if(!memcmp(name, "SERVER_ADDR", 11))
151  serverAddress.assign(value, value+valueSize);
152  else if(!memcmp(name, "REMOTE_ADDR", 11))
153  remoteAddress.assign(value, value+valueSize);
154  else if(!memcmp(name, "SERVER_PORT", 11))
155  serverPort=atoi(value, value+valueSize);
156  else if(!memcmp(name, "REMOTE_PORT", 11))
157  remotePort=atoi(value, value+valueSize);
158  else if(!memcmp(name, "SCRIPT_NAME", 11))
159  charToString(value, valueSize, scriptName);
160  else if(!memcmp(name, "REQUEST_URI", 11))
161  charToString(value, valueSize, requestUri);
162  break;
163  case 12:
164  if(!memcmp(name, "HTTP_REFERER", 12) && valueSize)
165  charToString(value, valueSize, referer);
166  else if(!memcmp(name, "CONTENT_TYPE", 12))
167  {
168  const char* end=(char*)memchr(value, ';', valueSize);
169  charToString(value, end?end-value:valueSize, contentType);
170  if(end)
171  {
172  const char* start=(char*)memchr(end, '=', valueSize-(end-data));
173  if(start)
174  {
175  boundarySize=valueSize-(++start-value);
176  boundary.reset(new char[boundarySize]);
177  memcpy(boundary.get(), start, boundarySize);
178  }
179  }
180  }
181  else if(!memcmp(name, "QUERY_STRING", 12) && valueSize)
182  decodeUrlEncoded(value, valueSize, gets);
183  break;
184  case 13:
185  if(!memcmp(name, "DOCUMENT_ROOT", 13))
186  charToString(value, valueSize, root);
187  break;
188  case 14:
189  if(!memcmp(name, "REQUEST_METHOD", 14))
190  {
191  requestMethod = HTTP_METHOD_ERROR;
192  switch(valueSize)
193  {
194  case 3:
195  if(!memcmp(value, requestMethodLabels[HTTP_METHOD_GET], 3)) requestMethod = HTTP_METHOD_GET;
196  else if(!memcmp(value, requestMethodLabels[HTTP_METHOD_PUT], 3)) requestMethod = HTTP_METHOD_PUT;
197  break;
198  case 4:
199  if(!memcmp(value, requestMethodLabels[HTTP_METHOD_HEAD], 4)) requestMethod = HTTP_METHOD_HEAD;
200  else if(!memcmp(value, requestMethodLabels[HTTP_METHOD_POST], 4)) requestMethod = HTTP_METHOD_POST;
201  break;
202  case 5:
203  if(!memcmp(value, requestMethodLabels[HTTP_METHOD_TRACE], 5)) requestMethod = HTTP_METHOD_TRACE;
204  break;
205  case 6:
206  if(!memcmp(value, requestMethodLabels[HTTP_METHOD_DELETE], 6)) requestMethod = HTTP_METHOD_DELETE;
207  break;
208  case 7:
209  if(!memcmp(value, requestMethodLabels[HTTP_METHOD_OPTIONS], 7)) requestMethod = HTTP_METHOD_OPTIONS;
210  else if(!memcmp(value, requestMethodLabels[HTTP_METHOD_OPTIONS], 7)) requestMethod = HTTP_METHOD_CONNECT;
211  break;
212  }
213  }
214  else if(!memcmp(name, "CONTENT_LENGTH", 14))
215  contentLength=atoi(value, value+valueSize);
216  break;
217  case 15:
218  if(!memcmp(name, "HTTP_USER_AGENT", 15))
219  charToString(value, valueSize, userAgent);
220  else if(!memcmp(name, "HTTP_KEEP_ALIVE", 15))
221  keepAlive=atoi(value, value+valueSize);
222  break;
223  case 18:
224  if(!memcmp(name, "HTTP_IF_NONE_MATCH", 18))
225  etag=atoi(value, value+valueSize);
226  break;
227  case 19:
228  if(!memcmp(name, "HTTP_ACCEPT_CHARSET", 19))
229  charToString(value, valueSize, acceptCharsets);
230  break;
231  case 20:
232  if(!memcmp(name, "HTTP_ACCEPT_LANGUAGE", 20))
233  charToString(value, valueSize, acceptLanguages);
234  break;
235  case 22:
236  if(!memcmp(name, "HTTP_IF_MODIFIED_SINCE", 22))
237  {
238  stringstream dateStream;
239  dateStream.write(value, valueSize);
240  dateStream.imbue(locale(locale::classic(), new posix_time::time_input_facet("%a, %d %b %Y %H:%M:%S GMT")));
241  dateStream >> ifModifiedSince;
242  }
243  break;
244  }
245  }}
246 }
247 
248 template bool Fastcgipp::Http::Environment<char>::fillPostBuffer(const char* data, size_t size);
249 template bool Fastcgipp::Http::Environment<wchar_t>::fillPostBuffer(const char* data, size_t size);
250 template<class charT> bool Fastcgipp::Http::Environment<charT>::fillPostBuffer(const char* data, size_t size)
251 {
252  if(!postBuffer)
253  {
254  postBuffer.reset(new char[contentLength]);
255  pPostBuffer=postBuffer.get();
256  }
257 
258  size_t trueSize=minPostBufferSize(size);
259  if(trueSize)
260  {
261  std::memcpy(pPostBuffer, data, trueSize);
262  pPostBuffer+=trueSize;
263  return true;
264  }
265  else
266  return false;
267 }
268 
272 {
273  using namespace std;
274 
275  const char cName[] = "name=\"";
276  const char cFilename[] = "filename=\"";
277  const char cContentType[] = "Content-Type: ";
278  const char cBodyStart[] = "\r\n\r\n";
279 
280  pPostBuffer=postBuffer.get()+boundarySize+1;
281  const char* contentTypeStart=0;
282  ssize_t contentTypeSize=-1;
283  const char* nameStart=0;
284  ssize_t nameSize=-1;
285  const char* filenameStart=0;
286  ssize_t filenameSize=-1;
287  const char* bodyStart=0;
288  ssize_t bodySize=-1;
289  enum ParseState { HEADER, NAME, FILENAME, CONTENT_TYPE, BODY } parseState=HEADER;
290  for(pPostBuffer=postBuffer.get()+boundarySize+2; pPostBuffer<postBuffer.get()+contentLength; ++pPostBuffer)
291  {
292  switch(parseState)
293  {
294  case HEADER:
295  {
296  if(nameSize == -1)
297  {
298  const size_t size=minPostBufferSize(sizeof(cName)-1);
299  if(!memcmp(pPostBuffer, cName, size))
300  {
301  pPostBuffer+=size-1;
302  nameStart=pPostBuffer+1;
303  parseState=NAME;
304  continue;
305  }
306  }
307  if(filenameSize == -1)
308  {
309  const size_t size=minPostBufferSize(sizeof(cFilename)-1);
310  if(!memcmp(pPostBuffer, cFilename, size))
311  {
312  pPostBuffer+=size-1;
313  filenameStart=pPostBuffer+1;
314  parseState=FILENAME;
315  continue;
316  }
317  }
318  if(contentTypeSize == -1)
319  {
320  const size_t size=minPostBufferSize(sizeof(cContentType)-1);
321  if(!memcmp(pPostBuffer, cContentType, size))
322  {
323  pPostBuffer+=size-1;
324  contentTypeStart=pPostBuffer+1;
325  parseState=CONTENT_TYPE;
326  continue;
327  }
328  }
329  if(bodySize == -1)
330  {
331  const size_t size=minPostBufferSize(sizeof(cBodyStart)-1);
332  if(!memcmp(pPostBuffer, cBodyStart, size))
333  {
334  pPostBuffer+=size-1;
335  bodyStart=pPostBuffer+1;
336  parseState=BODY;
337  continue;
338  }
339  }
340  continue;
341  }
342 
343  case NAME:
344  {
345  if(*pPostBuffer == '"')
346  {
347  nameSize=pPostBuffer-nameStart;
348  parseState=HEADER;
349  }
350  continue;
351  }
352 
353  case FILENAME:
354  {
355  if(*pPostBuffer == '"')
356  {
357  filenameSize=pPostBuffer-filenameStart;
358  parseState=HEADER;
359  }
360  continue;
361  }
362 
363  case CONTENT_TYPE:
364  {
365  if(*pPostBuffer == '\r' || *pPostBuffer == '\n')
366  {
367  contentTypeSize=pPostBuffer-contentTypeStart;
368  --pPostBuffer;
369  parseState=HEADER;
370  }
371  continue;
372  }
373 
374  case BODY:
375  {
376  const size_t size=minPostBufferSize(sizeof(boundarySize)-1);
377  if(!memcmp(pPostBuffer, boundary.get(), size))
378  {
379  bodySize=pPostBuffer-bodyStart-2;
380  if(bodySize<0) bodySize=0;
381  else if(bodySize>=2 && *(bodyStart+bodySize-1)=='\n' && *(bodyStart+bodySize-2)=='\r')
382  bodySize -= 2;
383 
384  if(nameSize != -1)
385  {
386  basic_string<charT> name;
387  charToString(nameStart, nameSize, name);
388 
389  Post<charT>& thePost=posts[name];
390  if(contentTypeSize != -1)
391  {
392  thePost.type=Post<charT>::file;
393  charToString(contentTypeStart, contentTypeSize, thePost.contentType);
394  if(filenameSize != -1) charToString(filenameStart, filenameSize, thePost.filename);
395  thePost.m_size=bodySize;
396  if(bodySize)
397  {
398  thePost.m_data = new char[bodySize];
399  memcpy(thePost.m_data, bodyStart, bodySize);
400  }
401  }
402  else
403  {
404  thePost.type=Post<charT>::form;
405  charToString(bodyStart, bodySize, thePost.value);
406  }
407  }
408 
409  pPostBuffer+=size;
410  parseState=HEADER;
411  contentTypeStart=0;
412  contentTypeSize=-1;
413  nameStart=0;
414  nameSize=-1;
415  filenameStart=0;
416  filenameSize=-1;
417  bodyStart=0;
418  bodySize=-1;
419  }
420  continue;
421  }
422  }
423  }
424 }
425 
429 {
430  char* nameStart=postBuffer.get();
431  size_t nameSize;
432  char* valueStart=0;
433  size_t valueSize;
434 
435  for(char* i=postBuffer.get(); i<=postBuffer.get()+contentLength; ++i)
436  {
437  if(*i == '=' && nameStart && !valueStart)
438  {
439  nameSize=percentEscapedToRealBytes(nameStart, nameStart, i-nameStart);
440  valueStart=i+1;
441  }
442  else if( (i==postBuffer.get()+contentLength || *i == '&') && nameStart && valueStart)
443  {
444  valueSize=percentEscapedToRealBytes(valueStart, valueStart, i-valueStart);
445 
446  std::basic_string<charT> name;
447  charToString(nameStart, nameSize, name);
448  nameStart=i+1;
449  Post<charT>& thePost=posts[name];
450  thePost.type=Post<charT>::form;
451  charToString(valueStart, valueSize, thePost.value);
452  valueStart=0;
453  }
454  }
455 }
456 
458 
460 {
461  if(!seeded)
462  {
463  std::srand(boost::posix_time::microsec_clock::universal_time().time_of_day().fractional_seconds());
464  seeded=true;
465  }
466 
467  for(char* i=data; i<data+size; ++i)
468  *i=char(rand()%256);
469  timestamp = boost::posix_time::second_clock::universal_time();
470 }
471 
472 template const Fastcgipp::Http::SessionId& Fastcgipp::Http::SessionId::operator=<const char>(const char* data_);
473 template const Fastcgipp::Http::SessionId& Fastcgipp::Http::SessionId::operator=<const wchar_t>(const wchar_t* data_);
474 template<class charT> const Fastcgipp::Http::SessionId& Fastcgipp::Http::SessionId::operator=(charT* data_)
475 {
476  std::memset(data, 0, size);
477  base64Decode(data_, data_+size*4/3, data);
478  timestamp = boost::posix_time::second_clock::universal_time();
479  return *this;
480 }
481 
482 template void Fastcgipp::Http::decodeUrlEncoded<char>(const char* data, size_t size, std::map<std::basic_string<char>, std::basic_string<char> >& output, const char fieldSeperator);
483 template void Fastcgipp::Http::decodeUrlEncoded<wchar_t>(const char* data, size_t size, std::map<std::basic_string<wchar_t>, std::basic_string<wchar_t> >& output, const char fieldSeperator);
484 template<class charT> void Fastcgipp::Http::decodeUrlEncoded(const char* data, size_t size, std::map<std::basic_string<charT>, std::basic_string<charT> >& output, const char fieldSeperator)
485 {
486  using namespace std;
487 
488  boost::scoped_array<char> buffer(new char[size]);
489  memcpy(buffer.get(), data, size);
490 
491  char* nameStart=buffer.get();
492  size_t nameSize;
493  char* valueStart=0;
494  size_t valueSize;
495  for(char* i=buffer.get(); i<=buffer.get()+size; ++i)
496  {
497  if(i==buffer.get()+size || *i == fieldSeperator)
498  {
499  if(nameStart && valueStart)
500  {
501  valueSize=percentEscapedToRealBytes(valueStart, valueStart, i-valueStart);
502 
503  basic_string<charT> name;
504  charToString(nameStart, nameSize, name);
505  nameStart=i+1;
506  basic_string<charT>& value=output[name];
507  charToString(valueStart, valueSize, value);
508  valueStart=0;
509  }
510  }
511 
512  else if(*i == ' ' && nameStart && !valueStart)
513  ++nameStart;
514 
515  else if(*i == '=' && nameStart && !valueStart)
516  {
517  nameSize=percentEscapedToRealBytes(nameStart, nameStart, i-nameStart);
518  valueStart=i+1;
519  }
520  }
521 }
522 
523 const char Fastcgipp::Http::base64Characters[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
525  "ERROR",
526  "HEAD",
527  "GET",
528  "POST",
529  "PUT",
530  "DELETE",
531  "TRACE",
532  "OPTIONS",
533  "CONNECT"
534 };
535 
536 template const std::basic_string<char>& Fastcgipp::Http::Environment<char>::findCookie(const char* key) const;
537 template const std::basic_string<wchar_t>& Fastcgipp::Http::Environment<wchar_t>::findCookie(const wchar_t* key) const;
538 template<class charT> const std::basic_string<charT>& Fastcgipp::Http::Environment<charT>::findCookie(const charT* key) const
539 {
540  static const std::basic_string<charT> emptyString;
541  typename Cookies::const_iterator it=cookies.find(key);
542  if(it==cookies.end())
543  return emptyString;
544  else
545  return it->second;
546 }
547 
548 template const std::basic_string<char>& Fastcgipp::Http::Environment<char>::findGet(const char* key) const;
549 template const std::basic_string<wchar_t>& Fastcgipp::Http::Environment<wchar_t>::findGet(const wchar_t* key) const;
550 template<class charT> const std::basic_string<charT>& Fastcgipp::Http::Environment<charT>::findGet(const charT* key) const
551 {
552  static const std::basic_string<charT> emptyString;
553  typename Gets::const_iterator it=gets.find(key);
554  if(it==gets.end())
555  return emptyString;
556  else
557  return it->second;
558 }
559 
562 template<class charT> const Fastcgipp::Http::Post<charT>& Fastcgipp::Http::Environment<charT>::findPost(const charT* key) const
563 {
564  static const Post<charT> emptyPost;
565  typename Posts::const_iterator it=posts.find(key);
566  if(it==posts.end())
567  return emptyPost;
568  else
569  return it->second;
570 }
571 
572 template bool Fastcgipp::Http::Environment<char>::checkForGet(const char* key) const;
573 template bool Fastcgipp::Http::Environment<wchar_t>::checkForGet(const wchar_t* key) const;
574 template<class charT> bool Fastcgipp::Http::Environment<charT>::checkForGet(const charT* key) const
575 {
576  typename Gets::const_iterator it=gets.find(key);
577  if(it==gets.end())
578  return false;
579  else
580  return true;
581 }
582 
583 template bool Fastcgipp::Http::Environment<char>::checkForPost(const char* key) const;
584 template bool Fastcgipp::Http::Environment<wchar_t>::checkForPost(const wchar_t* key) const;
585 template<class charT> bool Fastcgipp::Http::Environment<charT>::checkForPost(const charT* key) const
586 {
587  typename Posts::const_iterator it=posts.find(key);
588  if(it==posts.end())
589  return false;
590  else
591  return true;
592 }
593 
595 {
596  *(uint64_t*)m_data &= *(const uint64_t*)x.m_data;
597  *(uint64_t*)(m_data+size/2) &= *(const uint64_t*)(x.m_data+size/2);
598 
599  return *this;
600 }
601 
603 {
604  Address address(*this);
605  address &= x;
606 
607  return address;
608 }
609 
610 void Fastcgipp::Http::Address::assign(const char* start, const char* end)
611 {
612  const char* read=start-1;
613  unsigned char* write=m_data;
614  unsigned char* pad=0;
615  unsigned char offset;
616  uint16_t chunk=0;
617  bool error=false;
618 
619  while(1)
620  {
621  ++read;
622  if(read >= end || *read == ':')
623  {
624  if(read == start || *(read-1) == ':')
625  {
626  if(pad && pad != write)
627  {
628  error=true;
629  break;
630  }
631  else
632  pad = write;
633  }
634  else
635  {
636  *write = (chunk&0xff00)>>8;
637  *(write+1) = chunk&0x00ff;
638  chunk = 0;
639  write += 2;
640  if(write>=m_data+size || read >= end)
641  break;
642  }
643  continue;
644  }
645  else if('0' <= *read && *read <= '9')
646  offset = '0';
647  else if('A' <= *read && *read <= 'F')
648  offset = 'A'-10;
649  else if('a' <= *read && *read <= 'f')
650  offset = 'a'-10;
651  else if(*read == '.')
652  {
653  if(write == m_data)
654  {
655  // We must be getting a pure ipv4 formatted address. Not an ::ffff:xxx.xxx.xxx.xxx style ipv4 address.
656  *(uint16_t*)write = 0xffff;
657  pad = m_data;
658  write+=2;
659  }
660  else if(write - m_data > 12)
661  {
662  // We don't have enought space for an ipv4 address
663  error=true;
664  break;
665  }
666 
667  // First convert the value stored in chunk to the first part of the ipv4 address
668  *write = 0;
669  for(int i=0; i<3; ++i)
670  {
671  *write = *write * 10 + ((chunk&0x0f00)>>8);
672  chunk <<= 4;
673  }
674  ++write;
675 
676  // Now we'll get the remaining pieces
677  for(int i=0; i<3 && read<end; ++i)
678  {
679  const char* point=(const char*)memchr(read, '.', end-read);
680  if(point && point<end-1)
681  read=point;
682  else
683  {
684  error=true;
685  break;
686  }
687  *write++ = atoi(++read, end);
688  }
689  break;
690  }
691  else
692  {
693  error=true;
694  break;
695  }
696  chunk <<= 4;
697  chunk |= *read-offset;
698  }
699 
700  if(error)
701  std::memset(m_data, 0, size);
702  else if(pad)
703  {
704  if(pad==write)
705  std::memset(write, 0, size-(write-m_data));
706  else
707  {
708  const size_t padSize=m_data+size-write;
709  std::memmove(pad+padSize, pad, write-pad);
710  std::memset(pad, 0, padSize);
711  }
712  }
713 }
714 
715 template std::basic_ostream<char, std::char_traits<char> >& Fastcgipp::Http::operator<< <char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >& os, const Address& address);
716 template std::basic_ostream<wchar_t, std::char_traits<wchar_t> >& Fastcgipp::Http::operator<< <wchar_t, std::char_traits<wchar_t> >(std::basic_ostream<wchar_t, std::char_traits<wchar_t> >& os, const Address& address);
717 template<class charT, class Traits> std::basic_ostream<charT, Traits>& Fastcgipp::Http::operator<<(std::basic_ostream<charT, Traits>& os, const Address& address)
718 {
719  using namespace std;
720  if(!os.good()) return os;
721 
722  try
723  {
724  typename basic_ostream<charT, Traits>::sentry opfx(os);
725  if(opfx)
726  {
727  streamsize fieldWidth=os.width(0);
728  charT buffer[40];
729  charT* bufPtr=buffer;
730  locale loc(os.getloc(), new num_put<charT, charT*>);
731 
732  const uint16_t* subStart=0;
733  const uint16_t* subEnd=0;
734  {
735  const uint16_t* subStartCandidate;
736  const uint16_t* subEndCandidate;
737  bool inZero = false;
738 
739  for(const uint16_t* it = (const uint16_t*)address.data(); it < (const uint16_t*)(address.data()+Address::size); ++it)
740  {
741  if(*it == 0)
742  {
743  if(!inZero)
744  {
745  subStartCandidate = it;
746  subEndCandidate = it;
747  inZero=true;
748  }
749  ++subEndCandidate;
750  }
751  else if(inZero)
752  {
753  if(subEndCandidate-subStartCandidate > subEnd-subStart)
754  {
755  subStart=subStartCandidate;
756  subEnd=subEndCandidate-1;
757  }
758  inZero=false;
759  }
760  }
761  if(inZero)
762  {
763  if(subEndCandidate-subStartCandidate > subEnd-subStart)
764  {
765  subStart=subStartCandidate;
766  subEnd=subEndCandidate-1;
767  }
768  inZero=false;
769  }
770  }
771 
772  ios_base::fmtflags oldFlags = os.flags();
773  os.setf(ios::hex, ios::basefield);
774 
775  if(subStart==(const uint16_t*)address.data() && subEnd==(const uint16_t*)address.data()+4 && *((const uint16_t*)address.data()+5) == 0xffff)
776  {
777  // It is an ipv4 address
778  *bufPtr++=os.widen(':');
779  *bufPtr++=os.widen(':');
780  bufPtr=use_facet<num_put<charT, charT*> >(loc).put(bufPtr, os, os.fill(), static_cast<unsigned long int>(0xffff));
781  *bufPtr++=os.widen(':');
782  os.setf(ios::dec, ios::basefield);
783 
784  for(const unsigned char* it = address.data()+12; it < address.data()+Address::size; ++it)
785  {
786  bufPtr=use_facet<num_put<charT, charT*> >(loc).put(bufPtr, os, os.fill(), static_cast<unsigned long int>(*it));
787  *bufPtr++=os.widen('.');
788  }
789  --bufPtr;
790  }
791  else
792  {
793  // It is an ipv6 address
794  for(const uint16_t* it = (const uint16_t*)address.data(); it < (const uint16_t*)(address.data()+Address::size); ++it)
795  {
796  if(subStart <= it && it <= subEnd)
797  {
798  if(it == subStart && it == (const uint16_t*)address.data())
799  *bufPtr++=os.widen(':');
800  if(it == subEnd)
801  *bufPtr++=os.widen(':');
802  }
803  else
804  {
805  bufPtr=use_facet<num_put<charT, charT*> >(loc).put(bufPtr, os, os.fill(), static_cast<unsigned long int>(Protocol::readBigEndian(*it)));
806 
807  if(it < (const uint16_t*)(address.data()+Address::size)-1)
808  *bufPtr++=os.widen(':');
809  }
810  }
811  }
812 
813  os.flags(oldFlags);
814 
815  charT* ptr=buffer;
816  ostreambuf_iterator<charT,Traits> sink(os);
817  if(os.flags() & ios_base::left)
818  for(int i=max(fieldWidth, bufPtr-buffer); i>0; i--)
819  {
820  if(ptr!=bufPtr) *sink++=*ptr++;
821  else *sink++=os.fill();
822  }
823  else
824  for(int i=fieldWidth-(bufPtr-buffer); ptr!=bufPtr;)
825  {
826  if(i>0) { *sink++=os.fill(); --i; }
827  else *sink++=*ptr++;
828  }
829 
830  if(sink.failed()) os.setstate(ios_base::failbit);
831  }
832  }
833  catch(bad_alloc&)
834  {
835  ios_base::iostate exception_mask = os.exceptions();
836  os.exceptions(ios_base::goodbit);
837  os.setstate(ios_base::badbit);
838  os.exceptions(exception_mask);
839  if(exception_mask & ios_base::badbit) throw;
840  }
841  catch(...)
842  {
843  ios_base::iostate exception_mask = os.exceptions();
844  os.exceptions(ios_base::goodbit);
845  os.setstate(ios_base::failbit);
846  os.exceptions(exception_mask);
847  if(exception_mask & ios_base::failbit) throw;
848  }
849  return os;
850 }
851 
852 template std::basic_istream<char, std::char_traits<char> >& Fastcgipp::Http::operator>> <char, std::char_traits<char> >(std::basic_istream<char, std::char_traits<char> >& is, Address& address);
853 template std::basic_istream<wchar_t, std::char_traits<wchar_t> >& Fastcgipp::Http::operator>> <wchar_t, std::char_traits<wchar_t> >(std::basic_istream<wchar_t, std::char_traits<wchar_t> >& is, Address& address);
854 template<class charT, class Traits> std::basic_istream<charT, Traits>& Fastcgipp::Http::operator>>(std::basic_istream<charT, Traits>& is, Address& address)
855 {
856  using namespace std;
857  if(!is.good()) return is;
858 
859  ios_base::iostate err = ios::goodbit;
860  try
861  {
862  typename basic_istream<charT, Traits>::sentry ipfx(is);
863  if(ipfx)
864  {
865  istreambuf_iterator<charT, Traits> read(is);
866  unsigned char buffer[Address::size];
867  unsigned char* write=buffer;
868  unsigned char* pad=0;
869  unsigned char offset;
870  unsigned char count=0;
871  uint16_t chunk=0;
872  charT lastChar=0;
873 
874  for(;;++read)
875  {
876  if(++count>40)
877  {
878  err = ios::failbit;
879  break;
880  }
881  else if('0' <= *read && *read <= '9')
882  offset = '0';
883  else if('A' <= *read && *read <= 'F')
884  offset = 'A'-10;
885  else if('a' <= *read && *read <= 'f')
886  offset = 'a'-10;
887  else if(*read == '.')
888  {
889  if(write == buffer)
890  {
891  // We must be getting a pure ipv4 formatted address. Not an ::ffff:xxx.xxx.xxx.xxx style ipv4 address.
892  *(uint16_t*)write = 0xffff;
893  pad = buffer;
894  write+=2;
895  }
896  else if(write - buffer > 12)
897  {
898  // We don't have enought space for an ipv4 address
899  err = ios::failbit;
900  break;
901  }
902 
903  // First convert the value stored in chunk to the first part of the ipv4 address
904  *write = 0;
905  for(int i=0; i<3; ++i)
906  {
907  *write = *write * 10 + ((chunk&0x0f00)>>8);
908  chunk <<= 4;
909  }
910  ++write;
911 
912  // Now we'll get the remaining pieces
913  for(int i=0; i<3; ++i)
914  {
915  if(*read != is.widen('.'))
916  {
917  err = ios::failbit;
918  break;
919  }
920  unsigned int value;
921  use_facet<num_get<charT, istreambuf_iterator<charT, Traits> > >(is.getloc()).get(++read, istreambuf_iterator<charT, Traits>(), is, err, value);
922  *write++ = value;
923  }
924  break;
925  }
926  else
927  {
928  if(*read == ':' && (!lastChar || lastChar == ':'))
929  {
930  if(pad && pad != write)
931  {
932  err = ios::failbit;
933  break;
934  }
935  else
936  pad = write;
937  }
938  else
939  {
940  *write = (chunk&0xff00)>>8;
941  *(write+1) = chunk&0x00ff;
942  chunk = 0;
943  write += 2;
944  if(write>=buffer+Address::size)
945  break;
946  if(*read!=':')
947  {
948  if(!pad)
949  err = ios::failbit;
950  break;
951  }
952  }
953  lastChar=':';
954  continue;
955  }
956  chunk <<= 4;
957  chunk |= *read-offset;
958  lastChar=*read;
959 
960  }
961 
962  if(err == ios::goodbit)
963  {
964  if(pad)
965  {
966  if(pad==write)
967  std::memset(write, 0, Address::size-(write-buffer));
968  else
969  {
970  const size_t padSize=buffer+Address::size-write;
971  std::memmove(pad+padSize, pad, write-pad);
972  std::memset(pad, 0, padSize);
973  }
974  }
975  address=buffer;
976  }
977  else
978  is.setstate(err);
979  }
980  }
981  catch(bad_alloc&)
982  {
983  ios_base::iostate exception_mask = is.exceptions();
984  is.exceptions(ios_base::goodbit);
985  is.setstate(ios_base::badbit);
986  is.exceptions(exception_mask);
987  if(exception_mask & ios_base::badbit) throw;
988  }
989  catch(...)
990  {
991  ios_base::iostate exception_mask = is.exceptions();
992  is.exceptions(ios_base::goodbit);
993  is.setstate(ios_base::failbit);
994  is.exceptions(exception_mask);
995  if(exception_mask & ios_base::failbit) throw;
996  }
997 
998  return is;
999 }
1000 
1001 Fastcgipp::Http::Address::operator bool() const
1002 {
1003  static const unsigned char nullString[size] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
1004  if(std::memcmp(m_data, nullString, size) == 0)
1005  return false;
1006  return true;
1007 }