Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in / Register
Toggle navigation
C
capnproto
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
capnproto
Commits
9032d060
Commit
9032d060
authored
Feb 11, 2018
by
Kenton Varda
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'master' of github.com:kentonv/capnproto
parents
5b34c066
c4943308
Show whitespace changes
Inline
Side-by-side
Showing
19 changed files
with
255 additions
and
73 deletions
+255
-73
CMakeLists.txt
CMakeLists.txt
+1
-0
CMakeLists.txt
c++/CMakeLists.txt
+1
-1
json.c++
c++/src/capnp/compat/json.c++
+5
-2
json.h
c++/src/capnp/compat/json.h
+6
-0
capnpc-c++.c++
c++/src/capnp/compiler/capnpc-c++.c++
+19
-19
dynamic-test.c++
c++/src/capnp/dynamic-test.c++
+10
-0
dynamic.c++
c++/src/capnp/dynamic.c++
+27
-12
dynamic.h
c++/src/capnp/dynamic.h
+26
-14
rpc-test.c++
c++/src/capnp/rpc-test.c++
+0
-2
http-test.c++
c++/src/kj/compat/http-test.c++
+6
-0
http.c++
c++/src/kj/compat/http.c++
+8
-2
tls.c++
c++/src/kj/compat/tls.c++
+4
-4
url-test.c++
c++/src/kj/compat/url-test.c++
+12
-1
url.c++
c++/src/kj/compat/url.c++
+14
-6
encoding-test.c++
c++/src/kj/encoding-test.c++
+38
-0
encoding.c++
c++/src/kj/encoding.c++
+24
-3
encoding.h
c++/src/kj/encoding.h
+51
-4
main.c++
c++/src/kj/main.c++
+1
-1
timer.c++
c++/src/kj/timer.c++
+2
-2
No files found.
CMakeLists.txt
View file @
9032d060
cmake_minimum_required
(
VERSION 3.1
)
project
(
"Cap'n Proto Root"
CXX
)
add_subdirectory
(
c++
)
c++/CMakeLists.txt
View file @
9032d060
project
(
"Cap'n Proto"
CXX
)
cmake_minimum_required
(
VERSION 3.1
)
project
(
"Cap'n Proto"
CXX
)
set
(
VERSION 0.7-dev
)
set
(
CMAKE_MODULE_PATH
"
${
CMAKE_CURRENT_SOURCE_DIR
}
/cmake"
)
...
...
c++/src/capnp/compat/json.c++
View file @
9032d060
...
...
@@ -49,6 +49,7 @@ struct FieldHash {
struct
JsonCodec
::
Impl
{
bool
prettyPrint
=
false
;
HasMode
hasMode
=
HasMode
::
NON_NULL
;
size_t
maxNestingDepth
=
64
;
std
::
unordered_map
<
Type
,
HandlerBase
*
,
TypeHash
>
typeHandlers
;
...
...
@@ -195,6 +196,8 @@ void JsonCodec::setMaxNestingDepth(size_t maxNestingDepth) {
impl
->
maxNestingDepth
=
maxNestingDepth
;
}
void
JsonCodec
::
setHasMode
(
HasMode
mode
)
{
impl
->
hasMode
=
mode
;
}
kj
::
String
JsonCodec
::
encode
(
DynamicValue
::
Reader
value
,
Type
type
)
const
{
MallocMessageBuilder
message
;
auto
json
=
message
.
getRoot
<
JsonValue
>
();
...
...
@@ -308,7 +311,7 @@ void JsonCodec::encode(DynamicValue::Reader input, Type type, JsonValue::Builder
uint
fieldCount
=
0
;
for
(
auto
i
:
kj
::
indices
(
nonUnionFields
))
{
fieldCount
+=
(
hasField
[
i
]
=
structValue
.
has
(
nonUnionFields
[
i
]));
fieldCount
+=
(
hasField
[
i
]
=
structValue
.
has
(
nonUnionFields
[
i
]
,
impl
->
hasMode
));
}
// We try to write the union field, if any, in proper order with the rest.
...
...
@@ -318,7 +321,7 @@ void JsonCodec::encode(DynamicValue::Reader input, Type type, JsonValue::Builder
KJ_IF_MAYBE
(
field
,
which
)
{
// Even if the union field is null, if it is not the default field of the union then we
// have to print it anyway.
unionFieldIsNull
=
!
structValue
.
has
(
*
field
);
unionFieldIsNull
=
!
structValue
.
has
(
*
field
,
impl
->
hasMode
);
if
(
field
->
getProto
().
getDiscriminantValue
()
!=
0
||
!
unionFieldIsNull
)
{
++
fieldCount
;
}
else
{
...
...
c++/src/capnp/compat/json.h
View file @
9032d060
...
...
@@ -74,6 +74,12 @@ public:
// Set maximum nesting depth when decoding JSON to prevent highly nested input from overflowing
// the call stack. The default is 64.
void
setHasMode
(
HasMode
mode
);
// Normally, primitive field values are always included even if they are equal to the default
// value (HasMode::NON_NULL -- only null pointers are omitted). You can use
// setHasMode(HasMode::NON_DEFAULT) to specify that default-valued primitive fields should be
// omitted as well.
template
<
typename
T
>
kj
::
String
encode
(
T
&&
value
)
const
;
// Encode any Cap'n Proto value to JSON, including primitives and
...
...
c++/src/capnp/compiler/capnpc-c++.c++
View file @
9032d060
...
...
@@ -2475,16 +2475,16 @@ private:
const
char
*
linkage
=
scope
.
size
()
==
0
?
"extern "
:
"static "
;
switch
(
type
.
which
())
{
case
schema
:
:
Valu
e
::
BOOL
:
case
schema
:
:
Valu
e
::
INT8
:
case
schema
:
:
Valu
e
::
INT16
:
case
schema
:
:
Valu
e
::
INT32
:
case
schema
:
:
Valu
e
::
INT64
:
case
schema
:
:
Valu
e
::
UINT8
:
case
schema
:
:
Valu
e
::
UINT16
:
case
schema
:
:
Valu
e
::
UINT32
:
case
schema
:
:
Valu
e
::
UINT64
:
case
schema
:
:
Valu
e
::
ENUM
:
case
schema
:
:
Typ
e
::
BOOL
:
case
schema
:
:
Typ
e
::
INT8
:
case
schema
:
:
Typ
e
::
INT16
:
case
schema
:
:
Typ
e
::
INT32
:
case
schema
:
:
Typ
e
::
INT64
:
case
schema
:
:
Typ
e
::
UINT8
:
case
schema
:
:
Typ
e
::
UINT16
:
case
schema
:
:
Typ
e
::
UINT32
:
case
schema
:
:
Typ
e
::
UINT64
:
case
schema
:
:
Typ
e
::
ENUM
:
return
ConstText
{
false
,
kj
::
strTree
(
"static constexpr "
,
typeName_
,
' '
,
upperCase
,
" = "
,
...
...
@@ -2497,9 +2497,9 @@ private:
"#endif
\n
"
)
};
case
schema
:
:
Valu
e
::
VOID
:
case
schema
:
:
Valu
e
::
FLOAT32
:
case
schema
:
:
Valu
e
::
FLOAT64
:
{
case
schema
:
:
Typ
e
::
VOID
:
case
schema
:
:
Typ
e
::
FLOAT32
:
case
schema
:
:
Typ
e
::
FLOAT64
:
{
// TODO(msvc): MSVC doesn't like float- or class-typed constexprs. As soon as this is fixed,
// treat VOID, FLOAT32, and FLOAT64 the same as the other primitives.
kj
::
String
value
=
literalValue
(
schema
.
getType
(),
constProto
.
getValue
()).
flatten
();
...
...
@@ -2513,7 +2513,7 @@ private:
};
}
case
schema
:
:
Valu
e
::
TEXT
:
{
case
schema
:
:
Typ
e
::
TEXT
:
{
kj
::
String
constType
=
kj
::
strTree
(
"::capnp::_::ConstText<"
,
schema
.
as
<
Text
>
().
size
(),
">"
).
flatten
();
return
ConstText
{
...
...
@@ -2524,7 +2524,7 @@ private:
};
}
case
schema
:
:
Valu
e
::
DATA
:
{
case
schema
:
:
Typ
e
::
DATA
:
{
kj
::
String
constType
=
kj
::
strTree
(
"::capnp::_::ConstData<"
,
schema
.
as
<
Data
>
().
size
(),
">"
).
flatten
();
return
ConstText
{
...
...
@@ -2535,7 +2535,7 @@ private:
};
}
case
schema
:
:
Valu
e
::
STRUCT
:
{
case
schema
:
:
Typ
e
::
STRUCT
:
{
kj
::
String
constType
=
kj
::
strTree
(
"::capnp::_::ConstStruct<"
,
typeName_
,
">"
).
flatten
();
return
ConstText
{
...
...
@@ -2546,7 +2546,7 @@ private:
};
}
case
schema
:
:
Valu
e
::
LIST
:
{
case
schema
:
:
Typ
e
::
LIST
:
{
kj
::
String
constType
=
kj
::
strTree
(
"::capnp::_::ConstList<"
,
typeName
(
type
.
asList
().
getElementType
(),
nullptr
),
">"
)
.
flatten
();
...
...
@@ -2558,8 +2558,8 @@ private:
};
}
case
schema
:
:
Valu
e
::
ANY_POINTER
:
case
schema
:
:
Valu
e
::
INTERFACE
:
case
schema
:
:
Typ
e
::
ANY_POINTER
:
case
schema
:
:
Typ
e
::
INTERFACE
:
return
ConstText
{
false
,
kj
::
strTree
(),
kj
::
strTree
()
};
}
...
...
c++/src/capnp/dynamic-test.c++
View file @
9032d060
...
...
@@ -422,15 +422,20 @@ TEST(DynamicApi, Has) {
// Primitive fields are always present even if set to default.
EXPECT_TRUE
(
root
.
has
(
"int32Field"
));
EXPECT_FALSE
(
root
.
has
(
"int32Field"
,
HasMode
::
NON_DEFAULT
));
root
.
set
(
"int32Field"
,
123
);
EXPECT_TRUE
(
root
.
has
(
"int32Field"
));
EXPECT_TRUE
(
root
.
has
(
"int32Field"
,
HasMode
::
NON_DEFAULT
));
root
.
set
(
"int32Field"
,
-
12345678
);
EXPECT_TRUE
(
root
.
has
(
"int32Field"
));
EXPECT_FALSE
(
root
.
has
(
"int32Field"
,
HasMode
::
NON_DEFAULT
));
// Pointers are absent until initialized.
EXPECT_FALSE
(
root
.
has
(
"structField"
));
EXPECT_FALSE
(
root
.
has
(
"structField"
,
HasMode
::
NON_DEFAULT
));
root
.
init
(
"structField"
);
EXPECT_TRUE
(
root
.
has
(
"structField"
));
EXPECT_TRUE
(
root
.
has
(
"structField"
,
HasMode
::
NON_DEFAULT
));
}
TEST
(
DynamicApi
,
HasWhenEmpty
)
{
...
...
@@ -443,6 +448,11 @@ TEST(DynamicApi, HasWhenEmpty) {
EXPECT_TRUE
(
root
.
has
(
"int32Field"
));
EXPECT_FALSE
(
root
.
has
(
"structField"
));
EXPECT_FALSE
(
root
.
has
(
"int32List"
));
EXPECT_FALSE
(
root
.
has
(
"voidField"
,
HasMode
::
NON_DEFAULT
));
EXPECT_FALSE
(
root
.
has
(
"int32Field"
,
HasMode
::
NON_DEFAULT
));
EXPECT_FALSE
(
root
.
has
(
"structField"
,
HasMode
::
NON_DEFAULT
));
EXPECT_FALSE
(
root
.
has
(
"int32List"
,
HasMode
::
NON_DEFAULT
));
}
TEST
(
DynamicApi
,
SetEnumFromNative
)
{
...
...
c++/src/capnp/dynamic.c++
View file @
9032d060
...
...
@@ -414,7 +414,7 @@ DynamicValue::Pipeline DynamicStruct::Pipeline::get(StructSchema::Field field) {
KJ_UNREACHABLE
;
}
bool
DynamicStruct
::
Reader
::
has
(
StructSchema
::
Field
field
)
const
{
bool
DynamicStruct
::
Reader
::
has
(
StructSchema
::
Field
field
,
HasMode
mode
)
const
{
KJ_REQUIRE
(
field
.
getContainingStruct
()
==
schema
,
"`field` is not a field of this struct."
);
auto
proto
=
field
.
getProto
();
...
...
@@ -441,20 +441,35 @@ bool DynamicStruct::Reader::has(StructSchema::Field field) const {
switch
(
type
.
which
())
{
case
schema
:
:
Type
::
VOID
:
// Void is always equal to the default.
return
mode
==
HasMode
::
NON_NULL
;
case
schema
:
:
Type
::
BOOL
:
return
mode
==
HasMode
::
NON_NULL
||
reader
.
getDataField
<
bool
>
(
assumeDataOffset
(
slot
.
getOffset
()),
0
)
!=
0
;
case
schema
:
:
Type
::
INT8
:
case
schema
:
:
Type
::
INT16
:
case
schema
:
:
Type
::
INT32
:
case
schema
:
:
Type
::
INT64
:
case
schema
:
:
Type
::
UINT8
:
return
mode
==
HasMode
::
NON_NULL
||
reader
.
getDataField
<
uint8_t
>
(
assumeDataOffset
(
slot
.
getOffset
()),
0
)
!=
0
;
case
schema
:
:
Type
::
INT16
:
case
schema
:
:
Type
::
UINT16
:
case
schema
:
:
Type
::
ENUM
:
return
mode
==
HasMode
::
NON_NULL
||
reader
.
getDataField
<
uint16_t
>
(
assumeDataOffset
(
slot
.
getOffset
()),
0
)
!=
0
;
case
schema
:
:
Type
::
INT32
:
case
schema
:
:
Type
::
UINT32
:
case
schema
:
:
Type
::
UINT64
:
case
schema
:
:
Type
::
FLOAT32
:
return
mode
==
HasMode
::
NON_NULL
||
reader
.
getDataField
<
uint32_t
>
(
assumeDataOffset
(
slot
.
getOffset
()),
0
)
!=
0
;
case
schema
:
:
Type
::
INT64
:
case
schema
:
:
Type
::
UINT64
:
case
schema
:
:
Type
::
FLOAT64
:
case
schema
:
:
Type
::
ENUM
:
// Primitive types are always present.
return
true
;
return
mode
==
HasMode
::
NON_NULL
||
reader
.
getDataField
<
uint64_t
>
(
assumeDataOffset
(
slot
.
getOffset
()),
0
)
!=
0
;
case
schema
:
:
Type
::
TEXT
:
case
schema
:
:
Type
::
DATA
:
...
...
@@ -985,11 +1000,11 @@ DynamicValue::Builder DynamicStruct::Builder::get(kj::StringPtr name) {
DynamicValue
::
Pipeline
DynamicStruct
::
Pipeline
::
get
(
kj
::
StringPtr
name
)
{
return
get
(
schema
.
getFieldByName
(
name
));
}
bool
DynamicStruct
::
Reader
::
has
(
kj
::
StringPtr
name
)
const
{
return
has
(
schema
.
getFieldByName
(
name
));
bool
DynamicStruct
::
Reader
::
has
(
kj
::
StringPtr
name
,
HasMode
mode
)
const
{
return
has
(
schema
.
getFieldByName
(
name
)
,
mode
);
}
bool
DynamicStruct
::
Builder
::
has
(
kj
::
StringPtr
name
)
{
return
has
(
schema
.
getFieldByName
(
name
));
bool
DynamicStruct
::
Builder
::
has
(
kj
::
StringPtr
name
,
HasMode
mode
)
{
return
has
(
schema
.
getFieldByName
(
name
)
,
mode
);
}
void
DynamicStruct
::
Builder
::
set
(
kj
::
StringPtr
name
,
const
DynamicValue
::
Reader
&
value
)
{
set
(
schema
.
getFieldByName
(
name
),
value
);
...
...
c++/src/capnp/dynamic.h
View file @
9032d060
...
...
@@ -169,6 +169,21 @@ private:
// -------------------------------------------------------------------
enum
class
HasMode
:
uint8_t
{
// Specifies the meaning of "has(field)".
NON_NULL
,
// "has(field)" only returns false if the field is a pointer and the pointer is null. This is the
// default behavior.
NON_DEFAULT
// "has(field)" returns false if the field is set to its default value. This differs from
// NON_NULL only in the handling of primitive values.
//
// "Equal to default value" is technically defined as the field value being encoded as all-zero
// on the wire (since primitive values are XORed by their defined default value when encoded).
};
class
DynamicStruct
::
Reader
{
public
:
typedef
DynamicStruct
Reads
;
...
...
@@ -191,12 +206,10 @@ public:
DynamicValue
::
Reader
get
(
StructSchema
::
Field
field
)
const
;
// Read the given field value.
bool
has
(
StructSchema
::
Field
field
)
const
;
// Tests whether the given field is set to its default value. For pointer values, this does
// not actually traverse the value comparing it with the default, but simply returns true if the
// pointer is non-null. For members of unions, has() returns false if the union member is not
// active, but does not necessarily return true if the member is active (depends on the field's
// value).
bool
has
(
StructSchema
::
Field
field
,
HasMode
mode
=
HasMode
::
NON_NULL
)
const
;
// Tests whether the given field is "present". If the field is a union member and is not the
// active member, this always returns false. Otherwise, the field's value is interpreted
// according to `mode`.
kj
::
Maybe
<
StructSchema
::
Field
>
which
()
const
;
// If the struct contains an (unnamed) union, and the currently-active field within that union
...
...
@@ -206,7 +219,7 @@ public:
// newer version of the protocol and is using a field of the union that you don't know about yet.
DynamicValue
::
Reader
get
(
kj
::
StringPtr
name
)
const
;
bool
has
(
kj
::
StringPtr
name
)
const
;
bool
has
(
kj
::
StringPtr
name
,
HasMode
mode
=
HasMode
::
NON_NULL
)
const
;
// Shortcuts to access fields by name. These throw exceptions if no such field exists.
private
:
...
...
@@ -261,12 +274,11 @@ public:
DynamicValue
::
Builder
get
(
StructSchema
::
Field
field
);
// Read the given field value.
inline
bool
has
(
StructSchema
::
Field
field
)
{
return
asReader
().
has
(
field
);
}
// Tests whether the given field is set to its default value. For pointer values, this does
// not actually traverse the value comparing it with the default, but simply returns true if the
// pointer is non-null. For members of unions, has() returns whether the field is currently
// active and the union as a whole is non-default -- so, the only time has() will return false
// for an active union field is if it is the default active field and it has its default value.
inline
bool
has
(
StructSchema
::
Field
field
,
HasMode
mode
=
HasMode
::
NON_NULL
)
{
return
asReader
().
has
(
field
,
mode
);
}
// Tests whether the given field is "present". If the field is a union member and is not the
// active member, this always returns false. Otherwise, the field's value is interpreted
// according to `mode`.
kj
::
Maybe
<
StructSchema
::
Field
>
which
();
// If the struct contains an (unnamed) union, and the currently-active field within that union
...
...
@@ -292,7 +304,7 @@ public:
// field null.
DynamicValue
::
Builder
get
(
kj
::
StringPtr
name
);
bool
has
(
kj
::
StringPtr
name
);
bool
has
(
kj
::
StringPtr
name
,
HasMode
mode
=
HasMode
::
NON_NULL
);
void
set
(
kj
::
StringPtr
name
,
const
DynamicValue
::
Reader
&
value
);
void
set
(
kj
::
StringPtr
name
,
std
::
initializer_list
<
DynamicValue
::
Reader
>
value
);
DynamicValue
::
Builder
init
(
kj
::
StringPtr
name
);
...
...
c++/src/capnp/rpc-test.c++
View file @
9032d060
...
...
@@ -1168,7 +1168,6 @@ TEST(Rpc, RealmGatewayImportExport) {
kj
::
EventLoop
loop
;
kj
::
WaitScope
waitScope
(
loop
);
TestNetwork
network
;
TestRestorer
restorer
;
TestNetworkAdapter
&
clientNetwork
=
network
.
add
(
"client"
);
TestNetworkAdapter
&
serverNetwork
=
network
.
add
(
"server"
);
RpcSystem
<
test
::
TestSturdyRefHostId
>
rpcClient
=
...
...
@@ -1222,7 +1221,6 @@ TEST(Rpc, RealmGatewayImportExport) {
kj
::
EventLoop
loop
;
kj
::
WaitScope
waitScope
(
loop
);
TestNetwork
network
;
TestRestorer
restorer
;
TestNetworkAdapter
&
clientNetwork
=
network
.
add
(
"client"
);
TestNetworkAdapter
&
serverNetwork
=
network
.
add
(
"server"
);
RpcSystem
<
test
::
TestSturdyRefHostId
>
rpcClient
=
...
...
c++/src/kj/compat/http-test.c++
View file @
9032d060
...
...
@@ -2619,6 +2619,12 @@ KJ_TEST("HttpClient connection management") {
.
wait
(
io
.
waitScope
);
KJ_EXPECT
(
count
==
0
);
// Connections where we didn't even wait for the response headers are not reused.
doRequest
().
wait
(
io
.
waitScope
);
KJ_EXPECT
(
count
==
1
);
client
->
request
(
HttpMethod
::
GET
,
kj
::
str
(
"/foo"
),
HttpHeaders
(
headerTable
));
KJ_EXPECT
(
count
==
0
);
// Connections where we failed to write the full request body are not reused.
doRequest
().
wait
(
io
.
waitScope
);
KJ_EXPECT
(
count
==
1
);
...
...
c++/src/kj/compat/http.c++
View file @
9032d060
...
...
@@ -993,7 +993,7 @@ public:
}
bool
canReuse
()
{
return
!
broken
;
return
!
broken
&&
pendingMessageCount
==
0
;
}
// ---------------------------------------------------------------------------
...
...
@@ -1005,6 +1005,7 @@ public:
KJ_REQUIRE_NONNULL
(
onMessageDone
)
->
fulfill
();
onMessageDone
=
nullptr
;
--
pendingMessageCount
;
}
void
abortRead
()
{
...
...
@@ -1066,6 +1067,7 @@ public:
}
kj
::
Promise
<
kj
::
ArrayPtr
<
char
>>
readMessageHeaders
()
{
++
pendingMessageCount
;
auto
paf
=
kj
::
newPromiseAndFulfiller
<
void
>
();
auto
promise
=
messageReadQueue
...
...
@@ -1194,6 +1196,9 @@ private:
bool
broken
=
false
;
// Becomes true if the caller failed to read the whole entity-body before closing the stream.
uint
pendingMessageCount
=
0
;
// Number of reads we have queued up.
kj
::
Promise
<
void
>
messageReadQueue
=
kj
::
READY_NOW
;
kj
::
Maybe
<
kj
::
Own
<
kj
::
PromiseFulfiller
<
void
>>>
onMessageDone
;
...
...
@@ -2413,7 +2418,8 @@ public:
settings
(
kj
::
mv
(
settings
))
{}
bool
canReuse
()
{
// Returns true if we can reuse this HttpClient for another request.
// Returns true if we can immediately reuse this HttpClient for another message (so all
// previous messages have been fully read).
return
!
upgraded
&&
!
closed
&&
httpInput
.
canReuse
()
&&
httpOutput
.
canReuse
();
}
...
...
c++/src/kj/compat/tls.c++
View file @
9032d060
...
...
@@ -99,7 +99,7 @@ void ensureOpenSslInitialized() {
// AsyncIoStream is simply wrapping a file descriptor (or other readiness-based stream?) and use
// that directly if so.
class
TlsConnection
:
public
kj
::
AsyncIoStream
{
class
TlsConnection
final
:
public
kj
::
AsyncIoStream
{
public
:
TlsConnection
(
kj
::
Own
<
kj
::
AsyncIoStream
>
stream
,
SSL_CTX
*
ctx
)
:
TlsConnection
(
*
stream
,
ctx
)
{
...
...
@@ -372,7 +372,7 @@ private:
// =======================================================================================
// Implementations of ConnectionReceiver, NetworkAddress, and Network as wrappers adding TLS.
class
TlsConnectionReceiver
:
public
kj
::
ConnectionReceiver
{
class
TlsConnectionReceiver
final
:
public
kj
::
ConnectionReceiver
{
public
:
TlsConnectionReceiver
(
TlsContext
&
tls
,
kj
::
Own
<
kj
::
ConnectionReceiver
>
inner
)
:
tls
(
tls
),
inner
(
kj
::
mv
(
inner
))
{}
...
...
@@ -400,7 +400,7 @@ private:
kj
::
Own
<
kj
::
ConnectionReceiver
>
inner
;
};
class
TlsNetworkAddress
:
public
kj
::
NetworkAddress
{
class
TlsNetworkAddress
final
:
public
kj
::
NetworkAddress
{
public
:
TlsNetworkAddress
(
TlsContext
&
tls
,
kj
::
String
hostname
,
kj
::
Own
<
kj
::
NetworkAddress
>&&
inner
)
:
tls
(
tls
),
hostname
(
kj
::
mv
(
hostname
)),
inner
(
kj
::
mv
(
inner
))
{}
...
...
@@ -435,7 +435,7 @@ private:
kj
::
Own
<
kj
::
NetworkAddress
>
inner
;
};
class
TlsNetwork
:
public
kj
::
Network
{
class
TlsNetwork
final
:
public
kj
::
Network
{
public
:
TlsNetwork
(
TlsContext
&
tls
,
kj
::
Network
&
inner
)
:
tls
(
tls
),
inner
(
inner
)
{}
TlsNetwork
(
TlsContext
&
tls
,
kj
::
Own
<
kj
::
Network
>
inner
)
...
...
c++/src/kj/compat/url-test.c++
View file @
9032d060
...
...
@@ -132,6 +132,14 @@ KJ_TEST("parse / stringify URL") {
KJ_EXPECT
(
KJ_ASSERT_NONNULL
(
url
.
fragment
)
==
"garply"
);
}
{
auto
url
=
parseAndCheck
(
"https://capnproto.org/foo?bar%20baz=qux+quux"
,
"https://capnproto.org/foo?bar+baz=qux+quux"
);
KJ_ASSERT
(
url
.
query
.
size
()
==
1
);
KJ_EXPECT
(
url
.
query
[
0
].
name
==
"bar baz"
);
KJ_EXPECT
(
url
.
query
[
0
].
value
==
"qux quux"
);
}
{
auto
url
=
parseAndCheck
(
"https://capnproto.org/foo/bar#garply"
);
KJ_EXPECT
(
url
.
scheme
==
"https"
);
...
...
@@ -232,7 +240,7 @@ KJ_TEST("URL percent encoding") {
parseAndCheck
(
"https://b b: bcd@capnproto.org/f o?b r=b z#q x"
,
"https://b%20b:%20bcd@capnproto.org/f%20o?b
%20r=b%20
z#q%20x"
);
"https://b%20b:%20bcd@capnproto.org/f%20o?b
+r=b+
z#q%20x"
);
}
KJ_TEST
(
"URL relative paths"
)
{
...
...
@@ -340,6 +348,9 @@ KJ_TEST("parse relative URL") {
parseAndCheckRelative
(
"https://capnproto.org/foo/bar?baz=qux#corge"
,
"?grault"
,
"https://capnproto.org/foo/bar?grault"
);
parseAndCheckRelative
(
"https://capnproto.org/foo/bar?baz=qux#corge"
,
"?grault+garply=waldo"
,
"https://capnproto.org/foo/bar?grault+garply=waldo"
);
parseAndCheckRelative
(
"https://capnproto.org/foo/bar?baz=qux#corge"
,
"grault"
,
"https://capnproto.org/foo/grault"
);
...
...
c++/src/kj/compat/url.c++
View file @
9032d060
...
...
@@ -88,6 +88,12 @@ String percentDecode(ArrayPtr<const char> text, bool& hadErrors) {
return
kj
::
mv
(
result
);
}
String
percentDecodeQuery
(
ArrayPtr
<
const
char
>
text
,
bool
&
hadErrors
)
{
auto
result
=
decodeWwwForm
(
text
);
if
(
result
.
hadErrors
)
hadErrors
=
true
;
return
kj
::
mv
(
result
);
}
}
// namespace
Url
::~
Url
()
noexcept
(
false
)
{}
...
...
@@ -195,9 +201,10 @@ Maybe<Url> Url::tryParse(StringPtr text, Context context) {
if
(
part
.
size
()
>
0
)
{
KJ_IF_MAYBE
(
key
,
trySplit
(
part
,
'='
))
{
result
.
query
.
add
(
QueryParam
{
percentDecode
(
*
key
,
err
),
percentDecode
(
part
,
err
)
});
result
.
query
.
add
(
QueryParam
{
percentDecodeQuery
(
*
key
,
err
),
percentDecodeQuery
(
part
,
err
)
});
}
else
{
result
.
query
.
add
(
QueryParam
{
percentDecode
(
part
,
err
),
nullptr
});
result
.
query
.
add
(
QueryParam
{
percentDecode
Query
(
part
,
err
),
nullptr
});
}
}
}
while
(
text
.
startsWith
(
"&"
));
...
...
@@ -331,9 +338,10 @@ Maybe<Url> Url::tryParseRelative(StringPtr text) const {
if
(
part
.
size
()
>
0
)
{
KJ_IF_MAYBE
(
key
,
trySplit
(
part
,
'='
))
{
result
.
query
.
add
(
QueryParam
{
percentDecode
(
*
key
,
err
),
percentDecode
(
part
,
err
)
});
result
.
query
.
add
(
QueryParam
{
percentDecodeQuery
(
*
key
,
err
),
percentDecodeQuery
(
part
,
err
)
});
}
else
{
result
.
query
.
add
(
QueryParam
{
percentDecode
(
part
,
err
),
nullptr
});
result
.
query
.
add
(
QueryParam
{
percentDecode
Query
(
part
,
err
),
nullptr
});
}
}
}
while
(
text
.
startsWith
(
"&"
));
...
...
@@ -407,10 +415,10 @@ String Url::toString(Context context) const {
for
(
auto
&
param
:
query
)
{
chars
.
add
(
first
?
'?'
:
'&'
);
first
=
false
;
chars
.
addAll
(
encode
UriComponent
(
param
.
name
));
chars
.
addAll
(
encode
WwwForm
(
param
.
name
));
if
(
param
.
value
.
size
()
>
0
)
{
chars
.
add
(
'='
);
chars
.
addAll
(
encode
UriComponent
(
param
.
value
));
chars
.
addAll
(
encode
WwwForm
(
param
.
value
));
}
}
...
...
c++/src/kj/encoding-test.c++
View file @
9032d060
...
...
@@ -279,6 +279,9 @@ KJ_TEST("URI encoding/decoding") {
KJ_EXPECT
(
encodeUriComponent
(
"
\xab\xba
"
)
==
"%AB%BA"
);
KJ_EXPECT
(
encodeUriComponent
(
StringPtr
(
"foo
\0
bar"
,
7
))
==
"foo%00bar"
);
// Encode characters reserved by application/x-www-form-urlencoded, but not by RFC 2396.
KJ_EXPECT
(
encodeUriComponent
(
"'foo'! (~)"
)
==
"'foo'!%20(~)"
);
expectRes
(
decodeUriComponent
(
"foo%20bar"
),
"foo bar"
);
expectRes
(
decodeUriComponent
(
"%ab%BA"
),
"
\xab\xba
"
);
...
...
@@ -287,8 +290,43 @@ KJ_TEST("URI encoding/decoding") {
expectRes
(
decodeUriComponent
(
"foo%xxx"
),
"fooxxx"
,
true
);
expectRes
(
decodeUriComponent
(
"foo%"
),
"foo"
,
true
);
{
byte
bytes
[]
=
{
12
,
34
,
56
};
KJ_EXPECT
(
decodeBinaryUriComponent
(
encodeUriComponent
(
bytes
)).
asPtr
()
==
bytes
);
// decodeBinaryUriComponent() takes a DecodeUriOptions struct as its second parameter, but it
// once took a single `bool nulTerminate`. Verify that the old behavior still compiles and
// works.
auto
bytesWithNul
=
decodeBinaryUriComponent
(
encodeUriComponent
(
bytes
),
true
);
KJ_ASSERT
(
bytesWithNul
.
size
()
==
4
);
KJ_EXPECT
(
bytesWithNul
[
3
]
==
'\0'
);
KJ_EXPECT
(
bytesWithNul
.
slice
(
0
,
3
)
==
bytes
);
}
}
KJ_TEST
(
"application/x-www-form-urlencoded encoding/decoding"
)
{
KJ_EXPECT
(
encodeWwwForm
(
"foo"
)
==
"foo"
);
KJ_EXPECT
(
encodeWwwForm
(
"foo bar"
)
==
"foo+bar"
);
KJ_EXPECT
(
encodeWwwForm
(
"
\xab\xba
"
)
==
"%AB%BA"
);
KJ_EXPECT
(
encodeWwwForm
(
StringPtr
(
"foo
\0
bar"
,
7
))
==
"foo%00bar"
);
// Encode characters reserved by application/x-www-form-urlencoded, but not by RFC 2396.
KJ_EXPECT
(
encodeWwwForm
(
"'foo'! (~)"
)
==
"%27foo%27%21+%28%7E%29"
);
expectRes
(
decodeWwwForm
(
"foo%20bar"
),
"foo bar"
);
expectRes
(
decodeWwwForm
(
"foo+bar"
),
"foo bar"
);
expectRes
(
decodeWwwForm
(
"%ab%BA"
),
"
\xab\xba
"
);
expectRes
(
decodeWwwForm
(
"foo%1xxx"
),
"foo
\1
xxx"
,
true
);
expectRes
(
decodeWwwForm
(
"foo%1"
),
"foo
\1
"
,
true
);
expectRes
(
decodeWwwForm
(
"foo%xxx"
),
"fooxxx"
,
true
);
expectRes
(
decodeWwwForm
(
"foo%"
),
"foo"
,
true
);
{
byte
bytes
[]
=
{
12
,
34
,
56
};
DecodeUriOptions
options
{
/*.nulTerminate=*/
false
,
/*.plusToSpace=*/
true
};
KJ_EXPECT
(
decodeBinaryUriComponent
(
encodeWwwForm
(
bytes
),
options
)
==
bytes
);
}
}
KJ_TEST
(
"C escape encoding/decoding"
)
{
...
...
c++/src/kj/encoding.c++
View file @
9032d060
...
...
@@ -404,9 +404,27 @@ String encodeUriComponent(ArrayPtr<const byte> bytes) {
return
String
(
result
.
releaseAsArray
());
}
String
encodeWwwForm
(
ArrayPtr
<
const
byte
>
bytes
)
{
Vector
<
char
>
result
(
bytes
.
size
()
+
1
);
for
(
byte
b
:
bytes
)
{
if
((
'A'
<=
b
&&
b
<=
'Z'
)
||
(
'a'
<=
b
&&
b
<=
'z'
)
||
(
'0'
<=
b
&&
b
<=
'9'
)
||
b
==
'-'
||
b
==
'_'
||
b
==
'.'
||
b
==
'*'
)
{
result
.
add
(
b
);
}
else
if
(
b
==
' '
)
{
result
.
add
(
'+'
);
}
else
{
result
.
add
(
'%'
);
result
.
add
(
HEX_DIGITS_URI
[
b
/
16
]);
result
.
add
(
HEX_DIGITS_URI
[
b
%
16
]);
}
}
result
.
add
(
'\0'
);
return
String
(
result
.
releaseAsArray
());
}
EncodingResult
<
Array
<
byte
>>
decodeBinaryUriComponent
(
ArrayPtr
<
const
char
>
text
,
bool
nulTerminate
)
{
Vector
<
byte
>
result
(
text
.
size
()
+
nulTerminate
);
ArrayPtr
<
const
char
>
text
,
DecodeUriOptions
options
)
{
Vector
<
byte
>
result
(
text
.
size
()
+
options
.
nulTerminate
);
bool
hadErrors
=
false
;
const
char
*
ptr
=
text
.
begin
();
...
...
@@ -432,12 +450,15 @@ EncodingResult<Array<byte>> decodeBinaryUriComponent(
}
else
{
hadErrors
=
true
;
}
}
else
if
(
options
.
plusToSpace
&&
*
ptr
==
'+'
)
{
++
ptr
;
result
.
add
(
' '
);
}
else
{
result
.
add
(
*
ptr
++
);
}
}
if
(
nulTerminate
)
result
.
add
(
0
);
if
(
options
.
nulTerminate
)
result
.
add
(
0
);
return
{
result
.
releaseAsArray
(),
hadErrors
};
}
...
...
c++/src/kj/encoding.h
View file @
9032d060
...
...
@@ -124,10 +124,40 @@ EncodingResult<Array<byte>> decodeHex(ArrayPtr<const char> text);
String
encodeUriComponent
(
ArrayPtr
<
const
byte
>
bytes
);
String
encodeUriComponent
(
ArrayPtr
<
const
char
>
bytes
);
EncodingResult
<
Array
<
byte
>>
decodeBinaryUriComponent
(
ArrayPtr
<
const
char
>
text
,
bool
nulTerminate
=
false
);
EncodingResult
<
String
>
decodeUriComponent
(
ArrayPtr
<
const
char
>
text
);
// Encode/decode URI components using % escapes. See Javascript's encodeURIComponent().
// Encode/decode URI components using % escapes for characters listed as "reserved" in RFC 2396.
// This is the same behavior as JavaScript's `encodeURIComponent()`.
//
// See https://tools.ietf.org/html/rfc2396#section-2.3
String
encodeWwwForm
(
ArrayPtr
<
const
byte
>
bytes
);
String
encodeWwwForm
(
ArrayPtr
<
const
char
>
bytes
);
EncodingResult
<
String
>
decodeWwwForm
(
ArrayPtr
<
const
char
>
text
);
// Encode/decode URI components using % escapes and '+' (for spaces) according to the
// application/x-www-form-urlencoded format defined by the WHATWG URL specification.
//
// See https://url.spec.whatwg.org/#concept-urlencoded-byte-serializer
struct
DecodeUriOptions
{
// Parameter to `decodeBinaryUriComponent()`.
// This struct is intentionally convertible from bool, in order to maintain backwards
// compatibility with code written when `decodeBinaryUriComponent()` took a boolean second
// parameter.
DecodeUriOptions
(
bool
nulTerminate
=
false
,
bool
plusToSpace
=
false
)
:
nulTerminate
(
nulTerminate
),
plusToSpace
(
plusToSpace
)
{}
bool
nulTerminate
;
// Append a terminal NUL byte.
bool
plusToSpace
;
// Convert '+' to ' ' characters before percent decoding. Used to decode
// application/x-www-form-urlencoded text, such as query strings.
};
EncodingResult
<
Array
<
byte
>>
decodeBinaryUriComponent
(
ArrayPtr
<
const
char
>
text
,
DecodeUriOptions
options
=
DecodeUriOptions
());
// Decode URI components using % escapes. This is a lower-level interface used to implement both
// `decodeUriComponent()` and `decodeWwwForm()`
String
encodeCEscape
(
ArrayPtr
<
const
byte
>
bytes
);
String
encodeCEscape
(
ArrayPtr
<
const
char
>
bytes
);
...
...
@@ -181,7 +211,16 @@ inline String encodeUriComponent(ArrayPtr<const char> text) {
return
encodeUriComponent
(
text
.
asBytes
());
}
inline
EncodingResult
<
String
>
decodeUriComponent
(
ArrayPtr
<
const
char
>
text
)
{
auto
result
=
decodeBinaryUriComponent
(
text
,
true
);
auto
result
=
decodeBinaryUriComponent
(
text
,
DecodeUriOptions
{
/*.nulTerminate=*/
true
});
return
{
String
(
result
.
releaseAsChars
()),
result
.
hadErrors
};
}
inline
String
encodeWwwForm
(
ArrayPtr
<
const
char
>
text
)
{
return
encodeWwwForm
(
text
.
asBytes
());
}
inline
EncodingResult
<
String
>
decodeWwwForm
(
ArrayPtr
<
const
char
>
text
)
{
auto
result
=
decodeBinaryUriComponent
(
text
,
DecodeUriOptions
{
/*.nulTerminate=*/
true
,
/*.plusToSpace=*/
true
});
return
{
String
(
result
.
releaseAsChars
()),
result
.
hadErrors
};
}
...
...
@@ -239,6 +278,14 @@ inline EncodingResult<String> decodeUriComponent(const char (&text)[s]) {
return
decodeUriComponent
(
arrayPtr
(
text
,
s
-
1
));
}
template
<
size_t
s
>
inline
String
encodeWwwForm
(
const
char
(
&
text
)[
s
])
{
return
encodeWwwForm
(
arrayPtr
(
text
,
s
-
1
));
}
template
<
size_t
s
>
inline
EncodingResult
<
String
>
decodeWwwForm
(
const
char
(
&
text
)[
s
])
{
return
decodeWwwForm
(
arrayPtr
(
text
,
s
-
1
));
}
template
<
size_t
s
>
inline
String
encodeCEscape
(
const
char
(
&
text
)[
s
])
{
return
encodeCEscape
(
arrayPtr
(
text
,
s
-
1
));
}
...
...
c++/src/kj/main.c++
View file @
9032d060
...
...
@@ -631,7 +631,7 @@ void MainBuilder::MainImpl::usageError(StringPtr programName, StringPtr message)
class
MainBuilder
::
Impl
::
OptionDisplayOrder
{
public
:
bool
operator
()(
const
Option
*
a
,
const
Option
*
b
)
{
bool
operator
()(
const
Option
*
a
,
const
Option
*
b
)
const
{
if
(
a
==
b
)
return
false
;
char
aShort
=
'\0'
;
...
...
c++/src/kj/timer.c++
View file @
9032d060
...
...
@@ -32,7 +32,7 @@ kj::Exception Timer::makeTimeoutException() {
struct
TimerImpl
::
Impl
{
struct
TimerBefore
{
bool
operator
()(
TimerPromiseAdapter
*
lhs
,
TimerPromiseAdapter
*
rhs
);
bool
operator
()(
TimerPromiseAdapter
*
lhs
,
TimerPromiseAdapter
*
rhs
)
const
;
};
using
Timers
=
std
::
multiset
<
TimerPromiseAdapter
*
,
TimerBefore
>
;
Timers
timers
;
...
...
@@ -66,7 +66,7 @@ private:
};
inline
bool
TimerImpl
::
Impl
::
TimerBefore
::
operator
()(
TimerPromiseAdapter
*
lhs
,
TimerPromiseAdapter
*
rhs
)
{
TimerPromiseAdapter
*
lhs
,
TimerPromiseAdapter
*
rhs
)
const
{
return
lhs
->
time
<
rhs
->
time
;
}
...
...
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