diff --git a/Server/BasicDatastructures.h b/Server/BasicDatastructures.h index 5cc269f63..2e8d23000 100644 --- a/Server/BasicDatastructures.h +++ b/Server/BasicDatastructures.h @@ -45,7 +45,7 @@ struct Header { }; enum CompressionType { - none, + noCompression, gzipRFC1952, deflateRFC1951 } Compression; diff --git a/Server/Connection.h b/Server/Connection.h index 166cab15c..8e82eacab 100644 --- a/Server/Connection.h +++ b/Server/Connection.h @@ -42,126 +42,137 @@ namespace http { /// Represents a single connection from a client. class Connection : public boost::enable_shared_from_this, private boost::noncopyable { public: - explicit Connection(boost::asio::io_service& io_service, RequestHandler& handler) : strand(io_service), TCPsocket(io_service), requestHandler(handler) {} + explicit Connection(boost::asio::io_service& io_service, RequestHandler& handler) : strand(io_service), TCPsocket(io_service), requestHandler(handler) {} - boost::asio::ip::tcp::socket& socket() { - return TCPsocket; - } + boost::asio::ip::tcp::socket& socket() { + return TCPsocket; + } - /// Start the first asynchronous operation for the connection. - void start() { - TCPsocket.async_read_some(boost::asio::buffer(incomingDataBuffer), strand.wrap( boost::bind(&Connection::handleRead, this->shared_from_this(), boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred))); - } + /// Start the first asynchronous operation for the connection. + void start() { + TCPsocket.async_read_some(boost::asio::buffer(incomingDataBuffer), strand.wrap( boost::bind(&Connection::handleRead, this->shared_from_this(), boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred))); + } private: - void handleRead(const boost::system::error_code& e, std::size_t bytes_transferred) { - if (!e) { - CompressionType compressionType(none); - boost::tribool result; - boost::tie(result, boost::tuples::ignore) = requestParser.Parse( request, incomingDataBuffer.data(), incomingDataBuffer.data() + bytes_transferred, &compressionType); + void handleRead(const boost::system::error_code& e, std::size_t bytes_transferred) { + if (!e) { + CompressionType compressionType(noCompression); + boost::tribool result; + boost::tie(result, boost::tuples::ignore) = requestParser.Parse( request, incomingDataBuffer.data(), incomingDataBuffer.data() + bytes_transferred, &compressionType); - if (result) { -// std::cout << "----" << std::endl; -// if(compressionType == gzipRFC1952) -// std::cout << "[debug] supports gzip" << std::endl; -// if(compressionType == deflateRFC1951) -// std::cout << "[debug] Supports deflate" << std::endl; + if (result) { + // std::cout << "----" << std::endl; + // if(compressionType == gzipRFC1952) + // std::cout << "[debug] using gzip" << std::endl; + // if(compressionType == deflateRFC1951) + // std::cout << "[debug] using deflate" << std::endl; + // if(compressionType == noCompression) + // std::cout << "[debug] no compression" << std::endl; - requestHandler.handle_request(request, reply); + requestHandler.handle_request(request, reply); - if(compressionType == deflateRFC1951) { - Header compressionHeader; - compressionHeader.name = "Content-Encoding"; - compressionHeader.value = "deflate"; - reply.headers.insert(reply.headers.begin(), compressionHeader); //push_back(compressionHeader); + Header compressionHeader; + std::vector compressed; + std::vector outputBuffer; + switch(compressionType) { + case deflateRFC1951: + compressionHeader.name = "Content-Encoding"; + compressionHeader.value = "deflate"; + reply.headers.insert(reply.headers.begin(), compressionHeader); //push_back(compressionHeader); + compressCharArray(reply.content.c_str(), strlen(reply.content.c_str()), compressed, compressionType); + reply.setSize(compressed.size()); + outputBuffer = reply.HeaderstoBuffers(); + outputBuffer.push_back(boost::asio::buffer(compressed)); + boost::asio::async_write(TCPsocket, outputBuffer, strand.wrap( boost::bind(&Connection::handleWrite, this->shared_from_this(), boost::asio::placeholders::error))); + break; + case gzipRFC1952: + compressionHeader.name = "Content-Encoding"; + compressionHeader.value = "gzip"; + reply.headers.insert(reply.headers.begin(), compressionHeader); + compressCharArray(reply.content.c_str(), strlen(reply.content.c_str()), compressed, compressionType); + reply.setSize(compressed.size()); + outputBuffer = reply.HeaderstoBuffers(); + outputBuffer.push_back(boost::asio::buffer(compressed)); + boost::asio::async_write(TCPsocket, outputBuffer, strand.wrap( boost::bind(&Connection::handleWrite, this->shared_from_this(), boost::asio::placeholders::error)));break; + case noCompression: + boost::asio::async_write(TCPsocket, reply.toBuffers(), strand.wrap( boost::bind(&Connection::handleWrite, this->shared_from_this(), boost::asio::placeholders::error))); + break; + } - std::vector compressed; -// double time = get_timestamp(); - compressCharArray(reply.content.c_str(), strlen(reply.content.c_str()), compressed); -// std::cout << "[info] compression took " << get_timestamp() - time << " seconds." << std::endl; + } else if (!result) { + reply = Reply::stockReply(Reply::badRequest); + boost::asio::async_write(TCPsocket, reply.toBuffers(), strand.wrap( boost::bind(&Connection::handleWrite, this->shared_from_this(), boost::asio::placeholders::error))); + } else { + TCPsocket.async_read_some(boost::asio::buffer(incomingDataBuffer), strand.wrap( boost::bind(&Connection::handleRead, this->shared_from_this(), boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred))); + } + } + } - reply.setSize(compressed.size()); - std::vector outputBuffer = reply.HeaderstoBuffers(); - outputBuffer.push_back(boost::asio::buffer(compressed)); -// std::cout << "[debug] outbuffer.size(): " << outputBuffer.size() << std::endl; - boost::asio::async_write(TCPsocket, outputBuffer, strand.wrap( boost::bind(&Connection::handleWrite, this->shared_from_this(), boost::asio::placeholders::error))); + /// Handle completion of a write operation. + void handleWrite(const boost::system::error_code& e) { + if (!e) { + // Initiate graceful connection closure. + boost::system::error_code ignoredEC; + TCPsocket.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ignoredEC); + } + // No new asynchronous operations are started. This means that all shared_ptr + // references to the connection object will disappear and the object will be + // destroyed automatically after this handler returns. The connection class's + // destructor closes the socket. + } - } else { - boost::asio::async_write(TCPsocket, reply.toBuffers(), strand.wrap( boost::bind(&Connection::handleWrite, this->shared_from_this(), boost::asio::placeholders::error))); - } + void compressCharArray(const void *in_data, size_t in_data_size, std::vector &buffer, CompressionType type) { + const size_t BUFSIZE = 128 * 1024; + unsigned char temp_buffer[BUFSIZE]; - } else if (!result) { - reply = Reply::stockReply(Reply::badRequest); - boost::asio::async_write(TCPsocket, reply.toBuffers(), strand.wrap( boost::bind(&Connection::handleWrite, this->shared_from_this(), boost::asio::placeholders::error))); - } else { - TCPsocket.async_read_some(boost::asio::buffer(incomingDataBuffer), strand.wrap( boost::bind(&Connection::handleRead, this->shared_from_this(), boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred))); - } - } - } + z_stream strm; + strm.zalloc = 0; + strm.zfree = 0; + strm.next_in = (unsigned char *)(in_data); + strm.avail_in = in_data_size; + strm.next_out = temp_buffer; + strm.avail_out = BUFSIZE; + strm.data_type = Z_ASCII; - /// Handle completion of a write operation. - void handleWrite(const boost::system::error_code& e) { - if (!e) { - // Initiate graceful connection closure. - boost::system::error_code ignoredEC; - TCPsocket.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ignoredEC); - } - // No new asynchronous operations are started. This means that all shared_ptr - // references to the connection object will disappear and the object will be - // destroyed automatically after this handler returns. The connection class's - // destructor closes the socket. - } + switch(type){ + case deflateRFC1951: + deflateInit(&strm, Z_BEST_SPEED); + break; + case gzipRFC1952: + /* + * Big thanks to deusty who explains how to have gzip compression turned on by the right call to deflateInit2(): + * http://deusty.blogspot.com/2007/07/gzip-compressiondecompression.html + */ + deflateInit2(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, (15+16), 8, Z_DEFAULT_STRATEGY); + break; + case noCompression: + std::cerr << "[error] contradicting compression request" << std::endl; + return; + } - void compressCharArray(const void *in_data, size_t in_data_size, std::vector &buffer) { -// std::vector buffer; + int deflate_res = Z_OK; + do { + if (strm.avail_out == 0) { + buffer.insert(buffer.end(), temp_buffer, temp_buffer + BUFSIZE); + strm.next_out = temp_buffer; + strm.avail_out = BUFSIZE; + } + deflate_res = deflate(&strm, Z_FINISH); - const size_t BUFSIZE = 128 * 1024; - unsigned char temp_buffer[BUFSIZE]; + } while (strm.avail_out == 0); - z_stream strm; - strm.zalloc = 0; - strm.zfree = 0; - strm.next_in = (unsigned char *)(in_data); - strm.avail_in = in_data_size; - strm.next_out = temp_buffer; - strm.avail_out = BUFSIZE; + assert(deflate_res == Z_STREAM_END); + buffer.insert(buffer.end(), temp_buffer, temp_buffer + BUFSIZE - strm.avail_out); + deflateEnd(&strm); + } - deflateInit(&strm, Z_BEST_SPEED); - - while (strm.avail_in != 0) { - int res = deflate(&strm, Z_NO_FLUSH); - assert(res == Z_OK); - if (strm.avail_out == 0) { - buffer.insert(buffer.end(), temp_buffer, temp_buffer + BUFSIZE); - strm.next_out = temp_buffer; - strm.avail_out = BUFSIZE; - } - } - - int deflate_res = Z_OK; - while (deflate_res == Z_OK) { - if (strm.avail_out == 0) { - buffer.insert(buffer.end(), temp_buffer, temp_buffer + BUFSIZE); - strm.next_out = temp_buffer; - strm.avail_out = BUFSIZE; - } - deflate_res = deflate(&strm, Z_FINISH); - } - - assert(deflate_res == Z_STREAM_END); - buffer.insert(buffer.end(), temp_buffer, temp_buffer + BUFSIZE - strm.avail_out); - deflateEnd(&strm); - -// out_data.swap(buffer); - } - - boost::asio::io_service::strand strand; - boost::asio::ip::tcp::socket TCPsocket; - RequestHandler& requestHandler; - boost::array incomingDataBuffer; - Request request; - RequestParser requestParser; - Reply reply; + boost::asio::io_service::strand strand; + boost::asio::ip::tcp::socket TCPsocket; + RequestHandler& requestHandler; + boost::array incomingDataBuffer; + Request request; + RequestParser requestParser; + Reply reply; }; } // namespace http diff --git a/Server/RequestParser.h b/Server/RequestParser.h index e45dd2be6..04abc82dd 100644 --- a/Server/RequestParser.h +++ b/Server/RequestParser.h @@ -164,10 +164,11 @@ private: } else { state_ = header_name; if(header.name == "Accept-Encoding") { - if(header.value.find("deflate") != std::string::npos) + /* giving gzip precedence over deflate */ + if(header.value.find("deflate") != std::string::npos) *compressionType = deflateRFC1951; -// if(header.value.find("gzip") != std::string::npos) -// *compressionType = gzipRFC1952; + if(header.value.find("gzip") != std::string::npos) + *compressionType = gzipRFC1952; } header.Clear(); header.name.push_back(input); diff --git a/Server/ServerFactory.h b/Server/ServerFactory.h index 6fcea5915..964aa21d6 100644 --- a/Server/ServerFactory.h +++ b/Server/ServerFactory.h @@ -75,6 +75,7 @@ struct ServerFactory { if(atoi(serverConfig.GetParameter("Threads").c_str()) != 0 && (unsigned)atoi(serverConfig.GetParameter("Threads").c_str()) <= threads) threads = atoi( serverConfig.GetParameter("Threads").c_str() ); + std::cout << "[info] http 1.1 compression handled by zlib version " << zlibVersion() << std::endl; Server * server = new Server(serverConfig.GetParameter("IP"), serverConfig.GetParameter("Port"), threads); return server; }