spectrum2/3rdparty/cpprestsdk/tests/functional/http/client/response_stream_tests.cpp
2015-11-19 15:19:14 +01:00

501 lines
18 KiB
C++

/***
* ==++==
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* 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.
*
* ==--==
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* response_stream_tests.cpp
*
* Tests cases for covering receiving various responses as a stream with http_client.
*
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
****/
#include "stdafx.h"
#if defined(__cplusplus_winrt)
using namespace Windows::Storage;
#endif
#ifndef __cplusplus_winrt
#include "cpprest/http_listener.h"
#endif
using namespace web;
using namespace utility;
using namespace concurrency;
using namespace web::http;
using namespace web::http::client;
using namespace tests::functional::http::utilities;
namespace tests { namespace functional { namespace http { namespace client {
template<typename _CharType>
pplx::task<streams::streambuf<_CharType>> OPENSTR_R(const utility::string_t &name)
{
#if !defined(__cplusplus_winrt)
return streams::file_buffer<_CharType>::open(name, std::ios_base::in);
#else
auto file = pplx::create_task(
KnownFolders::DocumentsLibrary->GetFileAsync(ref new Platform::String(name.c_str()))).get();
return streams::file_buffer<_CharType>::open(file, std::ios_base::in);
#endif
}
template<typename _CharType>
pplx::task<Concurrency::streams::basic_ostream<_CharType>> OPENSTR_W(const utility::string_t &name, std::ios_base::openmode mode = std::ios_base::out)
{
#if !defined(__cplusplus_winrt)
return Concurrency::streams::file_stream<_CharType>::open_ostream(name, mode);
#else
auto file = pplx::create_task(
KnownFolders::DocumentsLibrary->CreateFileAsync(
ref new Platform::String(name.c_str()), CreationCollisionOption::ReplaceExisting)).get();
return Concurrency::streams::file_stream<_CharType>::open_ostream(file, mode);
#endif
}
SUITE(response_stream_tests)
{
TEST_FIXTURE(uri_address, set_response_stream_producer_consumer_buffer)
{
test_http_server::scoped_server scoped(m_uri);
test_http_server * p_server = scoped.server();
http_client client(m_uri);
p_server->next_request().then([&](test_request *p_request)
{
std::map<utility::string_t, utility::string_t> headers;
headers[U("Content-Type")] = U("text/plain");
p_request->reply(200, U(""), headers, "This is just a bit of a string");
});
streams::producer_consumer_buffer<uint8_t> rwbuf;
auto ostr = streams::ostream(rwbuf);
http_request msg(methods::GET);
msg.set_response_stream(ostr);
http_response rsp = client.request(msg).get();
rsp.content_ready().get();
VERIFY_ARE_EQUAL(rwbuf.in_avail(), 30u);
VERIFY_THROWS(rsp.extract_string().get(), http_exception);
char chars[128];
memset(chars, 0, sizeof(chars));
rwbuf.getn((unsigned char *)chars, rwbuf.in_avail()).get();
VERIFY_ARE_EQUAL(0, strcmp("This is just a bit of a string", chars));
}
TEST_FIXTURE(uri_address, set_response_stream_container_buffer)
{
test_http_server::scoped_server scoped(m_uri);
test_http_server * p_server = scoped.server();
http_client client(m_uri);
p_server->next_request().then([&](test_request *p_request)
{
std::map<utility::string_t, utility::string_t> headers;
headers[U("Content-Type")] = U("text/plain");
p_request->reply(200, U(""), headers, "This is just a bit of a string");
});
{
streams::container_buffer<std::vector<uint8_t>> buf;
http_request msg(methods::GET);
msg.set_response_stream(buf.create_ostream());
http_response rsp = client.request(msg).get();
rsp.content_ready().get();
VERIFY_ARE_EQUAL(buf.collection().size(), 30);
char bufStr[31];
memset(bufStr, 0, sizeof(bufStr));
memcpy(&bufStr[0], &(buf.collection())[0], 30);
VERIFY_ARE_EQUAL(bufStr, "This is just a bit of a string");
VERIFY_THROWS(rsp.extract_string().get(), http_exception);
}
}
TEST_FIXTURE(uri_address, response_stream_file_stream)
{
std::string message = "A world without string is chaos.";
test_http_server::scoped_server scoped(m_uri);
test_http_server * p_server = scoped.server();
http_client client(m_uri);
p_server->next_request().then([&](test_request *p_request)
{
std::map<utility::string_t, utility::string_t> headers;
headers[U("Content-Type")] = U("text/plain");
p_request->reply(200, U(""), headers, message);
});
{
auto fstream = OPENSTR_W<uint8_t>(U("response_stream.txt")).get();
// Write the response into the file
http_request msg(methods::GET);
msg.set_response_stream(fstream);
http_response rsp = client.request(msg).get();
rsp.content_ready().get();
VERIFY_IS_TRUE(fstream.streambuf().is_open());
fstream.close().get();
char chars[128];
memset(chars,0,sizeof(chars));
streams::rawptr_buffer<uint8_t> buffer(reinterpret_cast<uint8_t *>(chars), sizeof(chars));
streams::basic_istream<uint8_t> fistream = OPENSTR_R<uint8_t>(U("response_stream.txt")).get();
VERIFY_ARE_EQUAL(message.length(), fistream.read_line(buffer).get());
VERIFY_ARE_EQUAL(message, std::string(chars));
fistream.close().get();
}
}
TEST_FIXTURE(uri_address, response_stream_file_stream_close_early)
{
// The test needs to be a little different between desktop and WinRT.
// In the latter case, the server will not see a message, and so the
// test will hang. In order to prevent that from happening, we will
// not have a server listening on WinRT.
#if !defined(__cplusplus_winrt)
test_http_server::scoped_server scoped(m_uri);
test_http_server * p_server = scoped.server();
p_server->next_request().then([&](test_request *p_request)
{
std::map<utility::string_t, utility::string_t> headers;
headers[U("Content-Type")] = U("text/plain");
p_request->reply(200, U(""), headers, "A world without string is chaos.");
});
#endif
auto fstream = OPENSTR_W<uint8_t>(U("response_stream_file_stream_close_early.txt")).get();
http_client client(m_uri);
http_request msg(methods::GET);
msg.set_response_stream(fstream);
fstream.close(std::make_exception_ptr(std::exception())).wait();
http_response resp;
VERIFY_THROWS((resp = client.request(msg).get(), resp.content_ready().get()), std::exception);
}
TEST_FIXTURE(uri_address, response_stream_large_file_stream)
{
// Send a 100 KB data in the response body, the server will send this in multiple chunks
// This data will get sent with content-length
const size_t workload_size = 100 * 1024;
utility::string_t fname(U("response_stream_large_file_stream.txt"));
std::string responseData;
responseData.resize(workload_size, 'a');
test_http_server::scoped_server scoped(m_uri);
test_http_server * p_server = scoped.server();
http_client client(m_uri);
p_server->next_request().then([&](test_request *p_request)
{
std::map<utility::string_t, utility::string_t> headers;
headers[U("Content-Type")] = U("text/plain");
p_request->reply(200, U(""), headers, responseData);
});
{
auto fstream = OPENSTR_W<uint8_t>(fname).get();
http_request msg(methods::GET);
msg.set_response_stream(fstream);
http_response rsp = client.request(msg).get();
rsp.content_ready().get();
VERIFY_IS_TRUE(fstream.streambuf().is_open());
fstream.close().get();
std::string rsp_string;
rsp_string.resize(workload_size, 0);
streams::rawptr_buffer<char> buffer(&rsp_string[0], rsp_string.size());
streams::basic_istream<char> fistream = OPENSTR_R<char>(fname).get();
VERIFY_ARE_EQUAL(fistream.read_to_end(buffer).get(), workload_size);
VERIFY_ARE_EQUAL(rsp_string, responseData);
fistream.close().get();
}
}
#if !defined(__cplusplus_winrt)
template<typename CharType>
class basic_throws_buffer : public streams::details::streambuf_state_manager<CharType>
{
public:
basic_throws_buffer() : streams::details::streambuf_state_manager<CharType>(std::ios_base::out) {}
typedef typename streams::details::basic_streambuf<CharType>::int_type int_type;
typedef typename streams::details::basic_streambuf<CharType>::pos_type pos_type;
typedef typename streams::details::basic_streambuf<CharType>::off_type off_type;
bool can_seek() const override { return true; }
bool has_size() const override { return false; }
size_t buffer_size(std::ios_base::openmode) const override { return 0; }
void set_buffer_size(size_t, std::ios_base::openmode) override {}
size_t in_avail() const override { return 0; }
pos_type getpos(std::ios_base::openmode) const override { return 0; }
pos_type seekpos(pos_type, std::ios_base::openmode) { return 0; }
pos_type seekoff(off_type, std::ios_base::seekdir, std::ios_base::openmode) override { return 0; }
bool acquire(_Out_writes_(count) CharType*&, _In_ size_t&) override { return false; }
void release(_Out_writes_(count) CharType *, _In_ size_t) override {}
protected:
pplx::task<int_type> _putc(CharType) override { throw std::runtime_error("error"); }
pplx::task<size_t> _putn(const CharType *, size_t) override { throw std::runtime_error("error"); }
pplx::task<int_type> _bumpc() override { throw std::runtime_error("error"); }
int_type _sbumpc() override { throw std::runtime_error("error"); }
pplx::task<int_type> _getc() override { throw std::runtime_error("error"); }
int_type _sgetc() override { throw std::runtime_error("error"); }
pplx::task<int_type> _nextc() override { throw std::runtime_error("error"); }
pplx::task<int_type> _ungetc() override { throw std::runtime_error("error"); }
pplx::task<size_t> _getn(_Out_writes_(count) CharType *, _In_ size_t) override { throw std::runtime_error("error"); }
size_t _scopy(_Out_writes_(count) CharType *, _In_ size_t) override { throw std::runtime_error("error"); }
pplx::task<bool> _sync() override { throw std::runtime_error("error"); }
CharType* _alloc(size_t) override { throw std::runtime_error("error"); }
void _commit(size_t) override { throw std::runtime_error("error"); }
pplx::task<void> _close_write() override
{
return pplx::task_from_exception<void>(std::invalid_argument("test"));
}
};
template<typename CharType>
class close_throws_buffer : public streams::streambuf<CharType>
{
public:
close_throws_buffer() : streams::streambuf<CharType>(std::shared_ptr<basic_throws_buffer<CharType>>(new basic_throws_buffer<CharType>())) {}
};
// Tests if an exception occurs and close throws an exception that the close
// one is ignored and doesn't bring down the process.
TEST_FIXTURE(uri_address, response_stream_close_throws_with_exception)
{
web::http::experimental::listener::http_listener listener(m_uri);
listener.open().wait();
streams::producer_consumer_buffer<uint8_t> buf;
listener.support([buf](http_request request)
{
http_response response(200);
response.set_body(streams::istream(buf), U("text/plain"));
response.headers().add(header_names::connection, U("close"));
request.reply(response);
});
http_client_config config;
config.set_timeout(utility::seconds(1));
http_client client(m_uri, config);
close_throws_buffer<uint8_t> responseBody;
http_request msg(methods::GET);
msg.set_response_stream(responseBody.create_ostream());
http_response rsp = client.request(msg).get();
VERIFY_THROWS(rsp.content_ready().get(), http_exception);
buf.close(std::ios_base::out).wait();
listener.close().wait();
}
TEST_FIXTURE(uri_address, content_ready)
{
http_client client(m_uri);
std::string responseData("Hello world");
web::http::experimental::listener::http_listener listener(m_uri);
listener.open().wait();
listener.support([responseData](http_request request)
{
streams::producer_consumer_buffer<uint8_t> buf;
http_response response(200);
response.set_body(buf.create_istream(), U("text/plain"));
response.headers().add(header_names::connection, U("close"));
request.reply(response);
VERIFY_ARE_EQUAL(buf.putn_nocopy((const uint8_t *)responseData.data(), responseData.size()).get(), responseData.size());
buf.close(std::ios_base::out).get();
});
{
http_request msg(methods::GET);
http_response rsp = client.request(msg).get().content_ready().get();
auto extract_string_task = rsp.extract_string();
VERIFY_ARE_EQUAL(extract_string_task.get(), ::utility::conversions::to_string_t(responseData));
rsp.content_ready().wait();
}
listener.close().wait();
}
TEST_FIXTURE(uri_address, xfer_chunked_with_length)
{
http_client client(m_uri);
utility::string_t responseData(U("Hello world"));
web::http::experimental::listener::http_listener listener(m_uri);
listener.open().wait();
listener.support([responseData](http_request request)
{
http_response response(200);
// This sets the content_length
response.set_body(responseData);
// overwrite content_length to 0
response.headers().add(header_names::content_length, 0);
// add chunked transfer encoding
response.headers().add(header_names::transfer_encoding, U("chunked"));
// add connection=close header, connection SHOULD NOT be considered persistent' after the current request/response is complete
response.headers().add(header_names::connection, U("close"));
// respond
request.reply(response);
});
{
http_request msg(methods::GET);
http_response rsp = client.request(msg).get();
auto rsp_string = rsp.extract_string().get();
VERIFY_ARE_EQUAL(rsp_string, responseData);
}
listener.close().wait();
}
TEST_FIXTURE(uri_address, get_resp_stream)
{
http_client client(m_uri);
std::string responseData("Hello world");
web::http::experimental::listener::http_listener listener(m_uri);
listener.open().wait();
listener.support([responseData](http_request request)
{
streams::producer_consumer_buffer<uint8_t> buf;
http_response response(200);
response.set_body(buf.create_istream(), U("text/plain"));
response.headers().add(header_names::connection, U("close"));
request.reply(response);
VERIFY_ARE_EQUAL(buf.putn_nocopy((const uint8_t *)responseData.data(), responseData.size()).get(), responseData.size());
buf.close(std::ios_base::out).get();
});
{
http_request msg(methods::GET);
http_response rsp = client.request(msg).get();
streams::stringstreambuf data;
auto t = rsp.body().read_to_delim(data, (uint8_t)(' '));
t.then([&data](size_t size)
{
VERIFY_ARE_EQUAL(size, 5);
auto s = data.collection();
VERIFY_ARE_EQUAL(s, std::string("Hello"));
}).wait();
rsp.content_ready().wait();
}
listener.close().wait();
}
TEST_FIXTURE(uri_address, xfer_chunked_multiple_chunks)
{
// With chunked transfer-encoding, send 2 chunks of different sizes in the response
http_client client(m_uri);
// Send two chunks, note: second chunk is bigger than the first.
std::string firstChunk("abcdefghijklmnopqrst");
std::string secondChunk("abcdefghijklmnopqrstuvwxyz");
web::http::experimental::listener::http_listener listener(m_uri);
listener.open().wait();
listener.support([firstChunk, secondChunk](http_request request)
{
streams::producer_consumer_buffer<uint8_t> buf;
http_response response(200);
response.set_body(buf.create_istream(), U("text/plain"));
response.headers().add(header_names::connection, U("close"));
request.reply(response);
VERIFY_ARE_EQUAL(buf.putn_nocopy((const uint8_t *)firstChunk.data(), firstChunk.size()).get(), firstChunk.size());
buf.sync().get();
VERIFY_ARE_EQUAL(buf.putn_nocopy((const uint8_t *)secondChunk.data(), secondChunk.size()).get(), secondChunk.size());
buf.close(std::ios_base::out).get();
});
{
utility::string_t fname(U("xfer_chunked_multiple_chunks.txt"));
auto fstream = OPENSTR_W<uint8_t>(fname).get();
http_request msg(methods::GET);
msg.set_response_stream(fstream);
http_response rsp = client.request(msg).get();
rsp.content_ready().wait();
VERIFY_IS_TRUE(fstream.streambuf().is_open());
fstream.close().get();
std::string rsp_string;
size_t workload_size = firstChunk.size() + secondChunk.size();
rsp_string.resize(workload_size, 0);
streams::rawptr_buffer<uint8_t> buffer(reinterpret_cast<uint8_t *>(&rsp_string[0]), rsp_string.size());
streams::basic_istream<uint8_t> fistream = OPENSTR_R<uint8_t>(fname).get();
VERIFY_ARE_EQUAL(fistream.read_to_end(buffer).get(), workload_size);
VERIFY_ARE_EQUAL(rsp_string, firstChunk + secondChunk);
fistream.close().get();
}
listener.close().wait();
}
#endif
} // SUITE(responses)
}}}}