#include "http_connection.h" #include #include #include #include #include #include #include #include namespace response_map_init { typedef std::pair P; const P m[] = { P(200, "OK"), P(206, "Partial Content"), P(400, "Bad Request"), P(403, "Forbidden"), P(404, "Not Found"), P(416, "Requested Range Not Satisfiable"), P(500, "Internal Server Error"), P(501, "Not Implemented") }; const P* begin = m; const P* end = m + sizeof(m) / sizeof(P); } const std::map response_map(response_map_init::begin, response_map_init::end); void HTTP::Connection::send_error(int code) { boost::asio::write(socket, boost::asio::buffer(boost::str(boost::format("HTTP/1.1 %1$d %2$s\r\n\r\n

%1$d %2$s

") % code % response_map.find(code)->second))); } void HTTP::Connection::add_header(std::string key, std::string value) { response_headers.push_back(std::make_pair(key, value)); } void HTTP::Connection::send_data(const std::string& data) { write_headers(); boost::asio::write(socket, boost::asio::buffer(data)); } void HTTP::Connection::send_data(const void* data, std::size_t size) { write_headers(); boost::asio::write(socket, boost::asio::buffer(data, size)); } void HTTP::Connection::send_data(std::istream& stream) { char data[0x1000]; std::streamsize size = 1; while(size) { stream.read(data, 0x1000); size = stream.gcount(); if(size > 0) send_data(data, size); } } void HTTP::Connection::send_file(const fs::path& filename) { //req->add_header("Content-Type", "application/octet-stream"); fs::ifstream is(filename, std::ios::in | std::ios::binary); is.seekg(0, std::ios::end); std::size_t length = is.tellg(); is.seekg(0, std::ios::beg); // Simple Range-parser. // TODO: Support other formats than a single range. if(headers.count("Range")) { std::string range_str = headers["Range"]; if(!boost::starts_with(range_str, "bytes=")) { // Broken header? We can't cope with that. return; } range_str = range_str.substr(6); std::string::iterator it = std::find(range_str.begin(), range_str.end(), '-'); std::size_t begin = boost::lexical_cast(std::string(range_str.begin(), it)); std::size_t end = length; if(++it != range_str.end()) { end = boost::lexical_cast(std::string(it, range_str.end())); } // Is the range past the end of the file? if(end > length) { end = length; } // Do the file contain requested range? if((end && end < begin) || begin > length) { send_error(416); } add_header("Content-Range", boost::str(boost::format("bytes %d-%d/%d") % begin % (end - 1) % length)); add_header("Content-Length", boost::str(boost::format("%d") % (end - begin))); write_headers(206); is.seekg(begin); boost::scoped_array buf(new char[end-begin]); is.read(buf.get(), end - begin); send_data(buf.get(), is.gcount()); } else { add_header("Content-Length", boost::str(boost::format("%d") % length)); send_data(is); } } HTTP::Connection::Connection(boost::asio::io_service& io_service) : socket(io_service) { headers_written = false; } void HTTP::Connection::handle_write(const boost::system::error_code& error, size_t bytes_transferred) { } void HTTP::Connection::handle_read(const boost::system::error_code& error, size_t bytes_transferred, Handler callback) { try { if(!parse_request(buf)) { // Request parse error. std::cout << "Request parse error." << std::endl; send_error(400); return; } std::cout << "Path: " << std::endl; for(PathList::iterator it = path.begin(); it != path.end(); it++) { std::cout << " " << *it << std::endl; } std::cout << "Args: " << std::endl; for(std::map::iterator it = args.begin(); it != args.end(); it++) { std::cout << " " << it->first << " = " << it->second << std::endl; } std::cout << "Headers: " << std::endl; for(std::map::iterator it = headers.begin(); it != headers.end(); it++) { std::cout << " " << it->first << " = " << it->second << std::endl; } callback(shared_from_this()); // Catch unhandled exceptions. } catch(std::exception& e) { std::cerr << "Unhandled exception while handling request: " << e.what() << std::endl; try { send_error(500); } catch(...) { // Ignored. } } } void HTTP::Connection::read_request(Handler callback) { boost::asio::async_read_until(socket, buf, "\r\n\r\n", boost::bind(&Connection::handle_read, shared_from_this(), boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred, callback)); } std::string HTTP::Connection::pop_path_base() { if(!path.size()) { return ""; } base_path.push_back(path.front()); path.pop_front(); return base_path.back(); } void HTTP::Connection::write_headers(int code) { if(headers_written) { return; } headers_written = true; boost::asio::write(socket, boost::asio::buffer(boost::str(boost::format("HTTP/1.1 %d %s\r\n") % code % response_map.find(code)->second))); for(HeaderList::iterator it = response_headers.begin(); it != response_headers.end(); it++) { boost::asio::write(socket, boost::asio::buffer(boost::str(boost::format("%s: %s\r\n") % it->first % it->second))); } boost::asio::write(socket, boost::asio::buffer(std::string("\r\n"))); }