pion-net  4.0.9
HTTPMessage.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 <iostream>
11 #include <algorithm>
12 #include <boost/asio.hpp>
13 #include <boost/regex.hpp>
14 #include <boost/logic/tribool.hpp>
15 #include <pion/net/HTTPMessage.hpp>
16 #include <pion/net/HTTPRequest.hpp>
17 #include <pion/net/HTTPParser.hpp>
18 #include <pion/net/TCPConnection.hpp>
19 
20 
21 namespace pion { // begin namespace pion
22 namespace net { // begin namespace net (Pion Network Library)
23 
24 // static members of HTTPMessage
25 
26 const boost::regex HTTPMessage::REGEX_ICASE_CHUNKED(".*chunked.*", boost::regex::icase);
27 
28 
29 // HTTPMessage member functions
30 
31 std::size_t HTTPMessage::send(TCPConnection& tcp_conn,
32  boost::system::error_code& ec,
33  bool headers_only)
34 {
35  // initialize write buffers for send operation using HTTP headers
36  WriteBuffers write_buffers;
37  prepareBuffersForSend(write_buffers, tcp_conn.getKeepAlive(), false);
38 
39  // append payload content to write buffers (if there is any)
40  if (!headers_only && getContentLength() > 0 && getContent() != NULL)
41  write_buffers.push_back(boost::asio::buffer(getContent(), getContentLength()));
42 
43  // send the message and return the result
44  return tcp_conn.write(write_buffers, ec);
45 }
46 
47 std::size_t HTTPMessage::receive(TCPConnection& tcp_conn,
48  boost::system::error_code& ec,
49  bool headers_only)
50 {
51  // assumption: this can only be either an HTTPRequest or an HTTPResponse
52  const bool is_request = (dynamic_cast<HTTPRequest*>(this) != NULL);
53  HTTPParser http_parser(is_request);
54  http_parser.parseHeadersOnly(headers_only);
55  std::size_t last_bytes_read = 0;
56 
57  // make sure that we start out with an empty message
58  clear();
59 
60  if (tcp_conn.getPipelined()) {
61  // there are pipelined messages available in the connection's read buffer
62  const char *read_ptr;
63  const char *read_end_ptr;
64  tcp_conn.loadReadPosition(read_ptr, read_end_ptr);
65  last_bytes_read = (read_end_ptr - read_ptr);
66  http_parser.setReadBuffer(read_ptr, last_bytes_read);
67  } else {
68  // read buffer is empty (not pipelined) -> read some bytes from the connection
69  last_bytes_read = tcp_conn.read_some(ec);
70  if (ec) return 0;
71  PION_ASSERT(last_bytes_read > 0);
72  http_parser.setReadBuffer(tcp_conn.getReadBuffer().data(), last_bytes_read);
73  }
74 
75  // incrementally read and parse bytes from the connection
76  bool force_connection_closed = false;
77  boost::tribool parse_result;
78  while (true) {
79  // parse bytes available in the read buffer
80  parse_result = http_parser.parse(*this, ec);
81  if (! boost::indeterminate(parse_result)) break;
82 
83  // read more bytes from the connection
84  last_bytes_read = tcp_conn.read_some(ec);
85  if (ec || last_bytes_read == 0) {
86  if (http_parser.checkPrematureEOF(*this)) {
87  // premature EOF encountered
88  if (! ec)
89  ec = make_error_code(boost::system::errc::io_error);
90  return http_parser.getTotalBytesRead();
91  } else {
92  // EOF reached when content length unknown
93  // assume it is the correct end of content
94  // and everything is OK
95  force_connection_closed = true;
96  parse_result = true;
97  ec.clear();
98  break;
99  }
100  break;
101  }
102 
103  // update the HTTP parser's read buffer
104  http_parser.setReadBuffer(tcp_conn.getReadBuffer().data(), last_bytes_read);
105  }
106 
107  if (parse_result == false) {
108  // an error occurred while parsing the message headers
109  return http_parser.getTotalBytesRead();
110  }
111 
112  // set the connection's lifecycle type
113  if (!force_connection_closed && checkKeepAlive()) {
114  if ( http_parser.eof() ) {
115  // the connection should be kept alive, but does not have pipelined messages
116  tcp_conn.setLifecycle(TCPConnection::LIFECYCLE_KEEPALIVE);
117  } else {
118  // the connection has pipelined messages
119  tcp_conn.setLifecycle(TCPConnection::LIFECYCLE_PIPELINED);
120 
121  // save the read position as a bookmark so that it can be retrieved
122  // by a new HTTP parser, which will be created after the current
123  // message has been handled
124  const char *read_ptr;
125  const char *read_end_ptr;
126  http_parser.loadReadPosition(read_ptr, read_end_ptr);
127  tcp_conn.saveReadPosition(read_ptr, read_end_ptr);
128  }
129  } else {
130  // default to close the connection
131  tcp_conn.setLifecycle(TCPConnection::LIFECYCLE_CLOSE);
132  }
133 
134  return (http_parser.getTotalBytesRead());
135 }
136 
137 std::size_t HTTPMessage::write(std::ostream& out,
138  boost::system::error_code& ec, bool headers_only)
139 {
140  // reset error_code
141  ec.clear();
142 
143  // initialize write buffers for send operation using HTTP headers
144  WriteBuffers write_buffers;
145  prepareBuffersForSend(write_buffers, true, false);
146 
147  // append payload content to write buffers (if there is any)
148  if (!headers_only && getContentLength() > 0 && getContent() != NULL)
149  write_buffers.push_back(boost::asio::buffer(getContent(), getContentLength()));
150 
151  // write message to the output stream
152  std::size_t bytes_out = 0;
153  for (WriteBuffers::const_iterator i=write_buffers.begin(); i!=write_buffers.end(); ++i) {
154  const char *ptr = boost::asio::buffer_cast<const char*>(*i);
155  size_t len = boost::asio::buffer_size(*i);
156  out.write(ptr, len);
157  bytes_out += len;
158  }
159 
160  return bytes_out;
161 }
162 
163 std::size_t HTTPMessage::read(std::istream& in,
164  boost::system::error_code& ec, bool headers_only)
165 {
166  // make sure that we start out with an empty message & clear error_code
167  clear();
168  ec.clear();
169 
170  // assumption: this can only be either an HTTPRequest or an HTTPResponse
171  const bool is_request = (dynamic_cast<HTTPRequest*>(this) != NULL);
172  HTTPParser http_parser(is_request);
173  http_parser.parseHeadersOnly(headers_only);
174 
175  // parse data from file one byte at a time
176  boost::tribool parse_result;
177  char c;
178  while (in) {
179  in.read(&c, 1);
180  if ( ! in ) {
181  ec = make_error_code(boost::system::errc::io_error);
182  break;
183  }
184  http_parser.setReadBuffer(&c, 1);
185  parse_result = http_parser.parse(*this, ec);
186  if (! boost::indeterminate(parse_result)) break;
187  }
188 
189  if (boost::indeterminate(parse_result)) {
190  if (http_parser.checkPrematureEOF(*this)) {
191  // premature EOF encountered
192  if (! ec)
193  ec = make_error_code(boost::system::errc::io_error);
194  } else {
195  // EOF reached when content length unknown
196  // assume it is the correct end of content
197  // and everything is OK
198  parse_result = true;
199  ec.clear();
200  }
201  }
202 
203  return (http_parser.getTotalBytesRead());
204 }
205 
207 {
208  setContentLength(m_chunk_cache.size());
209  char *post_buffer = createContentBuffer();
210  if (m_chunk_cache.size() > 0)
211  std::copy(m_chunk_cache.begin(), m_chunk_cache.end(), post_buffer);
212 }
213 
214 } // end namespace net
215 } // end namespace pion
std::size_t getTotalBytesRead(void) const
returns the total number of bytes read while parsing the HTTP message
Definition: HTTPParser.hpp:249
boost::tribool parse(HTTPMessage &http_msg, boost::system::error_code &ec)
Definition: HTTPParser.cpp:42
bool checkKeepAlive(void) const
returns true if the HTTP connection may be kept alive
bool getKeepAlive(void) const
returns true if the connection should be kept alive
void loadReadPosition(const char *&read_ptr, const char *&read_end_ptr) const
Definition: HTTPParser.hpp:186
virtual void clear(void)
clears all message data
bool getPipelined(void) const
returns true if the HTTP requests are pipelined
void setLifecycle(LifecycleType t)
sets the lifecycle type for the connection
void concatenateChunks(void)
std::size_t send(TCPConnection &tcp_conn, boost::system::error_code &ec, bool headers_only=false)
Definition: HTTPMessage.cpp:31
std::size_t read(std::istream &in, boost::system::error_code &ec, bool headers_only=false)
void saveReadPosition(const char *read_ptr, const char *read_end_ptr)
std::vector< boost::asio::const_buffer > WriteBuffers
data type for I/O write buffers (these wrap existing data to be sent)
Definition: HTTPMessage.hpp:43
std::size_t read_some(boost::system::error_code &ec)
std::size_t getContentLength(void) const
returns the length of the payload content (in bytes)
std::size_t receive(TCPConnection &tcp_conn, boost::system::error_code &ec, bool headers_only=false)
Definition: HTTPMessage.cpp:47
char * createContentBuffer(void)
void setContentLength(const std::size_t n)
sets the length of the payload content (in bytes)
std::size_t write(std::ostream &out, boost::system::error_code &ec, bool headers_only=false)
the following enables use of the lock-free cache
void loadReadPosition(const char *&read_ptr, const char *&read_end_ptr) const
std::size_t write(const ConstBufferSequence &buffers, boost::system::error_code &ec)
bool checkPrematureEOF(HTTPMessage &http_msg)
Definition: HTTPParser.hpp:199
void setReadBuffer(const char *ptr, size_t len)
Definition: HTTPParser.hpp:175
bool eof(void) const
returns true if there are no more bytes available in the read buffer
Definition: HTTPParser.hpp:240
void parseHeadersOnly(bool b=true)
Definition: HTTPParser.hpp:213
void prepareBuffersForSend(WriteBuffers &write_buffers, const bool keep_alive, const bool using_chunks)
ReadBuffer & getReadBuffer(void)
returns the buffer used for reading data from the TCP connection
char * getContent(void)
returns a pointer to the payload content, or NULL if there is none