Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
V
vlc
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Redmine
Redmine
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Operations
Operations
Metrics
Environments
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
videolan
vlc
Commits
052df94a
Commit
052df94a
authored
Nov 19, 2015
by
Rémi Denis-Courmont
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
HTTP/2 frame parser
parent
a64f235d
Changes
2
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
711 additions
and
0 deletions
+711
-0
modules/access/http/h2frame.c
modules/access/http/h2frame.c
+669
-0
modules/access/http/h2frame.h
modules/access/http/h2frame.h
+42
-0
No files found.
modules/access/http/h2frame.c
View file @
052df94a
...
@@ -62,6 +62,32 @@ vlc_h2_frame_alloc(uint_fast8_t type, uint_fast8_t flags,
...
@@ -62,6 +62,32 @@ vlc_h2_frame_alloc(uint_fast8_t type, uint_fast8_t flags,
#define vlc_h2_frame_payload(f) ((f)->data + 9)
#define vlc_h2_frame_payload(f) ((f)->data + 9)
static
uint_fast32_t
vlc_h2_frame_length
(
const
struct
vlc_h2_frame
*
f
)
{
const
uint8_t
*
buf
=
f
->
data
;
return
(
buf
[
0
]
<<
16
)
|
(
buf
[
1
]
<<
8
)
|
buf
[
2
];
}
size_t
vlc_h2_frame_size
(
const
struct
vlc_h2_frame
*
f
)
{
return
9
+
vlc_h2_frame_length
(
f
);
}
static
uint_fast8_t
vlc_h2_frame_type
(
const
struct
vlc_h2_frame
*
f
)
{
return
f
->
data
[
3
];
}
static
uint_fast8_t
vlc_h2_frame_flags
(
const
struct
vlc_h2_frame
*
f
)
{
return
f
->
data
[
4
];
}
static
uint_fast32_t
vlc_h2_frame_id
(
const
struct
vlc_h2_frame
*
f
)
{
return
GetDWBE
(
f
->
data
+
5
)
&
0x7FFFFFFF
;
}
enum
{
enum
{
VLC_H2_FRAME_DATA
,
VLC_H2_FRAME_DATA
,
VLC_H2_FRAME_HEADERS
,
VLC_H2_FRAME_HEADERS
,
...
@@ -75,6 +101,26 @@ enum {
...
@@ -75,6 +101,26 @@ enum {
VLC_H2_FRAME_CONTINUATION
,
VLC_H2_FRAME_CONTINUATION
,
};
};
static
const
char
*
vlc_h2_type_name
(
uint_fast8_t
type
)
{
static
const
char
names
[][
14
]
=
{
[
VLC_H2_FRAME_DATA
]
=
"DATA"
,
[
VLC_H2_FRAME_HEADERS
]
=
"HEADERS"
,
[
VLC_H2_FRAME_PRIORITY
]
=
"PRIORITY"
,
[
VLC_H2_FRAME_RST_STREAM
]
=
"RST_STREAM"
,
[
VLC_H2_FRAME_SETTINGS
]
=
"SETTINGS"
,
[
VLC_H2_FRAME_PUSH_PROMISE
]
=
"PUSH_PROMISE"
,
[
VLC_H2_FRAME_PING
]
=
"PING"
,
[
VLC_H2_FRAME_GOAWAY
]
=
"GOAWAY"
,
[
VLC_H2_FRAME_WINDOW_UPDATE
]
=
"WINDOW_UPDATE"
,
[
VLC_H2_FRAME_CONTINUATION
]
=
"CONTINUATION"
,
};
if
(
type
>=
(
sizeof
(
names
)
/
sizeof
(
names
[
0
]))
||
names
[
type
][
0
]
==
'\0'
)
return
"<unknown>"
;
return
names
[
type
];
}
enum
{
enum
{
VLC_H2_DATA_END_STREAM
=
0x01
,
VLC_H2_DATA_END_STREAM
=
0x01
,
VLC_H2_DATA_PADDED
=
0x08
,
VLC_H2_DATA_PADDED
=
0x08
,
...
@@ -347,3 +393,626 @@ const char *vlc_h2_strerror(uint_fast32_t code)
...
@@ -347,3 +393,626 @@ const char *vlc_h2_strerror(uint_fast32_t code)
return
"Unknown error"
;
return
"Unknown error"
;
return
names
[
code
];
return
names
[
code
];
}
}
void
(
vlc_h2_frame_dump
)(
vlc_object_t
*
obj
,
const
struct
vlc_h2_frame
*
f
,
const
char
*
msg
)
{
size_t
len
=
vlc_h2_frame_length
(
f
);
uint_fast8_t
type
=
vlc_h2_frame_type
(
f
);
uint_fast8_t
flags
=
vlc_h2_frame_flags
(
f
);
uint_fast32_t
sid
=
vlc_h2_frame_id
(
f
);
if
(
sid
!=
0
)
msg_Dbg
(
obj
,
"%s %s (0x%02"
PRIxFAST8
") frame of %"
PRIuFAST32
" bytes, "
"flags 0x%02"
PRIxFAST8
", stream %"
PRIuFAST32
,
msg
,
vlc_h2_type_name
(
type
),
type
,
len
,
flags
,
sid
);
else
msg_Dbg
(
obj
,
"%s %s (0x%02"
PRIxFAST8
") frame of %"
PRIuFAST32
" bytes, "
"flags 0x%02"
PRIxFAST8
", global"
,
msg
,
vlc_h2_type_name
(
type
),
type
,
len
,
flags
);
}
const
uint8_t
*
(
vlc_h2_frame_data_get
)(
const
struct
vlc_h2_frame
*
f
,
size_t
*
restrict
lenp
)
{
assert
(
vlc_h2_frame_type
(
f
)
==
VLC_H2_FRAME_DATA
);
size_t
len
=
vlc_h2_frame_length
(
f
);
uint_fast8_t
flags
=
vlc_h2_frame_flags
(
f
);
const
uint8_t
*
ptr
=
vlc_h2_frame_payload
(
f
);
/* At this point, the frame has already been validated by the parser. */
if
(
flags
&
VLC_H2_DATA_PADDED
)
{
assert
(
len
>=
1u
&&
len
>=
1u
+
ptr
[
0
]);
len
-=
1u
+
*
(
ptr
++
);
}
*
lenp
=
len
;
return
ptr
;
}
typedef
int
(
*
vlc_h2_parser
)(
struct
vlc_h2_parser
*
,
struct
vlc_h2_frame
*
,
size_t
,
uint_fast32_t
);
/** HTTP/2 incoming frames parser */
struct
vlc_h2_parser
{
void
*
opaque
;
const
struct
vlc_h2_parser_cbs
*
cbs
;
vlc_h2_parser
parser
;
/*< Parser state / callback for next frame */
struct
{
uint32_t
sid
;
/*< Ongoing stream identifier */
bool
eos
;
/*< End of stream after headers block */
size_t
len
;
/*< Compressed headers buffer length */
uint8_t
*
buf
;
/*< Compressed headers buffer base address */
struct
hpack_decoder
*
decoder
;
/*< HPACK decompressor state */
}
headers
;
/*< Compressed headers reception state */
uint32_t
rcwd_size
;
/*< Receive congestion window (bytes) */
};
static
int
vlc_h2_parse_generic
(
struct
vlc_h2_parser
*
,
struct
vlc_h2_frame
*
,
size_t
,
uint_fast32_t
);
static
int
vlc_h2_parse_headers_block
(
struct
vlc_h2_parser
*
,
struct
vlc_h2_frame
*
,
size_t
,
uint_fast32_t
);
static
int
vlc_h2_parse_error
(
struct
vlc_h2_parser
*
p
,
uint_fast32_t
code
)
{
p
->
cbs
->
error
(
p
->
opaque
,
code
);
return
-
1
;
}
static
int
vlc_h2_stream_error
(
struct
vlc_h2_parser
*
p
,
uint_fast32_t
id
,
uint_fast32_t
code
)
{
return
p
->
cbs
->
stream_error
(
p
->
opaque
,
id
,
code
);
}
static
void
*
vlc_h2_stream_lookup
(
struct
vlc_h2_parser
*
p
,
uint_fast32_t
id
)
{
return
p
->
cbs
->
stream_lookup
(
p
->
opaque
,
id
);
}
static
void
vlc_h2_parse_headers_start
(
struct
vlc_h2_parser
*
p
,
uint_fast32_t
sid
,
bool
eos
)
{
assert
(
sid
!=
0
);
assert
(
p
->
headers
.
sid
==
0
);
p
->
parser
=
vlc_h2_parse_headers_block
;
p
->
headers
.
sid
=
sid
;
p
->
headers
.
eos
=
eos
;
p
->
headers
.
len
=
0
;
}
static
int
vlc_h2_parse_headers_append
(
struct
vlc_h2_parser
*
p
,
const
uint8_t
*
data
,
size_t
len
)
{
assert
(
p
->
headers
.
sid
!=
0
);
if
(
p
->
headers
.
len
+
len
>
65536
)
return
vlc_h2_parse_error
(
p
,
VLC_H2_INTERNAL_ERROR
);
uint8_t
*
buf
=
realloc
(
p
->
headers
.
buf
,
p
->
headers
.
len
+
len
);
if
(
unlikely
(
buf
==
NULL
))
return
vlc_h2_parse_error
(
p
,
VLC_H2_INTERNAL_ERROR
);
p
->
headers
.
buf
=
buf
;
memcpy
(
p
->
headers
.
buf
+
p
->
headers
.
len
,
data
,
len
);
p
->
headers
.
len
+=
len
;
return
0
;
}
static
int
vlc_h2_parse_headers_end
(
struct
vlc_h2_parser
*
p
)
{
char
*
headers
[
VLC_H2_MAX_HEADERS
][
2
];
/* TODO: limit total decompressed size of the headers list */
int
val
=
hpack_decode
(
p
->
headers
.
decoder
,
p
->
headers
.
buf
,
p
->
headers
.
len
,
headers
,
VLC_H2_MAX_HEADERS
);
if
(
val
>
VLC_H2_MAX_HEADERS
)
{
for
(
unsigned
i
=
0
;
i
<
VLC_H2_MAX_HEADERS
;
i
++
)
{
free
(
headers
[
i
][
1
]);
free
(
headers
[
i
][
0
]);
}
val
=
-
1
;
}
if
(
val
<
0
)
return
vlc_h2_parse_error
(
p
,
VLC_H2_COMPRESSION_ERROR
);
void
*
s
=
vlc_h2_stream_lookup
(
p
,
p
->
headers
.
sid
);
if
(
s
!=
NULL
)
{
p
->
cbs
->
stream_headers
(
s
,
val
,
headers
);
val
=
0
;
}
else
/* NOTE: The specification implies that the error should also be sent
* for non-last header/continuation frames, but this does not make much
* sense. */
val
=
vlc_h2_stream_error
(
p
,
p
->
headers
.
sid
,
VLC_H2_REFUSED_STREAM
);
if
(
p
->
headers
.
eos
&&
s
!=
NULL
)
p
->
cbs
->
stream_end
(
s
);
p
->
parser
=
vlc_h2_parse_generic
;
p
->
headers
.
sid
=
0
;
return
val
;
}
/** Parses an HTTP/2 DATA frame */
static
int
vlc_h2_parse_frame_data
(
struct
vlc_h2_parser
*
p
,
struct
vlc_h2_frame
*
f
,
size_t
len
,
uint_fast32_t
id
)
{
uint_fast8_t
flags
=
vlc_h2_frame_flags
(
f
);
const
uint8_t
*
ptr
=
vlc_h2_frame_payload
(
f
);
if
(
id
==
0
)
{
free
(
f
);
return
vlc_h2_parse_error
(
p
,
VLC_H2_PROTOCOL_ERROR
);
}
if
(
len
>
VLC_H2_MAX_FRAME
)
{
free
(
f
);
return
vlc_h2_stream_error
(
p
,
id
,
VLC_H2_FRAME_SIZE_ERROR
);
}
if
(
flags
&
VLC_H2_DATA_PADDED
)
{
if
(
len
<
1
||
len
<
(
1u
+
ptr
[
0
]))
{
free
(
f
);
return
vlc_h2_stream_error
(
p
,
id
,
VLC_H2_FRAME_SIZE_ERROR
);
}
len
-=
1
+
ptr
[
0
];
}
if
(
len
>
p
->
rcwd_size
)
{
free
(
f
);
return
vlc_h2_parse_error
(
p
,
VLC_H2_FLOW_CONTROL_ERROR
);
}
p
->
rcwd_size
-=
len
;
p
->
cbs
->
window_status
(
p
->
opaque
,
&
p
->
rcwd_size
);
void
*
s
=
vlc_h2_stream_lookup
(
p
,
id
);
if
(
s
==
NULL
)
{
free
(
f
);
return
vlc_h2_stream_error
(
p
,
id
,
VLC_H2_STREAM_CLOSED
);
}
int
ret
=
p
->
cbs
->
stream_data
(
s
,
f
);
/* Frame gets consumed here ^^ */
if
(
flags
&
VLC_H2_DATA_END_STREAM
)
p
->
cbs
->
stream_end
(
s
);
return
ret
;
}
/** Parses an HTTP/2 HEADERS frame */
static
int
vlc_h2_parse_frame_headers
(
struct
vlc_h2_parser
*
p
,
struct
vlc_h2_frame
*
f
,
size_t
len
,
uint_fast32_t
id
)
{
uint_fast8_t
flags
=
vlc_h2_frame_flags
(
f
);
const
uint8_t
*
ptr
=
vlc_h2_frame_payload
(
f
);
if
(
id
==
0
)
{
free
(
f
);
return
vlc_h2_parse_error
(
p
,
VLC_H2_PROTOCOL_ERROR
);
}
if
(
len
>
VLC_H2_MAX_FRAME
)
{
free
(
f
);
return
vlc_h2_parse_error
(
p
,
VLC_H2_FRAME_SIZE_ERROR
);
}
if
(
flags
&
VLC_H2_HEADERS_PADDED
)
{
if
(
len
<
1
||
len
<
(
1u
+
ptr
[
0
]))
{
free
(
f
);
return
vlc_h2_parse_error
(
p
,
VLC_H2_FRAME_SIZE_ERROR
);
}
len
-=
1
+
ptr
[
0
];
ptr
++
;
}
if
(
flags
&
VLC_H2_HEADERS_PRIORITY
)
{
/* Ignore priorities for now as we do not upload anything. */
if
(
len
<
5
)
{
free
(
f
);
return
vlc_h2_parse_error
(
p
,
VLC_H2_FRAME_SIZE_ERROR
);
}
ptr
+=
5
;
len
-=
5
;
}
vlc_h2_parse_headers_start
(
p
,
id
,
flags
&
VLC_H2_HEADERS_END_STREAM
);
int
ret
=
vlc_h2_parse_headers_append
(
p
,
ptr
,
len
);
if
(
ret
==
0
&&
(
flags
&
VLC_H2_HEADERS_END_HEADERS
))
ret
=
vlc_h2_parse_headers_end
(
p
);
free
(
f
);
return
ret
;
}
/** Parses an HTTP/2 PRIORITY frame */
static
int
vlc_h2_parse_frame_priority
(
struct
vlc_h2_parser
*
p
,
struct
vlc_h2_frame
*
f
,
size_t
len
,
uint_fast32_t
id
)
{
free
(
f
);
if
(
id
==
0
)
return
vlc_h2_parse_error
(
p
,
VLC_H2_PROTOCOL_ERROR
);
if
(
len
!=
5
)
return
vlc_h2_stream_error
(
p
,
id
,
VLC_H2_FRAME_SIZE_ERROR
);
/* Ignore priorities for now as we do not upload much. */
return
0
;
}
/** Parses an HTTP/2 RST_STREAM frame */
static
int
vlc_h2_parse_frame_rst_stream
(
struct
vlc_h2_parser
*
p
,
struct
vlc_h2_frame
*
f
,
size_t
len
,
uint_fast32_t
id
)
{
if
(
id
==
0
)
{
free
(
f
);
return
vlc_h2_parse_error
(
p
,
VLC_H2_PROTOCOL_ERROR
);
}
if
(
len
!=
4
)
{
free
(
f
);
return
vlc_h2_parse_error
(
p
,
VLC_H2_FRAME_SIZE_ERROR
);
}
void
*
s
=
vlc_h2_stream_lookup
(
p
,
id
);
uint_fast32_t
code
=
GetDWBE
(
vlc_h2_frame_payload
(
f
));
free
(
f
);
if
(
s
==
NULL
)
return
0
;
return
p
->
cbs
->
stream_reset
(
s
,
code
);
}
/** Parses an HTTP/2 SETTINGS frame */
static
int
vlc_h2_parse_frame_settings
(
struct
vlc_h2_parser
*
p
,
struct
vlc_h2_frame
*
f
,
size_t
len
,
uint_fast32_t
id
)
{
const
uint8_t
*
ptr
=
vlc_h2_frame_payload
(
f
);
if
(
id
!=
0
)
{
free
(
f
);
return
vlc_h2_parse_error
(
p
,
VLC_H2_PROTOCOL_ERROR
);
}
if
(
len
%
6
||
len
>
VLC_H2_MAX_FRAME
)
{
free
(
f
);
return
vlc_h2_parse_error
(
p
,
VLC_H2_FRAME_SIZE_ERROR
);
}
if
(
vlc_h2_frame_flags
(
f
)
&
VLC_H2_SETTINGS_ACK
)
{
free
(
f
);
if
(
len
!=
0
)
return
vlc_h2_parse_error
(
p
,
VLC_H2_FRAME_SIZE_ERROR
);
/* Ignore ACKs for now as we never change settings. */
return
0
;
}
for
(
const
uint8_t
*
end
=
ptr
+
len
;
ptr
<
end
;
ptr
+=
6
)
p
->
cbs
->
setting
(
p
->
opaque
,
GetWBE
(
ptr
),
GetDWBE
(
ptr
+
2
));
free
(
f
);
return
p
->
cbs
->
settings_done
(
p
->
opaque
);
}
/** Parses an HTTP/2 PUSH_PROMISE frame */
static
int
vlc_h2_parse_frame_push_promise
(
struct
vlc_h2_parser
*
p
,
struct
vlc_h2_frame
*
f
,
size_t
len
,
uint_fast32_t
id
)
{
uint8_t
flags
=
vlc_h2_frame_flags
(
f
);
const
uint8_t
*
ptr
=
vlc_h2_frame_payload
(
f
);
if
(
id
==
0
)
{
free
(
f
);
return
vlc_h2_parse_error
(
p
,
VLC_H2_PROTOCOL_ERROR
);
}
if
(
len
>
VLC_H2_MAX_FRAME
)
{
free
(
f
);
return
vlc_h2_parse_error
(
p
,
VLC_H2_FRAME_SIZE_ERROR
);
}
if
(
flags
&
VLC_H2_PUSH_PROMISE_PADDED
)
{
if
(
len
<
1
||
len
<
(
1u
+
ptr
[
0
]))
{
free
(
f
);
return
vlc_h2_parse_error
(
p
,
VLC_H2_FRAME_SIZE_ERROR
);
}
len
-=
1
+
ptr
[
0
];
ptr
++
;
}
/* Not permitted by our settings. */
free
(
f
);
return
vlc_h2_parse_error
(
p
,
VLC_H2_PROTOCOL_ERROR
);
}
/** Parses an HTTP/2 PING frame */
static
int
vlc_h2_parse_frame_ping
(
struct
vlc_h2_parser
*
p
,
struct
vlc_h2_frame
*
f
,
size_t
len
,
uint_fast32_t
id
)
{
uint64_t
opaque
;
if
(
id
!=
0
)
{
free
(
f
);
return
vlc_h2_parse_error
(
p
,
VLC_H2_PROTOCOL_ERROR
);
}
if
(
len
!=
8
)
{
free
(
f
);
return
vlc_h2_parse_error
(
p
,
VLC_H2_FRAME_SIZE_ERROR
);
}
if
(
vlc_h2_frame_flags
(
f
)
&
VLC_H2_PING_ACK
)
{
free
(
f
);
return
0
;
}
memcpy
(
&
opaque
,
vlc_h2_frame_payload
(
f
),
8
);
free
(
f
);
return
p
->
cbs
->
ping
(
p
->
opaque
,
opaque
);
}
/** Parses an HTTP/2 GOAWAY frame */
static
int
vlc_h2_parse_frame_goaway
(
struct
vlc_h2_parser
*
p
,
struct
vlc_h2_frame
*
f
,
size_t
len
,
uint_fast32_t
id
)
{
const
uint8_t
*
ptr
=
vlc_h2_frame_payload
(
f
);
if
(
id
!=
0
)
{
free
(
f
);
return
vlc_h2_parse_error
(
p
,
VLC_H2_PROTOCOL_ERROR
);
}
if
(
len
<
8
||
len
>
VLC_H2_MAX_FRAME
)
{
free
(
f
);
return
vlc_h2_parse_error
(
p
,
VLC_H2_FRAME_SIZE_ERROR
);
}
uint_fast32_t
last_id
=
GetDWBE
(
ptr
)
&
0x7FFFFFFF
;
uint_fast32_t
code
=
GetDWBE
(
ptr
+
4
);
free
(
f
);
return
p
->
cbs
->
reset
(
p
->
opaque
,
last_id
,
code
);
}
/** Parses an HTTP/2 WINDOW_UPDATE frame */
static
int
vlc_h2_parse_frame_window_update
(
struct
vlc_h2_parser
*
p
,
struct
vlc_h2_frame
*
f
,
size_t
len
,
uint_fast32_t
id
)
{
free
(
f
);
if
(
len
!=
4
)
{
if
(
id
==
0
)
return
vlc_h2_parse_error
(
p
,
VLC_H2_FRAME_SIZE_ERROR
);
return
vlc_h2_stream_error
(
p
,
id
,
VLC_H2_FRAME_SIZE_ERROR
);
}
/* Nothing to do as we do not send data for the time being. */
return
0
;
}
/** Parses an HTTP/2 CONTINUATION frame */
static
int
vlc_h2_parse_frame_continuation
(
struct
vlc_h2_parser
*
p
,
struct
vlc_h2_frame
*
f
,
size_t
len
,
uint_fast32_t
id
)
{
const
uint8_t
*
ptr
=
vlc_h2_frame_payload
(
f
);
/* Stream ID must match with the previous frame. */
if
(
id
==
0
||
id
!=
p
->
headers
.
sid
)
{
free
(
f
);
return
vlc_h2_parse_error
(
p
,
VLC_H2_PROTOCOL_ERROR
);
}
if
(
len
>
VLC_H2_MAX_FRAME
)
{
free
(
f
);
return
vlc_h2_parse_error
(
p
,
VLC_H2_FRAME_SIZE_ERROR
);
}
int
ret
=
vlc_h2_parse_headers_append
(
p
,
ptr
,
len
);
if
(
ret
==
0
&&
(
vlc_h2_frame_flags
(
f
)
&
VLC_H2_CONTINUATION_END_HEADERS
))
ret
=
vlc_h2_parse_headers_end
(
p
);
free
(
f
);
return
0
;
}
/** Parses an HTTP/2 frame of unknown type */
static
int
vlc_h2_parse_frame_unknown
(
struct
vlc_h2_parser
*
p
,
struct
vlc_h2_frame
*
f
,
size_t
len
,
uint_fast32_t
id
)
{
free
(
f
);
if
(
len
>
VLC_H2_MAX_FRAME
)
{
if
(
id
==
0
)
return
vlc_h2_parse_error
(
p
,
VLC_H2_FRAME_SIZE_ERROR
);
return
vlc_h2_stream_error
(
p
,
id
,
VLC_H2_FRAME_SIZE_ERROR
);
}
/* Ignore frames of unknown type as specified. */
return
0
;
}
static
const
vlc_h2_parser
vlc_h2_parsers
[]
=
{
[
VLC_H2_FRAME_DATA
]
=
vlc_h2_parse_frame_data
,
[
VLC_H2_FRAME_HEADERS
]
=
vlc_h2_parse_frame_headers
,
[
VLC_H2_FRAME_PRIORITY
]
=
vlc_h2_parse_frame_priority
,
[
VLC_H2_FRAME_RST_STREAM
]
=
vlc_h2_parse_frame_rst_stream
,
[
VLC_H2_FRAME_SETTINGS
]
=
vlc_h2_parse_frame_settings
,
[
VLC_H2_FRAME_PUSH_PROMISE
]
=
vlc_h2_parse_frame_push_promise
,
[
VLC_H2_FRAME_PING
]
=
vlc_h2_parse_frame_ping
,
[
VLC_H2_FRAME_GOAWAY
]
=
vlc_h2_parse_frame_goaway
,
[
VLC_H2_FRAME_WINDOW_UPDATE
]
=
vlc_h2_parse_frame_window_update
,
[
VLC_H2_FRAME_CONTINUATION
]
=
vlc_h2_parse_frame_continuation
,
};
/** Parses the HTTP/2 connection preface. */
static
int
vlc_h2_parse_preface
(
struct
vlc_h2_parser
*
p
,
struct
vlc_h2_frame
*
f
,
size_t
len
,
uint_fast32_t
id
)
{
/* The length must be within the specification default limits. */
if
(
len
>
VLC_H2_DEFAULT_MAX_FRAME
/* The type must SETTINGS. */
||
vlc_h2_frame_type
(
f
)
!=
VLC_H2_FRAME_SETTINGS
/* The SETTINGS ACK flag must be clear. */
||
(
vlc_h2_frame_flags
(
f
)
&
VLC_H2_SETTINGS_ACK
))
{
free
(
f
);
return
vlc_h2_parse_error
(
p
,
VLC_H2_PROTOCOL_ERROR
);
}
p
->
parser
=
vlc_h2_parse_generic
;
return
vlc_h2_parse_frame_settings
(
p
,
f
,
len
,
id
);
}
/** Parses any HTTP/2 frame. */
static
int
vlc_h2_parse_generic
(
struct
vlc_h2_parser
*
p
,
struct
vlc_h2_frame
*
f
,
size_t
len
,
uint_fast32_t
id
)
{
uint_fast8_t
type
=
vlc_h2_frame_type
(
f
);
vlc_h2_parser
func
=
vlc_h2_parse_frame_unknown
;
assert
(
p
->
headers
.
sid
==
0
);
if
(
type
<
sizeof
(
vlc_h2_parsers
)
/
sizeof
(
vlc_h2_parsers
[
0
])
&&
vlc_h2_parsers
[
type
]
!=
NULL
)
func
=
vlc_h2_parsers
[
type
];
return
func
(
p
,
f
,
len
,
id
);
}
static
int
vlc_h2_parse_headers_block
(
struct
vlc_h2_parser
*
p
,
struct
vlc_h2_frame
*
f
,
size_t
len
,
uint_fast32_t
id
)
{
assert
(
p
->
headers
.
sid
!=
0
);
/* After a HEADER, PUSH_PROMISE of CONTINUATION frame without the
* END_HEADERS flag, must come a CONTINUATION frame. */
if
(
vlc_h2_frame_type
(
f
)
!=
VLC_H2_FRAME_CONTINUATION
)
{
free
(
f
);
return
vlc_h2_parse_error
(
p
,
VLC_H2_PROTOCOL_ERROR
);
}
return
vlc_h2_parse_frame_continuation
(
p
,
f
,
len
,
id
);
}
static
int
vlc_h2_parse_failed
(
struct
vlc_h2_parser
*
p
,
struct
vlc_h2_frame
*
f
,
size_t
len
,
uint_fast32_t
id
)
{
free
(
f
);
(
void
)
p
;
(
void
)
len
;
(
void
)
id
;
return
-
1
;
}
int
vlc_h2_parse
(
struct
vlc_h2_parser
*
p
,
struct
vlc_h2_frame
*
f
)
{
int
ret
=
0
;
while
(
f
!=
NULL
)
{
struct
vlc_h2_frame
*
next
=
f
->
next
;
size_t
len
=
vlc_h2_frame_length
(
f
);
uint_fast32_t
id
=
vlc_h2_frame_id
(
f
);
f
->
next
=
NULL
;
ret
=
p
->
parser
(
p
,
f
,
len
,
id
);
if
(
ret
)
p
->
parser
=
vlc_h2_parse_failed
;
f
=
next
;
}
return
ret
;
}
struct
vlc_h2_parser
*
vlc_h2_parse_init
(
void
*
ctx
,
const
struct
vlc_h2_parser_cbs
*
cbs
)
{
struct
vlc_h2_parser
*
p
=
malloc
(
sizeof
(
*
p
));
if
(
unlikely
(
p
==
NULL
))
return
NULL
;
p
->
opaque
=
ctx
;
p
->
cbs
=
cbs
;
p
->
parser
=
vlc_h2_parse_preface
;
p
->
headers
.
sid
=
0
;
p
->
headers
.
buf
=
NULL
;
p
->
headers
.
len
=
0
;
p
->
headers
.
decoder
=
hpack_decode_init
(
VLC_H2_MAX_HEADER_TABLE
);
if
(
unlikely
(
p
->
headers
.
decoder
==
NULL
))
{
free
(
p
);
return
NULL
;
}
p
->
rcwd_size
=
65535
;
/* initial per-connection value */
return
p
;
}
void
vlc_h2_parse_destroy
(
struct
vlc_h2_parser
*
p
)
{
hpack_decode_destroy
(
p
->
headers
.
decoder
);
free
(
p
->
headers
.
buf
);
free
(
p
);
}
modules/access/http/h2frame.h
View file @
052df94a
...
@@ -27,6 +27,8 @@ struct vlc_h2_frame
...
@@ -27,6 +27,8 @@ struct vlc_h2_frame
uint8_t
data
[];
uint8_t
data
[];
};
};
size_t
vlc_h2_frame_size
(
const
struct
vlc_h2_frame
*
);
struct
vlc_h2_frame
*
struct
vlc_h2_frame
*
vlc_h2_frame_headers
(
uint_fast32_t
stream_id
,
uint_fast32_t
mtu
,
bool
eos
,
vlc_h2_frame_headers
(
uint_fast32_t
stream_id
,
uint_fast32_t
mtu
,
bool
eos
,
unsigned
count
,
const
char
*
const
headers
[][
2
]);
unsigned
count
,
const
char
*
const
headers
[][
2
]);
...
@@ -44,6 +46,12 @@ vlc_h2_frame_goaway(uint_fast32_t last_stream_id, uint_fast32_t error_code);
...
@@ -44,6 +46,12 @@ vlc_h2_frame_goaway(uint_fast32_t last_stream_id, uint_fast32_t error_code);
struct
vlc_h2_frame
*
struct
vlc_h2_frame
*
vlc_h2_frame_window_update
(
uint_fast32_t
stream_id
,
uint_fast32_t
credit
);
vlc_h2_frame_window_update
(
uint_fast32_t
stream_id
,
uint_fast32_t
credit
);
struct
vlc_object_t
;
void
vlc_h2_frame_dump
(
struct
vlc_object_t
*
,
const
struct
vlc_h2_frame
*
,
const
char
*
);
#define vlc_h2_frame_dump(o, f, m) vlc_h2_frame_dump(VLC_OBJECT(o), f, m)
enum
vlc_h2_error
{
enum
vlc_h2_error
{
VLC_H2_NO_ERROR
,
VLC_H2_NO_ERROR
,
VLC_H2_PROTOCOL_ERROR
,
VLC_H2_PROTOCOL_ERROR
,
...
@@ -85,3 +93,37 @@ const char *vlc_h2_setting_name(uint_fast16_t);
...
@@ -85,3 +93,37 @@ const char *vlc_h2_setting_name(uint_fast16_t);
#define VLC_H2_DEFAULT_MAX_HEADER_TABLE 4096
#define VLC_H2_DEFAULT_MAX_HEADER_TABLE 4096
#define VLC_H2_DEFAULT_INIT_WINDOW 65535
#define VLC_H2_DEFAULT_INIT_WINDOW 65535
#define VLC_H2_DEFAULT_MAX_FRAME 16384
#define VLC_H2_DEFAULT_MAX_FRAME 16384
struct
vlc_h2_parser
;
struct
vlc_h2_parser_cbs
{
void
(
*
setting
)(
void
*
ctx
,
uint_fast16_t
id
,
uint_fast32_t
value
);
int
(
*
settings_done
)(
void
*
ctx
);
int
(
*
ping
)(
void
*
ctx
,
uint_fast64_t
opaque
);
void
(
*
error
)(
void
*
ctx
,
uint_fast32_t
code
);
int
(
*
reset
)(
void
*
ctx
,
uint_fast32_t
last_seq
,
uint_fast32_t
code
);
void
(
*
window_status
)(
void
*
ctx
,
uint32_t
*
rcwd
);
void
*
(
*
stream_lookup
)(
void
*
ctx
,
uint_fast32_t
id
);
int
(
*
stream_error
)(
void
*
ctx
,
uint_fast32_t
id
,
uint_fast32_t
code
);
void
(
*
stream_headers
)(
void
*
ctx
,
unsigned
count
,
char
*
headers
[][
2
]);
int
(
*
stream_data
)(
void
*
ctx
,
struct
vlc_h2_frame
*
f
);
void
(
*
stream_end
)(
void
*
ctx
);
int
(
*
stream_reset
)(
void
*
ctx
,
uint_fast32_t
code
);
};
struct
vlc_h2_parser
*
vlc_h2_parse_init
(
void
*
ctx
,
const
struct
vlc_h2_parser_cbs
*
cbs
);
int
vlc_h2_parse
(
struct
vlc_h2_parser
*
,
struct
vlc_h2_frame
*
);
void
vlc_h2_parse_destroy
(
struct
vlc_h2_parser
*
);
#define VLC_H2_MAX_HEADERS 255
const
uint8_t
*
vlc_h2_frame_data_get
(
const
struct
vlc_h2_frame
*
f
,
size_t
*
restrict
len
);
#if (__STDC_VERSION__ >= 201112L)
#define vlc_h2_frame_data_get(f, l) \
_Generic((f), \
const struct vlc_h2_frame *: (vlc_h2_frame_data_get)(f, l), \
struct vlc_h2_frame *: (uint8_t *)(vlc_h2_frame_data_get)(f, l))
#endif
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