void http_exec(void) { switch(http.state) { case HTTP_SEND_HEADERS: http_start_get(); break; case HTTP_SEND_PAGE: http_get_pumping(0xff); break; case HTTP_COMPLETE: if(TCP_CONN_IS_CLOSED(http.tcp_session)) http.state = HTTP_IDLE; break; } // sse keep alive if(sys_clock_100ms > sse_ping_timer && http_can_send_sse()) { unsigned pkt = tcp_create_packet_sized(sse_sock, 256); if(pkt != 0xff) { unsigned len = sprintf( (char*)tcp_ref(pkt, 0), "event: sse_ping\n""data: -\n\n" ); tcp_send_packet(sse_sock, pkt, len); sse_ping_timer = sys_clock_100ms + 90; // 9s } } }
// result code is unused now. always 200 OK void http_reply(int code, char *data) // rewrite with tcp_ref() 16.12.2014 { unsigned pkt = tcp_create_packet(http.tcp_session); if(pkt != 0xFF) { unsigned dlen = strlen(data); if(dlen > 1024) dlen = 1024; char *p = tcp_ref(pkt, 0); char *s = p; p += sprintf(p, "HTTP/1.1 200 Ok\r\n" "Connection: Close\r\n" "Cache-Control: no-store, no-cache, must-revalidate\r\n" "Expires: Fri, 13 Oct 2000 00:00:00 GMT\r\n" ); if(req_origin[0] != 0) p += sprintf(p, "Access-Control-Allow-Origin: %s\r\n" "Access-Control-Allow-Credentials: true\r\n", req_origin ); p += sprintf(p, "Content-Type: %s\r\n" "Content-Length: %u\r\n" "\r\n", http.page->mime, dlen ); memcpy(p, data, dlen); p += dlen; tcp_send_packet(http.tcp_session, pkt, (unsigned)(p - s)); } tcp_close_session(http.tcp_session); http.state = HTTP_COMPLETE; }
static void win_notify_on_read(grpc_endpoint *ep, grpc_endpoint_read_cb cb, void *arg) { grpc_tcp *tcp = (grpc_tcp *) ep; grpc_winsocket *handle = tcp->socket; grpc_winsocket_callback_info *info = &handle->read_info; int status; DWORD bytes_read = 0; DWORD flags = 0; WSABUF buffer; GPR_ASSERT(!tcp->socket->read_info.outstanding); if (tcp->shutting_down) { cb(arg, NULL, 0, GRPC_ENDPOINT_CB_SHUTDOWN); return; } tcp_ref(tcp); tcp->socket->read_info.outstanding = 1; tcp->read_cb = cb; tcp->read_user_data = arg; tcp->read_slice = gpr_slice_malloc(8192); buffer.len = GPR_SLICE_LENGTH(tcp->read_slice); buffer.buf = (char *)GPR_SLICE_START_PTR(tcp->read_slice); /* First let's try a synchronous, non-blocking read. */ status = WSARecv(tcp->socket->socket, &buffer, 1, &bytes_read, &flags, NULL, NULL); info->wsa_error = status == 0 ? 0 : WSAGetLastError(); /* Did we get data immediately ? Yay. */ if (info->wsa_error != WSAEWOULDBLOCK) { info->bytes_transfered = bytes_read; /* This might heavily recurse. */ on_read(tcp, 1); return; } /* Otherwise, let's retry, by queuing a read. */ memset(&tcp->socket->read_info.overlapped, 0, sizeof(OVERLAPPED)); status = WSARecv(tcp->socket->socket, &buffer, 1, &bytes_read, &flags, &info->overlapped, NULL); if (status != 0) { int wsa_error = WSAGetLastError(); if (wsa_error != WSA_IO_PENDING) { info->wsa_error = wsa_error; on_read(tcp, 1); return; } } grpc_socket_notify_on_read(tcp->socket, on_read, tcp); }
/* Initiates a shutdown of the TCP endpoint. This will queue abort callbacks for the potential read and write operations. It is up to the caller to guarantee this isn't called in parallel to a read or write request, so we're not going to protect against these. However the IO Completion Port callback will happen from another thread, so we need to protect against concurrent access of the data structure in that regard. */ static void win_shutdown(grpc_endpoint *ep) { grpc_tcp *tcp = (grpc_tcp *) ep; int extra_refs = 0; gpr_mu_lock(&tcp->mu); /* At that point, what may happen is that we're already inside the IOCP callback. See the comments in on_read and on_write. */ tcp->shutting_down = 1; extra_refs = grpc_winsocket_shutdown(tcp->socket); while (extra_refs--) tcp_ref(tcp); gpr_mu_unlock(&tcp->mu); }
/* Initiates a write. */ static grpc_endpoint_write_status win_write(grpc_endpoint *ep, gpr_slice *slices, size_t nslices, grpc_endpoint_write_cb cb, void *arg) { grpc_tcp *tcp = (grpc_tcp *) ep; grpc_winsocket *socket = tcp->socket; grpc_winsocket_callback_info *info = &socket->write_info; unsigned i; DWORD bytes_sent; int status; WSABUF local_buffers[16]; WSABUF *allocated = NULL; WSABUF *buffers = local_buffers; GPR_ASSERT(!tcp->outstanding_write); GPR_ASSERT(!tcp->shutting_down); tcp_ref(tcp); tcp->outstanding_write = 1; tcp->write_cb = cb; tcp->write_user_data = arg; gpr_slice_buffer_addn(&tcp->write_slices, slices, nslices); if (tcp->write_slices.count > GPR_ARRAY_SIZE(local_buffers)) { buffers = (WSABUF *) gpr_malloc(sizeof(WSABUF) * tcp->write_slices.count); allocated = buffers; } for (i = 0; i < tcp->write_slices.count; i++) { buffers[i].len = GPR_SLICE_LENGTH(tcp->write_slices.slices[i]); buffers[i].buf = (char *)GPR_SLICE_START_PTR(tcp->write_slices.slices[i]); } /* First, let's try a synchronous, non-blocking write. */ status = WSASend(socket->socket, buffers, tcp->write_slices.count, &bytes_sent, 0, NULL, NULL); info->wsa_error = status == 0 ? 0 : WSAGetLastError(); /* We would kind of expect to get a WSAEWOULDBLOCK here, especially on a busy connection that has its send queue filled up. But if we don't, then we can avoid doing an async write operation at all. */ if (info->wsa_error != WSAEWOULDBLOCK) { grpc_endpoint_write_status ret = GRPC_ENDPOINT_WRITE_ERROR; if (status == 0) { ret = GRPC_ENDPOINT_WRITE_DONE; GPR_ASSERT(bytes_sent == tcp->write_slices.length); } else { char *utf8_message = gpr_format_message(info->wsa_error); gpr_log(GPR_ERROR, "WSASend error: %s", utf8_message); gpr_free(utf8_message); } if (allocated) gpr_free(allocated); gpr_slice_buffer_reset_and_unref(&tcp->write_slices); tcp->outstanding_write = 0; tcp_unref(tcp); return ret; } /* If we got a WSAEWOULDBLOCK earlier, then we need to re-do the same operation, this time asynchronously. */ memset(&socket->write_info.overlapped, 0, sizeof(OVERLAPPED)); status = WSASend(socket->socket, buffers, tcp->write_slices.count, &bytes_sent, 0, &socket->write_info.overlapped, NULL); if (allocated) gpr_free(allocated); /* It is possible the operation completed then. But we'd still get an IOCP notification. So let's ignore it and wait for the IOCP. */ if (status != 0) { int error = WSAGetLastError(); if (error != WSA_IO_PENDING) { char *utf8_message = gpr_format_message(WSAGetLastError()); gpr_log(GPR_ERROR, "WSASend error: %s - this means we're going to leak.", utf8_message); gpr_free(utf8_message); /* I'm pretty sure this is a very bad situation there. Hence the log. What will happen now is that the socket will neither wait for read or write, unless the caller retry, which is unlikely, but I am not sure if that's guaranteed. And there might also be a read pending. This means that the future orphanage of that socket will be in limbo, and we're going to leak it. I have no idea what could cause this specific case however, aside from a parameter error from our call. Normal read errors would actually happen during the overlapped operation, which is the supported way to go for that. */ tcp->outstanding_write = 0; tcp_unref(tcp); /* Per the comment above, I'm going to treat that case as a hard failure for now, and leave the option to catch that and debug. */ __debugbreak(); return GRPC_ENDPOINT_WRITE_ERROR; } } /* As all is now setup, we can now ask for the IOCP notification. It may trigger the callback immediately however, but no matter. */ grpc_socket_notify_on_write(socket, on_write, tcp); return GRPC_ENDPOINT_WRITE_PENDING; }
static void win_notify_on_read(grpc_endpoint *ep, grpc_endpoint_read_cb cb, void *arg) { grpc_tcp *tcp = (grpc_tcp *) ep; grpc_winsocket *handle = tcp->socket; grpc_winsocket_callback_info *info = &handle->read_info; int status; DWORD bytes_read = 0; DWORD flags = 0; int error; WSABUF buffer; GPR_ASSERT(!tcp->outstanding_read); GPR_ASSERT(!tcp->shutting_down); tcp_ref(tcp); tcp->outstanding_read = 1; tcp->read_cb = cb; tcp->read_user_data = arg; tcp->read_slice = gpr_slice_malloc(8192); buffer.len = GPR_SLICE_LENGTH(tcp->read_slice); buffer.buf = (char *)GPR_SLICE_START_PTR(tcp->read_slice); /* First let's try a synchronous, non-blocking read. */ status = WSARecv(tcp->socket->socket, &buffer, 1, &bytes_read, &flags, NULL, NULL); info->wsa_error = status == 0 ? 0 : WSAGetLastError(); /* Did we get data immediately ? Yay. */ if (info->wsa_error != WSAEWOULDBLOCK) { info->bytes_transfered = bytes_read; /* This might heavily recurse. */ on_read(tcp, 1); return; } /* Otherwise, let's retry, by queuing a read. */ memset(&tcp->socket->read_info.overlapped, 0, sizeof(OVERLAPPED)); status = WSARecv(tcp->socket->socket, &buffer, 1, &bytes_read, &flags, &info->overlapped, NULL); if (status == 0) { grpc_socket_notify_on_read(tcp->socket, on_read, tcp); return; } error = WSAGetLastError(); if (error != WSA_IO_PENDING) { char *utf8_message = gpr_format_message(WSAGetLastError()); gpr_log(GPR_ERROR, "WSARecv error: %s - this means we're going to leak.", utf8_message); gpr_free(utf8_message); /* I'm pretty sure this is a very bad situation there. Hence the log. What will happen now is that the socket will neither wait for read or write, unless the caller retry, which is unlikely, but I am not sure if that's guaranteed. And there might also be a write pending. This means that the future orphanage of that socket will be in limbo, and we're going to leak it. I have no idea what could cause this specific case however, aside from a parameter error from our call. Normal read errors would actually happen during the overlapped operation, which is the supported way to go for that. */ tcp->outstanding_read = 0; tcp_unref(tcp); cb(arg, NULL, 0, GRPC_ENDPOINT_CB_ERROR); /* Per the comment above, I'm going to treat that case as a hard failure for now, and leave the option to catch that and debug. */ __debugbreak(); return; } grpc_socket_notify_on_read(tcp->socket, on_read, tcp); }
/* Initiates a write. */ static grpc_endpoint_write_status win_write(grpc_endpoint *ep, gpr_slice *slices, size_t nslices, grpc_endpoint_write_cb cb, void *arg) { grpc_tcp *tcp = (grpc_tcp *) ep; grpc_winsocket *socket = tcp->socket; grpc_winsocket_callback_info *info = &socket->write_info; unsigned i; DWORD bytes_sent; int status; WSABUF local_buffers[16]; WSABUF *allocated = NULL; WSABUF *buffers = local_buffers; GPR_ASSERT(!tcp->socket->write_info.outstanding); if (tcp->shutting_down) { return GRPC_ENDPOINT_WRITE_ERROR; } tcp_ref(tcp); tcp->socket->write_info.outstanding = 1; tcp->write_cb = cb; tcp->write_user_data = arg; gpr_slice_buffer_addn(&tcp->write_slices, slices, nslices); if (tcp->write_slices.count > GPR_ARRAY_SIZE(local_buffers)) { buffers = (WSABUF *) gpr_malloc(sizeof(WSABUF) * tcp->write_slices.count); allocated = buffers; } for (i = 0; i < tcp->write_slices.count; i++) { buffers[i].len = GPR_SLICE_LENGTH(tcp->write_slices.slices[i]); buffers[i].buf = (char *)GPR_SLICE_START_PTR(tcp->write_slices.slices[i]); } /* First, let's try a synchronous, non-blocking write. */ status = WSASend(socket->socket, buffers, tcp->write_slices.count, &bytes_sent, 0, NULL, NULL); info->wsa_error = status == 0 ? 0 : WSAGetLastError(); /* We would kind of expect to get a WSAEWOULDBLOCK here, especially on a busy connection that has its send queue filled up. But if we don't, then we can avoid doing an async write operation at all. */ if (info->wsa_error != WSAEWOULDBLOCK) { grpc_endpoint_write_status ret = GRPC_ENDPOINT_WRITE_ERROR; if (status == 0) { ret = GRPC_ENDPOINT_WRITE_DONE; GPR_ASSERT(bytes_sent == tcp->write_slices.length); } else { char *utf8_message = gpr_format_message(info->wsa_error); gpr_log(GPR_ERROR, "WSASend error: %s", utf8_message); gpr_free(utf8_message); } if (allocated) gpr_free(allocated); gpr_slice_buffer_reset_and_unref(&tcp->write_slices); tcp->socket->write_info.outstanding = 0; tcp_unref(tcp); return ret; } /* If we got a WSAEWOULDBLOCK earlier, then we need to re-do the same operation, this time asynchronously. */ memset(&socket->write_info.overlapped, 0, sizeof(OVERLAPPED)); status = WSASend(socket->socket, buffers, tcp->write_slices.count, &bytes_sent, 0, &socket->write_info.overlapped, NULL); if (allocated) gpr_free(allocated); if (status != 0) { int wsa_error = WSAGetLastError(); if (wsa_error != WSA_IO_PENDING) { gpr_slice_buffer_reset_and_unref(&tcp->write_slices); tcp->socket->write_info.outstanding = 0; tcp_unref(tcp); return GRPC_ENDPOINT_WRITE_ERROR; } } /* As all is now setup, we can now ask for the IOCP notification. It may trigger the callback immediately however, but no matter. */ grpc_socket_notify_on_write(socket, on_write, tcp); return GRPC_ENDPOINT_WRITE_PENDING; }
void http_start_get(void) { http.state = HTTP_SEND_HEADERS; unsigned pkt = tcp_create_packet(http.tcp_session); if(pkt == 0xff) return; tcp_tx_body_pointer=0; ADD_HTTP_STRING(pkt, "HTTP/1.1 200 Ok\r\n" "Connection: Close\r\n"); if(http.page->flags & HTML_FLG_NOCACHE) { ADD_HTTP_STRING(pkt, "Cache-Control: no-store, no-cache, must-revalidate\r\n" "Expires: Fri, 28 Apr 2000 00:00:00 GMT\r\n"); } else { ADD_HTTP_STRING(pkt, "Cache-Control: max-age=10000, private\r\n"); } if(http.page->flags & HTML_FLG_COMPRESSED) { ADD_HTTP_STRING(pkt, "Content-Encoding: gzip\r\n"); } char *p = tcp_ref(pkt, tcp_tx_body_pointer); char *s = p; p += sprintf(p, "Content-Type: %s\r\n", http.page->mime); if(req_origin[0] != 0) { p += sprintf(p, "Access-Control-Allow-Origin: %s\r\n", req_origin); p += sprintf(p, "Access-Control-Allow-Credentials: true\r\n"); } p += sprintf(p, "\r\n"); // end of headers tcp_tx_body_pointer += (unsigned)(p - s); if(http.page->flags & HTML_FLG_SS_EVENT) { if(sse_sock != 0xff) { // prepare old SSE socket to work as new http socket if(tcp_socket[sse_sock].tcp_state != TCP_RESERVED) { tcp_send_flags(TCP_MSK_ACK | TCP_MSK_RST | TCP_MSK_PSH, sse_sock); tcp_send_flags(TCP_MSK_ACK | TCP_MSK_RST | TCP_MSK_PSH, sse_sock); tcp_clear_connection(sse_sock); } // pass current http socket to SSE use unsigned swap_sock = sse_sock; sse_sock = http.tcp_session; http.tcp_session = swap_sock; // add initial data on SSE channel static char sse_retry_cmd[] = "retry: 2000\n\n"; tcp_put_tx_body(pkt, (void*)sse_retry_cmd, strlen(sse_retry_cmd)); ((unsigned(*)(unsigned, unsigned))(http.page->addr))(pkt, 0); // send 1st reply on SSE channel tcp_send_packet(sse_sock, pkt, tcp_tx_body_pointer); // start listening on alternative socket tcp_socket[http.tcp_session].tcp_state = TCP_LISTEN; http.state = HTTP_IDLE; } } else { http.state = HTTP_SEND_PAGE; http.sent = 0; http.more_data = 0; http_get_pumping(pkt); } }