Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in / Register
Toggle navigation
B
brpc
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Packages
Packages
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
submodule
brpc
Commits
b4c70ac2
Commit
b4c70ac2
authored
Jan 07, 2020
by
jamesge
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Rename URI.schema to scheme and calling schema/set_schema will be notified with deprecation
parent
1e2f4e83
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
73 additions
and
71 deletions
+73
-71
http_client.md
docs/cn/http_client.md
+1
-1
http_client.md
docs/en/http_client.md
+1
-1
http_parser.cpp
src/brpc/details/http_parser.cpp
+24
-24
http_parser.h
src/brpc/details/http_parser.h
+1
-1
http2_rpc_protocol.cpp
src/brpc/policy/http2_rpc_protocol.cpp
+2
-2
http_rpc_protocol.cpp
src/brpc/policy/http_rpc_protocol.cpp
+5
-5
uri.cpp
src/brpc/uri.cpp
+17
-17
uri.h
src/brpc/uri.h
+8
-6
brpc_uri_unittest.cpp
test/brpc_uri_unittest.cpp
+14
-14
No files found.
docs/cn/http_client.md
View file @
b4c70ac2
...
...
@@ -87,7 +87,7 @@ URL的一般形式如下图:
// | | | | | | | |
// | userinfo host port | | query fragment
// | \________________________________/\_____________|____|/ \__/ \__/
// schem
a
| | | | | |
// schem
e
| | | | | |
// authority | | | | |
// path | | interpretable as keys
// | |
...
...
docs/en/http_client.md
View file @
b4c70ac2
...
...
@@ -88,7 +88,7 @@ Genaral form of an URL:
// | | | | | | | |
// | userinfo host port | | query fragment
// | \________________________________/\_____________|____|/ \__/ \__/
// schem
a
| | | | | |
// schem
e
| | | | | |
// authority | | | | |
// path | | interpretable as keys
// | |
...
...
src/brpc/details/http_parser.cpp
View file @
b4c70ac2
...
...
@@ -258,9 +258,9 @@ enum state
,
s_req_method
,
s_req_spaces_before_url
,
s_req_schem
a
,
s_req_schem
a
_slash
,
s_req_schem
a
_slash_slash
,
s_req_schem
e
,
s_req_schem
e
_slash
,
s_req_schem
e
_slash_slash
,
s_req_server_start
,
s_req_server
,
s_req_server_with_at
...
...
@@ -343,9 +343,9 @@ const char* http_parser_state_name(unsigned int state) {
case
s_start_req
:
return
"s_start_req"
;
case
s_req_method
:
return
"s_req_method"
;
case
s_req_spaces_before_url
:
return
"s_req_spaces_before_url"
;
case
s_req_schem
a
:
return
"s_req_schema
"
;
case
s_req_schem
a_slash
:
return
"s_req_schema
_slash"
;
case
s_req_schem
a_slash_slash
:
return
"s_req_schema
_slash_slash"
;
case
s_req_schem
e
:
return
"s_req_scheme
"
;
case
s_req_schem
e_slash
:
return
"s_req_scheme
_slash"
;
case
s_req_schem
e_slash_slash
:
return
"s_req_scheme
_slash_slash"
;
case
s_req_server_start
:
return
"s_req_server_start"
;
case
s_req_server
:
return
"s_req_server"
;
case
s_req_server_with_at
:
return
"s_req_server_with_at"
;
...
...
@@ -562,30 +562,30 @@ parse_url_char(enum state s, const char ch)
}
if
(
IS_ALPHA
(
ch
))
{
return
s_req_schem
a
;
return
s_req_schem
e
;
}
break
;
case
s_req_schem
a
:
case
s_req_schem
e
:
if
(
IS_ALPHA
(
ch
))
{
return
s
;
}
if
(
ch
==
':'
)
{
return
s_req_schem
a
_slash
;
return
s_req_schem
e
_slash
;
}
break
;
case
s_req_schem
a
_slash
:
case
s_req_schem
e
_slash
:
if
(
ch
==
'/'
)
{
return
s_req_schem
a
_slash_slash
;
return
s_req_schem
e
_slash_slash
;
}
break
;
case
s_req_schem
a
_slash_slash
:
case
s_req_schem
e
_slash_slash
:
if
(
ch
==
'/'
)
{
return
s_req_server_start
;
}
...
...
@@ -725,9 +725,9 @@ size_t http_parser_execute (http_parser *parser,
header_value_mark
=
data
;
switch
(
parser
->
state
)
{
case
s_req_path
:
case
s_req_schem
a
:
case
s_req_schem
a
_slash
:
case
s_req_schem
a
_slash_slash
:
case
s_req_schem
e
:
case
s_req_schem
e
_slash
:
case
s_req_schem
e
_slash_slash
:
case
s_req_server_start
:
case
s_req_server
:
case
s_req_server_with_at
:
...
...
@@ -1160,9 +1160,9 @@ size_t http_parser_execute (http_parser *parser,
break
;
}
case
s_req_schem
a
:
case
s_req_schem
a
_slash
:
case
s_req_schem
a
_slash_slash
:
case
s_req_schem
e
:
case
s_req_schem
e
_slash
:
case
s_req_schem
e
_slash_slash
:
case
s_req_server_start
:
{
enum
state
new_state
;
...
...
@@ -2254,15 +2254,15 @@ http_parser_parse_url(const char *buf, size_t buflen, int is_connect,
return
1
;
/* Skip delimeters */
case
s_req_schem
a
_slash
:
case
s_req_schem
a
_slash_slash
:
case
s_req_schem
e
_slash
:
case
s_req_schem
e
_slash_slash
:
case
s_req_server_start
:
case
s_req_query_string_start
:
case
s_req_fragment_start
:
continue
;
case
s_req_schem
a
:
uf
=
UF_SCHEM
A
;
case
s_req_schem
e
:
uf
=
UF_SCHEM
E
;
break
;
case
s_req_server_with_at
:
...
...
@@ -2302,9 +2302,9 @@ http_parser_parse_url(const char *buf, size_t buflen, int is_connect,
old_uf
=
uf
;
}
/* host must be present if there is a schem
a
*/
/* host must be present if there is a schem
e
*/
/* parsing http:///toto will fail */
if
((
u
->
field_set
&
((
1
<<
UF_SCHEM
A
)
|
(
1
<<
UF_HOST
)))
!=
0
)
{
if
((
u
->
field_set
&
((
1
<<
UF_SCHEM
E
)
|
(
1
<<
UF_HOST
)))
!=
0
)
{
if
(
http_parse_host
(
buf
,
u
,
found_at
)
!=
0
)
{
return
1
;
}
...
...
src/brpc/details/http_parser.h
View file @
b4c70ac2
...
...
@@ -236,7 +236,7 @@ struct http_parser_settings {
enum
http_parser_url_fields
{
UF_SCHEM
A
=
0
{
UF_SCHEM
E
=
0
,
UF_HOST
=
1
,
UF_PORT
=
2
,
UF_PATH
=
3
...
...
src/brpc/policy/http2_rpc_protocol.cpp
View file @
b4c70ac2
...
...
@@ -1263,7 +1263,7 @@ int H2StreamContext::ConsumeHeaders(butil::IOBufBytesIterator& it) {
case
's'
:
if
(
strcmp
(
name
+
2
,
/*:s*/
"cheme"
)
==
0
)
{
matched
=
true
;
h
.
uri
().
set_schem
a
(
pair
.
value
);
h
.
uri
().
set_schem
e
(
pair
.
value
);
}
else
if
(
strcmp
(
name
+
2
,
/*:s*/
"tatus"
)
==
0
)
{
matched
=
true
;
char
*
endptr
=
NULL
;
...
...
@@ -1408,7 +1408,7 @@ H2UnsentRequest* H2UnsentRequest::New(Controller* c) {
msg
->
push
(
common
->
H2_METHOD
)
=
HttpMethod2Str
(
h
.
method
());
}
// :scheme
const
std
::
string
*
scheme
=
&
h
.
uri
().
schem
a
();
const
std
::
string
*
scheme
=
&
h
.
uri
().
schem
e
();
if
(
scheme
->
empty
())
{
scheme
=
(
c
->
is_ssl
()
?
&
common
->
H2_SCHEME_HTTPS
:
&
common
->
H2_SCHEME_HTTP
);
...
...
src/brpc/policy/http_rpc_protocol.cpp
View file @
b4c70ac2
...
...
@@ -1492,23 +1492,23 @@ void ProcessHttpRequest(InputMessageBase *msg) {
}
bool
ParseHttpServerAddress
(
butil
::
EndPoint
*
point
,
const
char
*
server_addr_and_port
)
{
std
::
string
schem
a
;
std
::
string
schem
e
;
std
::
string
host
;
int
port
=
-
1
;
if
(
ParseURL
(
server_addr_and_port
,
&
schem
a
,
&
host
,
&
port
)
!=
0
)
{
if
(
ParseURL
(
server_addr_and_port
,
&
schem
e
,
&
host
,
&
port
)
!=
0
)
{
LOG
(
ERROR
)
<<
"Invalid address=`"
<<
server_addr_and_port
<<
'\''
;
return
false
;
}
if
(
schem
a
.
empty
()
||
schema
==
"http"
)
{
if
(
schem
e
.
empty
()
||
scheme
==
"http"
)
{
if
(
port
<
0
)
{
port
=
80
;
}
}
else
if
(
schem
a
==
"https"
)
{
}
else
if
(
schem
e
==
"https"
)
{
if
(
port
<
0
)
{
port
=
443
;
}
}
else
{
LOG
(
ERROR
)
<<
"Invalid schem
a=`"
<<
schema
<<
'\''
;
LOG
(
ERROR
)
<<
"Invalid schem
e=`"
<<
scheme
<<
'\''
;
return
false
;
}
if
(
str2endpoint
(
host
.
c_str
(),
port
,
point
)
!=
0
&&
...
...
src/brpc/uri.cpp
View file @
b4c70ac2
...
...
@@ -41,7 +41,7 @@ void URI::Clear() {
_path
.
clear
();
_user_info
.
clear
();
_fragment
.
clear
();
_schem
a
.
clear
();
_schem
e
.
clear
();
_query
.
clear
();
_query_map
.
clear
();
}
...
...
@@ -55,7 +55,7 @@ void URI::Swap(URI &rhs) {
_path
.
swap
(
rhs
.
_path
);
_user_info
.
swap
(
rhs
.
_user_info
);
_fragment
.
swap
(
rhs
.
_fragment
);
_schem
a
.
swap
(
rhs
.
_schema
);
_schem
e
.
swap
(
rhs
.
_scheme
);
_query
.
swap
(
rhs
.
_query
);
_query_map
.
swap
(
rhs
.
_query_map
);
}
...
...
@@ -138,7 +138,7 @@ static const char* const g_url_parsing_fast_action_map =
g_url_parsing_fast_action_map_raw
+
128
;
// This implementation is faster than http_parser_parse_url() and allows
// ignoring of schem
a
("http://")
// ignoring of schem
e
("http://")
int
URI
::
SetHttpURL
(
const
char
*
url
)
{
Clear
();
...
...
@@ -148,8 +148,8 @@ int URI::SetHttpURL(const char* url) {
for
(
++
p
;
*
p
==
' '
;
++
p
)
{}
}
const
char
*
start
=
p
;
// Find end of host, locate schem
a
and user_info during the searching
bool
need_schem
a
=
true
;
// Find end of host, locate schem
e
and user_info during the searching
bool
need_schem
e
=
true
;
bool
need_user_info
=
true
;
for
(;
true
;
++
p
)
{
const
char
action
=
g_url_parsing_fast_action_map
[(
int
)
*
p
];
...
...
@@ -160,9 +160,9 @@ int URI::SetHttpURL(const char* url) {
break
;
}
if
(
*
p
==
':'
)
{
if
(
p
[
1
]
==
'/'
&&
p
[
2
]
==
'/'
&&
need_schem
a
)
{
need_schem
a
=
false
;
_schem
a
.
assign
(
start
,
p
-
start
);
if
(
p
[
1
]
==
'/'
&&
p
[
2
]
==
'/'
&&
need_schem
e
)
{
need_schem
e
=
false
;
_schem
e
.
assign
(
start
,
p
-
start
);
p
+=
2
;
start
=
p
+
1
;
}
...
...
@@ -226,15 +226,15 @@ int URI::SetHttpURL(const char* url) {
}
int
ParseURL
(
const
char
*
url
,
std
::
string
*
schem
a
_out
,
std
::
string
*
host_out
,
int
*
port_out
)
{
std
::
string
*
schem
e
_out
,
std
::
string
*
host_out
,
int
*
port_out
)
{
const
char
*
p
=
url
;
// skip heading blanks
if
(
*
p
==
' '
)
{
for
(
++
p
;
*
p
==
' '
;
++
p
)
{}
}
const
char
*
start
=
p
;
// Find end of host, locate schem
a
and user_info during the searching
bool
need_schem
a
=
true
;
// Find end of host, locate schem
e
and user_info during the searching
bool
need_schem
e
=
true
;
bool
need_user_info
=
true
;
for
(;
true
;
++
p
)
{
const
char
action
=
g_url_parsing_fast_action_map
[(
int
)
*
p
];
...
...
@@ -245,10 +245,10 @@ int ParseURL(const char* url,
break
;
}
if
(
*
p
==
':'
)
{
if
(
p
[
1
]
==
'/'
&&
p
[
2
]
==
'/'
&&
need_schem
a
)
{
need_schem
a
=
false
;
if
(
schem
a
_out
)
{
schem
a
_out
->
assign
(
start
,
p
-
start
);
if
(
p
[
1
]
==
'/'
&&
p
[
2
]
==
'/'
&&
need_schem
e
)
{
need_schem
e
=
false
;
if
(
schem
e
_out
)
{
schem
e
_out
->
assign
(
start
,
p
-
start
);
}
p
+=
2
;
start
=
p
+
1
;
...
...
@@ -279,8 +279,8 @@ int ParseURL(const char* url,
void
URI
::
Print
(
std
::
ostream
&
os
)
const
{
if
(
!
_host
.
empty
())
{
if
(
!
_schem
a
.
empty
())
{
os
<<
_schem
a
<<
"://"
;
if
(
!
_schem
e
.
empty
())
{
os
<<
_schem
e
<<
"://"
;
}
else
{
os
<<
"http://"
;
}
...
...
src/brpc/uri.h
View file @
b4c70ac2
...
...
@@ -37,7 +37,7 @@ namespace brpc {
// | | | | | | | |
// | userinfo host port | | query fragment
// | \________________________________/\_____________|____|/ \__/ \__/
// schem
a
| | | | | |
// schem
e
| | | | | |
// authority | | | | |
// path | | interpretable as keys
// | |
...
...
@@ -78,7 +78,8 @@ public:
const
butil
::
Status
&
status
()
const
{
return
_st
;
}
// Sub fields. Empty string if the field is not set.
const
std
::
string
&
schema
()
const
{
return
_schema
;
}
// scheme in http2
const
std
::
string
&
scheme
()
const
{
return
_scheme
;
}
BAIDU_DEPRECATED
const
std
::
string
&
schema
()
const
{
return
scheme
();
}
const
std
::
string
&
host
()
const
{
return
_host
;
}
int
port
()
const
{
return
_port
;
}
// -1 on unset.
const
std
::
string
&
path
()
const
{
return
_path
;
}
...
...
@@ -92,7 +93,8 @@ public:
// Overwrite parts of the URL.
// NOTE: The input MUST be guaranteed to be valid.
void
set_schema
(
const
std
::
string
&
schema
)
{
_schema
=
schema
;
}
void
set_scheme
(
const
std
::
string
&
scheme
)
{
_scheme
=
scheme
;
}
BAIDU_DEPRECATED
void
set_schema
(
const
std
::
string
&
s
)
{
set_scheme
(
s
);
}
void
set_path
(
const
std
::
string
&
path
)
{
_path
=
path
;
}
void
set_host
(
const
std
::
string
&
host
)
{
_host
=
host
;
}
void
set_port
(
int
port
)
{
_port
=
port
;
}
...
...
@@ -151,14 +153,14 @@ friend class HttpMessage;
std
::
string
_path
;
std
::
string
_user_info
;
std
::
string
_fragment
;
std
::
string
_schem
a
;
std
::
string
_schem
e
;
mutable
std
::
string
_query
;
mutable
QueryMap
_query_map
;
};
// Parse host/port/schem
a
from `url' if the corresponding parameter is not NULL.
// Parse host/port/schem
e
from `url' if the corresponding parameter is not NULL.
// Returns 0 on success, -1 otherwise.
int
ParseURL
(
const
char
*
url
,
std
::
string
*
schem
a
,
std
::
string
*
host
,
int
*
port
);
int
ParseURL
(
const
char
*
url
,
std
::
string
*
schem
e
,
std
::
string
*
host
,
int
*
port
);
inline
void
URI
::
SetQuery
(
const
std
::
string
&
key
,
const
std
::
string
&
value
)
{
get_query_map
()[
key
]
=
value
;
...
...
test/brpc_uri_unittest.cpp
View file @
b4c70ac2
...
...
@@ -23,7 +23,7 @@ TEST(URITest, everything) {
brpc
::
URI
uri
;
std
::
string
uri_str
=
" foobar://user:passwd@www.baidu.com:80/s?wd=uri#frag "
;
ASSERT_EQ
(
0
,
uri
.
SetHttpURL
(
uri_str
));
ASSERT_EQ
(
"foobar"
,
uri
.
schem
a
());
ASSERT_EQ
(
"foobar"
,
uri
.
schem
e
());
ASSERT_EQ
(
80
,
uri
.
port
());
ASSERT_EQ
(
"www.baidu.com"
,
uri
.
host
());
ASSERT_EQ
(
"/s"
,
uri
.
path
());
...
...
@@ -33,11 +33,11 @@ TEST(URITest, everything) {
ASSERT_EQ
(
*
uri
.
GetQuery
(
"wd"
),
"uri"
);
ASSERT_FALSE
(
uri
.
GetQuery
(
"nonkey"
));
std
::
string
schem
a
;
std
::
string
schem
e
;
std
::
string
host_out
;
int
port_out
=
-
1
;
brpc
::
ParseURL
(
uri_str
.
c_str
(),
&
schem
a
,
&
host_out
,
&
port_out
);
ASSERT_EQ
(
"foobar"
,
schem
a
);
brpc
::
ParseURL
(
uri_str
.
c_str
(),
&
schem
e
,
&
host_out
,
&
port_out
);
ASSERT_EQ
(
"foobar"
,
schem
e
);
ASSERT_EQ
(
"www.baidu.com"
,
host_out
);
ASSERT_EQ
(
80
,
port_out
);
}
...
...
@@ -45,7 +45,7 @@ TEST(URITest, everything) {
TEST
(
URITest
,
only_host
)
{
brpc
::
URI
uri
;
ASSERT_EQ
(
0
,
uri
.
SetHttpURL
(
" foo1://www.baidu1.com?wd=uri2&nonkey=22 "
));
ASSERT_EQ
(
"foo1"
,
uri
.
schem
a
());
ASSERT_EQ
(
"foo1"
,
uri
.
schem
e
());
ASSERT_EQ
(
-
1
,
uri
.
port
());
ASSERT_EQ
(
"www.baidu1.com"
,
uri
.
host
());
ASSERT_EQ
(
""
,
uri
.
path
());
...
...
@@ -58,7 +58,7 @@ TEST(URITest, only_host) {
ASSERT_EQ
(
*
uri
.
GetQuery
(
"nonkey"
),
"22"
);
ASSERT_EQ
(
0
,
uri
.
SetHttpURL
(
"foo2://www.baidu2.com:1234?wd=uri2&nonkey=22 "
));
ASSERT_EQ
(
"foo2"
,
uri
.
schem
a
());
ASSERT_EQ
(
"foo2"
,
uri
.
schem
e
());
ASSERT_EQ
(
1234
,
uri
.
port
());
ASSERT_EQ
(
"www.baidu2.com"
,
uri
.
host
());
ASSERT_EQ
(
""
,
uri
.
path
());
...
...
@@ -71,7 +71,7 @@ TEST(URITest, only_host) {
ASSERT_EQ
(
*
uri
.
GetQuery
(
"nonkey"
),
"22"
);
ASSERT_EQ
(
0
,
uri
.
SetHttpURL
(
" www.baidu3.com:4321 "
));
ASSERT_EQ
(
""
,
uri
.
schem
a
());
ASSERT_EQ
(
""
,
uri
.
schem
e
());
ASSERT_EQ
(
4321
,
uri
.
port
());
ASSERT_EQ
(
"www.baidu3.com"
,
uri
.
host
());
ASSERT_EQ
(
""
,
uri
.
path
());
...
...
@@ -80,7 +80,7 @@ TEST(URITest, only_host) {
ASSERT_EQ
(
0
,
uri
.
QueryCount
());
ASSERT_EQ
(
0
,
uri
.
SetHttpURL
(
" www.baidu4.com "
));
ASSERT_EQ
(
""
,
uri
.
schem
a
());
ASSERT_EQ
(
""
,
uri
.
schem
e
());
ASSERT_EQ
(
-
1
,
uri
.
port
());
ASSERT_EQ
(
"www.baidu4.com"
,
uri
.
host
());
ASSERT_EQ
(
""
,
uri
.
path
());
...
...
@@ -89,10 +89,10 @@ TEST(URITest, only_host) {
ASSERT_EQ
(
0
,
uri
.
QueryCount
());
}
TEST
(
URITest
,
no_schem
a
)
{
TEST
(
URITest
,
no_schem
e
)
{
brpc
::
URI
uri
;
ASSERT_EQ
(
0
,
uri
.
SetHttpURL
(
" user:passwd2@www.baidu1.com/s?wd=uri2&nonkey=22#frag "
));
ASSERT_EQ
(
""
,
uri
.
schem
a
());
ASSERT_EQ
(
""
,
uri
.
schem
e
());
ASSERT_EQ
(
-
1
,
uri
.
port
());
ASSERT_EQ
(
"www.baidu1.com"
,
uri
.
host
());
ASSERT_EQ
(
"/s"
,
uri
.
path
());
...
...
@@ -104,10 +104,10 @@ TEST(URITest, no_schema) {
ASSERT_EQ
(
*
uri
.
GetQuery
(
"nonkey"
),
"22"
);
}
TEST
(
URITest
,
no_schem
a
_and_user_info
)
{
TEST
(
URITest
,
no_schem
e
_and_user_info
)
{
brpc
::
URI
uri
;
ASSERT_EQ
(
0
,
uri
.
SetHttpURL
(
" www.baidu2.com/s?wd=uri2&nonkey=22#frag "
));
ASSERT_EQ
(
""
,
uri
.
schem
a
());
ASSERT_EQ
(
""
,
uri
.
schem
e
());
ASSERT_EQ
(
-
1
,
uri
.
port
());
ASSERT_EQ
(
"www.baidu2.com"
,
uri
.
host
());
ASSERT_EQ
(
"/s"
,
uri
.
path
());
...
...
@@ -122,7 +122,7 @@ TEST(URITest, no_schema_and_user_info) {
TEST
(
URITest
,
no_host
)
{
brpc
::
URI
uri
;
ASSERT_EQ
(
0
,
uri
.
SetHttpURL
(
" /sb?wd=uri3#frag2 "
))
<<
uri
.
status
();
ASSERT_EQ
(
""
,
uri
.
schem
a
());
ASSERT_EQ
(
""
,
uri
.
schem
e
());
ASSERT_EQ
(
-
1
,
uri
.
port
());
ASSERT_EQ
(
""
,
uri
.
host
());
ASSERT_EQ
(
"/sb"
,
uri
.
path
());
...
...
@@ -134,7 +134,7 @@ TEST(URITest, no_host) {
// set_path should do as its name says.
uri
.
set_path
(
"/x/y/z/"
);
ASSERT_EQ
(
""
,
uri
.
schem
a
());
ASSERT_EQ
(
""
,
uri
.
schem
e
());
ASSERT_EQ
(
-
1
,
uri
.
port
());
ASSERT_EQ
(
""
,
uri
.
host
());
ASSERT_EQ
(
"/x/y/z/"
,
uri
.
path
());
...
...
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