Created
December 7, 2008 20:32
-
-
Save aristidb/33238 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// checks if `c' is a space or a h-tab (see RFC 2616 chapter 2.2) | |
inline bool isspht(char c) { | |
return c == ' ' || c == '\t'; | |
} | |
template<class Source, typename Char> | |
bool expect(Source &in, Char c) { | |
int t = boost::iostreams::get(in); | |
if(t == c) | |
return true; | |
else if(t == EOF) | |
throw remote_close(); | |
boost::iostreams::putback(in, t); | |
return false; | |
} | |
template<class Source> | |
int remove_spaces(Source &in) { | |
int c; | |
do { | |
c = boost::iostreams::get(in); | |
} while(isspht(c)); | |
boost::iostreams::putback(in, c); | |
return c; | |
} | |
typedef boost::tuple<std::string, std::string, std::string> request_line; | |
enum { REQUEST_METHOD, REQUEST_URI, REQUEST_HTTP_VERSION }; | |
template<class Source> | |
void get_until(char end, Source &in, std::string &ret, | |
bool allow_cr_or_lf = false, | |
std::size_t max_length = 0) { | |
int t; | |
while ((t = boost::iostreams::get(in)) != end) { | |
if(t == EOF) | |
throw remote_close(); | |
if (!allow_cr_or_lf && (t == '\r' || t == '\n')) | |
throw bad_format(); | |
if (max_length != 0 && ret.size() >= max_length) | |
throw bad_format(); | |
ret += t; | |
} | |
} | |
#define REST_RESERVE_LIKE(x, s) \ | |
(x).reserve(sizeof(s) - 1) | |
template<class Source> | |
request_line get_request_line( | |
Source &in, | |
boost::tuple<std::size_t, std::size_t, std::size_t> const &max_sizes = | |
boost::make_tuple(0, 0, 0)) | |
{ | |
while (expect(in, '\r')) | |
if (!expect(in, '\n')) | |
throw bad_format(); | |
request_line x; | |
REST_RESERVE_LIKE(x.get<REQUEST_METHOD>(), "OPTIONS"); | |
get_until(' ', in, x.get<REQUEST_METHOD>(), false, | |
max_sizes.get<REQUEST_METHOD>()); | |
if (x.get<REQUEST_METHOD>().empty()) | |
throw bad_format(); | |
get_until(' ', in, x.get<REQUEST_URI>(), false, | |
max_sizes.get<REQUEST_URI>()); | |
if (x.get<REQUEST_URI>().empty()) | |
throw bad_format(); | |
REST_RESERVE_LIKE(x.get<REQUEST_HTTP_VERSION>(), "HTTP/1.0"); | |
get_until('\r', in, x.get<REQUEST_HTTP_VERSION>(), false, | |
max_sizes.get<REQUEST_HTTP_VERSION>()); | |
if (x.get<REQUEST_HTTP_VERSION>().empty()) | |
throw bad_format(); | |
if (!expect(in, '\n')) | |
throw bad_format(); | |
return x; | |
} | |
// reads a header field from `in' and adds it to `fields' | |
// see RFC 2616 chapter 4.2 | |
// Warning: Field names are converted to all lower-case! | |
template<class Source, class HeaderFields> | |
void get_header_field( | |
Source &in, | |
HeaderFields &fields, | |
std::size_t max_name_length, | |
std::size_t max_value_length) | |
{ | |
namespace io = boost::iostreams; | |
std::string name; | |
REST_RESERVE_LIKE(name, "If-Unmodified-Since"); | |
get_until(':', in, name, false, max_name_length); | |
boost::algorithm::to_lower(name); | |
remove_spaces(in); | |
std::string &value = fields[name]; | |
if (!value.empty()) | |
value += ", "; | |
for (;;) { | |
int t; | |
if ((t = io::get(in)) == EOF) | |
throw remote_close(); | |
if (t == '\n' || t == '\r') { | |
// Newlines in header fields are allowed when followed | |
// by an SP (space or horizontal tab) | |
if (t == '\r') | |
expect(in, '\n'); | |
t = io::get(in); | |
if (!isspht(t)) { | |
io::putback(in, t); | |
break; | |
} | |
remove_spaces(in); | |
value += ' '; | |
} else { | |
if (max_value_length != 0 && value.size() >= max_value_length) | |
throw bad_format(); | |
value += t; | |
} | |
} | |
std::string::reverse_iterator xend = value.rbegin(); | |
while (isspht(*xend)) | |
++xend; | |
value.erase(xend.base(), value.end()); | |
} | |
#undef REST_RESERVE_LIKE | |
template<class Source, class HeaderFields> | |
void read_headers( | |
Source &source, | |
HeaderFields &fields, | |
std::size_t max_name_length = 0, | |
std::size_t max_value_length = 0, | |
std::size_t max_header_count = 0) | |
{ | |
std::size_t n = 0; | |
for (;;) { | |
if (expect(source, '\r')) { | |
if (!expect(source, '\n')) | |
throw bad_format(); | |
return; | |
} | |
if (max_header_count != 0 && ++n > max_header_count) | |
throw bad_format(); | |
get_header_field(source, fields, max_name_length, max_value_length); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment