{"action":"created","issue":{"url":"https://api.github.com/repos/http4s/http4s/issues/316","labels_url":"https://api.github.com/repos/http4s/http4s/issues/316/labels{/name}","comments_url":"https://api.github.com/repos/http4s/http4s/issues/316/comments","events_url":"https://api.github.com/repos/http4s/http4s/issues/316/events","html_url":"https://github.com/http4s/http4s/issues/316","id":91058052,"number":316,"title":"Explore alternative API for websocket server programming","user":{"login":"jedesah","id":1780116,"avatar_url":"https://avatars.githubusercontent.com/u/1780116?v=3","gravatar_id":"","url":"https://api.github.com/users/jedesah","html_url":"https://github.com/jedesah","followers_url":"https://api.github.com/users/jedesah/followers","following_url":"https://api.github.com/users/jedesah/following{/other_user}","gists_url":"https://api.github.com/users/jedesah/gists{/gist_id}","starred_url":"https://api.github.com/users/jedesah/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jedesah/subscriptions","organizations_url":"https://api.github.com/users/jedesah/orgs","repos_url":"https://api.github.com/users/jedesah/repos","events_url":"https://api.github.com/users/jedesah/events{/privacy}","received_events_url":"https://api.github.com/users/jedesah/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":18,"created_at":"2015-06-25T19:47:14Z","updated_at":"2015-07-01T17:07:35Z","closed_at":null,"body":"As talked about in #311, it is unfortunate that an echo server requires a `Queue` and I have spent a little time to explore whether a better API might be possible.\r\n\r\nAs suggested by @rossabaker, I tried modifying the [chat server in the demo application](https://github.com/http4s/http4s_demo/blob/b8b4781aa904ec65a37f029cfb6e1adc7118837a/src/main/scala/org/http4s/example/Routes.scala#L33-L47) to fit this new model and see what it looks like. This is what I came up with:\r\n\r\n    case r @ GET -> Root / \"wschat\" / name =>\r\n\r\n      def frameToMsg(f: WebSocketFrame) = f match {\r\n        case Text(msg, _) => s\"$name says: $msg\"\r\n        case _            => s\"$name sent bad message! Booo...\"\r\n      }\r\n\r\n      chatTopic.publishOne(s\"New user '$name' joined chat\")\r\n      .flatMap {_ => WS { in =>\r\n\r\n        val receiveMsgs = in.map(frameToMsg).onComplete(Process.eval_(chatTopic.publishOne(s\"$name left the chat\"))).to(chatTopic.publish)\r\n        receiveMsgs.run.runAsync(_ => ())\r\n        Process.emit(Text(s\"Welcome to the chat, $name!\")) ++ chatTopic.subscribe.map(Text(_))\r\n      }\r\n\r\nThe rest is unchanged.\r\n\r\nUnfortunately, this requires calling `runAsync` which is not ideal, but I don't think there is a better way. The problem stems from the fact that the `in` and `out` streams are not really connected. In the case of an echo server, this model shines because the framework will run the out stream in order to send the messages to the client and that will consume the messages from the client. But in our case, without running the in process in order to pipe it's results out to `chatTopic.publish`, the out stream will simply indefinitely wait on someone to publish, but no one will be running in order to do so.\r\n\r\nI tend to still prefer this solution because it is simple in the straightforward case where input is being mapped in some way to some output. Even though `topic` is a really cool construct, it is in the `async` package for a reason. Like `Queue` it is using mutability to accomplish things and as such I would expect a user of a `Queue` or a `Topic` to be aware that the `input` stream needs to be run somehow in oder to actually be mutating either the `Queue` or the `Topic`.\r\n\r\nThoughts?"},"comment":{"url":"https://api.github.com/repos/http4s/http4s/issues/comments/117753109","html_url":"https://github.com/http4s/http4s/issues/316#issuecomment-117753109","issue_url":"https://api.github.com/repos/http4s/http4s/issues/316","id":117753109,"user":{"login":"jedesah","id":1780116,"avatar_url":"https://avatars.githubusercontent.com/u/1780116?v=3","gravatar_id":"","url":"https://api.github.com/users/jedesah","html_url":"https://github.com/jedesah","followers_url":"https://api.github.com/users/jedesah/followers","following_url":"https://api.github.com/users/jedesah/following{/other_user}","gists_url":"https://api.github.com/users/jedesah/gists{/gist_id}","starred_url":"https://api.github.com/users/jedesah/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jedesah/subscriptions","organizations_url":"https://api.github.com/users/jedesah/orgs","repos_url":"https://api.github.com/users/jedesah/repos","events_url":"https://api.github.com/users/jedesah/events{/privacy}","received_events_url":"https://api.github.com/users/jedesah/received_events","type":"User","site_admin":false},"created_at":"2015-07-01T17:07:35Z","updated_at":"2015-07-01T17:07:35Z","body":"@djspiewak Thanks for the awesome explanation! Things are much clearer to me now on what those two statements mean."}}