pion  5.0.6
http_reader.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 <boost/asio.hpp>
11 #include <boost/logic/tribool.hpp>
12 #include <pion/http/reader.hpp>
13 #include <pion/http/request.hpp>
14 
15 
16 namespace pion { // begin namespace pion
17 namespace http { // begin namespace http
18 
19 
20 // reader static members
21 
22 const boost::uint32_t reader::DEFAULT_READ_TIMEOUT = 10;
23 
24 
25 // reader member functions
26 
27 void reader::receive(void)
28 {
29  if (m_tcp_conn->get_pipelined()) {
30  // there are pipelined messages available in the connection's read buffer
31  m_tcp_conn->set_lifecycle(tcp::connection::LIFECYCLE_CLOSE); // default to close the connection
32  m_tcp_conn->load_read_pos(m_read_ptr, m_read_end_ptr);
33  consume_bytes();
34  } else {
35  // no pipelined messages available in the read buffer -> read bytes from the socket
36  m_tcp_conn->set_lifecycle(tcp::connection::LIFECYCLE_CLOSE); // default to close the connection
37  read_bytes_with_timeout();
38  }
39 }
40 
41 void reader::consume_bytes(const boost::system::error_code& read_error,
42  std::size_t bytes_read)
43 {
44  // cancel read timer if operation didn't time-out
45  if (m_timer_ptr) {
46  m_timer_ptr->cancel();
47  m_timer_ptr.reset();
48  }
49 
50  if (read_error) {
51  // a read error occured
52  handle_read_error(read_error);
53  return;
54  }
55 
56  PION_LOG_DEBUG(m_logger, "Read " << bytes_read << " bytes from HTTP "
57  << (is_parsing_request() ? "request" : "response"));
58 
59  // set pointers for new HTTP header data to be consumed
60  set_read_buffer(m_tcp_conn->get_read_buffer().data(), bytes_read);
61 
62  consume_bytes();
63 }
64 
65 
67 {
68  // parse the bytes read from the last operation
69  //
70  // note that boost::tribool may have one of THREE states:
71  //
72  // false: encountered an error while parsing message
73  // true: finished successfully parsing the message
74  // indeterminate: parsed bytes, but the message is not yet finished
75  //
76  boost::system::error_code ec;
77  boost::tribool result = parse(get_message(), ec);
78 
79  if (gcount() > 0) {
80  // parsed > 0 bytes in HTTP headers
81  PION_LOG_DEBUG(m_logger, "Parsed " << gcount() << " HTTP bytes");
82  }
83 
84  if (result == true) {
85  // finished reading HTTP message and it is valid
86 
87  // set the connection's lifecycle type
88  if (get_message().check_keep_alive()) {
89  if ( eof() ) {
90  // the connection should be kept alive, but does not have pipelined messages
91  m_tcp_conn->set_lifecycle(tcp::connection::LIFECYCLE_KEEPALIVE);
92  } else {
93  // the connection has pipelined messages
94  m_tcp_conn->set_lifecycle(tcp::connection::LIFECYCLE_PIPELINED);
95 
96  // save the read position as a bookmark so that it can be retrieved
97  // by a new HTTP parser, which will be created after the current
98  // message has been handled
99  m_tcp_conn->save_read_pos(m_read_ptr, m_read_end_ptr);
100 
101  PION_LOG_DEBUG(m_logger, "HTTP pipelined "
102  << (is_parsing_request() ? "request (" : "response (")
103  << bytes_available() << " bytes available)");
104  }
105  } else {
106  m_tcp_conn->set_lifecycle(tcp::connection::LIFECYCLE_CLOSE);
107  }
108 
109  // we have finished parsing the HTTP message
110  finished_reading(ec);
111 
112  } else if (result == false) {
113  // the message is invalid or an error occured
114  m_tcp_conn->set_lifecycle(tcp::connection::LIFECYCLE_CLOSE); // make sure it will get closed
115  get_message().set_is_valid(false);
116  finished_reading(ec);
117  } else {
118  // not yet finished parsing the message -> read more data
119  read_bytes_with_timeout();
120  }
121 }
122 
123 void reader::read_bytes_with_timeout(void)
124 {
125  if (m_read_timeout > 0) {
126  m_timer_ptr.reset(new tcp::timer(m_tcp_conn));
127  m_timer_ptr->start(m_read_timeout);
128  } else if (m_timer_ptr) {
129  m_timer_ptr.reset();
130  }
131  read_bytes();
132 }
133 
134 void reader::handle_read_error(const boost::system::error_code& read_error)
135 {
136  // close the connection, forcing the client to establish a new one
137  m_tcp_conn->set_lifecycle(tcp::connection::LIFECYCLE_CLOSE); // make sure it will get closed
138 
139  // check if this is just a message with unknown content length
140  if (! check_premature_eof(get_message())) {
141  boost::system::error_code ec; // clear error code
142  finished_reading(ec);
143  return;
144  }
145 
146  // only log errors if the parsing has already begun
147  if (get_total_bytes_read() > 0) {
148  if (read_error == boost::asio::error::operation_aborted) {
149  // if the operation was aborted, the acceptor was stopped,
150  // which means another thread is shutting-down the server
151  PION_LOG_INFO(m_logger, "HTTP " << (is_parsing_request() ? "request" : "response")
152  << " parsing aborted (shutting down)");
153  } else {
154  PION_LOG_INFO(m_logger, "HTTP " << (is_parsing_request() ? "request" : "response")
155  << " parsing aborted (" << read_error.message() << ')');
156  }
157  }
158 
159  finished_reading(read_error);
160 }
161 
162 } // end namespace http
163 } // end namespace pion
bool is_parsing_request(void) const
returns true if the parser is being used to parse an HTTP request
Definition: parser.hpp:276
void receive(void)
Incrementally reads & parses the HTTP message.
Definition: http_reader.cpp:27
void consume_bytes(void)
Consumes bytes that have been read using an HTTP parser.
Definition: http_reader.cpp:66
const char * m_read_end_ptr
points to the end of the read_buffer (last byte + 1)
Definition: parser.hpp:574
std::size_t gcount(void) const
returns the number of bytes read during the last parse operation
Definition: parser.hpp:255
logger m_logger
primary logging interface used by this class
Definition: parser.hpp:565
void set_is_valid(bool b=true)
sets whether or not the message is valid
Definition: message.hpp:281
virtual http::message & get_message(void)=0
Returns a reference to the HTTP message being parsed.
bool eof(void) const
returns true if there are no more bytes available in the read buffer
Definition: parser.hpp:249
virtual void finished_reading(const boost::system::error_code &ec)=0
Called after we have finished reading/parsing the HTTP message.
virtual void read_bytes(void)=0
Reads more bytes from the TCP connection.
boost::tribool parse(http::message &http_msg, boost::system::error_code &ec)
Definition: http_parser.cpp:46
const char * m_read_ptr
points to the next character to be consumed in the read_buffer
Definition: parser.hpp:571
std::size_t get_total_bytes_read(void) const
returns the total number of bytes read while parsing the HTTP message
Definition: parser.hpp:258
bool check_premature_eof(http::message &http_msg)
Definition: parser.hpp:208
std::size_t bytes_available(void) const
returns the number of bytes available in the read buffer
Definition: parser.hpp:252
void set_read_buffer(const char *ptr, size_t len)
Definition: parser.hpp:184