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
eebec489
Unverified
Commit
eebec489
authored
May 01, 2018
by
Kenton Varda
Committed by
GitHub
May 01, 2018
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #666 from capnproto/http-body-cancellation
Improve HTTP body cancellation handling.
parents
fac09cf7
555753da
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
70 additions
and
16 deletions
+70
-16
http-test.c++
c++/src/kj/compat/http-test.c++
+34
-0
http.c++
c++/src/kj/compat/http.c++
+36
-16
No files found.
c++/src/kj/compat/http-test.c++
View file @
eebec489
...
...
@@ -869,6 +869,40 @@ KJ_TEST("HttpClient responses") {
}
}
KJ_TEST
(
"HttpClient canceled write"
)
{
kj
::
EventLoop
eventLoop
;
kj
::
WaitScope
waitScope
(
eventLoop
);
auto
pipe
=
kj
::
newTwoWayPipe
();
auto
serverPromise
=
pipe
.
ends
[
1
]
->
readAllText
();
{
HttpHeaderTable
table
;
auto
client
=
newHttpClient
(
table
,
*
pipe
.
ends
[
0
]);
auto
body
=
kj
::
heapArray
<
byte
>
(
4096
);
memset
(
body
.
begin
(),
0xcf
,
body
.
size
());
auto
req
=
client
->
request
(
HttpMethod
::
POST
,
"/"
,
HttpHeaders
(
table
),
uint64_t
(
4096
));
// Start a write and immediately cancel it.
(
void
)
req
.
body
->
write
(
body
.
begin
(),
body
.
size
());
KJ_EXPECT_THROW_MESSAGE
(
"overwrote"
,
req
.
body
->
write
(
"foo"
,
3
).
wait
(
waitScope
));
req
.
body
=
nullptr
;
KJ_EXPECT
(
!
serverPromise
.
poll
(
waitScope
));
KJ_EXPECT_THROW_MESSAGE
(
"can't start new request until previous request body"
,
client
->
request
(
HttpMethod
::
GET
,
"/"
,
HttpHeaders
(
table
)).
response
.
wait
(
waitScope
));
}
pipe
.
ends
[
0
]
->
shutdownWrite
();
auto
text
=
serverPromise
.
wait
(
waitScope
);
KJ_EXPECT
(
text
==
"POST / HTTP/1.1
\r\n
Content-Length: 4096
\r\n\r\n
"
,
text
);
}
KJ_TEST
(
"HttpServer requests"
)
{
HttpResponseTestCase
RESPONSE
=
{
"HTTP/1.1 200 OK
\r\n
"
...
...
c++/src/kj/compat/http.c++
View file @
eebec489
...
...
@@ -1607,12 +1607,13 @@ public:
HttpOutputStream
(
AsyncOutputStream
&
inner
)
:
inner
(
inner
)
{}
bool
canReuse
()
{
return
!
inBody
&&
!
broken
;
return
!
inBody
&&
!
broken
&&
!
writeInProgress
;
}
void
writeHeaders
(
String
content
)
{
// Writes some header content and begins a new entity body.
KJ_REQUIRE
(
!
writeInProgress
,
"concurrent write()s not allowed"
)
{
return
;
}
KJ_REQUIRE
(
!
inBody
,
"previous HTTP message body incomplete; can't write more messages"
);
inBody
=
true
;
...
...
@@ -1620,42 +1621,56 @@ public:
}
void
writeBodyData
(
kj
::
String
content
)
{
KJ_REQUIRE
(
!
writeInProgress
,
"concurrent write()s not allowed"
)
{
return
;
}
KJ_REQUIRE
(
inBody
)
{
return
;
}
queueWrite
(
kj
::
mv
(
content
));
}
kj
::
Promise
<
void
>
writeBodyData
(
const
void
*
buffer
,
size_t
size
)
{
KJ_REQUIRE
(
!
writeInProgress
,
"concurrent write()s not allowed"
)
{
return
kj
::
READY_NOW
;
}
KJ_REQUIRE
(
inBody
)
{
return
kj
::
READY_NOW
;
}
auto
fork
=
writeQueue
.
then
([
this
,
buffer
,
size
]()
{
return
inner
.
write
(
buffer
,
size
);
}).
fork
();
writeInProgress
=
true
;
auto
fork
=
writeQueue
.
fork
();
writeQueue
=
fork
.
addBranch
();
return
fork
.
addBranch
();
return
fork
.
addBranch
().
then
([
this
,
buffer
,
size
]()
{
return
inner
.
write
(
buffer
,
size
);
}).
then
([
this
]()
{
writeInProgress
=
false
;
});
}
kj
::
Promise
<
void
>
writeBodyData
(
kj
::
ArrayPtr
<
const
kj
::
ArrayPtr
<
const
byte
>>
pieces
)
{
KJ_REQUIRE
(
!
writeInProgress
,
"concurrent write()s not allowed"
)
{
return
kj
::
READY_NOW
;
}
KJ_REQUIRE
(
inBody
)
{
return
kj
::
READY_NOW
;
}
auto
fork
=
writeQueue
.
then
([
this
,
pieces
]()
{
return
inner
.
write
(
pieces
);
}).
fork
();
writeInProgress
=
true
;
auto
fork
=
writeQueue
.
fork
();
writeQueue
=
fork
.
addBranch
();
return
fork
.
addBranch
();
return
fork
.
addBranch
().
then
([
this
,
pieces
]()
{
return
inner
.
write
(
pieces
);
}).
then
([
this
]()
{
writeInProgress
=
false
;
});
}
Promise
<
uint64_t
>
pumpBodyFrom
(
AsyncInputStream
&
input
,
uint64_t
amount
)
{
KJ_REQUIRE
(
!
writeInProgress
,
"concurrent write()s not allowed"
)
{
return
uint64_t
(
0
);
}
KJ_REQUIRE
(
inBody
)
{
return
uint64_t
(
0
);
}
auto
fork
=
writeQueue
.
then
([
this
,
&
input
,
amount
]()
{
return
input
.
pumpTo
(
inner
,
amount
);
}).
fork
();
writeInProgress
=
true
;
auto
fork
=
writeQueue
.
fork
(
);
writeQueue
=
fork
.
addBranch
();
writeQueue
=
fork
.
addBranch
().
ignoreResult
();
return
fork
.
addBranch
();
return
fork
.
addBranch
().
then
([
this
,
&
input
,
amount
]()
{
return
input
.
pumpTo
(
inner
,
amount
);
}).
then
([
this
](
uint64_t
actual
)
{
writeInProgress
=
false
;
return
actual
;
});
}
void
finishBody
()
{
...
...
@@ -1689,6 +1704,11 @@ private:
bool
inBody
=
false
;
bool
broken
=
false
;
bool
writeInProgress
=
false
;
// True if a write method has been called and has not completed successfully. In the case that
// a write throws an exception or is canceled, this remains true forever. In these cases, the
// underlying stream is in an inconsistent state and cannot be reused.
void
queueWrite
(
kj
::
String
content
)
{
writeQueue
=
writeQueue
.
then
(
kj
::
mvCapture
(
content
,
[
this
](
kj
::
String
&&
content
)
{
auto
promise
=
inner
.
write
(
content
.
begin
(),
content
.
size
());
...
...
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