Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

windows (mingw-w64) support for libh2o #380

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ IF (NOT WSLAY_FOUND)
SET(WSLAY_LIBRARIES -lwslay)
ENDIF (NOT WSLAY_FOUND)

SET(CMAKE_C_FLAGS "-O2 -g -Wall -Wno-unused-function ${CMAKE_C_FLAGS} -DH2O_ROOT=\"\\\"${CMAKE_INSTALL_PREFIX}\\\"\"")
SET(CMAKE_C_FLAGS "-O2 -g -Wall -Wno-unused-function ${CMAKE_C_FLAGS} -D_POSIX_C_SOURCE -DH2O_ROOT=\"\\\"${CMAKE_INSTALL_PREFIX}\\\"\"")

INCLUDE_DIRECTORIES(
include
Expand Down Expand Up @@ -217,12 +217,20 @@ LIST(REMOVE_ITEM UNIT_TEST_SOURCE_FILES

SET(EXTRA_LIBRARIES ${CMAKE_THREAD_LIBS_INIT} ${CMAKE_DL_LIBS})

IF (WIN32)
SET(EXTRA_LIBRARIES ${EXTRA_LIBRARIES} ws2_32)
ENDIF()

ADD_LIBRARY(libh2o ${LIB_SOURCE_FILES})
ADD_LIBRARY(libh2o-evloop ${LIB_SOURCE_FILES})
SET_TARGET_PROPERTIES(libh2o PROPERTIES OUTPUT_NAME h2o)
SET_TARGET_PROPERTIES(libh2o-evloop PROPERTIES
OUTPUT_NAME h2o-evloop
COMPILE_FLAGS "-DH2O_USE_LIBUV=0")
IF(WIN32)
SET_TARGET_PROPERTIES(libh2o-evloop PROPERTIES
EXCLUDE_FROM_ALL 1)
ENDIF(WIN32)
IF (OPENSSL_FOUND)
PREPEND_INCLUDE_DIRECTORY(libh2o ${OPENSSL_INCLUDE_DIR})
PREPEND_INCLUDE_DIRECTORY(libh2o-evloop ${OPENSSL_INCLUDE_DIR})
Expand Down Expand Up @@ -275,6 +283,10 @@ ADD_EXECUTABLE(h2o
${LIBYAML_SOURCE_FILES}
src/main.c)
SET_TARGET_PROPERTIES(h2o PROPERTIES COMPILE_FLAGS "-DH2O_USE_LIBUV=0")
IF(WIN32)
SET_TARGET_PROPERTIES(h2o PROPERTIES
EXCLUDE_FROM_ALL 1)
ENDIF(WIN32)
IF (WITH_BUNDLED_SSL)
PREPEND_INCLUDE_DIRECTORY(h2o ${BUNDLED_SSL_INCLUDE_DIR})
TARGET_LINK_LIBRARIES(h2o ${BUNDLED_SSL_LIBRARIES})
Expand All @@ -287,12 +299,16 @@ ELSE (WITH_BUNDLED_SSL)
ENDIF (WITH_BUNDLED_SSL)
TARGET_LINK_LIBRARIES(h2o ${EXTRA_LIBRARIES})

IF(NOT WIN32)
INSTALL(TARGETS h2o
RUNTIME DESTINATION bin
LIBRARY DESTINATION lib)
ENDIF(NOT WIN32)

INSTALL(DIRECTORY include/ DESTINATION include FILES_MATCHING PATTERN "*.h")
IF(NOT WIN32)
INSTALL(TARGETS libh2o-evloop DESTINATION lib)
ENDIF(NOT WIN32)
# only install libh2o if libuv is found
IF (LIBUV_FOUND)
INSTALL(TARGETS libh2o DESTINATION lib)
Expand Down
6 changes: 6 additions & 0 deletions examples/libh2o/http1client.c
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,12 @@ h2o_http1client_head_cb on_connect(h2o_http1client_t *client, const char *errstr

int main(int argc, char **argv)
{

#ifdef _WIN32
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 0), &wsaData);
#endif

h2o_multithread_queue_t *queue;
h2o_multithread_receiver_t getaddr_receiver;
h2o_timeout_t io_timeout;
Expand Down
11 changes: 10 additions & 1 deletion examples/libh2o/simple.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,15 @@
*/
#include <errno.h>
#include <limits.h>
#include <netinet/in.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#ifdef _WIN32
#include <winsock2.h>
#else
#include <netinet/in.h>
#include <sys/socket.h>
#endif
#include <sys/stat.h>
#include "h2o.h"
#include "h2o/http1.h"
Expand Down Expand Up @@ -215,7 +219,12 @@ int main(int argc, char **argv)
{
h2o_hostconf_t *hostconf;

#ifdef _WIN32
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 0), &wsaData);
#else
signal(SIGPIPE, SIG_IGN);
#endif

h2o_config_init(&config);
hostconf = h2o_config_register_host(&config, h2o_iovec_init(H2O_STRLIT("default")), 65535);
Expand Down
6 changes: 5 additions & 1 deletion include/h2o.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,11 @@ extern "C" {
#include <string.h>
#include <stdlib.h>
#include <sys/time.h>
#include <sys/socket.h>
#ifdef _WIN32
# include <winsock2.h>
#else
# include <sys/socket.h>
#endif
#include <time.h>
#include <unistd.h>
#include <openssl/ssl.h>
Expand Down
4 changes: 2 additions & 2 deletions include/h2o/configurator.h
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ int h2o_configurator_apply_commands(h2o_configurator_context_t *ctx, yoml_t *nod
* emits configuration error
*/
void h2o_configurator_errprintf(h2o_configurator_command_t *cmd, yoml_t *node, const char *reason, ...)
__attribute__((format(printf, 3, 4)));
__attribute__((format(gnu_printf, 3, 4)));
/**
* interprets the configuration value using sscanf, or prints an error upon failure
* @param configurator configurator
Expand All @@ -128,7 +128,7 @@ void h2o_configurator_errprintf(h2o_configurator_command_t *cmd, yoml_t *node, c
* @return 0 if successful, -1 if not
*/
int h2o_configurator_scanf(h2o_configurator_command_t *cmd, yoml_t *node, const char *fmt, ...)
__attribute__((format(scanf, 3, 4)));
__attribute__((format(gnu_scanf, 3, 4)));
/**
* interprets the configuration value and returns the index of the matched string within the candidate strings, or prints an error
* upon failure
Expand Down
23 changes: 16 additions & 7 deletions include/h2o/hostinfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,22 @@
#ifndef h2o__hostinfo_h
#define h2o__hostinfo_h

#include <arpa/inet.h>
#include <netdb.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#ifdef _WIN32
# include <ws2tcpip.h>
# ifndef AI_ADDRCONFIG
# define AI_ADDRCONFIG 0
# endif
# ifndef AI_NUMERICSERV
# define AI_NUMERICSERV 8
# endif
#else
# include <netdb.h>
# include <netinet/in.h>
# include <stdlib.h>
# include <string.h>
# include <sys/socket.h>
# include <sys/types.h>
#endif
#include "h2o/multithread.h"

typedef struct st_h2o_hostinfo_getaddr_req_t h2o_hostinfo_getaddr_req_t;
Expand Down
34 changes: 33 additions & 1 deletion include/h2o/socket.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,11 @@ extern "C" {
#endif

#include <stdint.h>
#include <sys/socket.h>
#ifdef _WIN32
# include <ws2tcpip.h>
#else
# include <sys/socket.h>
#endif
#include <openssl/ssl.h>
#include "h2o/memory.h"

Expand All @@ -52,6 +56,12 @@ extern "C" {

#define H2O_SOCKET_INITIAL_INPUT_BUFFER_SIZE 4096

#ifdef _WIN32
typedef char sockopt_value;
#else
typedef int sockopt_value;
#endif

typedef struct st_h2o_socket_t h2o_socket_t;

typedef void (*h2o_socket_cb)(h2o_socket_t *sock, int err);
Expand Down Expand Up @@ -206,6 +216,28 @@ inline int h2o_socket_is_reading(h2o_socket_t *sock)
return sock->_cb.read != NULL;
}

// We could look at possibly adding fcntl to mingw-w64 ?
#ifdef _WIN32
// the values do not matter
#define F_SETFD 2
#define O_NONBLOCK 0x4000

static inline int fcntl(int s, int cmd, long arg)
{
u_long nonblock = 0;

if (arg && O_NONBLOCK)
nonblock = 1;
else
return -1;

if (cmd == F_SETFD)
ioctlsocket(s, FIONBIO, &nonblock);

return s != SOCKET_ERROR ? s : -1;
}
#endif

#ifdef __cplusplus
}
#endif
Expand Down
2 changes: 2 additions & 0 deletions include/h2o/socketpool.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,10 @@
extern "C" {
#endif

#ifndef _WIN32
#include <netinet/in.h>
#include <arpa/inet.h>
#endif
#include <pthread.h>
#include "h2o/linklist.h"
#include "h2o/multithread.h"
Expand Down
3 changes: 3 additions & 0 deletions include/h2o/time_.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@
#ifndef h2o__time_h
#define h2o__time_h

#ifndef _WIN32
#define _timezone localt.tm_gmtoff
#endif
#include <time.h>

#ifdef __cplusplus
Expand Down
14 changes: 9 additions & 5 deletions lib/common/http1client.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,15 @@
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include <arpa/inet.h>
#include <netdb.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/types.h>
#ifdef _WIN32
# include <ws2tcpip.h>
#else
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please move the includes and defines (e.g. AI_ADDRCONFIG) mandatory to use the functions provided by hostinfo.h to hostinfo.h, instead of repeating the definitions in each source file.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes I agree this makes sense.

done: haven't pushed changes yet as we need to address all the issues

# include <arpa/inet.h>
# include <netdb.h>
# include <netinet/in.h>
# include <sys/socket.h>
# include <sys/types.h>
#endif
#include "picohttpparser.h"
#include "h2o/string_.h"
#include "h2o/hostinfo.h"
Expand Down
5 changes: 5 additions & 0 deletions lib/common/multithread.c
Original file line number Diff line number Diff line change
Expand Up @@ -87,11 +87,16 @@ static void init_async(h2o_multithread_queue_t *queue, h2o_loop_t *loop)
{
int fds[2];

#ifndef _WIN32
if (cloexec_pipe(fds) != 0) {
perror("pipe");
abort();
}
fcntl(fds[1], F_SETFL, O_NONBLOCK);
#else
u_long nonblock = 1;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Call to cloexec_pipe (or an equivalent function) is missing.

I presume that this is the reason why you are seeing timeout errors.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you look a few lines above this is only relevant for the evloop implementation
#if H2O_USE_LIBUV
#else

With reguard to the evloop implementation I do need some advice here :)
In cloexec to setup fds[0] and fds[1] on windows we have to create the socket before hand
This means we need to know if it is tcp or udp a stream etc beforehand which I don't think we know at this point ?
This is done in the set_cloexec function

I think I should focus on supporting doing 1 of the 2 first and not both at the same time.
libuv seemed like the easiest one as it already supports windows and mingw-w64.
Your thoughts ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In cloexec to setup fds[0] and fds[1] on windows we have to create the socket before hand
This means we need to know if it is tcp or udp a stream etc beforehand which I don't think we know at this point ?
This is done in the set_cloexec function

In the unix-side, fds[0] and fds[1] are initialized by calling cloexec_pipe (which in turn calls pipe(2)). So on the Windows-side, the descriptors should be initialized as well, before setting them to non-blocking mode.

The file descriptors being created are used for signalling between the threads, and the threads listen to the sockets using the event loop. So I think on Windows you would need to create a pair of TCP sockets connected to each other under my understanding that the HANDLEs created by CreatePipe cannot be passed to select.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

libuv seemed like the easiest one as it already supports windows and mingw-w64.
Your thoughts ?

The H2O standalone server only supports the embedded event loop (evloop). Libuv binding exists for users of libh2o (since it is a popular event loop that is used, with rich documentation and other protocol bindings that application developers can use).

I agree that porting the libuv-side would be faster, but if your intention is to make the H2O standalone server runnable on Windows, you would need to port evloop (and it seems like that you have already done that to some extent!).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So I think on Windows you would need to create a pair of TCP sockets connected to each other under my understanding that the HANDLEs created by CreatePipe cannot be passed to select.

Yes I did this yesterday evening using the socket function and making it a stream socket with ipv4 support and tcp but it didn't change much in that the in ev-loop mode example-httpclient hangs (still stuck in the loop)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that porting the libuv-side would be faster, but if your intention is to make the H2O standalone server runnable on Windows, you would need to port evloop (and it seems like that you have already done that to some extent!).

Yes I started working on that because I couldn't get the examples that use libuv working :(
I would like to get libh2o working first if possible because I want to create a few mockups with it.

I still intend to get the full h2o package working on windows but I would like to focus on the lib aspect first.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes I did this yesterday evening using the socket function and making it a stream socket with ipv4 support and tcp but it didn't change much in that the in ev-loop mode example-httpclient hangs (still stuck in the loop)

To which part of the callbacks does the client proceed? exmaples/libh2o/http1client.c defines a number of callbacks that are called one by one. on_connect is called first, then on_head, and on_body. You could also set breakpoints to (or emit logs in) the callback functions within socketpool.c or http1client.c to determine which callbacks are called, and which are not.

That would help you analyze the cause of the issue.

Should I focus on the evloop version first or the libuv ?

That depends on what you want to do.

If your intention is to use libh2o, then you should better concentrate on libh2o and the examples under examples/libh2o. OTOH if your intention is porting the standalone server to Windows, I would suggest concentrating on evloop and then working on the libuv-side.

Personally I did not expect somebody to start porting the standalone server to Windows, and am fascinated by how far you have achieved so far.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To which part of the callbacks does the client proceed? exmaples/libh2o/http1client.c defines a number of callbacks that are called one by one. on_connect is called first, then on_head, and on_body. You could also set breakpoints to (or emit logs in) the callback functions within socketpool.c or http1client.c to determine which callbacks are called, and which are not.

When using libuv on_connect is called and it doesn't complain and that comes from a call stack of

1 on_connect
2 call_connect_cb
3 uv_process_tcp_connect_req
4 uv_process_reqs
5 uv_run

then after that it hits on_head and exits on line 108 after fprintf "I/O timeout"

the stack for this is

1 on_head
2 on_error_before_head
3 h2o_timeout_run
4 on_timeout

After debugging in fiddler2 the http req debugger I can see that the http request is infact never made to argv[1] in my case hxxp://google.com (xx = tt)

now the only strange piece of code left is this
/uv-binding.c.h:147:25: warning: passing argument 1 of 'dup' makes integer from pointer without a cast
if ((info->fd = dup(fd)) == -1)

I tried using _get_osfhanle but it doesn't change anything :/

I'm not quite sure whats going on here exactly.
The handle is being duplicated for some reason to hold on to after close ?

If your intention is to use libh2o, then you should better concentrate on libh2o and the examples under examples/libh2o. OTOH if your intention is porting the standalone server to Windows, I would suggest concentrating on evloop and then working on the libuv-side.

I would like to get the lib working first, but I do intend to get the server working after that :)

Personally I did not expect somebody to start porting the standalone server to Windows, and am fascinated by how far you have achieved so far.

Someone was bound to try it sooner or later :D

ioctlsocket(fds[1], FIONBIO, &nonblock);
#endif
queue->async.write = fds[1];
queue->async.read = h2o_evloop_socket_create(loop, fds[0], NULL, 0, 0);
queue->async.read->data = queue;
Expand Down
15 changes: 12 additions & 3 deletions lib/common/socket.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,14 @@
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <netdb.h>
#include <string.h>
#include <sys/un.h>
#ifdef _WIN32
# include <ws2tcpip.h>
# define IOV_MAX 65536
#else
# include <netdb.h>
# include <sys/un.h>
#endif
#include <unistd.h>
#include <openssl/err.h>
#include "h2o/socket.h"
Expand Down Expand Up @@ -404,12 +409,16 @@ int h2o_socket_compare_address(struct sockaddr *x, struct sockaddr *y)

CMP(x->sa_family, y->sa_family);

#ifndef _WIN32
if (x->sa_family == AF_UNIX) {
struct sockaddr_un *xun = (void *)x, *yun = (void *)y;
int r = strcmp(xun->sun_path, yun->sun_path);
if (r != 0)
return r;
} else if (x->sa_family == AF_INET) {
} else
#endif

if (x->sa_family == AF_INET) {
struct sockaddr_in *xin = (void *)x, *yin = (void *)y;
CMP(ntohl(xin->sin_addr.s_addr), ntohl(yin->sin_addr.s_addr));
CMP(ntohs(xin->sin_port), ntohs(yin->sin_port));
Expand Down
Loading