pion-net
4.0.9
|
00001 // ------------------------------------------------------------------ 00002 // pion-net: a C++ framework for building lightweight HTTP interfaces 00003 // ------------------------------------------------------------------ 00004 // Copyright (C) 2007-2008 Atomic Labs, Inc. (http://www.atomiclabs.com) 00005 // 00006 // Distributed under the Boost Software License, Version 1.0. 00007 // See http://www.boost.org/LICENSE_1_0.txt 00008 // 00009 00010 #include <iostream> 00011 #include <algorithm> 00012 #include <boost/asio.hpp> 00013 #include <boost/regex.hpp> 00014 #include <boost/logic/tribool.hpp> 00015 #include <pion/net/HTTPMessage.hpp> 00016 #include <pion/net/HTTPRequest.hpp> 00017 #include <pion/net/HTTPParser.hpp> 00018 #include <pion/net/TCPConnection.hpp> 00019 00020 00021 namespace pion { // begin namespace pion 00022 namespace net { // begin namespace net (Pion Network Library) 00023 00024 // static members of HTTPMessage 00025 00026 const boost::regex HTTPMessage::REGEX_ICASE_CHUNKED(".*chunked.*", boost::regex::icase); 00027 00028 00029 // HTTPMessage member functions 00030 00031 std::size_t HTTPMessage::send(TCPConnection& tcp_conn, 00032 boost::system::error_code& ec, 00033 bool headers_only) 00034 { 00035 // initialize write buffers for send operation using HTTP headers 00036 WriteBuffers write_buffers; 00037 prepareBuffersForSend(write_buffers, tcp_conn.getKeepAlive(), false); 00038 00039 // append payload content to write buffers (if there is any) 00040 if (!headers_only && getContentLength() > 0 && getContent() != NULL) 00041 write_buffers.push_back(boost::asio::buffer(getContent(), getContentLength())); 00042 00043 // send the message and return the result 00044 return tcp_conn.write(write_buffers, ec); 00045 } 00046 00047 std::size_t HTTPMessage::receive(TCPConnection& tcp_conn, 00048 boost::system::error_code& ec, 00049 bool headers_only) 00050 { 00051 // assumption: this can only be either an HTTPRequest or an HTTPResponse 00052 const bool is_request = (dynamic_cast<HTTPRequest*>(this) != NULL); 00053 HTTPParser http_parser(is_request); 00054 http_parser.parseHeadersOnly(headers_only); 00055 std::size_t last_bytes_read = 0; 00056 00057 // make sure that we start out with an empty message 00058 clear(); 00059 00060 if (tcp_conn.getPipelined()) { 00061 // there are pipelined messages available in the connection's read buffer 00062 const char *read_ptr; 00063 const char *read_end_ptr; 00064 tcp_conn.loadReadPosition(read_ptr, read_end_ptr); 00065 last_bytes_read = (read_end_ptr - read_ptr); 00066 http_parser.setReadBuffer(read_ptr, last_bytes_read); 00067 } else { 00068 // read buffer is empty (not pipelined) -> read some bytes from the connection 00069 last_bytes_read = tcp_conn.read_some(ec); 00070 if (ec) return 0; 00071 PION_ASSERT(last_bytes_read > 0); 00072 http_parser.setReadBuffer(tcp_conn.getReadBuffer().data(), last_bytes_read); 00073 } 00074 00075 // incrementally read and parse bytes from the connection 00076 bool force_connection_closed = false; 00077 boost::tribool parse_result; 00078 while (true) { 00079 // parse bytes available in the read buffer 00080 parse_result = http_parser.parse(*this, ec); 00081 if (! boost::indeterminate(parse_result)) break; 00082 00083 // read more bytes from the connection 00084 last_bytes_read = tcp_conn.read_some(ec); 00085 if (ec || last_bytes_read == 0) { 00086 if (http_parser.checkPrematureEOF(*this)) { 00087 // premature EOF encountered 00088 if (! ec) 00089 ec = make_error_code(boost::system::errc::io_error); 00090 return http_parser.getTotalBytesRead(); 00091 } else { 00092 // EOF reached when content length unknown 00093 // assume it is the correct end of content 00094 // and everything is OK 00095 force_connection_closed = true; 00096 parse_result = true; 00097 ec.clear(); 00098 break; 00099 } 00100 break; 00101 } 00102 00103 // update the HTTP parser's read buffer 00104 http_parser.setReadBuffer(tcp_conn.getReadBuffer().data(), last_bytes_read); 00105 } 00106 00107 if (parse_result == false) { 00108 // an error occurred while parsing the message headers 00109 return http_parser.getTotalBytesRead(); 00110 } 00111 00112 // set the connection's lifecycle type 00113 if (!force_connection_closed && checkKeepAlive()) { 00114 if ( http_parser.eof() ) { 00115 // the connection should be kept alive, but does not have pipelined messages 00116 tcp_conn.setLifecycle(TCPConnection::LIFECYCLE_KEEPALIVE); 00117 } else { 00118 // the connection has pipelined messages 00119 tcp_conn.setLifecycle(TCPConnection::LIFECYCLE_PIPELINED); 00120 00121 // save the read position as a bookmark so that it can be retrieved 00122 // by a new HTTP parser, which will be created after the current 00123 // message has been handled 00124 const char *read_ptr; 00125 const char *read_end_ptr; 00126 http_parser.loadReadPosition(read_ptr, read_end_ptr); 00127 tcp_conn.saveReadPosition(read_ptr, read_end_ptr); 00128 } 00129 } else { 00130 // default to close the connection 00131 tcp_conn.setLifecycle(TCPConnection::LIFECYCLE_CLOSE); 00132 } 00133 00134 return (http_parser.getTotalBytesRead()); 00135 } 00136 00137 std::size_t HTTPMessage::write(std::ostream& out, 00138 boost::system::error_code& ec, bool headers_only) 00139 { 00140 // reset error_code 00141 ec.clear(); 00142 00143 // initialize write buffers for send operation using HTTP headers 00144 WriteBuffers write_buffers; 00145 prepareBuffersForSend(write_buffers, true, false); 00146 00147 // append payload content to write buffers (if there is any) 00148 if (!headers_only && getContentLength() > 0 && getContent() != NULL) 00149 write_buffers.push_back(boost::asio::buffer(getContent(), getContentLength())); 00150 00151 // write message to the output stream 00152 std::size_t bytes_out = 0; 00153 for (WriteBuffers::const_iterator i=write_buffers.begin(); i!=write_buffers.end(); ++i) { 00154 const char *ptr = boost::asio::buffer_cast<const char*>(*i); 00155 size_t len = boost::asio::buffer_size(*i); 00156 out.write(ptr, len); 00157 bytes_out += len; 00158 } 00159 00160 return bytes_out; 00161 } 00162 00163 std::size_t HTTPMessage::read(std::istream& in, 00164 boost::system::error_code& ec, bool headers_only) 00165 { 00166 // make sure that we start out with an empty message & clear error_code 00167 clear(); 00168 ec.clear(); 00169 00170 // assumption: this can only be either an HTTPRequest or an HTTPResponse 00171 const bool is_request = (dynamic_cast<HTTPRequest*>(this) != NULL); 00172 HTTPParser http_parser(is_request); 00173 http_parser.parseHeadersOnly(headers_only); 00174 00175 // parse data from file one byte at a time 00176 boost::tribool parse_result; 00177 char c; 00178 while (in) { 00179 in.read(&c, 1); 00180 if ( ! in ) { 00181 ec = make_error_code(boost::system::errc::io_error); 00182 break; 00183 } 00184 http_parser.setReadBuffer(&c, 1); 00185 parse_result = http_parser.parse(*this, ec); 00186 if (! boost::indeterminate(parse_result)) break; 00187 } 00188 00189 if (boost::indeterminate(parse_result)) { 00190 if (http_parser.checkPrematureEOF(*this)) { 00191 // premature EOF encountered 00192 if (! ec) 00193 ec = make_error_code(boost::system::errc::io_error); 00194 } else { 00195 // EOF reached when content length unknown 00196 // assume it is the correct end of content 00197 // and everything is OK 00198 parse_result = true; 00199 ec.clear(); 00200 } 00201 } 00202 00203 return (http_parser.getTotalBytesRead()); 00204 } 00205 00206 void HTTPMessage::concatenateChunks(void) 00207 { 00208 setContentLength(m_chunk_cache.size()); 00209 char *post_buffer = createContentBuffer(); 00210 if (m_chunk_cache.size() > 0) 00211 std::copy(m_chunk_cache.begin(), m_chunk_cache.end(), post_buffer); 00212 } 00213 00214 } // end namespace net 00215 } // end namespace pion