Repository navigation
Conversation
Transport.handleWrite removed sent bytes from the front of outgoingBuffer after every partial write, which memmoves the whole remaining body each time the socket buffer fills. Track an offset instead, release the storage only when fully drained (keeping capacity), and compact the consumed prefix on append once it passes 64 KB. The writer callback is also registered once per backlog instead of on every partial write; each set/remove was two kevent syscalls. The three close paths share one tearDown. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
SelectorKey.events and every select() result carried a Set<IOEvent> for two possible flags, allocating and hashing on each registration and each loop iteration. IOEvent is now an OptionSet over a UInt8 with the same .read / .write spellings; array literals, union, subtracting, contains and isEmpty all keep working. An `elements` accessor covers the two places that iterate. Source-breaking only for Selector conformers, of which there are none outside this package. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
SWSGIUtils built a second [String: Any] for the HTTP_* header keys and merged it into environ through a private += operator; write the keys straight into environ. HTTPConnection.startResponse built a whole MultiDictionary to test for two header names; a case-insensitive linear scan over a handful of headers does the same without allocating. MultiDictionary's two inits shared a duplicated loop, now one init using the default-value subscript. HTTPConnection also loses its implicitly unwrapped state: eventLoop is a let, headerParser is a lazy var, and request is a local. A header block whose first element is not the request line now logs and closes instead of trapping on a nil unwrap. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Behavior-preserving modernizations across Sources: - embassy.version comes from a new Embassy.version constant. The old Bundle(identifier:) lookup never resolved under SwiftPM, so every request reported "unknown"; bump the constant when tagging. - DefaultHTTPServer.acceptSocket is a real Optional. stop() resets it so a server can be restarted, and listenAddress fails with a message instead of a nil unwrap when read before start(). - KqueueSelector.register/unregister share one kevent-building helper. - HTTPRequest.Method and LogLevel.name use switch expressions; single- expression bodies drop their explicit return; `.count > 0` becomes `!isEmpty`; String.Encoding.utf8 spelled `.utf8`. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This was referenced Sep 23, 2026
Contributor
Author
|
Warning This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite.
This stack of pull requests is managed by Graphite. Learn more about stacking. |
mshehan
marked this pull request as ready for review
September 23, 2026 18:41
mshehan
requested review from
a team,
HunterFrancis,
anessence and
joshua-envoy
September 23, 2026 18:41
HunterFrancis
approved these changes
Sep 23, 2026
joshua-envoy
approved these changes
Sep 23, 2026
joshua-envoy
left a comment
There was a problem hiding this comment.
LGTM. The Transport offset/compaction and the writerRegistered tracking look correct, and the environ rewrite is equivalent to the old one. Non-blocking:
public enum EmbassyinVersion.swifthas the same name as the module, so module-qualified references likeEmbassy.DefaultHTTPServerorEmbassy.Loggerstop compiling ('not a member type of enum Embassy.Embassy'). Nothing downstream qualifies names today, but I'd suggest renaming it (e.g.EmbassyVersion) before 5.0.0 final.start()now retries properly after a failed bind, which is an improvement. But envoy-ipad'sUITestBase.setupWebAppcallsstart()in arepeat … whileloop with no backoff, so a port that stays taken would spin hot and flood/tmp/embassy.log. That's a follow-up on the iPad side.- The description says a malformed header block is now logged and closed. However,
HTTPHeaderParserstill traps onparts[1]for a request line with no spaces, such as a leading bare CRLF. This predates the PR.
Contributor
Author
Merge activity
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Summary
Performance-focused pass over the I/O hot path and small allocation sites, plus a behavior-preserving idiom cleanup. No public API changes beyond
IOEventbecoming anOptionSetandDefaultHTTPServer.acceptSocketbeing a trueOptional.Changes
Transport's outgoing buffer by offset instead ofremoveFirst, avoiding a memmove of the remaining body on every partial write; compact the consumed prefix past 64 KB; register the writer callback once per backlog instead of on every partial write; share onetearDownacross the three close pathsIOEventanOptionSet(single byte) instead ofSet<enum>, removing per-registration and per-loop-iteration allocation and hashingenvirondictionary and response headers in one pass instead of merging a second dictionary; dropMultiDictionary's duplicated init loop;HTTPConnectionloses its implicitly-unwrapped state (eventLoopis alet,headerParserislazy,requestis local) and logs + closes on a malformed header block instead of trappingembassy.versionnow comes from anEmbassy.versionconstant (the oldBundle(identifier:)lookup never resolved under SwiftPM);DefaultHTTPServer.acceptSocketis a realOptionalso the server can be restarted andlistenAddressfails with a message instead of a nil unwrap;KqueueSelector.register/unregistershare one kevent-building helper; switch expressions and other minor idiom updatesTest plan
swift build/swift testpassKqueueSelectorTestsupdated for the newIOEventOptionSetshape