pion  5.0.6
spdy_parser.cpp
1 // ---------------------------------------------------------------------
2 // pion: a Boost C++ framework for building lightweight HTTP interfaces
3 // ---------------------------------------------------------------------
4 // Copyright (C) 2007-2014 Splunk Inc. (https://github.com/splunk/pion)
5 //
6 // Distributed under the Boost Software License, Version 1.0.
7 // See http://www.boost.org/LICENSE_1_0.txt
8 //
9 
10 #include <cstdlib>
11 #include <boost/regex.hpp>
12 #include <boost/logic/tribool.hpp>
13 #include <boost/asio/detail/socket_ops.hpp>
14 #include <pion/algorithm.hpp>
15 #include <pion/spdy/parser.hpp>
16 #include <pion/spdy/decompressor.hpp>
17 #include <pion/spdy/types.hpp>
18 
19 
20 namespace pion { // begin namespace pion
21 namespace spdy { // begin namespace spdy
22 
24 static char const* rst_stream_status(boost::uint32_t rst_stream_status_code)
25 {
26  switch (rst_stream_status_code)
27  {
28  case 1: return "PROTOCOL_ERROR";
29  case 2: return "INVALID_STREAM";
30  case 3: return "REFUSED_STREAM";
31  case 4: return "UNSUPPORTED_VERSION";
32  case 5: return "CANCEL";
33  case 6: return "INTERNAL_ERROR";
34  case 7: return "FLOW_CONTROL_ERROR";
35  case 8: return "STREAM_IN_USE";
36  case 9: return "STREAM_ALREADY_CLOSED";
37  case 10: return "INVALID_CREDENTIALS";
38  case 11: return "FRAME_TOO_LARGE";
39  case 12: return "INVALID";
40  default: return NULL;
41  }
42 }
43 
44 parser::error_category_t * parser::m_error_category_ptr = NULL;
45 boost::once_flag parser::m_instance_flag = BOOST_ONCE_INIT;
46 
47 // parser member functions
48 
50  : m_read_ptr(NULL),
51  m_uncompressed_ptr(NULL),
52  m_current_data_chunk_ptr(NULL),
53  m_last_data_chunk_ptr(NULL),
54  m_logger(PION_GET_LOGGER("pion.spdy.parser"))
55 {}
56 
57 boost::tribool parser::parse(http_protocol_info& http_info,
58  boost::system::error_code& ec,
59  decompressor_ptr& decompressor,
60  const char *packet_ptr,
61  boost::uint32_t& length_packet,
62  boost::uint32_t current_stream_count)
63 {
64  // initialize read position
65  set_read_ptr(packet_ptr);
66 
67  // Parse the frame
68  return parse_spdy_frame(ec, decompressor, http_info, length_packet, current_stream_count);
69 }
70 
71 bool parser::is_spdy_control_frame(const char *ptr)
72 {
73  // Parse further for higher accuracy
74 
75  // Get the control bit
76  boost::uint8_t control_bit;
77  boost::uint16_t version, type;
78  boost::uint16_t byte_value = algorithm::to_uint16(ptr);
79  control_bit = byte_value >> (sizeof(short) * CHAR_BIT - 1);
80 
81  if (!control_bit) return false;
82 
83  // Control bit is set; This is a control frame
84 
85  // Get the version number
86  boost::uint16_t two_bytes = algorithm::to_uint16(ptr);
87  version = two_bytes & 0x7FFF;
88 
89  if(version < 1 || version > 3){
90  // SPDY does not have a version higher than 3 and lower than 1 at the moment
91  return false;
92  }
93 
94  // Increment the read pointer
95  ptr += 2;
96 
97  type = algorithm::to_uint16(ptr);
98 
99  if (type >= SPDY_INVALID) {
100  // Not among the recognized SPDY types
101  return false;
102  }
103 
104  return true;
105 }
106 
107 spdy_frame_type parser::get_spdy_frame_type(const char *ptr)
108 {
109  // Determine if this a SPDY frame
110  BOOST_ASSERT(ptr);
111 
112  /*
113  * The first byte of a SPDY frame must be either 0 or
114  * 0x80. If it's not, assume that this is not SPDY.
115  * (In theory, a data frame could have a stream ID
116  * >= 2^24, in which case it won't have 0 for a first
117  * byte, but this is a pretty reliable heuristic for
118  * now.)
119  */
120 
121  spdy_frame_type spdy_frame;
122  boost::uint8_t first_byte = *((unsigned char *)ptr);
123  if(first_byte == 0x80){
124  spdy_frame = spdy_control_frame;
125  }else if(first_byte == 0x0){
126  spdy_frame = spdy_data_frame;
127  }else{
128  spdy_frame = spdy_invalid_frame;
129  }
130  return spdy_frame;
131 }
132 
133 boost::uint32_t parser::get_control_frame_stream_id(const char *ptr)
134 {
135  // The stream ID for control frames is at a 8 bit offser from start
136  ptr += 8;
137 
138  boost::uint32_t four_bytes = algorithm::to_uint32(ptr);
139  return four_bytes & 0x7FFFFFFF;
140 }
141 
142 boost::tribool parser::parse_spdy_frame(boost::system::error_code& ec,
143  decompressor_ptr& decompressor,
144  http_protocol_info& http_info,
145  boost::uint32_t& length_packet,
146  boost::uint32_t current_stream_count)
147 {
148  boost::tribool rc = true;
149 
150  // Verify that this is a spdy frame
151 
152  BOOST_ASSERT(m_read_ptr);
153  boost::uint8_t first_byte = (boost::uint8_t)*m_read_ptr;
154  if (first_byte != 0x80 && first_byte != 0x0) {
155  // This is not a SPDY frame, throw an error
156  PION_LOG_ERROR(m_logger, "Invalid SPDY Frame");
157  set_error(ec, ERROR_INVALID_SPDY_FRAME);
158  return false;
159  }
160 
161  boost::uint8_t control_bit;
163  boost::uint32_t stream_id = 0;
164 
165  ec.clear();
166 
167  // Populate the frame
168  bool populate_frame_result = populate_frame(ec, frame, length_packet, stream_id, http_info);
169 
170  if(!populate_frame_result){
172  return false;
173  }
174 
175  BOOST_ASSERT(stream_id != 0);
176 
177  control_bit = (boost::uint8_t)frame.control_bit;
178 
179  // There is a possibility that there are more than one SPDY frames in one TCP frame
180  if(length_packet > frame.length){
181  m_current_data_chunk_ptr = m_read_ptr + frame.length;
182  length_packet -= frame.length;
183  rc = boost::indeterminate;
184  }
185 
186  if (!control_bit) {
187  // Parse the data packet
188  parse_spdy_data(ec, frame, stream_id, http_info);
189  }
190 
191  /* Abort here if the version is too low. */
192 
193  if (frame.version > MIN_SPDY_VERSION) {
194  // Version less that min SPDY version, throw an error
195  PION_LOG_ERROR(m_logger, "Invalid SPDY Version Number");
196  set_error(ec, ERROR_INVALID_SPDY_VERSION);
197  return false;
198  }
199 
200  if(frame.type == SPDY_SYN_STREAM){
201  http_info.http_type = HTTP_REQUEST;
202  }else if (frame.type == SPDY_SYN_REPLY){
203  http_info.http_type = HTTP_RESPONSE;
204  }else if (frame.type == SPDY_DATA){
205  http_info.http_type = HTTP_DATA;
206  }
207 
208  switch (frame.type) {
209  case SPDY_SYN_STREAM:
210  case SPDY_SYN_REPLY:
211  case SPDY_HEADERS:
212  parse_header_payload(ec, decompressor, frame, http_info, current_stream_count);
213  break;
214 
215  case SPDY_RST_STREAM:
216  parse_spdy_rst_stream(ec, frame);
217  http_info.http_type = SPDY_CONTROL;
218  break;
219 
220  case SPDY_SETTINGS:
221  parse_spdy_settings_frame(ec, frame);
222  http_info.http_type = SPDY_CONTROL;
223  break;
224 
225  case SPDY_PING:
226  parse_spdy_ping_frame(ec, frame);
227  http_info.http_type = SPDY_CONTROL;
228  break;
229 
230  case SPDY_GOAWAY:
231  parse_spdy_goaway_frame(ec, frame);
232  http_info.http_type = SPDY_CONTROL;
233  break;
234 
235  case SPDY_WINDOW_UPDATE:
237  http_info.http_type = SPDY_CONTROL;
238  break;
239 
240  case SPDY_CREDENTIAL:
241  // We dont need to parse this for now
242  http_info.http_type = SPDY_CONTROL;
243  break;
244 
245  default:
246  break;
247  }
248 
249  if (ec)
250  return false;
251 
252  m_last_data_chunk_ptr = m_read_ptr;
253  m_read_ptr = m_current_data_chunk_ptr;
254 
255  return rc;
256 }
257 
259 {
260  static error_category_t UNIQUE_ERROR_CATEGORY;
261  m_error_category_ptr = &UNIQUE_ERROR_CATEGORY;
262 }
263 
264 bool parser::populate_frame(boost::system::error_code& ec,
266  boost::uint32_t& length_packet,
267  boost::uint32_t& stream_id,
268  http_protocol_info& http_info)
269 {
270  // Get the control bit
271  boost::uint8_t control_bit;
272  boost::uint16_t byte_value = algorithm::to_uint16(m_read_ptr);
273  control_bit = byte_value >> (sizeof(short) * CHAR_BIT - 1);
274 
275  frame.control_bit = (control_bit != 0);
276 
277  if(control_bit){
278 
279  // Control bit is set; This is a control frame
280 
281  // Get the version number
282  boost::uint16_t two_bytes = algorithm::to_uint16(m_read_ptr);
283  frame.version = two_bytes & 0x7FFF;
284 
285  // Increment the read pointer
286  m_read_ptr += 2;
287  length_packet -= 2;
288  http_info.data_offset +=2;
289 
290  // Get the type
291  frame.type = algorithm::to_uint16(m_read_ptr);
292 
293  if (frame.type >= SPDY_INVALID) {
294  // SPDY Frame is invalid
295 
296  // This is not a SPDY frame, throw an error
297  PION_LOG_ERROR(m_logger, "Invalid SPDY Frame");
298  set_error(ec, ERROR_INVALID_SPDY_FRAME);
299  return false;
300  }
301  }else {
302 
303  // Control bit is not set; This is a data frame
304 
305  frame.type = SPDY_DATA;
306  frame.version = 0; /* Version doesn't apply to DATA. */
307  // Get the stream id
308  boost::uint32_t four_bytes = algorithm::to_uint32(m_read_ptr);
309  stream_id = four_bytes & 0x7FFFFFFF;
310 
311  http_info.stream_id = stream_id;
312 
313  m_read_ptr +=2;
314  http_info.data_offset +=2;
315  length_packet -= 2;
316 
317  }
318 
319  // Increment the read pointer
320  m_read_ptr += 2;
321  length_packet -= 2;
322  http_info.data_offset +=2;
323 
324  // Get the flags
325  frame.flags = (boost::uint8_t)*m_read_ptr;
326 
327  // Increment the read pointer
328 
329  // Get the length
330  boost::uint32_t four_bytes = algorithm::to_uint32(m_read_ptr);
331  frame.length = four_bytes & 0xFFFFFF;
332 
333  // Increment the read pointer
334  m_read_ptr += 4;
335  length_packet -= 4;
336  http_info.data_offset +=4;
337 
338  http_info.data_size = frame.length;
339 
340  if(control_bit){
341  four_bytes = algorithm::to_uint32(m_read_ptr);
342  stream_id = four_bytes & 0x7FFFFFFF;
343  }
344 
345  return true;
346 }
347 
348 void parser::parse_header_payload(boost::system::error_code &ec,
349  decompressor_ptr& decompressor,
350  const spdy_control_frame_info& frame,
351  http_protocol_info& http_info,
352  boost::uint32_t current_stream_count)
353 {
354  boost::uint32_t stream_id = 0;
355  boost::uint32_t associated_stream_id;
356  boost::uint32_t header_block_length = frame.length;
357 
358  // Get the 31 bit stream id
359 
360  boost::uint32_t four_bytes = algorithm::to_uint32(m_read_ptr);
361  stream_id = four_bytes & 0x7FFFFFFF;
362 
363  m_read_ptr += 4;
364 
365  http_info.stream_id = stream_id;
366 
367  // Get SYN_STREAM-only fields.
368 
369  if (frame.type == SPDY_SYN_STREAM) {
370 
371  // Get associated stream ID.
372 
373  boost::uint32_t four_bytes = algorithm::to_uint32(m_read_ptr);
374  associated_stream_id = four_bytes & 0x7FFFFFFF;
375 
376  m_read_ptr += 4;
377 
378  // The next bits are priority, unused, and slot.
379  // Disregard these for now as we dont need them
380 
381  m_read_ptr +=2 ;
382 
383  } else if( frame.type == SPDY_SYN_REPLY || frame.type == SPDY_HEADERS ) {
384 
385  // Unused bits
386  m_read_ptr +=2 ;
387  }
388 
389  // Get our header block length.
390 
391  switch (frame.type) {
392  case SPDY_SYN_STREAM:
393  header_block_length -= 10;
394  break;
395  case SPDY_SYN_REPLY:
396  case SPDY_HEADERS:
397  // This is a very important distinction.
398  // It should be 6 bytes for SPDYv2 and 4 bytes for SPDYv3.
399  header_block_length -= 6;
400  break;
401  default:
402  // Unhandled case. This should never happen.
403  PION_LOG_ERROR(m_logger, "Invalid SPDY Frame Type");
404  set_error(ec, ERROR_INVALID_SPDY_FRAME);
405  return;
406  }
407 
408  // Decompress header block as necessary.
409  m_uncompressed_ptr = decompressor->decompress(m_read_ptr,
410  stream_id,
411  frame,
412  header_block_length);
413 
414  if (!m_uncompressed_ptr) {
415  set_error(ec, ERROR_DECOMPRESSION);
416  return;
417  }
418 
419  // Now parse the name/value pairs
420 
421  // The number of name/value pairs is 16 bit SPDYv2
422  // and it is 32 bit in SPDYv3
423 
424  // TBD : Add support for SPDYv3
425  boost::uint16_t num_name_val_pairs = algorithm::to_uint16(m_uncompressed_ptr);
426 
427  m_uncompressed_ptr += 2;
428 
429  std::string content_type = "";
430  std::string content_encoding = "";
431 
432  for(boost::uint16_t count = 0; count < num_name_val_pairs; ++count){
433 
434 
435  // Get the length of the name
436  boost::uint16_t length_name = algorithm::to_uint16(m_uncompressed_ptr);
437  std::string name = "";
438 
439  m_uncompressed_ptr += 2;
440 
441  {
442  for(boost::uint16_t count = 0; count < length_name; ++count){
443  name.push_back(*(m_uncompressed_ptr+count));
444  }
445  m_uncompressed_ptr += length_name;
446  }
447 
448  // Get the length of the value
449  boost::uint16_t length_value = algorithm::to_uint16(m_uncompressed_ptr);
450  std::string value = "";
451 
452  m_uncompressed_ptr += 2;
453 
454  {
455  for(boost::uint16_t count = 0; count < length_value; ++count){
456  value.push_back(*(m_uncompressed_ptr+count));
457  }
458  m_uncompressed_ptr += length_value;
459  }
460 
461  // Save these headers
462  http_info.http_headers.insert(std::make_pair(name, value));
463  }
464 }
465 
466 void parser::parse_spdy_data(boost::system::error_code &ec,
467  const spdy_control_frame_info& frame,
468  boost::uint32_t stream_id,
469  http_protocol_info& http_info)
470 {
471  // This marks the finish flag
472  if (frame.flags & SPDY_FLAG_FIN){
473  http_info.last_chunk = true;
474  }
475 }
476 
477 void parser::parse_spdy_rst_stream(boost::system::error_code &ec,
478  const spdy_control_frame_info& frame)
479 {
480  boost::uint32_t stream_id = 0;
481  boost::uint32_t status_code = 0;
482 
483  // First complete the check for size and flag
484  // The flag for RST frame should be 0, The length should be 8
485  if(frame.flags != 0 || frame.length != 8 ){
486  return;
487  }
488 
489  // Get the 31 bit stream id
490 
491  boost::uint32_t four_bytes = algorithm::to_uint32(m_read_ptr);
492  stream_id = four_bytes & 0x7FFFFFFF;
493 
494  m_read_ptr += 4;
495 
496  // Get the status code
497 
498  status_code = algorithm::to_uint32(m_read_ptr);
499 
500  char const* const status_code_str = rst_stream_status(status_code);
501  if(status_code_str){
502  PION_LOG_INFO(m_logger, "SPDY Status Code is : " << status_code_str);
503  }else{
504  PION_LOG_INFO(m_logger, "SPDY RST Invalid status code : " << status_code);
505  }
506 }
507 
508 void parser::parse_spdy_ping_frame(boost::system::error_code &ec,
509  const spdy_control_frame_info& frame)
510 {
511  // First complete the check for size
512  // The length should be 4 always
513  if(frame.length != 4){
514  return;
515  }
516 
517  boost::uint32_t ping_id = 0;
518 
519  // Get the 32 bit ping id
520 
521  ping_id = algorithm::to_uint32(m_read_ptr);
522 
523  m_read_ptr += 4;
524 
525  PION_LOG_INFO(m_logger, "SPDY " << "Ping ID is : " << ping_id);
526 }
527 
528 void parser::parse_spdy_settings_frame(boost::system::error_code &ec,
529  const spdy_control_frame_info& frame)
530 {
531  // Can ignore this frame for our purposes
532 }
533 
534 void parser::parse_spdy_goaway_frame(boost::system::error_code &ec,
535  const spdy_control_frame_info& frame)
536 {
537  // First complete the check for size
538  // The length should be 4 always
539  if(frame.length != 4){
540  return;
541  }
542 
543  boost::uint32_t last_good_stream_id = 0;
544  boost::uint32_t status_code = 0;
545 
546  // Get the 31 bit stream id
547 
548  boost::uint32_t four_bytes = algorithm::to_uint32(m_read_ptr);
549  last_good_stream_id = four_bytes & 0x7FFFFFFF;
550 
551  m_read_ptr += 4;
552 
553  // Get the status code
554 
555  status_code = algorithm::to_uint32(m_read_ptr);
556 
557  // Chek if there was an error
558  if(status_code == 1){
559 
560  PION_LOG_ERROR(m_logger, "There was a Protocol Error");
561  set_error(ec, ERROR_PROTOCOL_ERROR);
562  return;
563  }else if (status_code == 11) {
564 
565  PION_LOG_ERROR(m_logger, "There was an Internal Error");
566  set_error(ec, ERROR_INTERNAL_SPDY_ERROR);
567  return;
568  }
569 
570  PION_LOG_INFO(m_logger, "SPDY " << "Status Code is : " << status_code);
571 
572 }
573 
574 void parser::parse_spdy_window_update_frame(boost::system::error_code &ec,
575  const spdy_control_frame_info& frame)
576 {
577  // TBD : Do we really need this for our purpose
578 }
579 
580 } // end namespace spdy
581 } // end namespace pion
static void create_error_category(void)
creates the unique parser error_category_t
boost::tribool parse(http_protocol_info &http_headers, boost::system::error_code &ec, decompressor_ptr &decompressor, const char *packet_ptr, boost::uint32_t &length_packet, boost::uint32_t current_stream_count)
Definition: spdy_parser.cpp:57
void set_read_ptr(const char *ptr)
resets the read pointer
Definition: parser.hpp:124
boost::tribool parse_spdy_frame(boost::system::error_code &ec, decompressor_ptr &decompressor, http_protocol_info &http_headers, boost::uint32_t &length_packet, boost::uint32_t current_stream_count)
void parse_spdy_data(boost::system::error_code &ec, const spdy_control_frame_info &frame, boost::uint32_t stream_id, http_protocol_info &http_info)
This structure contains the HTTP Protocol information.
Definition: types.hpp:69
static boost::uint16_t to_uint16(unsigned char high, unsigned char low)
convert sequence of two bytes to 16-bit unsigned integer
Definition: algorithm.hpp:78
void parse_spdy_settings_frame(boost::system::error_code &ec, const spdy_control_frame_info &frame)
void parse_spdy_rst_stream(boost::system::error_code &ec, const spdy_control_frame_info &frame)
class-specific error category
Definition: parser.hpp:51
static boost::uint32_t get_control_frame_stream_id(const char *ptr)
parser()
constructs a new parser object (default constructor)
Definition: spdy_parser.cpp:49
bool populate_frame(boost::system::error_code &ec, spdy_control_frame_info &frame, boost::uint32_t &length_packet, boost::uint32_t &stream_id, http_protocol_info &http_headers)
static bool is_spdy_control_frame(const char *ptr)
Definition: spdy_parser.cpp:71
void parse_spdy_goaway_frame(boost::system::error_code &ec, const spdy_control_frame_info &frame)
void parse_spdy_window_update_frame(boost::system::error_code &ec, const spdy_control_frame_info &frame)
void parse_spdy_ping_frame(boost::system::error_code &ec, const spdy_control_frame_info &frame)
static spdy_frame_type get_spdy_frame_type(const char *ptr)
static void set_error(boost::system::error_code &ec, error_value_t ev)
Definition: parser.hpp:149
This structure will be tied to each SPDY frame.
Definition: types.hpp:48
static boost::uint32_t to_uint32(unsigned char high, unsigned char mid1, unsigned char mid2, unsigned char low)
convert sequence of four bytes to 32-bit unsigned integer
Definition: algorithm.hpp:98
void parse_header_payload(boost::system::error_code &ec, decompressor_ptr &decompressor, const spdy_control_frame_info &frame, http_protocol_info &http_headers, boost::uint32_t current_stream_count)