{"action":"created","comment":{"url":"https://api.github.com/repos/rack/rack/pulls/comments/186067832","pull_request_review_id":117586526,"id":186067832,"diff_hunk":"@@ -113,6 +113,25 @@ be implemented by the server.\n                         fatal(message, &block)\n <tt>rack.multipart.buffer_size</tt>:: An Integer hint to the multipart parser as to what chunk size to use for reads and writes.\n <tt>rack.multipart.tempfile_factory</tt>:: An object responding to #call with two arguments, the filename and content_type given for the multipart form field, and returning an IO-like object that responds to #<< and optionally #rewind. This factory will be used to instantiate the tempfile for each multipart form file upload field, rather than the default class of Tempfile.\n+\n+Servers that choose to support WebSockets and SSE connections should follow these additional environment specifications:\n+<tt>rack.upgrade?</tt>:: Is nil or missing if no connection upgrade is possible,\n+                         :websocket for a WebSocket upgrade, or :sse for an SSE upgrade.\n+<tt>rack.upgrade</tt>:: Is used to pass a handler object back to the server for WebSocket and SSE connections.\n+                     The handler MAY implement any or all of the following callbacks:\n+                      on_open(client)              # called when the connection is complete with a client object.\n+                      on_message(client, message)  # called when a WebSocket message is received by server. <tt>message</tt> will be UTF-8 encoded for text messages and Binary encoded for binary messages.\n+                      on_shutdown(client)          # may be called before a connection is closed due to server shutdown.\n+                      on_close(client)             # called after the connection is closed.\n+                      on_drained(client)           # may be called when the number of pending writes drops to zero.\n+                     The <tt>client</tt> is used for writing and checking status of the upgraded connect. It has these methods.\n+                      write(message)               # writes to the WebSocket or SSE connection\n+                      close()                      # forces a close of the WebSocket or SSE connection","path":"SPEC","position":17,"original_position":17,"commit_id":"a742739ed3413c1c7df2a79e7fec0d43a4458c92","original_commit_id":"a742739ed3413c1c7df2a79e7fec0d43a4458c92","user":{"login":"boazsegev","id":870622,"avatar_url":"https://avatars3.githubusercontent.com/u/870622?v=4","gravatar_id":"","url":"https://api.github.com/users/boazsegev","html_url":"https://github.com/boazsegev","followers_url":"https://api.github.com/users/boazsegev/followers","following_url":"https://api.github.com/users/boazsegev/following{/other_user}","gists_url":"https://api.github.com/users/boazsegev/gists{/gist_id}","starred_url":"https://api.github.com/users/boazsegev/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/boazsegev/subscriptions","organizations_url":"https://api.github.com/users/boazsegev/orgs","repos_url":"https://api.github.com/users/boazsegev/repos","events_url":"https://api.github.com/users/boazsegev/events{/privacy}","received_events_url":"https://api.github.com/users/boazsegev/received_events","type":"User","site_admin":false},"body":"@ohler55, so does iodine. I place a \"close\" marker on the packet queue, so `close` is always performed after all scheduled data is sent.\r\n\r\n\r\n@ioquatix , I think your missing the point o f abstracting away the network and the protocol.\r\n\r\nMy suggestion was about clarifying this little bit, not changing what both iodine and agoo already implement.\r\n\r\nWe aren't authoring a network layer. We are authoring an abstracted application side API.\r\n\r\nThe reasonable exception is that `write` is perfumed before `close`. i.e., if my code is:\r\n\r\n```ruby\r\nwrite \"hello\"\r\nclose\r\n```\r\n\r\nThe reasonable expectation is that \"hello\" is actually written.\r\n\r\nThere's no `force_close` or `close_write` in the specification because the application shouldn't be concerned with these things. If the application doesn't want data written, it can avoid writing it.\r\n","created_at":"2018-05-04T12:34:23Z","updated_at":"2018-05-04T12:34:23Z","html_url":"https://github.com/rack/rack/pull/1272#discussion_r186067832","pull_request_url":"https://api.github.com/repos/rack/rack/pulls/1272","author_association":"NONE","_links":{"self":{"href":"https://api.github.com/repos/rack/rack/pulls/comments/186067832"},"html":{"href":"https://github.com/rack/rack/pull/1272#discussion_r186067832"},"pull_request":{"href":"https://api.github.com/repos/rack/rack/pulls/1272"}},"in_reply_to_id":185997879},"pull_request":{"url":"https://api.github.com/repos/rack/rack/pulls/1272","id":184808661,"html_url":"https://github.com/rack/rack/pull/1272","diff_url":"https://github.com/rack/rack/pull/1272.diff","patch_url":"https://github.com/rack/rack/pull/1272.patch","issue_url":"https://api.github.com/repos/rack/rack/issues/1272","number":1272,"state":"open","locked":false,"title":"Add WebSocket and SSE options","user":{"login":"ohler55","id":118856,"avatar_url":"https://avatars3.githubusercontent.com/u/118856?v=4","gravatar_id":"","url":"https://api.github.com/users/ohler55","html_url":"https://github.com/ohler55","followers_url":"https://api.github.com/users/ohler55/followers","following_url":"https://api.github.com/users/ohler55/following{/other_user}","gists_url":"https://api.github.com/users/ohler55/gists{/gist_id}","starred_url":"https://api.github.com/users/ohler55/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ohler55/subscriptions","organizations_url":"https://api.github.com/users/ohler55/orgs","repos_url":"https://api.github.com/users/ohler55/repos","events_url":"https://api.github.com/users/ohler55/events{/privacy}","received_events_url":"https://api.github.com/users/ohler55/received_events","type":"User","site_admin":false},"body":"This suggested addition is the result of a collaboration between @boazsegev of Iodine fame and myself. Both Agoo and Iodine will support these suggested additions. At this point I don't think any other servers are supporting WebSockets and SSE so this would set the standard for others to follow.","created_at":"2018-04-28T22:41:04Z","updated_at":"2018-05-04T12:34:23Z","closed_at":null,"merged_at":null,"merge_commit_sha":"6e5ac01af1aeabe04bb2e713a87dcf563ebd5f50","assignee":null,"assignees":[],"requested_reviewers":[],"requested_teams":[],"labels":[],"milestone":null,"commits_url":"https://api.github.com/repos/rack/rack/pulls/1272/commits","review_comments_url":"https://api.github.com/repos/rack/rack/pulls/1272/comments","review_comment_url":"https://api.github.com/repos/rack/rack/pulls/comments{/number}","comments_url":"https://api.github.com/repos/rack/rack/issues/1272/comments","statuses_url":"https://api.github.com/repos/rack/rack/statuses/a742739ed3413c1c7df2a79e7fec0d43a4458c92","head":{"label":"ohler55:master","ref":"master","sha":"a742739ed3413c1c7df2a79e7fec0d43a4458c92","user":{"login":"ohler55","id":118856,"avatar_url":"https://avatars3.githubusercontent.com/u/118856?v=4","gravatar_id":"","url":"https://api.github.com/users/ohler55","html_url":"https://github.com/ohler55","followers_url":"https://api.github.com/users/ohler55/followers","following_url":"https://api.github.com/users/ohler55/following{/other_user}","gists_url":"https://api.github.com/users/ohler55/gists{/gist_id}","starred_url":"https://api.github.com/users/ohler55/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ohler55/subscriptions","organizations_url":"https://api.github.com/users/ohler55/orgs","repos_url":"https://api.github.com/users/ohler55/repos","events_url":"https://api.github.com/users/ohler55/events{/privacy}","received_events_url":"https://api.github.com/users/ohler55/received_events","type":"User","site_admin":false},"repo":{"id":131448635,"name":"rack","full_name":"ohler55/rack","owner":{"login":"ohler55","id":118856,"avatar_url":"https://avatars3.githubusercontent.com/u/118856?v=4","gravatar_id":"","url":"https://api.github.com/users/ohler55","html_url":"https://github.com/ohler55","followers_url":"https://api.github.com/users/ohler55/followers","following_url":"https://api.github.com/users/ohler55/following{/other_user}","gists_url":"https://api.github.com/users/ohler55/gists{/gist_id}","starred_url":"https://api.github.com/users/ohler55/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ohler55/subscriptions","organizations_url":"https://api.github.com/users/ohler55/orgs","repos_url":"https://api.github.com/users/ohler55/repos","events_url":"https://api.github.com/users/ohler55/events{/privacy}","received_events_url":"https://api.github.com/users/ohler55/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/ohler55/rack","description":"a modular Ruby webserver interface","fork":true,"url":"https://api.github.com/repos/ohler55/rack","forks_url":"https://api.github.com/repos/ohler55/rack/forks","keys_url":"https://api.github.com/repos/ohler55/rack/keys{/key_id}","collaborators_url":"https://api.github.com/repos/ohler55/rack/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/ohler55/rack/teams","hooks_url":"https://api.github.com/repos/ohler55/rack/hooks","issue_events_url":"https://api.github.com/repos/ohler55/rack/issues/events{/number}","events_url":"https://api.github.com/repos/ohler55/rack/events","assignees_url":"https://api.github.com/repos/ohler55/rack/assignees{/user}","branches_url":"https://api.github.com/repos/ohler55/rack/branches{/branch}","tags_url":"https://api.github.com/repos/ohler55/rack/tags","blobs_url":"https://api.github.com/repos/ohler55/rack/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/ohler55/rack/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/ohler55/rack/git/refs{/sha}","trees_url":"https://api.github.com/repos/ohler55/rack/git/trees{/sha}","statuses_url":"https://api.github.com/repos/ohler55/rack/statuses/{sha}","languages_url":"https://api.github.com/repos/ohler55/rack/languages","stargazers_url":"https://api.github.com/repos/ohler55/rack/stargazers","contributors_url":"https://api.github.com/repos/ohler55/rack/contributors","subscribers_url":"https://api.github.com/repos/ohler55/rack/subscribers","subscription_url":"https://api.github.com/repos/ohler55/rack/subscription","commits_url":"https://api.github.com/repos/ohler55/rack/commits{/sha}","git_commits_url":"https://api.github.com/repos/ohler55/rack/git/commits{/sha}","comments_url":"https://api.github.com/repos/ohler55/rack/comments{/number}","issue_comment_url":"https://api.github.com/repos/ohler55/rack/issues/comments{/number}","contents_url":"https://api.github.com/repos/ohler55/rack/contents/{+path}","compare_url":"https://api.github.com/repos/ohler55/rack/compare/{base}...{head}","merges_url":"https://api.github.com/repos/ohler55/rack/merges","archive_url":"https://api.github.com/repos/ohler55/rack/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/ohler55/rack/downloads","issues_url":"https://api.github.com/repos/ohler55/rack/issues{/number}","pulls_url":"https://api.github.com/repos/ohler55/rack/pulls{/number}","milestones_url":"https://api.github.com/repos/ohler55/rack/milestones{/number}","notifications_url":"https://api.github.com/repos/ohler55/rack/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/ohler55/rack/labels{/name}","releases_url":"https://api.github.com/repos/ohler55/rack/releases{/id}","deployments_url":"https://api.github.com/repos/ohler55/rack/deployments","created_at":"2018-04-28T22:31:21Z","updated_at":"2018-05-02T23:46:26Z","pushed_at":"2018-05-02T23:46:22Z","git_url":"git://github.com/ohler55/rack.git","ssh_url":"git@github.com:ohler55/rack.git","clone_url":"https://github.com/ohler55/rack.git","svn_url":"https://github.com/ohler55/rack","homepage":"http://rack.github.io/","size":3387,"stargazers_count":0,"watchers_count":0,"language":"Ruby","has_issues":false,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"archived":false,"open_issues_count":0,"license":{"key":"other","name":"Other","spdx_id":null,"url":null},"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"rack:master","ref":"master","sha":"f9393b9b0852fc064eb21ac4df71e07290980d9d","user":{"login":"rack","id":42379,"avatar_url":"https://avatars3.githubusercontent.com/u/42379?v=4","gravatar_id":"","url":"https://api.github.com/users/rack","html_url":"https://github.com/rack","followers_url":"https://api.github.com/users/rack/followers","following_url":"https://api.github.com/users/rack/following{/other_user}","gists_url":"https://api.github.com/users/rack/gists{/gist_id}","starred_url":"https://api.github.com/users/rack/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rack/subscriptions","organizations_url":"https://api.github.com/users/rack/orgs","repos_url":"https://api.github.com/users/rack/repos","events_url":"https://api.github.com/users/rack/events{/privacy}","received_events_url":"https://api.github.com/users/rack/received_events","type":"Organization","site_admin":false},"repo":{"id":96071,"name":"rack","full_name":"rack/rack","owner":{"login":"rack","id":42379,"avatar_url":"https://avatars3.githubusercontent.com/u/42379?v=4","gravatar_id":"","url":"https://api.github.com/users/rack","html_url":"https://github.com/rack","followers_url":"https://api.github.com/users/rack/followers","following_url":"https://api.github.com/users/rack/following{/other_user}","gists_url":"https://api.github.com/users/rack/gists{/gist_id}","starred_url":"https://api.github.com/users/rack/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rack/subscriptions","organizations_url":"https://api.github.com/users/rack/orgs","repos_url":"https://api.github.com/users/rack/repos","events_url":"https://api.github.com/users/rack/events{/privacy}","received_events_url":"https://api.github.com/users/rack/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/rack/rack","description":"a modular Ruby webserver interface","fork":false,"url":"https://api.github.com/repos/rack/rack","forks_url":"https://api.github.com/repos/rack/rack/forks","keys_url":"https://api.github.com/repos/rack/rack/keys{/key_id}","collaborators_url":"https://api.github.com/repos/rack/rack/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/rack/rack/teams","hooks_url":"https://api.github.com/repos/rack/rack/hooks","issue_events_url":"https://api.github.com/repos/rack/rack/issues/events{/number}","events_url":"https://api.github.com/repos/rack/rack/events","assignees_url":"https://api.github.com/repos/rack/rack/assignees{/user}","branches_url":"https://api.github.com/repos/rack/rack/branches{/branch}","tags_url":"https://api.github.com/repos/rack/rack/tags","blobs_url":"https://api.github.com/repos/rack/rack/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/rack/rack/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/rack/rack/git/refs{/sha}","trees_url":"https://api.github.com/repos/rack/rack/git/trees{/sha}","statuses_url":"https://api.github.com/repos/rack/rack/statuses/{sha}","languages_url":"https://api.github.com/repos/rack/rack/languages","stargazers_url":"https://api.github.com/repos/rack/rack/stargazers","contributors_url":"https://api.github.com/repos/rack/rack/contributors","subscribers_url":"https://api.github.com/repos/rack/rack/subscribers","subscription_url":"https://api.github.com/repos/rack/rack/subscription","commits_url":"https://api.github.com/repos/rack/rack/commits{/sha}","git_commits_url":"https://api.github.com/repos/rack/rack/git/commits{/sha}","comments_url":"https://api.github.com/repos/rack/rack/comments{/number}","issue_comment_url":"https://api.github.com/repos/rack/rack/issues/comments{/number}","contents_url":"https://api.github.com/repos/rack/rack/contents/{+path}","compare_url":"https://api.github.com/repos/rack/rack/compare/{base}...{head}","merges_url":"https://api.github.com/repos/rack/rack/merges","archive_url":"https://api.github.com/repos/rack/rack/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/rack/rack/downloads","issues_url":"https://api.github.com/repos/rack/rack/issues{/number}","pulls_url":"https://api.github.com/repos/rack/rack/pulls{/number}","milestones_url":"https://api.github.com/repos/rack/rack/milestones{/number}","notifications_url":"https://api.github.com/repos/rack/rack/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/rack/rack/labels{/name}","releases_url":"https://api.github.com/repos/rack/rack/releases{/id}","deployments_url":"https://api.github.com/repos/rack/rack/deployments","created_at":"2008-12-24T03:03:12Z","updated_at":"2018-05-02T15:33:10Z","pushed_at":"2018-05-02T23:46:25Z","git_url":"git://github.com/rack/rack.git","ssh_url":"git@github.com:rack/rack.git","clone_url":"https://github.com/rack/rack.git","svn_url":"https://github.com/rack/rack","homepage":"http://rack.github.io/","size":3374,"stargazers_count":3351,"watchers_count":3351,"language":"Ruby","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":1263,"mirror_url":null,"archived":false,"open_issues_count":173,"license":{"key":"other","name":"Other","spdx_id":null,"url":null},"forks":1263,"open_issues":173,"watchers":3351,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/rack/rack/pulls/1272"},"html":{"href":"https://github.com/rack/rack/pull/1272"},"issue":{"href":"https://api.github.com/repos/rack/rack/issues/1272"},"comments":{"href":"https://api.github.com/repos/rack/rack/issues/1272/comments"},"review_comments":{"href":"https://api.github.com/repos/rack/rack/pulls/1272/comments"},"review_comment":{"href":"https://api.github.com/repos/rack/rack/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/rack/rack/pulls/1272/commits"},"statuses":{"href":"https://api.github.com/repos/rack/rack/statuses/a742739ed3413c1c7df2a79e7fec0d43a4458c92"}},"author_association":"CONTRIBUTOR"}}
{"actor":{"display_login":"boazsegev"}}