TFCweb  1.0.4 $Rev: 483 $
TFC Primavera 2012: Nucli d'un servidor web
Transformacions.cc
Veure la documentació d'aquest fitxer.
1 
8 /*
9  * Copyright (c) 2012 Toni Corvera
10  *
11  * This file is part of TFCWeb.
12  *
13  * TFCWeb is free software: you can redistribute it and/or modify
14  * it under the terms of the GNU General Public License as published by
15  * the Free Software Foundation, either version 3 of the License, or
16  * (at your option) any later version.
17  *
18  * TFCWeb is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with TFCWeb. If not, see <http://www.gnu.org/licenses/>.
25  */
26 
27 #include "Transformacions.h"
28 #include "utils.h"
29 
30 #include <algorithm>
31 #include <sstream>
32 #include <boost/regex.hpp>
33 
34 using namespace std;
35 using namespace boost;
36 
37 namespace {
38 
52 // ZLIB_CONST és una addició relativament recent, per evitar problemes
53 // de compatibilitat amb versions anteriors de moment es fa servir la
54 // forma clàssica
55 #if 1 || !defined(ZLIB_CONST)
56 inline Bytef* converteix_next_in(const string & s) {
57  return const_cast<Bytef*>(reinterpret_cast<const Bytef*>(s.c_str()));
58 }
59 #else
60 inline const Bytef* converteix_next_in(const string & s) {
61  return reinterpret_cast<const Bytef*>(s.c_str());
62 }
63 #endif
64 
65 } // ns anònim
66 
67 namespace tfc {
68 
69 bool FuncioExtractoraIstream::operator()(string & bloc) {
70  bloc.erase();
71  if (!ist_.good()) {
72  return false;
73  }
74  const streamsize bytes = ist_.readsome(&buffer_[0], MIDA_BUFFER);
75  bloc.assign(&buffer_[0], static_cast<string::size_type>(bytes));
76  return bytes > 0;
77 }
78 
79 bool FuncioEscriptoraOstream::operator()(const string & bloc) {
80  if (!ost_.good()) {
81  return false;
82  }
83  ost_.write(bloc.c_str(), bloc.length());
84  return !ost_.bad();
85 }
86 
87 string TransformacioChunked::transforma(const string & bloc) const {
88  ostringstream oss;
89  // chunk-size [ chunk-extension ] CRLF
90  // "The chunk-size field is a string of hex digits indicating the size of the chunk"
91  oss << std::hex << bloc.length() << "\r\n";
92  // chunk-data CRLF
93  oss.write(bloc.c_str(), bloc.length());
94  oss << "\r\n";
95  return oss.str();
96 }
97 
98 string TransformacioChunked::destransforma(const string & text_transformat,
99  EstatTransformacioInversa &estat) const {
100  // L'RFC 2616 presenta un possible algorisme en pseudocodi a la pàgina 169
101  istringstream iss(text_transformat);
102  string r;
103  r.reserve(text_transformat.length()); // La mida serà menor, però similar
104  iss.exceptions(ios::badbit|ios::failbit|ios::eofbit);
105  static const size_t MIDA_BUFFER = 4096;
106  std::array<char, MIDA_BUFFER> buffer;
107  estat = TRANSFORMAT;
108  try {
109  string params;
110  const regex re_parcial_mida("^([[:xdigit:]]+)", regbase::perl);
111  smatch m;
112  // Lectura d'un "chunk"
113  while (iss.good()) {
114  // Inici de bloc: mida [params] CRLF. p.e. f33;xyxCRLF
115  getline(iss, params, '\n');
116  if (!regex_search(params, m, re_parcial_mida)) {
117  estat = FORMAT_INCORRECTE;
118  break;
119  }
120  unsigned long mida_bloc;
121  mida_bloc = utils::from_string<unsigned long>(m[1], std::hex);
122  if (0 == mida_bloc) {
123  // Final
124  break;
125  }
126  // Bloc
127  size_t llegits=0;
128  // XXX: iss.good() no serveix per què l'EOF no s'activa (???) (G++ 4.4)
129  const streamsize fi = iss.str().size();
130  while (iss.tellg() < fi && llegits < mida_bloc) {
131  const size_t bufvalids = std::min<size_t>(MIDA_BUFFER, mida_bloc-llegits);
132  const size_t bytes = static_cast<size_t>(iss.readsome(&buffer[0], bufvalids));
133  r.append(&buffer[0], static_cast<streamsize>(bytes));
134  llegits += bytes;
135  }
136  if (llegits != mida_bloc) {
137  // El bloc no s'ha llegit correctament
138  estat = NO_TRANSFORMAT;
139  break;
140  }
141  // Final de bloc: CRLF
142  iss.seekg(2, ios::cur );
143  }
144  // Tots els blocs llegits o EOF
145  // Lectura de trailer
146  while (estat != FORMAT_INCORRECTE && false && iss.good()) {
147  // TODO: (pàg. 169:)
148  // read entity-header
149  // while (entity-header not empty) {
150  // append entity-header to existing header fields
151  // read entity-header
152  // }
153  }
154  }
155  catch (const ios_base::failure &) {
156  }
157  if (TRANSFORMAT == estat && (iss.fail() || iss.bad())) {
158  estat = NO_TRANSFORMAT;
159  }
160  return r;
161 }
162 
163 string TransformacioChunked::finalitza() const {
164  // Chunked-Body = *chunk
165  // last-chunk
166  // trailer
167  // CRLF
168  // last-chunk = 1*("0") [ chunk-extension ] CRLF
169  // trailer = *(entity-header CRLF)
170  return "0\r\n" // last-chunk
171  "\r\n" // trailer
172  "\r\n"; // CRLF
173 }
174 
175 AplicadorTransformacioBase::~AplicadorTransformacioBase() {
176  // Cal definir el destructor (virtual) en un mòdul (és a dir,
177  // fora de la capçalera) per que l'enllaçador el col·loqui
178  // correctament (altrament no enllaça)
179 }
180 
181 void AplicadorTransformacioBase::aplica() {
182  string buffer;
183  Transformacio * pTrans = transformacio();
184  while (extractora_(buffer)) {
185  const bool ok = escriptora_(pTrans->transforma(buffer));
186  buffer.erase();
187  if (!ok) {
188  break;
189  }
190  }
191  escriptora_(pTrans->finalitza());
192 }
193 
194 bool TransformacioDeflate::inicialitza(Mode mode) const {
195  if (CAP == mode) {
196  return false;
197  }
198  desinicialitza();
199  //memset(&strm_, 0x00, sizeof(z_stream));
200  strm_.zalloc = Z_NULL;
201  strm_.zfree = Z_NULL;
202  strm_.opaque = Z_NULL;
203  int ret;
204  if (DEFLATE == mode) {
205  // IMPORTANT:
206  // Internet Explorer no accepta el format per defecte
207  // Si es fa servir deflateInit es genera el format "zlib", és a dir,
208  // deflate amb capçaleres (RFC 1950+RFC 1951).
209  // En base a l'RFC d'HTTP (2616, pàg. 23), aquesta és la codificació "deflate",
210  // i els altres clients provats (Firefox, Chrome, wget) l'accepten com a tal,
211  // però IE dona error.
212  // Amb deflateInit2 es poden suprimir les capçaleres zlib, enviant nomes dades
213  // comprimides amb deflate. Aquest format és el que IE espera i la resta de
214  // navegadors l'accepten, però estrictament parlant no és el correcte, així
215  // que no és decartable que doni altres problemes en el futur...
216  // Discussió relacionada: http://stackoverflow.com/questions/1077869/internet-explorer-8-deflate
217  //ret = ::deflateInit(&strm_, Z_BEST_SPEED);
218  // http://www.zlib.net/manual.html#deflateInit2
219  ret = ::deflateInit2(&strm_, Z_BEST_SPEED,
220  Z_DEFLATED, // mètode (només existeix aquest per ara)
221  -15, // bits de finestra (log 2),
222  // valors negatius -8..-15 suprimeixen les capçaleres
223  // valors > 15 canvien al format gzip
224  8, // nivell de memòria reservada, 8 per defecte
225  Z_DEFAULT_STRATEGY); // estratègia
226  }
227  else {
228  // XXX: Què passa llavors amb l'inflate? Cal també utilitzar inflateInit2()?
229  ret = ::inflateInit(&strm_);
230  }
231  const bool ok = ( Z_OK == ret );
232  inicialitzat_ = (ok ? mode : CAP);
233  return ok;
234 }
235 
236 // La transformació GZIP / ZLIB podria utilitzar Boost.Iostreams,
237 // que a la pràctica presenta unes intefícies més generals de transformació
238 // http://www.boost.org/doc/libs/1_49_0/libs/iostreams/doc/classes/zlib.html
239 // http://www.boost.org/doc/libs/1_49_0/libs/iostreams/doc/classes/gzip.html
240 
241 string TransformacioDeflate::transforma(const string & bloc) const {
242  string t;
243  if (DEFLATE != inicialitzat_) {
244  inicialitza(DEFLATE);
245  }
246  //::compress2(dest, destLen, bloc.c_str(), bloc.length(), Z_BEST_SPEED);
247  strm_.avail_in = bloc.length();
248  strm_.next_in = converteix_next_in(bloc);
249  std::array<unsigned char,MIDA_BUFFER_ZLIB> buf;
250  do {
251  strm_.avail_out = MIDA_BUFFER_ZLIB;
252  strm_.next_out = buf.data();
253  const int ret = ::deflate(&strm_, Z_SYNC_FLUSH);
254  if (Z_STREAM_ERROR == ret) {
255  return string(); // FIXME: <--
256  }
257  const size_t bytes = MIDA_BUFFER_ZLIB - strm_.avail_out;
258  t.append(reinterpret_cast<char*>(buf.data()), bytes);
259  } while (0 == strm_.avail_out);
260  assert( 0 == strm_.avail_in );
261  return t;
262 }
263 
264 string TransformacioDeflate::finalitza() const {
265  desinicialitza();
266  return "";
267 }
268 
269 string TransformacioDeflate::destransforma(const string & text_transformat,
270  EstatTransformacioInversa & estat) const {
271  string d;
272  estat = TRANSFORMAT;
273  inicialitza(INFLATE);
274  strm_.avail_in = text_transformat.length();
275  strm_.next_in = converteix_next_in(text_transformat);
276  std::array<unsigned char,MIDA_BUFFER_ZLIB> buf;
277  do {
278  strm_.avail_out = MIDA_BUFFER_ZLIB;
279  strm_.next_out = buf.data();
280  const int ret = ::inflate(&strm_, Z_SYNC_FLUSH);
281  switch (ret) {
282  // TODO: Diferenciar entre format incorrecte i altres errors
283  case Z_STREAM_ERROR:
284  case Z_NEED_DICT:
285  case Z_DATA_ERROR:
286  case Z_MEM_ERROR:
287  desinicialitza();
288  estat = NO_TRANSFORMAT;
289  return string(); // FIXME: <--
290  default:
291  break;
292  }
293  const size_t bytes = MIDA_BUFFER_ZLIB - strm_.avail_out;
294  d.append(reinterpret_cast<char*>(buf.data()), bytes);
295  } while (0 == strm_.avail_out);
296  return d;
297 }
298 
299 void TransformacioDeflate::desinicialitza() const {
300  if (CAP == inicialitzat_) {
301  return;
302  }
303 
304  if (DEFLATE == inicialitzat_) {
305  ::deflateEnd(&strm_);
306  }
307  else { assert( INFLATE == inicialitzat_ );
308  ::inflateEnd(&strm_);
309  }
310  inicialitzat_ = CAP;
311  memset(&strm_, 0x00, sizeof(z_stream));
312 }
313 
314 string CadenaTransformacions::transforma(const string &bloc) const {
315  string r(bloc);
316  for (auto it=cadena_.begin(); it != cadena_.end(); ++it) {
317  r = (*it)->transforma(r);
318  }
319  return r;
320 }
321 
322 string CadenaTransformacions::finalitza() const {
323  string f;
324  for (auto it=cadena_.begin(); it != cadena_.end(); ++it) {
325  f.append( (*it)->finalitza() );
326  }
327  return f;
328 }
329 
330 string CadenaTransformacions::destransforma(const string & text,
331  EstatTransformacioInversa & estat) const {
332  estat = TRANSFORMAT;
333  string r(text);
334  for (auto rit=cadena_.rbegin(); estat == TRANSFORMAT && rit != cadena_.rend(); ++rit) {
335  r = (*rit)->destransforma(r, estat);
336  }
337  return r;
338 }
339 
340 } // ns tfc
341 
342 // vim:set ts=4 et ai: //