Support http 1.1 deflate compression
This commit is contained in:
parent
e48b47f1ec
commit
d0547f3d69
@ -20,7 +20,6 @@ or see http://www.gnu.org/licenses/agpl.txt.
|
||||
|
||||
#ifndef BASIC_DATASTRUCTURES_H
|
||||
#define BASIC_DATASTRUCTURES_H
|
||||
|
||||
#include <string>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
@ -39,8 +38,18 @@ const char crlf[] = { '\r', '\n' };
|
||||
struct Header {
|
||||
std::string name;
|
||||
std::string value;
|
||||
void Clear() {
|
||||
name.clear();
|
||||
value.clear();
|
||||
}
|
||||
};
|
||||
|
||||
enum CompressionType {
|
||||
none,
|
||||
gzipRFC1952,
|
||||
deflateRFC1951
|
||||
} Compression;
|
||||
|
||||
struct Request {
|
||||
std::string uri;
|
||||
};
|
||||
@ -56,7 +65,18 @@ struct Reply {
|
||||
std::vector<Header> headers;
|
||||
std::string content;
|
||||
std::vector<boost::asio::const_buffer> toBuffers();
|
||||
std::vector<boost::asio::const_buffer> HeaderstoBuffers();
|
||||
static Reply stockReply(status_type status);
|
||||
void setSize(unsigned size) {
|
||||
for (std::size_t i = 0; i < headers.size(); ++i) {
|
||||
Header& h = headers[i];
|
||||
if("Content-Length" == h.name) {
|
||||
std::stringstream sizeString;
|
||||
sizeString << size;
|
||||
h.value = sizeString.str();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
boost::asio::const_buffer ToBuffer(Reply::status_type status) {
|
||||
@ -96,6 +116,21 @@ std::vector<boost::asio::const_buffer> Reply::toBuffers(){
|
||||
return buffers;
|
||||
}
|
||||
|
||||
std::vector<boost::asio::const_buffer> Reply::HeaderstoBuffers(){
|
||||
std::vector<boost::asio::const_buffer> buffers;
|
||||
buffers.push_back(ToBuffer(status));
|
||||
for (std::size_t i = 0; i < headers.size(); ++i) {
|
||||
Header& h = headers[i];
|
||||
// std::cout << h.name << ": " << h.value << std::endl;
|
||||
buffers.push_back(boost::asio::buffer(h.name));
|
||||
buffers.push_back(boost::asio::buffer(seperators));
|
||||
buffers.push_back(boost::asio::buffer(h.value));
|
||||
buffers.push_back(boost::asio::buffer(crlf));
|
||||
}
|
||||
buffers.push_back(boost::asio::buffer(crlf));
|
||||
return buffers;
|
||||
}
|
||||
|
||||
Reply Reply::stockReply(Reply::status_type status) {
|
||||
Reply rep;
|
||||
rep.status = status;
|
||||
|
@ -16,7 +16,7 @@ You should have received a copy of the GNU Affero General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
or see http://www.gnu.org/licenses/agpl.txt.
|
||||
*/
|
||||
*/
|
||||
|
||||
#ifndef CONNECTION_H
|
||||
#define CONNECTION_H
|
||||
@ -29,10 +29,14 @@ or see http://www.gnu.org/licenses/agpl.txt.
|
||||
#include <boost/noncopyable.hpp>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/enable_shared_from_this.hpp>
|
||||
|
||||
#include "../DataStructures/Util.h"
|
||||
#include "BasicDatastructures.h"
|
||||
#include "RequestHandler.h"
|
||||
#include "RequestParser.h"
|
||||
|
||||
#include "zlib.h"
|
||||
|
||||
namespace http {
|
||||
|
||||
/// Represents a single connection from a client.
|
||||
@ -52,12 +56,40 @@ public:
|
||||
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);
|
||||
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;
|
||||
|
||||
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);
|
||||
|
||||
std::vector<unsigned char> 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;
|
||||
|
||||
reply.setSize(compressed.size());
|
||||
std::vector<boost::asio::const_buffer> 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)));
|
||||
|
||||
} else {
|
||||
boost::asio::async_write(TCPsocket, reply.toBuffers(), strand.wrap( boost::bind(&Connection::handleWrite, this->shared_from_this(), boost::asio::placeholders::error)));
|
||||
}
|
||||
|
||||
} 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)));
|
||||
@ -80,6 +112,49 @@ private:
|
||||
// destructor closes the socket.
|
||||
}
|
||||
|
||||
void compressCharArray(const void *in_data, size_t in_data_size, std::vector<unsigned char> &buffer) {
|
||||
// std::vector<unsigned char> buffer;
|
||||
|
||||
const size_t BUFSIZE = 128 * 1024;
|
||||
unsigned char temp_buffer[BUFSIZE];
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
@ -32,9 +32,9 @@ public:
|
||||
RequestParser() : state_(method_start) { }
|
||||
void Reset() { state_ = method_start; }
|
||||
|
||||
boost::tuple<boost::tribool, char*> Parse(Request& req, char* begin, char* end) {
|
||||
boost::tuple<boost::tribool, char*> Parse(Request& req, char* begin, char* end, CompressionType * compressionType) {
|
||||
while (begin != end) {
|
||||
boost::tribool result = consume(req, *begin++);
|
||||
boost::tribool result = consume(req, *begin++, compressionType);
|
||||
if (result || !result){
|
||||
return boost::make_tuple(result, begin);
|
||||
}
|
||||
@ -44,7 +44,7 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
boost::tribool consume(Request& req, char input) {
|
||||
boost::tribool consume(Request& req, char input, CompressionType * compressionType) {
|
||||
switch (state_) {
|
||||
case method_start:
|
||||
if (!isChar(input) || isCTL(input) || isTSpecial(input)) {
|
||||
@ -163,6 +163,14 @@ private:
|
||||
return false;
|
||||
} else {
|
||||
state_ = header_name;
|
||||
if(header.name == "Accept-Encoding") {
|
||||
if(header.value.find("deflate") != std::string::npos)
|
||||
*compressionType = deflateRFC1951;
|
||||
// if(header.value.find("gzip") != std::string::npos)
|
||||
// *compressionType = gzipRFC1952;
|
||||
}
|
||||
header.Clear();
|
||||
header.name.push_back(input);
|
||||
return boost::indeterminate;
|
||||
}
|
||||
case header_lws:
|
||||
@ -185,6 +193,7 @@ private:
|
||||
} else if (!isChar(input) || isCTL(input) || isTSpecial(input)) {
|
||||
return false;
|
||||
} else {
|
||||
header.name.push_back(input);
|
||||
return boost::indeterminate;
|
||||
}
|
||||
case space_before_header_value:
|
||||
@ -201,6 +210,7 @@ private:
|
||||
} else if (isCTL(input)) {
|
||||
return false;
|
||||
} else {
|
||||
header.value.push_back(input);
|
||||
return boost::indeterminate;
|
||||
}
|
||||
case expecting_newline_2:
|
||||
@ -264,6 +274,8 @@ private:
|
||||
expecting_newline_2,
|
||||
expecting_newline_3
|
||||
} state_;
|
||||
|
||||
Header header;
|
||||
};
|
||||
|
||||
} // namespace http
|
||||
|
Loading…
Reference in New Issue
Block a user