Skip to content

Trim whitespace and nulls the same way. #1233

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Aug 28, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions Release/include/cpprest/asyncrt_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,26 @@ inline bool __cdecl is_alnum(Elem ch) CPPREST_NOEXCEPT
return (uch <= static_cast<UElem>('z') && is_alnum(static_cast<unsigned char>(uch)));
}

/// <summary>
/// Our own implementation of whitespace test instead of std::isspace to avoid
/// taking global lock for performance reasons.
/// The following characters are considered whitespace:
/// 0x09 == Horizontal Tab
/// 0x0A == Line Feed
/// 0x0B == Vertical Tab
/// 0x0C == Form Feed
/// 0x0D == Carrage Return
/// 0x20 == Space
/// </summary>
template<class Elem>
inline bool __cdecl is_space(Elem ch) CPPREST_NOEXCEPT
{
// assumes 'x' == L'x' for the ASCII range
typedef typename std::make_unsigned<Elem>::type UElem;
const auto uch = static_cast<UElem>(ch);
return uch == 0x20u || (uch >= 0x09u && uch <= 0x0Du);
}

/// <summary>
/// Simplistic implementation of make_unique. A better implementation would be based on variadic templates
/// and therefore not be compatible with Dev10.
Expand Down
48 changes: 2 additions & 46 deletions Release/src/http/client/http_client_winhttp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include "stdafx.h"

#include "../common/x509_cert_utilities.h"
#include "../common/internal_http_helpers.h"
#include "cpprest/http_headers.h"
#include "http_client_impl.h"
#include <Wincrypt.h>
Expand Down Expand Up @@ -99,51 +100,6 @@ static http::status_code parse_status_code(HINTERNET request_handle)
return (unsigned short)_wtoi(buffer.c_str());
}

// Helper function to trim leading and trailing null characters from a string.
static void trim_nulls(utility::string_t& str)
{
if (str.empty())
{
return;
}

auto first = str.begin();
auto last = str.end();

if (*first)
{
--last;
if (*last)
{
// no nulls to remove
return;
}

// nulls at the back to remove
do
{
--last;
} while (*last == utility::char_t {});
++last;
str.erase(last, str.end());
return;
}

// nulls at the front, and maybe the back, to remove
first = std::find_if(str.begin(), last, [](const utility::char_t c) { return c != utility::char_t {}; });

if (first != last)
{
do
{
--last;
} while (*last == utility::char_t {});
++last;
}

str.assign(first, last);
}

// Helper function to get the reason phrase from a WinHTTP response.
static utility::string_t parse_reason_phrase(HINTERNET request_handle)
{
Expand All @@ -159,7 +115,7 @@ static utility::string_t parse_reason_phrase(HINTERNET request_handle)
&length,
WINHTTP_NO_HEADER_INDEX);
// WinHTTP reports back the wrong length, trim any null characters.
trim_nulls(phrase);
::web::http::details::trim_nulls(phrase);
return phrase;
}

Expand Down
73 changes: 63 additions & 10 deletions Release/src/http/common/internal_http_helpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

#pragma once

#include "cpprest/asyncrt_utils.h"
#include "cpprest/details/basic_types.h"
#include <string>

Expand All @@ -19,19 +20,71 @@ namespace details
/// </summary>
utility::string_t get_default_reason_phrase(status_code code);

// simple helper functions to trim whitespace.
template<class Char, class Fn>
void trim_if(std::basic_string<Char>& str, Fn test)
{
if (str.empty())
{
return;
}

auto first = str.begin();
auto last = str.end();

if (test(*first))
{
// removals at the front, and maybe the back
for (;;)
{
++first;
if (first == last)
{
// all removals
str.clear();
return;
}

if (!test(*first))
{
break;
}
}

do
{
--last;
} while (test(*last));
++last;
str.assign(first, last);
return;
}

// no removals at the front, only maybe the back
--last;
if (!test(*last))
{
// no removals at all
return;
}

do
{
--last;
} while (test(*last));
++last;
str.erase(last, str.end());
}

template<class Char>
void trim_nulls(std::basic_string<Char>& str)
{
trim_if(str, [](const Char c) { return c == Char {}; });
}

template<class Char>
void trim_whitespace(std::basic_string<Char>& str)
{
size_t index;
// trim left whitespace
for (index = 0; index < str.size() && isspace(str[index]); ++index)
;
str.erase(0, index);
// trim right whitespace
for (index = str.size(); index > 0 && isspace(str[index - 1]); --index)
;
str.erase(index);
trim_if(str, [](const Char c) { return ::utility::details::is_space(c); });
}

bool validate_method(const utility::string_t& method);
Expand Down