Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
P
pistache
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Libraries
pistache
Commits
feb47116
Commit
feb47116
authored
Nov 02, 2015
by
octal
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Reimplemented mime type parsing using the Stream* classes
parent
0d0d82fd
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
142 additions
and
80 deletions
+142
-80
src/mime.cc
src/mime.cc
+53
-79
src/stream.cc
src/stream.cc
+60
-0
src/stream.h
src/stream.h
+29
-0
tests/mime_test.cc
tests/mime_test.cc
+0
-1
No files found.
src/mime.cc
View file @
feb47116
...
...
@@ -53,14 +53,6 @@ MediaType::fromRaw(const char* str, size_t len) {
void
MediaType
::
parseRaw
(
const
char
*
str
,
size_t
len
)
{
auto
eof
=
[
&
](
const
char
*
p
)
{
return
p
-
str
==
len
;
};
auto
offset
=
[
&
](
const
char
*
ptr
)
{
return
static_cast
<
size_t
>
(
ptr
-
str
);
};
auto
raise
=
[
&
](
const
char
*
str
)
{
// TODO: eventually, we should throw a more generic exception
// that could then be catched in lower stack frames to rethrow
...
...
@@ -68,17 +60,8 @@ MediaType::parseRaw(const char* str, size_t len) {
throw
HttpError
(
Http
::
Code
::
Unsupported_Media_Type
,
str
);
};
// Macro to ensure that we do not overflow when comparing strings
// The trick here is to use sizeof on a raw string literal of type
// const char[N] instead of strlen to avoid additional
// runtime computation
#define MAX_SIZE(s) std::min(sizeof(s) - 1, len - offset(p))
// Parse type
const
char
*
p
=
static_cast
<
const
char
*>
(
memchr
(
str
,
'/'
,
len
));
if
(
p
==
NULL
)
{
raise
(
"Malformated Media Type"
);
}
StreamBuf
buf
(
str
,
len
);
StreamCursor
cursor
(
buf
);
raw_
=
string
(
str
,
len
);
...
...
@@ -93,10 +76,10 @@ MediaType::parseRaw(const char* str, size_t len) {
//
// Watch out, this pattern is repeated throughout the function
do
{
#define TYPE(val, s) \
if (m
emcmp(str, s, MAX_SIZE(s)) == 0
) { \
top = Type::val; \
break; \
#define TYPE(val, s)
\
if (m
atch_raw(s, sizeof s - 1, cursor)
) { \
top = Type::val;
\
break;
\
}
MIME_TYPES
#undef TYPE
...
...
@@ -105,21 +88,25 @@ MediaType::parseRaw(const char* str, size_t len) {
top_
=
top
;
if
(
!
match_literal
(
'/'
,
cursor
))
raise
(
"Malformed Media Type, expected a '/' after the top type"
);
if
(
cursor
.
eof
())
raise
(
"Malformed Media type, missing subtype"
);
// Parse subtype
Mime
::
Subtype
sub
;
++
p
;
if
(
eof
(
p
))
raise
(
"Malformed Media Type"
);
StreamCursor
::
Token
subToken
(
cursor
);
if
(
m
emcmp
(
p
,
"vnd."
,
MAX_SIZE
(
"vnd."
))
==
0
)
{
if
(
m
atch_raw
(
"vnd."
,
4
,
cursor
)
)
{
sub
=
Subtype
::
Vendor
;
}
else
{
do
{
#define SUB_TYPE(val, s) \
if (memcmp(p, s, MAX_SIZE(s)) == 0) { \
sub = Subtype::val; \
p += sizeof(s) - 1; \
break; \
#define SUB_TYPE(val, s) \
if (match_raw(s, sizeof s - 1, cursor)) { \
sub = Subtype::val; \
break; \
}
MIME_SUBTYPES
#undef SUB_TYPE
...
...
@@ -128,29 +115,28 @@ MediaType::parseRaw(const char* str, size_t len) {
}
if
(
sub
==
Subtype
::
Ext
||
sub
==
Subtype
::
Vendor
)
{
rawSubIndex
.
beg
=
offset
(
p
);
while
(
!
eof
(
p
)
&&
(
*
p
!=
';'
&&
*
p
!=
'+'
))
++
p
;
rawSubIndex
.
end
=
offset
(
p
)
-
1
;
(
void
)
match_until
({
';'
,
'+'
},
cursor
);
rawSubIndex
.
beg
=
subToken
.
start
()
;
rawSubIndex
.
end
=
subToken
.
end
(
)
-
1
;
}
sub_
=
sub
;
if
(
eof
(
p
))
return
;
if
(
cursor
.
eof
(
))
return
;
// Parse suffix
Mime
::
Suffix
suffix
=
Suffix
::
None
;
if
(
*
p
==
'+'
)
{
if
(
match_literal
(
'+'
,
cursor
)
)
{
++
p
;
if
(
cursor
.
eof
())
raise
(
"Malformed Media Type, expected suffix, got EOF"
)
;
if
(
eof
(
p
))
raise
(
"Malformed Media Type"
);
StreamCursor
::
Token
suffixToken
(
cursor
);
do
{
#define SUFFIX(val, s, _) \
if (memcmp(p, s, MAX_SIZE(s)) == 0) { \
suffix = Suffix::val; \
p += sizeof(s) - 1; \
break; \
#define SUFFIX(val, s, _) \
if (match_raw(s, sizeof s - 1, cursor)) { \
suffix = Suffix::val; \
break; \
}
MIME_SUFFIXES
#undef SUFFIX
...
...
@@ -158,66 +144,54 @@ MediaType::parseRaw(const char* str, size_t len) {
}
while
(
0
);
if
(
suffix
==
Suffix
::
Ext
)
{
rawSuffixIndex
.
beg
=
offset
(
p
);
while
(
!
eof
(
p
)
&&
(
*
p
!=
';'
&&
*
p
!=
'+'
))
++
p
;
rawSuffixIndex
.
end
=
offset
(
p
)
-
1
;
(
void
)
match_until
({
';'
,
'+'
},
cursor
);
rawSuffixIndex
.
beg
=
suffixToken
.
start
()
;
rawSuffixIndex
.
end
=
suffixToken
.
end
(
)
-
1
;
}
suffix_
=
suffix
;
}
// Parse parameters
while
(
!
eof
(
p
))
{
while
(
!
cursor
.
eof
(
))
{
if
(
*
p
==
';'
||
*
p
==
' '
)
{
if
(
eof
(
p
+
1
))
raise
(
"Malformed Media Type"
);
++
p
;
if
(
cursor
.
current
()
==
';'
||
cursor
.
current
()
==
' '
)
{
if
(
cursor
.
next
()
==
StreamCursor
::
Eof
)
raise
(
"Malformed Media Type, expected parameter got EOF"
);
cursor
.
advance
(
1
);
}
else
if
(
*
p
==
'q'
)
{
++
p
;
else
if
(
match_literal
(
'q'
,
cursor
))
{
if
(
eof
(
p
))
{
raise
(
"Invalid quality factor"
);
}
if
(
cursor
.
eof
())
raise
(
"Invalid quality factor"
);
if
(
*
p
==
'='
)
{
char
*
end
;
double
val
=
strtod
(
p
+
1
,
&
end
);
if
(
!
eof
(
end
)
&&
*
end
!=
';'
&&
*
end
!=
' '
)
{
if
(
match_literal
(
'='
,
cursor
))
{
double
val
;
if
(
!
match_double
(
&
val
,
cursor
))
raise
(
"Invalid quality factor"
);
}
q_
=
Some
(
Q
::
fromFloat
(
val
));
p
=
end
;
}
else
{
raise
(
"
Invalid
quality factor"
);
raise
(
"
Missing
quality factor"
);
}
}
else
{
const
char
*
begin
=
p
;
while
(
!
eof
(
p
)
&&
*
p
!=
'='
)
++
p
;
if
(
eof
(
p
))
raise
(
"Unfinished Media Type parameter"
);
const
char
*
end
=
p
;
++
p
;
if
(
eof
(
p
))
raise
(
"Unfinished Media Type parameter"
);
StreamCursor
::
Token
keyToken
(
cursor
);
(
void
)
match_until
(
'='
,
cursor
);
std
::
string
key
(
begin
,
end
);
if
(
cursor
.
eof
()
||
cursor
.
next
()
==
StreamCursor
::
Eof
)
raise
(
"Unfinished Media Type parameter"
);
begin
=
p
;
while
(
!
eof
(
p
)
&&
*
p
!=
' '
&&
*
p
!=
';'
)
++
p
;
std
::
string
value
(
begin
,
p
);
params
.
insert
(
std
::
make_pair
(
std
::
move
(
key
),
std
::
move
(
value
)));
std
::
string
key
=
keyToken
.
text
();
cursor
.
advance
(
1
);
StreamCursor
::
Token
valueToken
(
cursor
);
(
void
)
match_until
({
' '
,
';'
},
cursor
);
params
.
insert
(
std
::
make_pair
(
std
::
move
(
key
),
valueToken
.
text
()));
}
}
#undef MAX_SIZE
}
void
...
...
@@ -275,8 +249,8 @@ MediaType::toString() const {
}
};
// @Improvement: allocating and concatenating many small strings is probably slow
std
::
string
res
;
res
.
reserve
(
128
);
res
+=
topString
(
top_
);
res
+=
"/"
;
res
+=
subString
(
sub_
);
...
...
src/stream.cc
View file @
feb47116
...
...
@@ -4,6 +4,8 @@
*/
#include "stream.h"
#include <algorithm>
#include <iostream>
void
BasicStreamBuf
::
setArea
(
const
char
*
begin
,
const
char
*
end
,
const
char
*
brk
)
{
...
...
@@ -104,3 +106,61 @@ match_raw(const void* buf, size_t len, StreamCursor& cursor) {
return
false
;
}
bool
match_literal
(
char
c
,
StreamCursor
&
cursor
,
CaseSensitivity
cs
)
{
if
(
cursor
.
eof
())
return
false
;
char
lhs
=
(
cs
==
CaseSensitivity
::
Sensitive
?
c
:
std
::
tolower
(
c
));
char
rhs
=
(
cs
==
CaseSensitivity
::
Insensitive
?
cursor
.
current
()
:
std
::
tolower
(
cursor
.
current
()));
if
(
lhs
==
rhs
)
{
cursor
.
advance
(
1
);
return
true
;
}
return
false
;
}
bool
match_until
(
char
c
,
StreamCursor
&
cursor
,
CaseSensitivity
cs
)
{
return
match_until
(
{
c
},
cursor
,
cs
);
}
bool
match_until
(
std
::
initializer_list
<
char
>
chars
,
StreamCursor
&
cursor
,
CaseSensitivity
cs
)
{
if
(
cursor
.
eof
())
return
false
;
auto
find
=
[
&
](
char
val
)
{
for
(
auto
c
:
chars
)
{
char
lhs
=
cs
==
CaseSensitivity
::
Sensitive
?
c
:
std
::
tolower
(
c
);
char
rhs
=
cs
==
CaseSensitivity
::
Insensitive
?
val
:
std
::
tolower
(
val
);
if
(
lhs
==
rhs
)
return
true
;
}
return
false
;
};
while
(
!
cursor
.
eof
())
{
const
char
c
=
cursor
.
current
();
if
(
find
(
c
))
return
true
;
cursor
.
advance
(
1
);
}
return
false
;
}
bool
match_double
(
double
*
val
,
StreamCursor
&
cursor
)
{
// @Todo: strtod does not support a length argument
char
*
end
;
*
val
=
strtod
(
cursor
.
offset
(),
&
end
);
if
(
end
==
cursor
.
offset
())
return
false
;
cursor
.
advance
(
static_cast
<
ptrdiff_t
>
(
end
-
cursor
.
offset
()));
return
true
;
}
src/stream.h
View file @
feb47116
...
...
@@ -87,6 +87,27 @@ public:
static
constexpr
int
Eof
=
-
1
;
struct
Token
{
Token
(
StreamCursor
&
cursor
)
:
cursor
(
cursor
)
,
pos
(
cursor
.
value
)
{
}
size_t
start
()
const
{
return
pos
;
}
size_t
end
()
const
{
return
cursor
.
value
;
}
std
::
string
text
()
{
const
char
*
beg
=
cursor
.
offset
(
pos
);
return
std
::
string
(
beg
,
end
()
-
start
());
}
private:
StreamCursor
&
cursor
;
size_t
pos
;
};
struct
Revert
{
Revert
(
StreamCursor
&
cursor
)
:
cursor
(
cursor
)
...
...
@@ -136,4 +157,12 @@ private:
size_t
value
;
};
enum
class
CaseSensitivity
{
Sensitive
,
Insensitive
};
bool
match_raw
(
const
void
*
buf
,
size_t
len
,
StreamCursor
&
cursor
);
bool
match_literal
(
char
c
,
StreamCursor
&
cursor
,
CaseSensitivity
cs
=
CaseSensitivity
::
Insensitive
);
bool
match_until
(
char
c
,
StreamCursor
&
cursor
,
CaseSensitivity
cs
=
CaseSensitivity
::
Insensitive
);
bool
match_until
(
std
::
initializer_list
<
char
>
chars
,
StreamCursor
&
cursor
,
CaseSensitivity
cs
=
CaseSensitivity
::
Insensitive
);
bool
match_double
(
double
*
val
,
StreamCursor
&
cursor
);
tests/mime_test.cc
View file @
feb47116
...
...
@@ -116,7 +116,6 @@ TEST(mime_test, valid_parsing_test) {
});
}
TEST
(
mime_test
,
invalid_parsing
)
{
ASSERT_THROW
(
MediaType
::
fromString
(
"applicationjson"
),
HttpError
);
ASSERT_THROW
(
MediaType
::
fromString
(
"my/json"
),
HttpError
);
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment