blob: 8f19aef5c5f88481bc7910a6dd8667e371a81bdf [file] [log] [blame]
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License
#include <gmock/gmock.h>
#include <deque>
#include <string>
#include <process/gtest.hpp>
#include <process/owned.hpp>
#include <stout/gtest.hpp>
#include "decoder.hpp"
namespace http = process::http;
using process::DataDecoder;
using process::Future;
using process::Owned;
using process::ResponseDecoder;
using process::StreamingRequestDecoder;
using process::StreamingResponseDecoder;
using std::deque;
using std::string;
// TODO(anand): Parameterize the response decoder tests.
template <typename T>
class RequestDecoderTest : public ::testing::Test {};
typedef ::testing::Types<DataDecoder, StreamingRequestDecoder>
RequestDecoderTypes;
// The request decoder tests are parameterized by the type of request decoder.
TYPED_TEST_CASE(RequestDecoderTest, RequestDecoderTypes);
TYPED_TEST(RequestDecoderTest, Request)
{
TypeParam decoder;
const string data =
"GET /path/file.json?key1=value1&key2=value2#fragment HTTP/1.1\r\n"
"Host: localhost\r\n"
"Connection: close\r\n"
"Accept-Encoding: compress, gzip\r\n"
"\r\n";
deque<http::Request*> requests = decoder.decode(data.data(), data.length());
ASSERT_FALSE(decoder.failed());
ASSERT_EQ(1u, requests.size());
Owned<http::Request> request(requests[0]);
EXPECT_EQ("GET", request->method);
EXPECT_EQ("/path/file.json", request->url.path);
EXPECT_SOME_EQ("fragment", request->url.fragment);
EXPECT_EQ(2u, request->url.query.size());
EXPECT_SOME_EQ("value1", request->url.query.get("key1"));
EXPECT_SOME_EQ("value2", request->url.query.get("key2"));
Future<string> body = [&request]() -> Future<string> {
if (request->type == http::Request::BODY) {
return request->body;
}
return request->reader->readAll();
}();
AWAIT_EXPECT_EQ(string(""), body);
EXPECT_FALSE(request->keepAlive);
EXPECT_EQ(3u, request->headers.size());
EXPECT_SOME_EQ("localhost", request->headers.get("Host"));
EXPECT_SOME_EQ("close", request->headers.get("Connection"));
EXPECT_SOME_EQ("compress, gzip", request->headers.get("Accept-Encoding"));
}
TYPED_TEST(RequestDecoderTest, HeaderContinuation)
{
TypeParam decoder;
const string data =
"GET /path/file.json HTTP/1.1\r\n"
"Host: localhost\r\n"
"Connection: close\r\n"
"Accept-Encoding: compress,"
" gzip\r\n"
"\r\n";
deque<http::Request*> requests = decoder.decode(data.data(), data.length());
ASSERT_FALSE(decoder.failed());
ASSERT_EQ(1u, requests.size());
Owned<http::Request> request(requests[0]);
EXPECT_SOME_EQ("compress, gzip",
request->headers.get("Accept-Encoding"));
}
TYPED_TEST(RequestDecoderTest, HeaderCaseInsensitive)
{
TypeParam decoder;
const string data =
"GET /path/file.json HTTP/1.1\r\n"
"Host: localhost\r\n"
"cOnnECtioN: close\r\n"
"accept-ENCODING: compress, gzip\r\n"
"\r\n";
deque<http::Request*> requests = decoder.decode(data.data(), data.length());
ASSERT_FALSE(decoder.failed());
ASSERT_EQ(1u, requests.size());
Owned<http::Request> request(requests[0]);
EXPECT_FALSE(request->keepAlive);
EXPECT_SOME_EQ("compress, gzip", request->headers.get("Accept-Encoding"));
}
TYPED_TEST(RequestDecoderTest, InvalidQueryArgs)
{
TypeParam decoder;
const string data =
"GET /path/file.json?%x=%y&key2=value2#fragment HTTP/1.1\r\n"
"Host: localhost\r\n"
"Connection: close\r\n"
"Accept-Encoding: compress, gzip\r\n"
"\r\n";
deque<http::Request*> requests = decoder.decode(data.data(), data.length());
EXPECT_TRUE(decoder.failed());
}
TEST(DecoderTest, Response)
{
ResponseDecoder decoder;
const string data =
"HTTP/1.1 200 OK\r\n"
"Date: Fri, 31 Dec 1999 23:59:59 GMT\r\n"
"Content-Type: text/plain\r\n"
"Content-Length: 2\r\n"
"\r\n"
"hi";
deque<http::Response*> responses = decoder.decode(data.data(), data.length());
ASSERT_FALSE(decoder.failed());
ASSERT_EQ(1u, responses.size());
Owned<http::Response> response(responses[0]);
EXPECT_EQ("200 OK", response->status);
EXPECT_EQ(http::Response::BODY, response->type);
EXPECT_EQ("hi", response->body);
EXPECT_EQ(3u, response->headers.size());
}
TEST(DecoderTest, ResponseWithUnspecifiedLength)
{
ResponseDecoder decoder;
const string data =
"HTTP/1.1 200 OK\r\n"
"Date: Fri, 31 Dec 1999 23:59:59 GMT\r\n"
"Content-Type: text/plain\r\n"
"\r\n"
"hi";
deque<http::Response*> responses = decoder.decode(data.data(), data.length());
ASSERT_FALSE(decoder.failed());
ASSERT_TRUE(responses.empty());
responses = decoder.decode("", 0);
ASSERT_FALSE(decoder.failed());
ASSERT_EQ(1u, responses.size());
Owned<http::Response> response(responses[0]);
EXPECT_EQ("200 OK", response->status);
EXPECT_EQ(http::Response::BODY, response->type);
EXPECT_EQ("hi", response->body);
EXPECT_EQ(2u, response->headers.size());
}
TEST(DecoderTest, StreamingResponse)
{
StreamingResponseDecoder decoder;
const string headers =
"HTTP/1.1 200 OK\r\n"
"Date: Fri, 31 Dec 1999 23:59:59 GMT\r\n"
"Content-Type: text/plain\r\n"
"Content-Length: 2\r\n"
"\r\n";
const string body = "hi";
deque<http::Response*> responses =
decoder.decode(headers.data(), headers.length());
EXPECT_FALSE(decoder.failed());
EXPECT_TRUE(decoder.writingBody());
ASSERT_EQ(1u, responses.size());
Owned<http::Response> response(responses[0]);
EXPECT_EQ("200 OK", response->status);
EXPECT_EQ(3u, response->headers.size());
ASSERT_EQ(http::Response::PIPE, response->type);
ASSERT_SOME(response->reader);
http::Pipe::Reader reader = response->reader.get();
Future<string> read = reader.read();
EXPECT_TRUE(read.isPending());
decoder.decode(body.data(), body.length());
EXPECT_FALSE(decoder.failed());
EXPECT_FALSE(decoder.writingBody());
// Feeding EOF to the decoder should be ok.
decoder.decode("", 0);
EXPECT_FALSE(decoder.failed());
EXPECT_FALSE(decoder.writingBody());
EXPECT_TRUE(read.isReady());
EXPECT_EQ("hi", read.get());
// Response should be complete.
read = reader.read();
EXPECT_TRUE(read.isReady());
EXPECT_EQ("", read.get());
}
TEST(DecoderTest, StreamingResponseFailure)
{
StreamingResponseDecoder decoder;
const string headers =
"HTTP/1.1 200 OK\r\n"
"Date: Fri, 31 Dec 1999 23:59:59 GMT\r\n"
"Content-Type: text/plain\r\n"
"Content-Length: 2\r\n"
"\r\n";
// The body is shorter than the content length!
const string body = "1";
deque<http::Response*> responses =
decoder.decode(headers.data(), headers.length());
EXPECT_FALSE(decoder.failed());
EXPECT_TRUE(decoder.writingBody());
ASSERT_EQ(1u, responses.size());
Owned<http::Response> response(responses[0]);
EXPECT_EQ("200 OK", response->status);
EXPECT_EQ(3u, response->headers.size());
ASSERT_EQ(http::Response::PIPE, response->type);
ASSERT_SOME(response->reader);
http::Pipe::Reader reader = response->reader.get();
Future<string> read = reader.read();
EXPECT_TRUE(read.isPending());
decoder.decode(body.data(), body.length());
EXPECT_FALSE(decoder.failed());
EXPECT_TRUE(decoder.writingBody());
EXPECT_TRUE(read.isReady());
EXPECT_EQ("1", read.get());
// Body is not yet complete.
read = reader.read();
EXPECT_TRUE(read.isPending());
// Feeding EOF to the decoder should trigger a failure!
decoder.decode("", 0);
EXPECT_TRUE(decoder.failed());
EXPECT_FALSE(decoder.writingBody());
EXPECT_TRUE(read.isFailed());
EXPECT_EQ("failed to decode body", read.failure());
}
TEST(DecoderTest, StreamingResponseInvalidHeader)
{
StreamingResponseDecoder decoder;
const string headers =
"HTTP/1.1 999 OK\r\n"
"Date: Fri, 31 Dec 1999 23:59:59 GMT\r\n"
"Content-Type: text/plain\r\n"
"\r\n";
deque<http::Response*> responses =
decoder.decode(headers.data(), headers.length());
EXPECT_TRUE(decoder.failed());
}