Skip to content

Commit

Permalink
Provide minimal support for Windows (fix #146)
Browse files Browse the repository at this point in the history
Make both platform-dependant 'gio-unix-2.0' and 'gio-windows-2.0'
dependencies optional and use features accordingly via 'GIO_UNIX' and
'GIO_WINDOWS' definitions.

Disable '--socket' option if 'gio-unix-2.0' is not available.

For FastCGI, use 'GLib.Win32InputStream' and 'GLib.Win32OutputStream'
and gracefully fallback with basic I/O wrapper.

Add basic cross files to target 32 and 64 bit targets with MinGW.

Drop non-portable 'fork' for an eventual portable strategy involving
'GLib.Subprocess'.
  • Loading branch information
arteymix committed Oct 30, 2016
1 parent b4edfb9 commit 8a907c1
Show file tree
Hide file tree
Showing 12 changed files with 145 additions and 87 deletions.
11 changes: 11 additions & 0 deletions cross/i686-w64-mingw32.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[binaries]
exe_wrapper = 'wine'
c = '/usr/bin/i686-w64-mingw32-gcc'
strip = '/usr/bin/i686-w64-mingw32-strip'
pkgconfig = '/usr/bin/i686-w64-mingw32-pkg-config'

[host_machine]
system = 'windows'
cpu_family = 'x86'
cpu = 'i686'
endian = 'little'
11 changes: 11 additions & 0 deletions cross/x86_64-w64-mingw32.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[binaries]
exe_wrapper = 'wine'
c = '/usr/bin/x86_64-w64-mingw32-gcc'
strip = '/usr/bin/x86_64-w64-mingw32-strip'
pkgconfig = '/usr/bin/x86_64-w64-mingw32-pkg-config'

[host_machine]
system = 'windows'
cpu_family = 'x86'
cpu = 'x86_64'
endian = 'little'
15 changes: 14 additions & 1 deletion meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,30 @@ endif
glib = dependency('glib-2.0', version: '>=2.40')
gobject = dependency('gobject-2.0', version: '>=2.40')
gio = dependency('gio-2.0', version: '>=2.40')
gio_unix = dependency('gio-unix-2.0', version: '>=2.40')
gio_unix = dependency('gio-unix-2.0', version: '>=2.40', required: false)
gio_windows = dependency('gio-windows-2.0', version: '>=2.40', required: false)
gmodule = dependency('gmodule-2.0', version: '>=2.40')
soup = dependency('libsoup-2.4', version: '>=2.44')

vala_defines = []

if meson.get_compiler('c').has_function('fork')
vala_defines += '--define=HAVE_FORK'
endif

# provide 'OutputStream.write_all_async'
if gio.version().version_compare('>=2.44')
vala_defines += '--define=GIO_2_44'
endif

if gio_unix.found()
vala_defines += '--define=GIO_UNIX'
endif

if gio_windows.found()
vala_defines += '--define=GIO_WINDOWS'
endif

# new 'Soup.Server' API
if soup.version().version_compare('>=2.48')
vala_defines += '--define=SOUP_2_48'
Expand Down
14 changes: 12 additions & 2 deletions src/vsgi/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ vsgi_sources = files(
'vsgi-socket-server.vala',
'vsgi-tee-output-stream.vala')
vsgi_lib = library('vsgi-' + api_version, vsgi_sources,
dependencies: [glib, gobject, gio, gio_unix, gmodule, soup],
dependencies: [glib, gobject, gio, gio_unix, gio_windows, gmodule, soup],
vala_args: ['--pkg=posix'] + vala_defines,
link_args: '-Wl,-rpath,$$ORIGIN/servers',
install: true,
Expand All @@ -33,9 +33,19 @@ install_headers(meson.current_build_dir() + '/vsgi-@[email protected]'.format(api_version), s
install_data(meson.current_build_dir() + '/vsgi-@[email protected]'.format(api_version), install_dir: 'share/vala/vapi')
install_data('vsgi-@[email protected]'.format(api_version), install_dir: 'share/vala/vapi')

vsgi_requires_private = ['gmodule-2.0']

if gio_unix.found ()
vsgi_requires_private += 'gio-unix-2.0'
endif

if gio_windows.found()
vsgi_requires_private += 'gio-windows-2.0'
endif

pkgconfig = import('pkgconfig')
pkgconfig.generate(requires: 'glib-2.0 gobject-2.0 gio-2.0 libsoup-2.4',
requires_private: 'gio-unix-2.0 gmodule-2.0',
requires_private: vsgi_requires_private,
libraries: vsgi_lib,
version: meson.project_version(),
name: 'VSGI',
Expand Down
4 changes: 2 additions & 2 deletions src/vsgi/servers/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@ shared_library('vsgi-http', ['vsgi-http.vala'],
install_dir: '@0@/vsgi-@1@/servers'.format(get_option('libdir'), api_version))

shared_library('vsgi-cgi', 'vsgi-cgi.vala',
dependencies: [glib, gobject, gio, gio_unix, soup, vsgi],
dependencies: [glib, gobject, gio, gio_unix, gio_windows, soup, vsgi],
vala_args: vala_defines,
install: true,
install_dir: '@0@/vsgi-@1@/servers'.format(get_option('libdir'), api_version))

fcgi = meson.get_compiler('c').find_library('fcgi', required: false)
if fcgi.found()
shared_library('vsgi-fastcgi', 'vsgi-fastcgi.vala',
dependencies: [glib, gobject, gio, gio_unix, soup, vsgi, fcgi],
dependencies: [glib, gobject, gio, gio_unix, gio_windows, soup, vsgi, fcgi],
vala_args: ['--pkg=fcgi', '--vapidir=' + meson.current_source_dir()] + vala_defines,
install: true,
install_dir: '@0@/vsgi-@1@/servers'.format(get_option('libdir'), api_version))
Expand Down
25 changes: 12 additions & 13 deletions src/vsgi/servers/vsgi-cgi.vala
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,18 @@ namespace VSGI.CGI {

public override OutputStream output_stream { get { return this._output_stream; } }

public Connection (Server server, InputStream input_stream, OutputStream output_stream) {
public Connection (Server server) {
Object (server: server);
#if GIO_UNIX
_input_stream = new UnixInputStream (stdin.fileno (), true),
_output_stream = new UnixOutputStream (stdout.fileno (), true);
#elif GIO_WINDOWS
_input_stream = new Win32InputStream (stdin, true);
_output_stream = new Win32OutputStream (stdout, true);
#else
// TODO: find an alternative
#endif

this._input_stream = input_stream;
this._output_stream = output_stream;
}
Expand Down Expand Up @@ -70,11 +80,7 @@ namespace VSGI.CGI {
}

Idle.add (() => {
var connection = new Connection (this,
new UnixInputStream (stdin.fileno (), true),
new UnixOutputStream (stdout.fileno (), true));

var req = new Request (connection, Environ.@get ());
var req = new Request (new Connection (this), Environ.@get ());
var res = new Response (req);

// handle a single request and quit
Expand Down Expand Up @@ -103,12 +109,5 @@ namespace VSGI.CGI {
public override void stop () {
// CGI handle a single connection
}

/**
* Forking does not make sense for CGI.
*/
public override Pid fork () {
return 0;
}
}
}
62 changes: 56 additions & 6 deletions src/vsgi/servers/vsgi-fastcgi.vala
Original file line number Diff line number Diff line change
Expand Up @@ -55,13 +55,31 @@ namespace VSGI.FastCGI {
return "Unknown error code '%d'".printf (error);
}

private class StreamInputStream : UnixInputStream {
private class StreamInputStream :
#if GIO_UNIX
UnixInputStream
#elif GIO_WINDOWS
Win32InputStream
#else
InputStream
#endif
{

public unowned global::FastCGI.Stream @in { construct; get; }

#if GIO_UNIX
public StreamInputStream (int fd, global::FastCGI.Stream @in) {
Object (fd: fd, close_fd: false, @in: @in);
}
#elif GIO_WINDOWS
public StreamInputStream (void* handle, global::FastCGI.Stream @in) {
Object (handle: handle, close_handle: false, @in: @in);
}
#else
public StreamInputStream (global::FastCGI.Stream @in) {
Object (@in: @in);
}
#endif

public override ssize_t read (uint8[] buffer, Cancellable? cancellable = null) throws IOError {
var read = this.in.read (buffer);
Expand All @@ -84,15 +102,33 @@ namespace VSGI.FastCGI {
}
}

private class StreamOutputStream : UnixOutputStream {
private class StreamOutputStream :
#if GIO_UNIX
UnixOutputStream
#elif GIO_WINDOWS
Win32OutputStream
#else
OutputStream
#endif
{

public unowned global::FastCGI.Stream @out { construct; get; }

public unowned global::FastCGI.Stream err { construct; get; }

#if GIO_UNIX
public StreamOutputStream (int fd, global::FastCGI.Stream @out, global::FastCGI.Stream err) {
Object (fd: fd, close_fd: false, @out: @out, err: err);
}
#elif GIO_WINDOWS
public StreamOutputStream (void* handle, global::FastCGI.Stream @out, global::FastCGI.Stream err) {
Object (handle: handle, close_handle: false, @out: @out, err: err);
}
#else
public StreamOutputStream (global::FastCGI.Stream @out, global::FastCGI.Stream err) {
Object (@out: @out, err: err);
}
#endif

public override ssize_t write (uint8[] buffer, Cancellable? cancellable = null) throws IOError {
var written = this.out.put_str (buffer);
Expand Down Expand Up @@ -191,7 +227,10 @@ namespace VSGI.FastCGI {
if (address == null) {
fd = global::FastCGI.LISTENSOCK_FILENO;
_uris.append (new Soup.URI ("fcgi+fd://%d/".printf (fd)));
} else if (address is UnixSocketAddress) {
}

#if GIO_UNIX
else if (address is UnixSocketAddress) {
var socket_address = address as UnixSocketAddress;

fd = global::FastCGI.open_socket (socket_address.path, backlog);
Expand All @@ -201,7 +240,10 @@ namespace VSGI.FastCGI {
}

_uris.append (new Soup.URI ("fcgi+unix://%s/".printf (socket_address.path)));
} else if (address is InetSocketAddress) {
}
#endif

else if (address is InetSocketAddress) {
var inet_address = address as InetSocketAddress;

if (inet_address.get_family () == SocketFamily.IPV6) {
Expand Down Expand Up @@ -311,8 +353,16 @@ namespace VSGI.FastCGI {

yield;

this._input_stream = new StreamInputStream (fd, request.in);
this._output_stream = new StreamOutputStream (fd, request.out, request.err);
#if GIO_UNIX
_input_stream = new StreamInputStream (fd, request.in);
_output_stream = new StreamOutputStream (fd, request.out, request.err);
#elif GIO_WINDOWS
_input_stream = new StreamInputStream ((void*) fd, request.in);
_output_stream = new StreamOutputStream ((void*) fd, request.out, request.err);
#else
_input_stream = new StreamInputStream (request.in);
_output_stream = new StreamOutputStream (request.out, request.err);
#endif

return true;
}
Expand Down
42 changes: 14 additions & 28 deletions src/vsgi/vsgi-application.vala
Original file line number Diff line number Diff line change
Expand Up @@ -40,17 +40,17 @@ public class VSGI.Application : GLib.Application {
ApplicationFlags.SEND_ENVIRONMENT |
ApplicationFlags.NON_UNIQUE;
const OptionEntry[] entries = {
// general options
{"forks", 0, 0, OptionArg.INT, null, "Number of forks to create", "0"},
// address
{"address", 'a', 0, OptionArg.STRING_ARRAY, null, "Listen on each addresses", "[]"},
// port
{"port", 'p', 0, OptionArg.STRING_ARRAY, null, "Listen on each ports, '0' for random", "[]"},
{"any", 'A', 0, OptionArg.NONE, null, "Listen on any address instead of only from the loopback interface"},
{"ipv4-only", '4', 0, OptionArg.NONE, null, "Listen only to IPv4 interfaces"},
{"ipv6-only", '6', 0, OptionArg.NONE, null, "Listen only to IPv6 interfaces"},
#if GIO_UNIX
// socket
{"socket", 's', 0, OptionArg.FILENAME_ARRAY, null, "Listen on each UNIX socket paths", "[]"},
#endif
// file descriptor
{"file-descriptor", 'f', 0, OptionArg.STRING_ARRAY, null, "Listen on each file descriptors", "[]"},
{null}
Expand Down Expand Up @@ -91,7 +91,9 @@ public class VSGI.Application : GLib.Application {
var any = options.lookup_value ("any", VariantType.BOOLEAN);
var ipv4_only = options.lookup_value ("ipv4-only", VariantType.BOOLEAN);
var ipv6_only = options.lookup_value ("ipv6-only", VariantType.BOOLEAN);
#if GIO_UNIX
var sockets = options.lookup_value ("socket", VariantType.BYTESTRING_ARRAY);
#endif
var fds = options.lookup_value ("file-descriptor", VariantType.STRING_ARRAY);

if (addresses != null) {
Expand Down Expand Up @@ -144,12 +146,14 @@ public class VSGI.Application : GLib.Application {
}
}

#if GIO_UNIX
// socket path
if (sockets != null) {
foreach (var socket in sockets.get_bytestring_array ()) {
server.listen (new UnixSocketAddress (socket));
}
}
#endif

// file descriptor
if (fds != null) {
Expand All @@ -164,7 +168,12 @@ public class VSGI.Application : GLib.Application {
}

// default listening interface
if (addresses == null && ports == null && sockets == null && fds == null) {
if (addresses == null &&
ports == null &&
#if GIO_UNIX
sockets == null &&
#endif
fds == null) {
server.listen ();
}

Expand All @@ -173,44 +182,21 @@ public class VSGI.Application : GLib.Application {
return 1;
}

if (options.lookup_value ("forks", VariantType.INT32) != null) {
var forks = options.lookup_value ("forks", VariantType.INT32).get_int32 ();
try {
for (var i = 0; i < forks; i++) {
var pid = server.fork ();

// worker
if (pid == 0) {
break;
}

// parent
else {
// monitor child process
ChildWatch.add (pid, (pid, status) => {
warning ("Worker %d exited with status '%d'.", pid, status);
});
}
}
} catch (Error err) {
critical ("%s (%s, %d)", err.message, err.domain.to_string (), err.code);
return 1;
}
}

foreach (var uri in server.uris) {
message ("Listening on '%s'.", uri.to_string (false)[0:-uri.path.length]);
}

// keep the process (and workers) alive
hold ();

#if GIO_UNIX
// release on 'SIGTERM'
Unix.signal_add (ProcessSignal.TERM, () => {
release ();
server.stop ();
return false;
}, Priority.LOW);
#endif

return 0;
}
Expand Down
24 changes: 0 additions & 24 deletions src/vsgi/vsgi-server.vala
Original file line number Diff line number Diff line change
Expand Up @@ -123,30 +123,6 @@ namespace VSGI {
[Version (since = "0.3")]
public abstract void stop ();

/**
* Fork the execution.
*
* This is typically called after {@link VSGI.Server.listen} such that
* workers can share listening interfaces and descriptors.
*
* The default implementation wraps {@link Posix.fork} and check its
* return value. To disable forking, simply override this and return
* '0'.
*
* @throws GLib.SpawnError.FORK if the {@link Posix.fork} call fails
*
* @return the process pid if this is the parent process,
* otherwise '0'
*/
[Version (since = "0.3")]
public virtual Pid fork () throws Error {
var pid = Posix.fork ();
if (pid == -1) {
throw new SpawnError.FORK (strerror (errno));
}
return pid;
}

/**
* Dispatch the request to the application callback.
*
Expand Down
Loading

0 comments on commit 8a907c1

Please sign in to comment.