pion-net  4.0.9
HTTPReader.cpp
1 // ------------------------------------------------------------------
2 // pion-net: a C++ framework for building lightweight HTTP interfaces
3 // ------------------------------------------------------------------
4 // Copyright (C) 2007-2008 Atomic Labs, Inc. (http://www.atomiclabs.com)
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/net/HTTPReader.hpp>
13 #include <pion/net/HTTPRequest.hpp>
14 
15 
16 namespace pion { // begin namespace pion
17 namespace net { // begin namespace net (Pion Network Library)
18 
19 
20 // HTTPReader static members
21 
22 const boost::uint32_t HTTPReader::DEFAULT_READ_TIMEOUT = 10;
23 
24 
25 // HTTPReader member functions
26 
28 {
29  if (m_tcp_conn->getPipelined()) {
30  // there are pipelined messages available in the connection's read buffer
31  m_tcp_conn->setLifecycle(TCPConnection::LIFECYCLE_CLOSE); // default to close the connection
32  m_tcp_conn->loadReadPosition(m_read_ptr, m_read_end_ptr);
33  consumeBytes();
34  } else {
35  // no pipelined messages available in the read buffer -> read bytes from the socket
36  m_tcp_conn->setLifecycle(TCPConnection::LIFECYCLE_CLOSE); // default to close the connection
37  readBytesWithTimeout();
38  }
39 }
40 
41 void HTTPReader::consumeBytes(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  handleReadError(read_error);
53  return;
54  }
55 
56  PION_LOG_DEBUG(m_logger, "Read " << bytes_read << " bytes from HTTP "
57  << (isParsingRequest() ? "request" : "response"));
58 
59  // set pointers for new HTTP header data to be consumed
60  setReadBuffer(m_tcp_conn->getReadBuffer().data(), bytes_read);
61 
62  consumeBytes();
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(getMessage(), 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 (getMessage().checkKeepAlive()) {
89  if ( eof() ) {
90  // the connection should be kept alive, but does not have pipelined messages
91  m_tcp_conn->setLifecycle(TCPConnection::LIFECYCLE_KEEPALIVE);
92  } else {
93  // the connection has pipelined messages
94  m_tcp_conn->setLifecycle(TCPConnection::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->saveReadPosition(m_read_ptr, m_read_end_ptr);
100 
101  PION_LOG_DEBUG(m_logger, "HTTP pipelined "
102  << (isParsingRequest() ? "request (" : "response (")
103  << bytes_available() << " bytes available)");
104  }
105  } else {
106  m_tcp_conn->setLifecycle(TCPConnection::LIFECYCLE_CLOSE);
107  }
108 
109  // we have finished parsing the HTTP message
110  finishedReading(ec);
111 
112  } else if (result == false) {
113  // the message is invalid or an error occured
114  m_tcp_conn->setLifecycle(TCPConnection::LIFECYCLE_CLOSE); // make sure it will get closed
115  getMessage().setIsValid(false);
116  finishedReading(ec);
117  } else {
118  // not yet finished parsing the message -> read more data
119  readBytesWithTimeout();
120  }
121 }
122 
123 void HTTPReader::readBytesWithTimeout(void)
124 {
125  if (m_read_timeout > 0) {
126  m_timer_ptr.reset(new TCPTimer(m_tcp_conn));
127  m_timer_ptr->start(m_read_timeout);
128  } else if (m_timer_ptr) {
129  m_timer_ptr.reset();
130  }
131  readBytes();
132 }
133 
134 void HTTPReader::handleReadError(const boost::system::error_code& read_error)
135 {
136  // close the connection, forcing the client to establish a new one
137  m_tcp_conn->setLifecycle(TCPConnection::LIFECYCLE_CLOSE); // make sure it will get closed
138 
139  // check if this is just a message with unknown content length
140  if (! checkPrematureEOF(getMessage())) {
141  boost::system::error_code ec; // clear error code
142  finishedReading(ec);
143  return;
144  }
145 
146  // only log errors if the parsing has already begun
147  if (getTotalBytesRead() > 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 " << (isParsingRequest() ? "request" : "response")
152  << " parsing aborted (shutting down)");
153  } else {
154  PION_LOG_INFO(m_logger, "HTTP " << (isParsingRequest() ? "request" : "response")
155  << " parsing aborted (" << read_error.message() << ')');
156  }
157  }
158 
159  finishedReading(read_error);
160 }
161 
162 } // end namespace net
163 } // end namespace pion
164