Skip to content

Commit e0c1418

Browse files
committed
Adding force option to extract_json and extract_string, this allows developers to use these methods regardless of content type/mime type. This helps workaround or deal with rare or uncommon mime types that we don't expect or know about.
1 parent e410cfb commit e0c1418

File tree

4 files changed

+111
-35
lines changed

4 files changed

+111
-35
lines changed

Release/include/cpprest/http_msg.h

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -224,8 +224,8 @@ class http_msg_base
224224
_ASYNCRTIMP void set_body(concurrency::streams::istream instream, utility::string_t contentType);
225225
_ASYNCRTIMP void set_body(concurrency::streams::istream instream, utility::size64_t contentLength, utility::string_t contentType);
226226

227-
_ASYNCRTIMP utility::string_t _extract_string();
228-
_ASYNCRTIMP json::value _extract_json();
227+
_ASYNCRTIMP utility::string_t _extract_string(bool force = false);
228+
_ASYNCRTIMP json::value _extract_json(bool force = false);
229229
_ASYNCRTIMP std::vector<unsigned char> _extract_vector();
230230

231231
virtual _ASYNCRTIMP utility::string_t to_string() const;
@@ -445,22 +445,24 @@ class http_response
445445
/// Extracts the body of the response message as a string value, checking that the content type is a MIME text type.
446446
/// A body can only be extracted once because in some cases an optimization is made where the data is 'moved' out.
447447
/// </summary>
448+
/// <param name="force">If true, ignores the Content-Type header and assumes UTF-8.</param>
448449
/// <returns>String containing body of the message.</returns>
449-
pplx::task<utility::string_t> extract_string() const
450+
pplx::task<utility::string_t> extract_string(bool force = false) const
450451
{
451452
auto impl = _m_impl;
452-
return pplx::create_task(_m_impl->_get_data_available()).then([impl](utility::size64_t) { return impl->_extract_string(); });
453+
return pplx::create_task(_m_impl->_get_data_available()).then([impl, force](utility::size64_t) { return impl->_extract_string(force); });
453454
}
454455

455456
/// <summary>
456457
/// Extracts the body of the response message into a json value, checking that the content type is application\json.
457458
/// A body can only be extracted once because in some cases an optimization is made where the data is 'moved' out.
458459
/// </summary>
460+
/// <param name="force">If true, ignores the Content-Type header and assumes UTF-8.</param>
459461
/// <returns>JSON value from the body of this message.</returns>
460-
pplx::task<json::value> extract_json() const
462+
pplx::task<json::value> extract_json(bool force = false) const
461463
{
462464
auto impl = _m_impl;
463-
return pplx::create_task(_m_impl->_get_data_available()).then([impl](utility::size64_t) { return impl->_extract_json(); });
465+
return pplx::create_task(_m_impl->_get_data_available()).then([impl, force](utility::size64_t) { return impl->_extract_json(force); });
464466
}
465467

466468
/// <summary>
@@ -826,22 +828,24 @@ class http_request
826828
/// Extract the body of the request message as a string value, checking that the content type is a MIME text type.
827829
/// A body can only be extracted once because in some cases an optimization is made where the data is 'moved' out.
828830
/// </summary>
831+
/// <param name="force">If true, ignores the Content-Type header and assumes UTF-8.</param>
829832
/// <returns>String containing body of the message.</returns>
830-
pplx::task<utility::string_t> extract_string()
833+
pplx::task<utility::string_t> extract_string(bool force = false)
831834
{
832835
auto impl = _m_impl;
833-
return pplx::create_task(_m_impl->_get_data_available()).then([impl](utility::size64_t) { return impl->_extract_string(); });
836+
return pplx::create_task(_m_impl->_get_data_available()).then([impl, force](utility::size64_t) { return impl->_extract_string(force); });
834837
}
835838

836839
/// <summary>
837840
/// Extracts the body of the request message into a json value, checking that the content type is application\json.
838841
/// A body can only be extracted once because in some cases an optimization is made where the data is 'moved' out.
839842
/// </summary>
843+
/// <param name="force">If true, ignores the Content-Type header and assumes UTF-8.</param>
840844
/// <returns>JSON value from the body of this message.</returns>
841-
pplx::task<json::value> extract_json() const
845+
pplx::task<json::value> extract_json(bool force = false) const
842846
{
843847
auto impl = _m_impl;
844-
return pplx::create_task(_m_impl->_get_data_available()).then([impl](utility::size64_t) { return impl->_extract_json(); });
848+
return pplx::create_task(_m_impl->_get_data_available()).then([impl, force](utility::size64_t) { return impl->_extract_json(force); });
845849
}
846850

847851
/// <summary>

Release/src/http/common/http_msg.cpp

Lines changed: 31 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -196,21 +196,24 @@ void http_msg_base::_complete(utility::size64_t body_size, std::exception_ptr ex
196196
}
197197
}
198198

199-
utility::string_t details::http_msg_base::_extract_string()
199+
utility::string_t details::http_msg_base::_extract_string(bool force)
200200
{
201-
utility::string_t content, charset;
202-
parse_content_type_and_charset(headers().content_type(), content, charset);
203-
204-
// If no Content-Type then just return an empty string.
205-
if(content.empty())
201+
utility::string_t content, charset = charset_types::utf8;
202+
if (!force)
206203
{
207-
return utility::string_t();
208-
}
204+
parse_content_type_and_charset(headers().content_type(), content, charset);
209205

210-
// Content-Type must have textual type.
211-
if(!is_content_type_textual(content))
212-
{
213-
throw http_exception(textual_content_type_missing);
206+
// If no Content-Type then just return an empty string.
207+
if (content.empty())
208+
{
209+
return utility::string_t();
210+
}
211+
212+
// Content-Type must have textual type.
213+
if (!is_content_type_textual(content))
214+
{
215+
throw http_exception(textual_content_type_missing);
216+
}
214217
}
215218

216219
if (!instream())
@@ -287,22 +290,25 @@ utility::string_t details::http_msg_base::_extract_string()
287290
}
288291
}
289292

290-
json::value details::http_msg_base::_extract_json()
293+
json::value details::http_msg_base::_extract_json(bool force)
291294
{
292-
utility::string_t content, charset;
293-
parse_content_type_and_charset(headers().content_type(), content, charset);
294-
295-
// If no Content-Type then just return a null json value.
296-
if(content.empty())
295+
utility::string_t content, charset = charset_types::utf8;
296+
if (!force)
297297
{
298-
return json::value();
299-
}
298+
parse_content_type_and_charset(headers().content_type(), content, charset);
300299

301-
// Content-Type must be "application/json" or one of the unofficial, but common JSON types.
302-
if(!is_content_type_json(content))
303-
{
304-
const utility::string_t actualContentType = utility::conversions::to_string_t(content);
305-
throw http_exception((_XPLATSTR("Content-Type must be JSON to extract (is: ") + actualContentType + _XPLATSTR(")")));
300+
// If no Content-Type then just return a null json value.
301+
if (content.empty())
302+
{
303+
return json::value();
304+
}
305+
306+
// Content-Type must be "application/json" or one of the unofficial, but common JSON types.
307+
if (!is_content_type_json(content))
308+
{
309+
const utility::string_t actualContentType = utility::conversions::to_string_t(content);
310+
throw http_exception((_XPLATSTR("Content-Type must be JSON to extract (is: ") + actualContentType + _XPLATSTR(")")));
311+
}
306312
}
307313

308314
if (!instream())

Release/tests/Functional/http/client/response_extract_tests.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,16 @@ TEST_FIXTURE(uri_address, extract_string)
153153
#endif
154154
}
155155

156+
TEST_FIXTURE(uri_address, extract_string_force)
157+
{
158+
test_http_server::scoped_server scoped(m_uri);
159+
http_client client(m_uri);
160+
161+
std::string data("YOU KNOW ITITITITI");
162+
http_response rsp = send_request_response(scoped.server(), &client, U("bad unknown charset"), data);
163+
VERIFY_ARE_EQUAL(to_string_t(data), rsp.extract_string(true).get());
164+
}
165+
156166
TEST_FIXTURE(uri_address, extract_string_incorrect)
157167
{
158168
test_http_server::scoped_server scoped(m_uri);
@@ -272,6 +282,16 @@ TEST_FIXTURE(uri_address, extract_json)
272282
VERIFY_ARE_EQUAL(data.serialize(), rsp.extract_json().get().serialize());
273283
}
274284

285+
TEST_FIXTURE(uri_address, extract_json_force)
286+
{
287+
test_http_server::scoped_server scoped(m_uri);
288+
http_client client(m_uri);
289+
290+
json::value data = json::value::string(U("JSON string object"));
291+
http_response rsp = send_request_response(scoped.server(), &client, U("bad charset"), to_utf8string(data.serialize()));
292+
VERIFY_ARE_EQUAL(data.serialize(), rsp.extract_json(true).get().serialize());
293+
}
294+
275295
TEST_FIXTURE(uri_address, extract_json_incorrect)
276296
{
277297
test_http_server::scoped_server scoped(m_uri);

Release/tests/Functional/http/listener/request_extract_tests.cpp

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,28 @@ TEST_FIXTURE(uri_address, extract_string)
5252
listener.close().wait();
5353
}
5454

55+
TEST_FIXTURE(uri_address, extract_string_force)
56+
{
57+
http_listener listener(m_uri);
58+
listener.open().wait();
59+
test_http_client::scoped_client client(m_uri);
60+
test_http_client * p_client = client.client();
61+
std::string data("HEHEHE");
62+
63+
listener.support([&](http_request request)
64+
{
65+
VERIFY_ARE_EQUAL(to_string_t(data), request.extract_string(true).get());
66+
request.reply(status_codes::OK);
67+
});
68+
VERIFY_ARE_EQUAL(0, p_client->request(methods::PUT, U(""), U("unknown charset"), data));
69+
p_client->next_response().then([](test_response *p_response)
70+
{
71+
http_asserts::assert_test_response_equals(p_response, status_codes::OK);
72+
}).wait();
73+
74+
listener.close().wait();
75+
}
76+
5577
TEST_FIXTURE(uri_address, extract_json)
5678
{
5779
http_listener listener(m_uri);
@@ -78,6 +100,30 @@ TEST_FIXTURE(uri_address, extract_json)
78100
listener.close().wait();
79101
}
80102

103+
TEST_FIXTURE(uri_address, extract_json_force)
104+
{
105+
http_listener listener(m_uri);
106+
listener.open().wait();
107+
test_http_client::scoped_client client(m_uri);
108+
test_http_client * p_client = client.client();
109+
110+
json::value j(true);
111+
listener.support([&](http_request request)
112+
{
113+
const json::value j_found = request.extract_json(true).get();
114+
VERIFY_ARE_EQUAL(j.serialize(), j_found.serialize());
115+
request.reply(status_codes::OK);
116+
});
117+
std::string data = to_utf8string(j.serialize());
118+
VERIFY_ARE_EQUAL(0, p_client->request(methods::PUT, U(""), U("unknown charset"), data));
119+
p_client->next_response().then([](test_response *p_response)
120+
{
121+
http_asserts::assert_test_response_equals(p_response, status_codes::OK);
122+
}).wait();
123+
124+
listener.close().wait();
125+
}
126+
81127
TEST_FIXTURE(uri_address, empty_vector)
82128
{
83129
http_listener listener(m_uri);

0 commit comments

Comments
 (0)