Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 14 additions & 7 deletions src/connection.toit
Original file line number Diff line number Diff line change
Expand Up @@ -144,10 +144,11 @@ class Connection:
// Set this before doing blocking operations on the socket, so that we
// don't let another task start another request on the same connection.
if has-body: current-writer_ = body-writer
socket_.no-delay = false

headers-written := false
no-delay-restored := false
try:
socket_.no-delay = false
writer.write status
headers.write-to writer
if is-client-request and host_:
Expand All @@ -157,13 +158,19 @@ class Connection:
writer.write "\r\n"
headers-written = true
finally:
// If we failed to write the headers the connection is unusable: the
// peer may have received a partial header. Close it, which also
// resets $current-writer_ so that later attempts to respond don't
// fail with "Previous request not completed".
if not headers-written: close
// The TCP_NODELAY setter can throw if the peer has gone away. The nested
// finally ensures that we still close while its exception propagates.
try:
if headers-written:
socket_.no-delay = true
no-delay-restored = true
finally:
// A partial header or a failed TCP_NODELAY restoration makes the
// connection unusable. Closing also resets $current-writer_.
if not headers-written or
not no-delay-restored:
close

socket_.no-delay = true
return body-writer

// Gets the next request from the client. If the client closes the
Expand Down
13 changes: 4 additions & 9 deletions tests/server-peer-gone-test.toit
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@

// A client that connects, sends a request and closes before the server
// writes the response must be treated as a normal disconnect: the server
// keeps serving and doesn't log any extraneous traces. The only acceptable
// trace is the one the server emits when the handler lets the socket error
// itself escape.
// keeps serving and doesn't log "Previous request not completed".

import expect show *
import http
Expand Down Expand Up @@ -49,12 +47,9 @@ main:
print "---------------------------------------"
recorder.traces.clear
test --max-tasks=max-tasks --scenario=scenario
traces-seen := recorder.traces.size
// The OS may report the disconnect during the response write or when
// the server next reads from the connection.
maximum := scenario == PROPAGATE ? 1 : 0
if traces-seen > maximum:
failures.add "$scenario/max-tasks=$max-tasks ($traces-seen > $maximum)"
recorder.traces.do: | trace/ByteArray |
if trace.to-string.contains "Previous request not completed":
failures.add "$scenario/max-tasks=$max-tasks"
finally:
recorder.uninstall
expect failures.is-empty --message="$failures"
Expand Down