| Andy Green | 34f3dd2 | 2014-04-03 07:42:50 +0800 | [diff] [blame] | 1 | /* |
| 2 | * libwebsockets - small server side websockets and web server implementation |
| 3 | * |
| 4 | * Copyright (C) 2010-2014 Andy Green <andy@warmcat.com> |
| 5 | * |
| 6 | * This library is free software; you can redistribute it and/or |
| 7 | * modify it under the terms of the GNU Lesser General Public |
| 8 | * License as published by the Free Software Foundation: |
| 9 | * version 2.1 of the License. |
| 10 | * |
| 11 | * This library is distributed in the hope that it will be useful, |
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 14 | * Lesser General Public License for more details. |
| 15 | * |
| 16 | * You should have received a copy of the GNU Lesser General Public |
| 17 | * License along with this library; if not, write to the Free Software |
| 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, |
| 19 | * MA 02110-1301 USA |
| 20 | */ |
| 21 | |
| 22 | #include "private-libwebsockets.h" |
| 23 | |
| 24 | int |
| 25 | insert_wsi_socket_into_fds(struct libwebsocket_context *context, |
| 26 | struct libwebsocket *wsi) |
| 27 | { |
| 28 | struct libwebsocket_pollargs pa = { wsi->sock, LWS_POLLIN, 0 }; |
| 29 | |
| 30 | if (context->fds_count >= context->max_fds) { |
| 31 | lwsl_err("Too many fds (%d)\n", context->max_fds); |
| 32 | return 1; |
| 33 | } |
| 34 | |
| Bud Davis | 229bfec | 2015-01-30 10:13:01 +0800 | [diff] [blame] | 35 | #ifndef _WIN32 |
| Andy Green | 34f3dd2 | 2014-04-03 07:42:50 +0800 | [diff] [blame] | 36 | if (wsi->sock >= context->max_fds) { |
| 37 | lwsl_err("Socket fd %d is too high (%d)\n", |
| 38 | wsi->sock, context->max_fds); |
| 39 | return 1; |
| 40 | } |
| Bud Davis | 229bfec | 2015-01-30 10:13:01 +0800 | [diff] [blame] | 41 | #endif |
| Andy Green | 34f3dd2 | 2014-04-03 07:42:50 +0800 | [diff] [blame] | 42 | |
| 43 | assert(wsi); |
| 44 | assert(wsi->sock >= 0); |
| 45 | |
| 46 | lwsl_info("insert_wsi_socket_into_fds: wsi=%p, sock=%d, fds pos=%d\n", |
| 47 | wsi, wsi->sock, context->fds_count); |
| 48 | |
| 49 | context->protocols[0].callback(context, wsi, |
| 50 | LWS_CALLBACK_LOCK_POLL, |
| 51 | wsi->user_space, (void *) &pa, 0); |
| 52 | |
| Bud Davis | 229bfec | 2015-01-30 10:13:01 +0800 | [diff] [blame] | 53 | insert_wsi(context, wsi); |
| Andy Green | 34f3dd2 | 2014-04-03 07:42:50 +0800 | [diff] [blame] | 54 | wsi->position_in_fds_table = context->fds_count; |
| 55 | context->fds[context->fds_count].fd = wsi->sock; |
| 56 | context->fds[context->fds_count].events = LWS_POLLIN; |
| 57 | |
| 58 | lws_plat_insert_socket_into_fds(context, wsi); |
| 59 | |
| 60 | /* external POLL support via protocol 0 */ |
| 61 | context->protocols[0].callback(context, wsi, |
| 62 | LWS_CALLBACK_ADD_POLL_FD, |
| 63 | wsi->user_space, (void *) &pa, 0); |
| 64 | |
| 65 | context->protocols[0].callback(context, wsi, |
| 66 | LWS_CALLBACK_UNLOCK_POLL, |
| 67 | wsi->user_space, (void *)&pa, 0); |
| 68 | |
| 69 | return 0; |
| 70 | } |
| 71 | |
| 72 | int |
| 73 | remove_wsi_socket_from_fds(struct libwebsocket_context *context, |
| 74 | struct libwebsocket *wsi) |
| 75 | { |
| 76 | int m; |
| 77 | struct libwebsocket_pollargs pa = { wsi->sock, 0, 0 }; |
| 78 | |
| Andy Green | a717df2 | 2014-04-11 13:14:37 +0800 | [diff] [blame] | 79 | lws_libev_io(context, wsi, LWS_EV_STOP | LWS_EV_READ | LWS_EV_WRITE); |
| Andy Green | 34f3dd2 | 2014-04-03 07:42:50 +0800 | [diff] [blame] | 80 | |
| 81 | if (!--context->fds_count) { |
| 82 | context->protocols[0].callback(context, wsi, |
| 83 | LWS_CALLBACK_LOCK_POLL, |
| 84 | wsi->user_space, (void *) &pa, 0); |
| 85 | goto do_ext; |
| 86 | } |
| 87 | |
| 88 | if (wsi->sock > context->max_fds) { |
| 89 | lwsl_err("Socket fd %d too high (%d)\n", |
| 90 | wsi->sock, context->max_fds); |
| 91 | return 1; |
| 92 | } |
| 93 | |
| 94 | lwsl_info("%s: wsi=%p, sock=%d, fds pos=%d\n", __func__, |
| 95 | wsi, wsi->sock, wsi->position_in_fds_table); |
| 96 | |
| 97 | context->protocols[0].callback(context, wsi, |
| 98 | LWS_CALLBACK_LOCK_POLL, |
| 99 | wsi->user_space, (void *)&pa, 0); |
| 100 | |
| 101 | m = wsi->position_in_fds_table; /* replace the contents for this */ |
| 102 | |
| 103 | /* have the last guy take up the vacant slot */ |
| 104 | context->fds[m] = context->fds[context->fds_count]; |
| 105 | |
| 106 | lws_plat_delete_socket_from_fds(context, wsi, m); |
| 107 | |
| 108 | /* |
| 109 | * end guy's fds_lookup entry remains unchanged |
| 110 | * (still same fd pointing to same wsi) |
| 111 | */ |
| 112 | /* end guy's "position in fds table" changed */ |
| Bud Davis | 229bfec | 2015-01-30 10:13:01 +0800 | [diff] [blame] | 113 | wsi_from_fd(context,context->fds[context->fds_count].fd)-> |
| 114 | position_in_fds_table = m; |
| Andy Green | 34f3dd2 | 2014-04-03 07:42:50 +0800 | [diff] [blame] | 115 | /* deletion guy's lws_lookup entry needs nuking */ |
| Bud Davis | 229bfec | 2015-01-30 10:13:01 +0800 | [diff] [blame] | 116 | delete_from_fd(context,wsi->sock); |
| Andy Green | 34f3dd2 | 2014-04-03 07:42:50 +0800 | [diff] [blame] | 117 | /* removed wsi has no position any more */ |
| 118 | wsi->position_in_fds_table = -1; |
| 119 | |
| 120 | do_ext: |
| 121 | /* remove also from external POLL support via protocol 0 */ |
| 122 | if (wsi->sock) { |
| 123 | context->protocols[0].callback(context, wsi, |
| 124 | LWS_CALLBACK_DEL_POLL_FD, wsi->user_space, |
| 125 | (void *) &pa, 0); |
| 126 | } |
| 127 | context->protocols[0].callback(context, wsi, |
| 128 | LWS_CALLBACK_UNLOCK_POLL, |
| 129 | wsi->user_space, (void *) &pa, 0); |
| 130 | return 0; |
| 131 | } |
| 132 | |
| 133 | int |
| 134 | lws_change_pollfd(struct libwebsocket *wsi, int _and, int _or) |
| 135 | { |
| Andy Green | 4edb452 | 2014-12-15 15:08:13 +0800 | [diff] [blame] | 136 | struct libwebsocket_context *context; |
| Andy Green | 34f3dd2 | 2014-04-03 07:42:50 +0800 | [diff] [blame] | 137 | int tid; |
| 138 | int sampled_tid; |
| 139 | struct libwebsocket_pollfd *pfd; |
| 140 | struct libwebsocket_pollargs pa; |
| 141 | |
| Andy Green | 4edb452 | 2014-12-15 15:08:13 +0800 | [diff] [blame] | 142 | if (!wsi || !wsi->protocol || wsi->position_in_fds_table < 0) |
| 143 | return 1; |
| 144 | |
| 145 | context = wsi->protocol->owning_server; |
| 146 | if (!context) |
| 147 | return 1; |
| 148 | |
| Andy Green | 34f3dd2 | 2014-04-03 07:42:50 +0800 | [diff] [blame] | 149 | pfd = &context->fds[wsi->position_in_fds_table]; |
| 150 | pa.fd = wsi->sock; |
| 151 | |
| 152 | context->protocols[0].callback(context, wsi, |
| Andy Green | d7340c1 | 2014-04-10 14:08:10 +0800 | [diff] [blame] | 153 | LWS_CALLBACK_LOCK_POLL, wsi->user_space, (void *) &pa, 0); |
| Andy Green | 34f3dd2 | 2014-04-03 07:42:50 +0800 | [diff] [blame] | 154 | |
| 155 | pa.prev_events = pfd->events; |
| 156 | pa.events = pfd->events = (pfd->events & ~_and) | _or; |
| 157 | |
| 158 | context->protocols[0].callback(context, wsi, |
| 159 | LWS_CALLBACK_CHANGE_MODE_POLL_FD, |
| Andy Green | d7340c1 | 2014-04-10 14:08:10 +0800 | [diff] [blame] | 160 | wsi->user_space, (void *) &pa, 0); |
| Andy Green | 34f3dd2 | 2014-04-03 07:42:50 +0800 | [diff] [blame] | 161 | |
| 162 | /* |
| 163 | * if we changed something in this pollfd... |
| 164 | * ... and we're running in a different thread context |
| 165 | * than the service thread... |
| 166 | * ... and the service thread is waiting ... |
| 167 | * then cancel it to force a restart with our changed events |
| 168 | */ |
| 169 | if (pa.prev_events != pa.events) { |
| 170 | |
| Andy Green | a717df2 | 2014-04-11 13:14:37 +0800 | [diff] [blame] | 171 | if (lws_plat_change_pollfd(context, wsi, pfd)) { |
| 172 | lwsl_info("%s failed\n", __func__); |
| Andy Green | 34f3dd2 | 2014-04-03 07:42:50 +0800 | [diff] [blame] | 173 | return 1; |
| Andy Green | a717df2 | 2014-04-11 13:14:37 +0800 | [diff] [blame] | 174 | } |
| Andy Green | 34f3dd2 | 2014-04-03 07:42:50 +0800 | [diff] [blame] | 175 | |
| 176 | sampled_tid = context->service_tid; |
| 177 | if (sampled_tid) { |
| 178 | tid = context->protocols[0].callback(context, NULL, |
| 179 | LWS_CALLBACK_GET_THREAD_ID, NULL, NULL, 0); |
| 180 | if (tid != sampled_tid) |
| 181 | libwebsocket_cancel_service(context); |
| 182 | } |
| 183 | } |
| 184 | |
| 185 | context->protocols[0].callback(context, wsi, |
| Andy Green | d7340c1 | 2014-04-10 14:08:10 +0800 | [diff] [blame] | 186 | LWS_CALLBACK_UNLOCK_POLL, wsi->user_space, (void *) &pa, 0); |
| Andy Green | 34f3dd2 | 2014-04-03 07:42:50 +0800 | [diff] [blame] | 187 | |
| 188 | return 0; |
| 189 | } |
| 190 | |
| 191 | |
| 192 | /** |
| 193 | * libwebsocket_callback_on_writable() - Request a callback when this socket |
| 194 | * becomes able to be written to without |
| 195 | * blocking |
| 196 | * |
| 197 | * @context: libwebsockets context |
| 198 | * @wsi: Websocket connection instance to get callback for |
| 199 | */ |
| 200 | |
| 201 | LWS_VISIBLE int |
| 202 | libwebsocket_callback_on_writable(struct libwebsocket_context *context, |
| 203 | struct libwebsocket *wsi) |
| 204 | { |
| Andy Green | 7df53c5 | 2014-10-22 15:37:28 +0800 | [diff] [blame] | 205 | #ifdef LWS_USE_HTTP2 |
| 206 | struct libwebsocket *network_wsi, *wsi2; |
| 207 | int already; |
| 208 | |
| 209 | lwsl_info("%s: %p\n", __func__, wsi); |
| 210 | |
| 211 | if (wsi->mode != LWS_CONNMODE_HTTP2_SERVING) |
| 212 | goto network_sock; |
| 213 | |
| 214 | if (wsi->u.http2.requested_POLLOUT) { |
| 215 | lwsl_info("already pending writable\n"); |
| 216 | return 1; |
| 217 | } |
| 218 | |
| Andy Green | 97ee57f | 2014-10-29 09:39:08 +0800 | [diff] [blame] | 219 | if (wsi->u.http2.tx_credit <= 0) { |
| 220 | /* |
| 221 | * other side is not able to cope with us sending |
| 222 | * anything so no matter if we have POLLOUT on our side. |
| 223 | * |
| 224 | * Delay waiting for our POLLOUT until peer indicates he has |
| 225 | * space for more using tx window command in http2 layer |
| 226 | */ |
| 227 | lwsl_info("%s: %p: waiting_tx_credit (%d)\n", __func__, wsi, wsi->u.http2.tx_credit); |
| 228 | wsi->u.http2.waiting_tx_credit = 1; |
| 229 | return 0; |
| 230 | } |
| 231 | |
| Andy Green | 7df53c5 | 2014-10-22 15:37:28 +0800 | [diff] [blame] | 232 | network_wsi = lws_http2_get_network_wsi(wsi); |
| 233 | already = network_wsi->u.http2.requested_POLLOUT; |
| 234 | |
| 235 | /* mark everybody above him as requesting pollout */ |
| 236 | |
| 237 | wsi2 = wsi; |
| 238 | while (wsi2) { |
| 239 | wsi2->u.http2.requested_POLLOUT = 1; |
| 240 | lwsl_info("mark %p pending writable\n", wsi2); |
| 241 | wsi2 = wsi2->u.http2.parent_wsi; |
| 242 | } |
| 243 | |
| 244 | /* for network action, act only on the network wsi */ |
| 245 | |
| 246 | wsi = network_wsi; |
| 247 | if (already) |
| 248 | return 1; |
| 249 | network_sock: |
| 250 | #endif |
| 251 | |
| Andy Green | 34f3dd2 | 2014-04-03 07:42:50 +0800 | [diff] [blame] | 252 | if (lws_ext_callback_for_each_active(wsi, |
| 253 | LWS_EXT_CALLBACK_REQUEST_ON_WRITEABLE, NULL, 0)) |
| 254 | return 1; |
| 255 | |
| 256 | if (wsi->position_in_fds_table < 0) { |
| 257 | lwsl_err("%s: failed to find socket %d\n", __func__, wsi->sock); |
| 258 | return -1; |
| 259 | } |
| 260 | |
| 261 | if (lws_change_pollfd(wsi, 0, LWS_POLLOUT)) |
| 262 | return -1; |
| 263 | |
| Andy Green | a717df2 | 2014-04-11 13:14:37 +0800 | [diff] [blame] | 264 | lws_libev_io(context, wsi, LWS_EV_START | LWS_EV_WRITE); |
| Andy Green | 34f3dd2 | 2014-04-03 07:42:50 +0800 | [diff] [blame] | 265 | |
| 266 | return 1; |
| 267 | } |
| 268 | |
| 269 | /** |
| 270 | * libwebsocket_callback_on_writable_all_protocol() - Request a callback for |
| 271 | * all connections using the given protocol when it |
| 272 | * becomes possible to write to each socket without |
| 273 | * blocking in turn. |
| 274 | * |
| 275 | * @protocol: Protocol whose connections will get callbacks |
| 276 | */ |
| 277 | |
| 278 | LWS_VISIBLE int |
| 279 | libwebsocket_callback_on_writable_all_protocol( |
| 280 | const struct libwebsocket_protocols *protocol) |
| 281 | { |
| 282 | struct libwebsocket_context *context = protocol->owning_server; |
| 283 | int n; |
| 284 | struct libwebsocket *wsi; |
| 285 | |
| 286 | for (n = 0; n < context->fds_count; n++) { |
| Bud Davis | 229bfec | 2015-01-30 10:13:01 +0800 | [diff] [blame] | 287 | wsi = wsi_from_fd(context,context->fds[n].fd); |
| Andy Green | 34f3dd2 | 2014-04-03 07:42:50 +0800 | [diff] [blame] | 288 | if (!wsi) |
| 289 | continue; |
| 290 | if (wsi->protocol == protocol) |
| 291 | libwebsocket_callback_on_writable(context, wsi); |
| 292 | } |
| 293 | |
| 294 | return 0; |
| 295 | } |