Menu

#49 POST broken on Windows (+patch)

open
nobody
None
5
2008-06-22
2008-06-22
Anonymous
No

POSTed data never makes it to the CGI program on Windows. $_POST (PHP) and STDIN (perl) are always empty.

This is because the thread that executes the compat_win32.c:stdoutput function starts before anything is written to the socket it reads from. Therefore recv (in stdoutput) fails with EWOULDBLOCK, and the thread terminates.

A possible solution could be to make the socket blocking on Windows. As it is read by a separate thread (that only transfers data from the socket to the CGI pipe) it should be safe to do so.

diff -u patch:

--- shttpd.c 2008-05-31 20:30:09.000000000 +0200
+++ shttpd.c 2008-06-22 19:57:37.000000000 +0200
@@ -1341,9 +1341,9 @@

(void) closesocket(sock);
(void) set_non_blocking_mode(sp[0]);
+#ifndef _WIN32
(void) set_non_blocking_mode(sp[1]);

-#ifndef _WIN32
(void) fcntl(sp[0], F_SETFD, FD_CLOEXEC);
(void) fcntl(sp[1], F_SETFD, FD_CLOEXEC);
#endif /* _WIN32*/

Thank you for this great software!

Regards, Roland

Discussion

  • Nobody/Anonymous

    Logged In: NO

    Thanks! I should really have a descent unit test for windows.
    The patch won't work for multithreaded case, I am afraid.
    The problem is that shttpd_socketpair is used to pass control messages between master and worker threads, and if made blocking, shttpd will hang at startup.
    The best way to do is to handle EWOULDBLOCK in CGI stdin/stdout threads.

     
  • Nobody/Anonymous

    Logged In: NO

    Lazy me, I never checked other uses of shttpd_socketpair.
    Let me try again:

    --- compat_win32.c 2008-05-31 20:30:09.000000000 +0200
    +++ compat_win32.c 2008-06-23 21:22:16.000000000 +0200
    @@ -300,6 +300,18 @@
    DWORD k;
    char buf[BUFSIZ];
    size_t max_recv;
    + fd_set socks;
    + struct timeval timeout;
    +
    + FD_ZERO(&socks);
    + FD_SET(tp->s, &socks);
    +
    + /* Use timeout, just in case */
    + timeout.tv_sec = 5;
    + timeout.tv_usec = 0;
    +
    + /* Wait until socket has data to read, or error */
    + select(tp->s + 1, &socks, NULL, &socks, &timeout);

    max_recv = min(sizeof(buf), tp->content_len - total);
    while (!stop && max_recv > 0 && (n = recv(tp->s, buf, max_recv, 0)) > 0) {

     
  • Nobody/Anonymous

    Logged In: NO

    A better one (handles multiple EWOULDBLOCKs).

    Regards, Roland

    --- compat_win32.c 2008-05-31 20:30:09.000000000 +0200
    +++ compat_win32.c 2008-06-24 18:01:28.000000000 +0200
    @@ -287,6 +287,14 @@
    big_int_t content_len;
    };

    +#define STDOUTPUT_FD_INIT(socket, pinput_socks, perror_socks, timeout) \
    + FD_ZERO(pinput_socks); \
    + FD_ZERO(perror_socks); \
    + FD_SET(socket, pinput_socks); \
    + FD_SET(socket, perror_socks); \
    + timeout.tv_sec = 5; /* Use timeout, just in case */ \
    + timeout.tv_usec = 0
    +
    /*
    * Thread function that reads POST data from the socket pair
    * and writes it to the CGI process.
    @@ -300,14 +308,22 @@
    DWORD k;
    char buf[BUFSIZ];
    size_t max_recv;
    + fd_set input_socks, error_socks;
    + struct timeval timeout;
    +
    + STDOUTPUT_FD_INIT(tp->s, &input_socks, &error_socks, timeout);

    max_recv = min(sizeof(buf), tp->content_len - total);
    - while (!stop && max_recv > 0 && (n = recv(tp->s, buf, max_recv, 0)) > 0) {
    + while (!stop && max_recv > 0
    + && select(tp->s + 1, &input_socks, NULL, &error_socks, &timeout) >= 0
    + && !FD_ISSET(tp->s, &error_socks)
    + && (n = recv(tp->s, buf, max_recv, 0)) > 0) {
    for (sent = 0; !stop && sent < n; sent += k)
    if (!WriteFile(tp->hPipe, buf + sent, n - sent, &k, 0))
    stop++;
    total += n;
    max_recv = min(sizeof(buf), tp->content_len - total);
    + STDOUTPUT_FD_INIT(tp->s, &input_socks, &error_socks, timeout);
    }

    CloseHandle(tp->hPipe); /* Suppose we have POSTed everything */

     

Log in to post a comment.

Auth0 Logo