Skip to content

Conversation

@artemredkin
Copy link
Collaborator

Motivation:
When we stream request body, current implementation expects that body
will finish streaming before we start to receive response body parts.
This is not correct, reponse body parts can start to arrive before we
finish sending the request.

Modifications:

  • Simplifies state machine, we only case about request being fully sent
    to prevent sending body parts after .end, but response state machine
    is mostly ignored and correct flow will be handled by NIOHTTP1
    pipeline
  • Adds HTTPEchoHandler, that replies to each response body part
  • Adds bi-directional streaming test

Result:
Closes#327

break
case.head whereself.ignoreUncleanSSLShutdown,
.body whereself.ignoreUncleanSSLShutdown:
case.sending whereself.ignoreUncleanSSLShutdown:
Copy link
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

test testNoResponseWithIgnoreErrorForSSLUncleanShutdown fails if I add .sent, I'm not sure why yet...

Copy link
CollaboratorAuthor

@artemredkinartemredkinMar 3, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.sending means that we have not sent client .end

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the difference between .sent and .endOrError? In many cases (content-length present) after having sent the response body, we're done?

Copy link
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you are looking at slightly outdated code

Copy link
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a slightly weird part, it tries to do the following: if ignoreUncleanSSLShutdown is set and we received response .head, then it's allowed to not fail

@artemredkinartemredkinforce-pushed the fix_bi_streaming branch 3 times, most recently from ce06b45 to 88f4dd3CompareMarch 3, 2021 11:45
}.flatMap{
context.eventLoop.assertInEventLoop()

if case .endOrError =self.state {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could we replace this if case and the fallthrough by one full switch self.state?

It's unclear to me why we can move from almost any state to .sent and why all those transitions are legal.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For example a transition from .sent to .sent seems odd, right? Or from .inactive to .sent seems wrong too.

Copy link
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

caseidle
casebodySent
caseinactive
casesending
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we could come up with slightly more prescriptive names? What are wen sending here? The request, the response, the head, the body, the trailers? Some of them, all of them?

Same for sent

Copy link
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

privatefunc writeBodyPart(context:ChannelHandlerContext, part:IOData, promise:EventLoopPromise<Void>){
switchself.state {
case.idle:
case.sending:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this seems a little dangerous? Previously we could send in .idle and now we can't?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A full switch without the default would be better too here.

Copy link
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

break
case.head whereself.ignoreUncleanSSLShutdown,
.body whereself.ignoreUncleanSSLShutdown:
case.sending whereself.ignoreUncleanSSLShutdown:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the difference between .sent and .endOrError? In many cases (content-length present) after having sent the response body, we're done?

Copy link
Contributor

@weissiweissi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thank you! This looks like a great start, left some comments

case sent
case head
case inactive
case sending_waiting
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • we should use lowerCamelCase to match swift's usual style.
  • I think we can do better than sendingWaiting, ie. what are we sending and what are we waiting for?

Copy link
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

case bodySent
case sent
case head
case inactive
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

idle is probably better than inactive because the channel is actually isActive = true, right?

Copy link
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

@artemredkinartemredkinforce-pushed the fix_bi_streaming branch 2 times, most recently from 4b3c27d to 06a4098CompareMarch 3, 2021 12:40
Copy link
Collaborator

@LukasaLukasa left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems reasonable enough to me, nothing obviously problematic in the code.

case.sendingBodyResponseHeadReceived:
self.state =.bodySentResponseHeadReceived
case.bodySentWaitingResponseHead,.bodySentResponseHeadReceived:
preconditionFailure("should not happen")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we print self.state in that message?

Copy link
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

self.state =.bodySentResponseHeadReceived
case.bodySentWaitingResponseHead,.bodySentResponseHeadReceived:
preconditionFailure("should not happen")
case.endOrError,.redirected:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mind adding a comment why that's okay in .endOrError, or .redirected? I don't quite get how we can be in .endbefore we get the end of the body.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(similar for .redirected)

Copy link
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rewrote, added a test

Motivation: When we stream request body, current implementation expects that body will finish streaming _before_ we start to receive response body parts. This is not correct, reponse body parts can start to arrive before we finish sending the request. Modifications: - Simplifies state machine, we only case about request being fully sent to prevent sending body parts after .end, but response state machine is mostly ignored and correct flow will be handled by NIOHTTP1 pipeline - Adds HTTPEchoHandler, that replies to each response body part - Adds bi-directional streaming test Result: Closesswift-server#327
Copy link
Contributor

@weissiweissi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

very nice, thank you!

@weissiweissi merged commit 5d9b784 into swift-server:mainMar 3, 2021
@artemredkinartemredkin deleted the fix_bi_streaming branch March 3, 2021 18:38
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bi-directional streaming broken

3 participants

@artemredkin@weissi@Lukasa