blob: 8d0cfefd839ca6986ec83f606a839f021142ff20 [file] [log] [blame]
#include <crow/app.h>
#include <gmock/gmock.h>
#include <zlib.h>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/iostreams/copy.hpp>
#include <boost/iostreams/filter/gzip.hpp>
#include <boost/iostreams/filtering_streambuf.hpp>
#include <boost/lexical_cast.hpp>
#include <sstream>
#include <webassets.hpp>
#include "gtest/gtest.h"
using namespace crow;
using namespace std;
using namespace testing;
bool gzipInflate(const std::string& compressedBytes,
std::string& uncompressedBytes) {
if (compressedBytes.size() == 0) {
uncompressedBytes = compressedBytes;
return true;
}
uncompressedBytes.clear();
unsigned full_length = compressedBytes.size();
unsigned half_length = compressedBytes.size() / 2;
unsigned uncompLength = full_length;
char* uncomp = (char*)calloc(sizeof(char), uncompLength);
z_stream strm;
strm.next_in = (Bytef*)compressedBytes.c_str();
strm.avail_in = compressedBytes.size();
strm.total_out = 0;
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
bool done = false;
if (inflateInit2(&strm, (16 + MAX_WBITS)) != Z_OK) {
free(uncomp);
return false;
}
while (!done) {
// If our output buffer is too small
if (strm.total_out >= uncompLength) {
// Increase size of output buffer
char* uncomp2 = (char*)calloc(sizeof(char), uncompLength + half_length);
memcpy(uncomp2, uncomp, uncompLength);
uncompLength += half_length;
free(uncomp);
uncomp = uncomp2;
}
strm.next_out = (Bytef*)(uncomp + strm.total_out);
strm.avail_out = uncompLength - strm.total_out;
// Inflate another chunk.
int err = inflate(&strm, Z_SYNC_FLUSH);
if (err == Z_STREAM_END)
done = true;
else if (err != Z_OK) {
break;
}
}
if (inflateEnd(&strm) != Z_OK) {
free(uncomp);
return false;
}
for (size_t i = 0; i < strm.total_out; ++i) {
uncompressedBytes += uncomp[i];
}
free(uncomp);
return true;
}
// Tests static files are loaded correctly
TEST(Webassets, StaticFilesFixedRoutes) {
std::array<char, 2048> buf;
SimpleApp app;
webassets::request_routes(app);
Server<SimpleApp> server(&app, "127.0.0.1", 45451);
auto _ = async(launch::async, [&] { server.run(); });
// Get the homepage
std::string sendmsg = "GET /\r\n\r\n";
asio::io_service is;
asio::ip::tcp::socket c(is);
c.connect(asio::ip::tcp::endpoint(asio::ip::address::from_string("127.0.0.1"),
45451));
c.send(asio::buffer(sendmsg));
c.receive(asio::buffer(buf, 2048));
c.close();
std::string response(std::begin(buf), std::end(buf));
// This is a routine to split strings until a newline is hit
// TODO(ed) this should really use the HTTP parser
std::vector<std::string> headers;
std::string::size_type pos = 0;
std::string::size_type prev = 0;
int content_length = 0;
std::string content_encoding("");
while ((pos = response.find("\r\n", prev)) != std::string::npos) {
auto this_string = response.substr(prev, pos - prev);
if (this_string == "") {
prev = pos + 2;
break;
}
if (boost::starts_with(this_string, "Content-Length: ")) {
content_length = boost::lexical_cast<int>(this_string.substr(16));
// TODO(ed) This is an unfortunate test, but it's all we have at this
// point
// Realistically, the index.html will be more than 500 bytes. This
// test will need to be improved at some point
EXPECT_GT(content_length, 500);
}
if (boost::starts_with(this_string, "Content-Encoding: ")) {
content_encoding = this_string.substr(18);
}
headers.push_back(this_string);
prev = pos + 2;
}
auto http_content = response.substr(prev);
// TODO(ed) ideally the server should support non-compressed gzip assets.
// Once this
// occurs, this line will be obsolete
std::string ungziped_content = http_content;
if (content_encoding == "gzip") {
EXPECT_TRUE(gzipInflate(http_content, ungziped_content));
}
EXPECT_EQ(headers[0], "HTTP/1.1 200 OK");
EXPECT_THAT(headers,
::testing::Contains("Content-Type: text/html;charset=UTF-8"));
EXPECT_EQ(ungziped_content.substr(0, 21), "<!DOCTYPE html>\n<html");
server.stop();
}
// Tests static files are loaded correctly
TEST(Webassets, EtagIsSane) {
std::array<char, 2048> buf;
SimpleApp app;
webassets::request_routes(app);
Server<SimpleApp> server(&app, "127.0.0.1", 45451);
auto _ = async(launch::async, [&] { server.run(); });
// Get the homepage
std::string sendmsg = "GET /\r\n\r\n";
asio::io_service is;
asio::ip::tcp::socket c(is);
c.connect(asio::ip::tcp::endpoint(asio::ip::address::from_string("127.0.0.1"),
45451));
c.send(asio::buffer(sendmsg));
c.receive(asio::buffer(buf, 2048));
c.close();
std::string response(std::begin(buf), std::end(buf));
// This is a routine to split strings until a newline is hit
// TODO(ed) this should really use the HTTP parser
std::vector<std::string> headers;
std::string::size_type pos = 0;
std::string::size_type prev = 0;
int content_length = 0;
std::string content_encoding("");
while ((pos = response.find("\r\n", prev)) != std::string::npos) {
auto this_string = response.substr(prev, pos - prev);
if (this_string == "") {
break;
}
if (boost::starts_with(this_string, "ETag: ")) {
auto etag = this_string.substr(6);
// ETAG should not be blank
EXPECT_NE(etag, "");
// SHa1 is 20 characters long
EXPECT_EQ(etag.size(), 40);
EXPECT_THAT(etag, MatchesRegex("^[a-f0-9]+$"));
}
headers.push_back(this_string);
prev = pos + 2;
}
server.stop();
}