blob: 8d0cfefd839ca6986ec83f606a839f021142ff20 [file] [log] [blame]
Ed Tanousb4a7bfa2017-04-04 17:23:00 -07001#include <crow/app.h>
2#include <gmock/gmock.h>
3#include <zlib.h>
4#include <boost/algorithm/string/predicate.hpp>
5#include <boost/iostreams/copy.hpp>
6#include <boost/iostreams/filter/gzip.hpp>
7#include <boost/iostreams/filtering_streambuf.hpp>
8#include <boost/lexical_cast.hpp>
9#include <sstream>
10#include <webassets.hpp>
11#include "gtest/gtest.h"
12using namespace crow;
13using namespace std;
14using namespace testing;
15
16bool gzipInflate(const std::string& compressedBytes,
17 std::string& uncompressedBytes) {
18 if (compressedBytes.size() == 0) {
19 uncompressedBytes = compressedBytes;
20 return true;
21 }
22
23 uncompressedBytes.clear();
24
25 unsigned full_length = compressedBytes.size();
26 unsigned half_length = compressedBytes.size() / 2;
27
28 unsigned uncompLength = full_length;
29 char* uncomp = (char*)calloc(sizeof(char), uncompLength);
30
31 z_stream strm;
32 strm.next_in = (Bytef*)compressedBytes.c_str();
33 strm.avail_in = compressedBytes.size();
34 strm.total_out = 0;
35 strm.zalloc = Z_NULL;
36 strm.zfree = Z_NULL;
37
38 bool done = false;
39
40 if (inflateInit2(&strm, (16 + MAX_WBITS)) != Z_OK) {
41 free(uncomp);
42 return false;
43 }
44
45 while (!done) {
46 // If our output buffer is too small
47 if (strm.total_out >= uncompLength) {
48 // Increase size of output buffer
49 char* uncomp2 = (char*)calloc(sizeof(char), uncompLength + half_length);
50 memcpy(uncomp2, uncomp, uncompLength);
51 uncompLength += half_length;
52 free(uncomp);
53 uncomp = uncomp2;
54 }
55
56 strm.next_out = (Bytef*)(uncomp + strm.total_out);
57 strm.avail_out = uncompLength - strm.total_out;
58
59 // Inflate another chunk.
60 int err = inflate(&strm, Z_SYNC_FLUSH);
61 if (err == Z_STREAM_END)
62 done = true;
63 else if (err != Z_OK) {
64 break;
65 }
66 }
67
68 if (inflateEnd(&strm) != Z_OK) {
69 free(uncomp);
70 return false;
71 }
72
73 for (size_t i = 0; i < strm.total_out; ++i) {
74 uncompressedBytes += uncomp[i];
75 }
76 free(uncomp);
77 return true;
78}
79
80
81
82
83// Tests static files are loaded correctly
84TEST(Webassets, StaticFilesFixedRoutes) {
85 std::array<char, 2048> buf;
86 SimpleApp app;
87 webassets::request_routes(app);
88 Server<SimpleApp> server(&app, "127.0.0.1", 45451);
89 auto _ = async(launch::async, [&] { server.run(); });
90
91 // Get the homepage
92 std::string sendmsg = "GET /\r\n\r\n";
93
94 asio::io_service is;
95
96 asio::ip::tcp::socket c(is);
97 c.connect(asio::ip::tcp::endpoint(asio::ip::address::from_string("127.0.0.1"),
98 45451));
99
100 c.send(asio::buffer(sendmsg));
101
102 c.receive(asio::buffer(buf, 2048));
103 c.close();
104
105 std::string response(std::begin(buf), std::end(buf));
106 // This is a routine to split strings until a newline is hit
107 // TODO(ed) this should really use the HTTP parser
108 std::vector<std::string> headers;
109 std::string::size_type pos = 0;
110 std::string::size_type prev = 0;
111 int content_length = 0;
112 std::string content_encoding("");
113 while ((pos = response.find("\r\n", prev)) != std::string::npos) {
114 auto this_string = response.substr(prev, pos - prev);
115 if (this_string == "") {
116 prev = pos + 2;
117 break;
118 }
119
120 if (boost::starts_with(this_string, "Content-Length: ")) {
121 content_length = boost::lexical_cast<int>(this_string.substr(16));
122 // TODO(ed) This is an unfortunate test, but it's all we have at this
123 // point
124 // Realistically, the index.html will be more than 500 bytes. This
125 // test will need to be improved at some point
126 EXPECT_GT(content_length, 500);
127 }
128 if (boost::starts_with(this_string, "Content-Encoding: ")) {
129 content_encoding = this_string.substr(18);
130 }
131
132 headers.push_back(this_string);
133 prev = pos + 2;
134 }
135
136 auto http_content = response.substr(prev);
137 // TODO(ed) ideally the server should support non-compressed gzip assets.
138 // Once this
139 // occurs, this line will be obsolete
140 std::string ungziped_content = http_content;
141 if (content_encoding == "gzip") {
142 EXPECT_TRUE(gzipInflate(http_content, ungziped_content));
143 }
144
145 EXPECT_EQ(headers[0], "HTTP/1.1 200 OK");
146 EXPECT_THAT(headers,
147 ::testing::Contains("Content-Type: text/html;charset=UTF-8"));
148
149 EXPECT_EQ(ungziped_content.substr(0, 21), "<!DOCTYPE html>\n<html");
150
151 server.stop();
152}
153
154
155
156// Tests static files are loaded correctly
157TEST(Webassets, EtagIsSane) {
158 std::array<char, 2048> buf;
159 SimpleApp app;
160 webassets::request_routes(app);
161 Server<SimpleApp> server(&app, "127.0.0.1", 45451);
162 auto _ = async(launch::async, [&] { server.run(); });
163
164 // Get the homepage
165 std::string sendmsg = "GET /\r\n\r\n";
166
167 asio::io_service is;
168
169 asio::ip::tcp::socket c(is);
170 c.connect(asio::ip::tcp::endpoint(asio::ip::address::from_string("127.0.0.1"),
171 45451));
172
173 c.send(asio::buffer(sendmsg));
174
175 c.receive(asio::buffer(buf, 2048));
176 c.close();
177
178 std::string response(std::begin(buf), std::end(buf));
179 // This is a routine to split strings until a newline is hit
180 // TODO(ed) this should really use the HTTP parser
181 std::vector<std::string> headers;
182 std::string::size_type pos = 0;
183 std::string::size_type prev = 0;
184 int content_length = 0;
185 std::string content_encoding("");
186 while ((pos = response.find("\r\n", prev)) != std::string::npos) {
187 auto this_string = response.substr(prev, pos - prev);
188 if (this_string == "") {
Ed Tanousb4a7bfa2017-04-04 17:23:00 -0700189 break;
190 }
191
192 if (boost::starts_with(this_string, "ETag: ")) {
193 auto etag = this_string.substr(6);
194 // ETAG should not be blank
195 EXPECT_NE(etag, "");
196 // SHa1 is 20 characters long
197 EXPECT_EQ(etag.size(), 40);
198 EXPECT_THAT(etag, MatchesRegex("^[a-f0-9]+$"));
199 }
200
201 headers.push_back(this_string);
202 prev = pos + 2;
203 }
204
205 server.stop();
206}