diff options
Diffstat (limited to 'websocketpp/http/impl/response.hpp')
-rw-r--r-- | websocketpp/http/impl/response.hpp | 266 |
1 files changed, 266 insertions, 0 deletions
diff --git a/websocketpp/http/impl/response.hpp b/websocketpp/http/impl/response.hpp new file mode 100644 index 00000000..4400cda5 --- /dev/null +++ b/websocketpp/http/impl/response.hpp @@ -0,0 +1,266 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef HTTP_PARSER_RESPONSE_IMPL_HPP +#define HTTP_PARSER_RESPONSE_IMPL_HPP + +#include <algorithm> +#include <istream> +#include <sstream> +#include <string> + +#include <websocketpp/http/parser.hpp> + +namespace websocketpp { +namespace http { +namespace parser { + +inline size_t response::consume(char const * buf, size_t len) { + if (m_state == DONE) {return 0;} + + if (m_state == BODY) { + return this->process_body(buf,len); + } + + // copy new header bytes into buffer + m_buf->append(buf,len); + + // Search for delimiter in buf. If found read until then. If not read all + std::string::iterator begin = m_buf->begin(); + std::string::iterator end = begin; + + + for (;;) { + // search for delimiter + end = std::search( + begin, + m_buf->end(), + header_delimiter, + header_delimiter + sizeof(header_delimiter) - 1 + ); + + m_header_bytes += (end-begin+sizeof(header_delimiter)); + + if (m_header_bytes > max_header_size) { + // exceeded max header size + throw exception("Maximum header size exceeded.", + status_code::request_header_fields_too_large); + } + + if (end == m_buf->end()) { + // we are out of bytes. Discard the processed bytes and copy the + // remaining unprecessed bytes to the beginning of the buffer + std::copy(begin,end,m_buf->begin()); + m_buf->resize(static_cast<std::string::size_type>(end-begin)); + + m_read += len; + m_header_bytes -= m_buf->size(); + + return len; + } + + //the range [begin,end) now represents a line to be processed. + + if (end-begin == 0) { + // we got a blank line + if (m_state == RESPONSE_LINE) { + throw exception("Incomplete Request",status_code::bad_request); + } + + // TODO: grab content-length + std::string length = get_header("Content-Length"); + + if (length.empty()) { + // no content length found, read indefinitely + m_read = 0; + } else { + std::istringstream ss(length); + + if ((ss >> m_read).fail()) { + throw exception("Unable to parse Content-Length header", + status_code::bad_request); + } + } + + m_state = BODY; + + // calc header bytes processed (starting bytes - bytes left) + size_t read = ( + len - static_cast<std::string::size_type>(m_buf->end() - end) + + sizeof(header_delimiter) - 1 + ); + + // if there were bytes left process them as body bytes + if (read < len) { + read += this->process_body(buf+read,(len-read)); + } + + // frees memory used temporarily during header parsing + m_buf.reset(); + + return read; + } else { + if (m_state == RESPONSE_LINE) { + this->process(begin,end); + m_state = HEADERS; + } else { + this->process_header(begin,end); + } + } + + begin = end+(sizeof(header_delimiter) - 1); + } +} + +inline size_t response::consume(std::istream & s) { + char buf[istream_buffer]; + size_t bytes_read; + size_t bytes_processed; + size_t total = 0; + + while (s.good()) { + s.getline(buf,istream_buffer); + bytes_read = static_cast<size_t>(s.gcount()); + + if (s.fail() || s.eof()) { + bytes_processed = this->consume(buf,bytes_read); + total += bytes_processed; + + if (bytes_processed != bytes_read) { + // problem + break; + } + } else if (s.bad()) { + // problem + break; + } else { + // the delimiting newline was found. Replace the trailing null with + // the newline that was discarded, since our raw consume function + // expects the newline to be be there. + buf[bytes_read-1] = '\n'; + bytes_processed = this->consume(buf,bytes_read); + total += bytes_processed; + + if (bytes_processed != bytes_read) { + // problem + break; + } + } + } + + return total; +} + +inline std::string response::raw() const { + // TODO: validation. Make sure all required fields have been set? + + std::stringstream ret; + + ret << get_version() << " " << m_status_code << " " << m_status_msg; + ret << "\r\n" << raw_headers() << "\r\n"; + + ret << m_body; + + return ret.str(); +} + +inline void response::set_status(status_code::value code) { + // TODO: validation? + m_status_code = code; + m_status_msg = get_string(code); +} + +inline void response::set_status(status_code::value code, std::string const & + msg) +{ + // TODO: validation? + m_status_code = code; + m_status_msg = msg; +} + +inline void response::process(std::string::iterator begin, + std::string::iterator end) +{ + std::string::iterator cursor_start = begin; + std::string::iterator cursor_end = std::find(begin,end,' '); + + if (cursor_end == end) { + throw exception("Invalid response line",status_code::bad_request); + } + + set_version(std::string(cursor_start,cursor_end)); + + cursor_start = cursor_end+1; + cursor_end = std::find(cursor_start,end,' '); + + if (cursor_end == end) { + throw exception("Invalid request line",status_code::bad_request); + } + + int code; + + std::istringstream ss(std::string(cursor_start,cursor_end)); + + if ((ss >> code).fail()) { + throw exception("Unable to parse response code",status_code::bad_request); + } + + set_status(status_code::value(code),std::string(cursor_end+1,end)); +} + +inline size_t response::process_body(char const * buf, size_t len) { + // If no content length was set then we read forever and never set m_ready + if (m_read == 0) { + //m_body.append(buf,len); + //return len; + m_state = DONE; + return 0; + } + + // Otherwise m_read is the number of bytes left. + size_t to_read; + + if (len >= m_read) { + // if we have more bytes than we need read, read only the amount needed + // then set done state + to_read = m_read; + m_state = DONE; + } else { + // we need more bytes than are available, read them all + to_read = len; + } + + m_body.append(buf,to_read); + m_read -= to_read; + return to_read; +} + +} // namespace parser +} // namespace http +} // namespace websocketpp + +#endif // HTTP_PARSER_RESPONSE_IMPL_HPP |