Skip to content

Commit

Permalink
feat(#3251): introduced socket.listen object for windows
Browse files Browse the repository at this point in the history
  • Loading branch information
maxonfjvipon committed Oct 2, 2024
1 parent a6dd8e3 commit 85926b8
Show file tree
Hide file tree
Showing 11 changed files with 866 additions and 100 deletions.
10 changes: 5 additions & 5 deletions .codacy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@
# package name contains capital letter and such names are conventional.
---
exclude_paths:
- "eo-runtime/src/main/java/EOorg/EOeolang/EOsys/Posix/BindSyscall.java"
- "eo-runtime/src/main/java/EOorg/EOeolang/EOsys/Posix/ListenSyscall.java"
- "eo-runtime/src/main/java/EOorg/EOeolang/EOsys/Posix/AcceptSyscall.java"
- "eo-runtime/src/main/java/EOorg/EOeolang/EOsys/Posix/SendSyscall.java"
- "eo-runtime/src/main/java/EOorg/EOeolang/EOsys/Posix/RecvSyscall.java"
- "eo-runtime/src/main/java/EOorg/EOeolang/EOsys/Win32/BindFuncCall.java"
- "eo-runtime/src/main/java/EOorg/EOeolang/EOsys/Win32/ListenFuncCall.java"
- "eo-runtime/src/main/java/EOorg/EOeolang/EOsys/Win32/AcceptFuncCall.java"
- "eo-runtime/src/main/java/EOorg/EOeolang/EOsys/Win32/SendFuncCall.java"
- "eo-runtime/src/main/java/EOorg/EOeolang/EOsys/Win32/RecvFuncCall.java"
240 changes: 185 additions & 55 deletions eo-runtime/src/main/eo/org/eolang/net/socket.eo
Original file line number Diff line number Diff line change
Expand Up @@ -250,12 +250,8 @@
closed.eq -1
error
sprintf
"Couldn't close a posix socket '%d' connected to '%s:%d', reason: '%s'"
*
sockfd
^.address
^.port
^.strerror.code
"Couldn't close a posix socket '%d', reason: '%s'"
* sockfd ^.strerror.code
true

# Safe posix socket that ensures that socket is closed even if error is occurred.
Expand Down Expand Up @@ -356,41 +352,110 @@
error ex > [ex]
sock.closed-socket client-sockfd

# The win32 platform specified socket.
#
# Attention! The object is for internal usage only, please don't use
# the object programmatically outside of `socket` object.
[address port] > win-socket
[scope] > connect
code. > started-up
win32
"WSAStartup"
* win32.winsock-version-2-2
code. > sd
win32
"socket"
* win32.af-inet win32.sock-stream win32.ipproto-tcp
code. > inet-addr
code. > sd
win32
"socket"
* win32.af-inet win32.sock-stream win32.ipproto-tcp
code. > inet-addr
win32
"inet_addr"
* address
if. > inet-addr-as-int
inet-addr.eq win32.inaddr-none
error
sprintf
"Couldn't convert an IPv4 address '%s' into a 32-bit integer via 'inet_addr' win32 function call, WSA error code: %d"
* ^.address last-error-code
inet-addr.as-i32
win32.sockaddr-in > sockaddr
win32.af-inet.as-i16
^.htons port
inet-addr-as-int

# Scoped win32 socket that is passed as argument
# to scope of `win32-socket.connect`, `win32-socket.listen` and `scoped-socket.accept`.
# Here `sockfd` is the socket descriptor.
[sockfd] > scoped-socket
# Makes an `input` from win32 `socket`.
# The object allows to use `socket` as input stream and `read` from it sequentially.
^.^.^.as-input recv > as-input
# Makes an `output` from win32 socket.
# The object allows to use `socket` as output stream and `write` to it sequentially.
^.^.^.as-output send > as-output

# Send bytes through the socket.
# Here `buffer` must be an object that can be dataized.
# On success the `number` of sent bytes is returned, otherwise `error` is returned.
[buffer] > send
buffer > buff!
code. > sent
win32
"send"
* ^.sockfd buff buff.size 0
if. > @
sent.eq -1
error
sprintf
"Failed to send message through the socket '%d', WSA error code: %d"
* ^.sockfd ^.^.last-error.code
sent

# Receive bytes from the socket.
# Here `size` must be a `number` of desired bytes to receive.
# On success the received `bytes` are returned, otherwise `error` is returned.
[size] > recv
called. > received
win32
"recv"
* ^.sockfd size 0
if. > @
received.code.eq -1
error
sprintf
"Failed to receive data from the socket '%d', WSA error code: %d"
* ^.sockfd ^.^.last-error.code
received.output

# Get last error.
#
# Attention! The object is for internal usage only, please don't use
# the object programmatically outside of `socket` object.
win32 "WSAGetLastError" * > [] > last-error

# Close socket by given descriptor `sockfd`.
# Returns `true` on success, returns `error` otherwise.
#
# Attention! The object is for internal usage only, please don't use
# the object programmatically outside of `socket` object.
[sockfd] > closed-socket
code. > closed
win32
"inet_addr"
* ^.address
if. > inet-addr-as-int
inet-addr.eq win32.inaddr-none
"closesocket"
* sockfd
if. > @
closed.eq win32.socket-error
error
sprintf
"Couldn't convert an IPv4 address '%s' into a 32-bit integer via 'inet_addr' win32 function call, WSA error code: %d"
* ^.address last-error-code
inet-addr.as-i32
win32.sockaddr-in > sockaddr
win32.af-inet.as-i16
^.^.htons ^.port
inet-addr-as-int
code. > connected
win32
"connect"
* sd sockaddr sockaddr.size
code. > closed
"Couldn't close a win32 socket '%d', WSA error code: %d"
* sockfd ^.last-error.code
true

# Safe win32 socket that ensures that socket is closed and Winsock resources are
# cleaned even if error is occurred.
#
# Attention! The object is for internal usage only, please don't use
# the object programmatically outside of `socket` object.
[scope] > safe-socket
code. > started-up
win32
"closesocket"
* sd
"WSAStartup"
* win32.winsock-version-2-2
(win32 "WSACleanup" *).code > cleaned-up
(win32 "WSAGetLastError" *).code > last-error-code
if. > @
(started-up.eq 0).not
error
Expand All @@ -399,34 +464,99 @@
* started-up
try
if.
sd.eq -1
^.sd.eq -1
error
sprintf
"Couldn't create a win32 socket, WSA error code: %d"
* last-error-code
* ^.last-error.code
try
if.
connected.eq -1
error
sprintf
"Couldn't connect to '%s:%d' on win32 socket '%d', WSA error code: %d"
* ^.address ^.port sd last-error-code
as-bytes.
dataized
scope
^.scoped-win-socket sd
scope
error ex > [ex]
if.
closed.eq win32.socket-error
error
sprintf
"Couldn't close a win32 socket '%d', WSA error code: %d"
* sd last-error-code
true
^.closed-socket ^.sd
error ex > [ex]
if.
cleaned-up.eq win32.socket-error
error "Couldn't cleanup Winsock resources"
true

[descriptor] > scoped-win-socket
# Initiate a connection on a socket.
# Here `scope` must be an abstract object with one free attribute which will be
# set to `scoped-socket` object and used as connector for socket messaging.
[scope] > connect
^.safe-socket > @
[] >>
^.^ > sock
code. > connected
win32
"connect"
* sock.sd sock.sockaddr sock.sockaddr.size
if. > @
connected.eq -1
error
sprintf
"Couldn't connect to '%s:%d' on win32 socket '%d', WSA error code: %d"
* sock.address sock.port sock.sd sock.last-error.code
as-bytes.
dataized
scope
sock.scoped-socket
sock.sd

# Listen for connections on a socket.
# Here `scope` must be an abstract object with one free attribute, but unlike `connect`,
# this free attribute is set to abstract object which decorates `scoped-socket` and also
# has attribute `accept` to accept other socket connections.
[scope] > listen
^.safe-socket > @
[] >>
^.^ > sock
code. > bound
win32
"bind"
* sock.sd sock.sockaddr sock.sockaddr.size
code. > listened
win32
"listen"
* sock.sd 2048
if. > @
bound.eq -1
error
sprintf
"Couldn't bind win32 socket '%d' to '%s:%d', WSA error code: %d"
* sock.sd sock.address sock.port sock.last-error.code
if.
listened.eq -1
error
sprintf
"Failed to listen for connections to '%s:%d' on socket '%d', WSA error code: %d"
* sock.address sock.port sock.sd sock.last-error.code
as-bytes.
dataized
scope
[] >>
^.sock.scoped-socket ^.sock.sd > @

# Accept incoming connection on socket
# On success the client socket is returned, otherwise `error` is returned.
# Client socket decorates `scoped-socket` object, so all the attributes like
# `send`, `recv`, `as-input`, `as-output` are available.
[scope] > accept
^.^.sock > sock
code. > client-sockfd
win32
"accept"
* sock.sd sock.sockaddr sock.sockaddr.size
try > @
if.
client-sockfd.eq -1
error
sprintf
"Failed to accept a connection on win32 socket '%d', WSA error code: %d"
* sock.sd sock.last-error.code
as-bytes.
dataized
scope
sock.scoped-socket
client-sockfd
error ex > [ex]
sock.closed-socket client-sockfd
10 changes: 10 additions & 0 deletions eo-runtime/src/main/java/EOorg/EOeolang/EOsys/EOwin32$EOφ.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,18 @@
*/
package EOorg.EOeolang.EOsys; // NOPMD

import EOorg.EOeolang.EOsys.Win32.AcceptFuncCall;
import EOorg.EOeolang.EOsys.Win32.BindFuncCall;
import EOorg.EOeolang.EOsys.Win32.ClosesocketFuncCall;
import EOorg.EOeolang.EOsys.Win32.ConnectFuncCall;
import EOorg.EOeolang.EOsys.Win32.GetCurrentProcessIdFuncCall;
import EOorg.EOeolang.EOsys.Win32.GetEnvironmentVariableFuncCall;
import EOorg.EOeolang.EOsys.Win32.GetSystemTimeFuncCall;
import EOorg.EOeolang.EOsys.Win32.InetAddrFuncCall;
import EOorg.EOeolang.EOsys.Win32.ListenFuncCall;
import EOorg.EOeolang.EOsys.Win32.ReadFileFuncCall;
import EOorg.EOeolang.EOsys.Win32.RecvFuncCall;
import EOorg.EOeolang.EOsys.Win32.SendFuncCall;
import EOorg.EOeolang.EOsys.Win32.SocketFuncCall;
import EOorg.EOeolang.EOsys.Win32.WSACleanupFuncCall;
import EOorg.EOeolang.EOsys.Win32.WSAGetLastErrorFuncCall;
Expand Down Expand Up @@ -75,6 +80,11 @@ public final class EOwin32$EOφ extends PhDefault implements Atom {
EOwin32$EOφ.FUNCTIONS.put("WSAGetLastError", WSAGetLastErrorFuncCall::new);
EOwin32$EOφ.FUNCTIONS.put("socket", SocketFuncCall::new);
EOwin32$EOφ.FUNCTIONS.put("connect", ConnectFuncCall::new);
EOwin32$EOφ.FUNCTIONS.put("accept", AcceptFuncCall::new);
EOwin32$EOφ.FUNCTIONS.put("bind", BindFuncCall::new);
EOwin32$EOφ.FUNCTIONS.put("listen", ListenFuncCall::new);
EOwin32$EOφ.FUNCTIONS.put("send", SendFuncCall::new);
EOwin32$EOφ.FUNCTIONS.put("recv", RecvFuncCall::new);
EOwin32$EOφ.FUNCTIONS.put("closesocket", ClosesocketFuncCall::new);
EOwin32$EOφ.FUNCTIONS.put("inet_addr", InetAddrFuncCall::new);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2016-2024 Objectionary.com
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/

/*
* @checkstyle PackageNameCheck (4 lines)
* @checkstyle TrailingCommentCheck (3 lines)
*/
package EOorg.EOeolang.EOsys.Win32; // NOPMD

import EOorg.EOeolang.EOsys.SockaddrIn;
import EOorg.EOeolang.EOsys.Syscall;
import com.sun.jna.ptr.IntByReference;
import org.eolang.Data;
import org.eolang.Dataized;
import org.eolang.PhDefault;
import org.eolang.Phi;

/**
* The socket WS2_32 function call.
* @see <a href="https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-socket">here for details</a>
* @since 0.40.0
*/
public final class AcceptFuncCall implements Syscall {
/**
* Win32 object.
*/
private final Phi win;

/**
* Ctor.
* @param win Win32 object
*/
public AcceptFuncCall(final Phi win) {
this.win = win;
}

@Override
public Phi make(final Phi... params) {
final Phi result = this.win.take("return").copy();
result.put(
0,
new Data.ToPhi(
Winsock.INSTANCE.accept(
new Dataized(params[0]).asNumber().intValue(),
new SockaddrIn(
new Dataized(params[1].take("sin-family")).take(Short.class),
new Dataized(params[1].take("sin-port")).take(Short.class),
new Dataized(params[1].take("sin-addr")).take(Integer.class),
new Dataized(params[1].take("sin-zero")).take()
),
new IntByReference(new Dataized(params[2]).asNumber().intValue())
)
)
);
result.put(1, new PhDefault());
return result;
}
}
Loading

0 comments on commit 85926b8

Please sign in to comment.