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