+ const char *name;
+ void *(*create)(struct fdev *fdev, struct afb_apiset *apiset, struct afb_context *context, void (*cleanup)(void*), void *cleanup_closure);
+};
+
+static const struct protodef *search_proto(const struct protodef *protodefs, const char *protocols)
+{
+ int i;
+ size_t len;
+
+ if (protocols == NULL) {
+ /* return NULL; */
+ return protodefs != NULL && protodefs->name != NULL ? protodefs : NULL;
+ }
+ for(;;) {
+ protocols += strspn(protocols, vseparators);
+ if (!*protocols)
+ return NULL;
+ len = strcspn(protocols, vseparators);
+ for (i = 0 ; protodefs[i].name != NULL ; i++)
+ if (!strncasecmp(protodefs[i].name, protocols, len)
+ && !protodefs[i].name[len])
+ return &protodefs[i];
+ protocols += len;
+ }
+}
+
+struct memo_websocket {
+ const struct protodef *proto;
+ struct afb_hreq *hreq;
+ struct afb_apiset *apiset;
+};
+
+static void close_websocket(void *closure)
+{
+ struct MHD_UpgradeResponseHandle *urh = closure;
+ MHD_upgrade_action (urh, MHD_UPGRADE_ACTION_CLOSE);
+}
+
+static void upgrade_to_websocket(
+ void *cls,
+ struct MHD_Connection *connection,
+ void *con_cls,
+ const char *extra_in,
+ size_t extra_in_size,
+ MHD_socket sock,
+ struct MHD_UpgradeResponseHandle *urh)
+{
+ struct memo_websocket *memo = cls;
+ void *ws;
+ struct fdev *fdev;
+
+ fdev = afb_fdev_create(sock);
+ if (!fdev) {
+ /* TODO */
+ close_websocket(urh);
+ } else {
+ fdev_set_autoclose(fdev, 0);
+ ws = memo->proto->create(fdev, memo->apiset, &memo->hreq->xreq.context, close_websocket, urh);
+ if (ws == NULL) {
+ /* TODO */
+ close_websocket(urh);
+ }
+ }
+#if MHD_VERSION <= 0x00095900
+ afb_hreq_unref(memo->hreq);
+#endif
+ free(memo);
+}
+
+static int check_websocket_upgrade(struct MHD_Connection *con, const struct protodef *protodefs, struct afb_hreq *hreq, struct afb_apiset *apiset)
+{
+ struct memo_websocket *memo;
+ struct MHD_Response *response;