-
Notifications
You must be signed in to change notification settings - Fork 266
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
wasi: fix nonblocking sockets on *NIX (gotip net/http) #1503
Changes from 5 commits
f3767fa
45af81e
b700d6e
8034b2f
b25663c
ca75194
646a974
969df2b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -556,9 +556,9 @@ func Test_fdFdstatSetFlags(t *testing.T) { | |
// Let's remove O_APPEND. | ||
requireErrnoResult(t, wasip1.ErrnoSuccess, mod, wasip1.FdFdstatSetFlagsName, uint64(fd), uint64(0)) | ||
require.Equal(t, ` | ||
==> wasi_snapshot_preview1.fd_fdstat_set_flags(fd=4,flags=0) | ||
==> wasi_snapshot_preview1.fd_fdstat_set_flags(fd=4,flags=) | ||
<== errno=ESUCCESS | ||
`, "\n"+log.String()) | ||
`, "\n"+log.String()) // FIXME? flags==0 prints 'flags=' | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. that's fine IMHO because there are no flags and the formatted flags are pipe delimited. that said it is easy to change to special case none differently. @achille-roussel @ncruces any preferences on this one? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this default applies to every flag type we have in wasi, btw. |
||
log.Reset() | ||
|
||
// Without O_APPEND flag, the data is written at the beginning. | ||
|
@@ -568,9 +568,9 @@ func Test_fdFdstatSetFlags(t *testing.T) { | |
// Restore the O_APPEND flag. | ||
requireErrnoResult(t, wasip1.ErrnoSuccess, mod, wasip1.FdFdstatSetFlagsName, uint64(fd), uint64(wasip1.FD_APPEND)) | ||
require.Equal(t, ` | ||
==> wasi_snapshot_preview1.fd_fdstat_set_flags(fd=4,flags=1) | ||
==> wasi_snapshot_preview1.fd_fdstat_set_flags(fd=4,flags=APPEND) | ||
<== errno=ESUCCESS | ||
`, "\n"+log.String()) | ||
`, "\n"+log.String()) // FIXME? flags==1 prints 'flags=APPEND' | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. according to docs, flags is an fdflags type, so usually in log formatting we format the flag name vs have people need to look up what it was and do the bit flag math in their head. Maybe it was surprising that the logging "fixed" in a different change? https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-fdflags-flagsu16 |
||
log.Reset() | ||
|
||
// with O_APPEND flag, the data is appended to buffer. | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,6 +7,8 @@ import ( | |
"io" | ||
"io/fs" | ||
"net" | ||
"net/http" | ||
"runtime" | ||
"strconv" | ||
"strings" | ||
"testing" | ||
|
@@ -23,6 +25,10 @@ import ( | |
"github.com/tetratelabs/wazero/sys" | ||
) | ||
|
||
// sleepALittle directly slows down test execution. So, use this sparingly and | ||
// only when so where proper signals are unavailable. | ||
var sleepALittle = func() { time.Sleep(500 * time.Millisecond) } | ||
|
||
// This file ensures that the behavior we've implemented not only the wasi | ||
// spec, but also at least two compilers use of sdks. | ||
|
||
|
@@ -383,7 +389,7 @@ func testSock(t *testing.T, bin []byte) { | |
tcpAddr := <-tcpAddrCh | ||
|
||
// Give a little time for _start to complete | ||
time.Sleep(800 * time.Millisecond) | ||
sleepALittle() | ||
|
||
// Now dial to the initial address, which should be now held by wazero. | ||
conn, err := net.Dial("tcp", tcpAddr.String()) | ||
|
@@ -396,3 +402,67 @@ func testSock(t *testing.T, bin []byte) { | |
require.NoError(t, err) | ||
require.Equal(t, "wazero\n", console) | ||
} | ||
|
||
func Test_HTTP(t *testing.T) { | ||
if runtime.GOOS == "windows" { | ||
t.Skip("syscall.Nonblocking() is not supported on wasip1+windows.") | ||
} | ||
toolchains := map[string][]byte{} | ||
if wasmGotip != nil { | ||
toolchains["gotip"] = wasmGotip | ||
} | ||
|
||
for toolchain, bin := range toolchains { | ||
toolchain := toolchain | ||
bin := bin | ||
t.Run(toolchain, func(t *testing.T) { | ||
testHTTP(t, bin) | ||
}) | ||
} | ||
} | ||
|
||
func testHTTP(t *testing.T, bin []byte) { | ||
sockCfg := experimentalsock.NewConfig().WithTCPListener("127.0.0.1", 0) | ||
ctx := experimentalsock.WithConfig(testCtx, sockCfg) | ||
ctx, cancelFunc := context.WithCancel(ctx) | ||
// Set context to one that has an experimental listener that logs all host functions. | ||
// ctx = context.WithValue(ctx, experimental.FunctionListenerFactoryKey{}, | ||
// logging.NewHostLoggingListenerFactory(os.Stdout, logging.LogScopeAll)) | ||
|
||
moduleConfig := wazero.NewModuleConfig(). | ||
WithSysWalltime().WithSysNanotime(). // HTTP middleware uses both clocks | ||
WithArgs("wasi", "http") | ||
tcpAddrCh := make(chan *net.TCPAddr, 1) | ||
ch := make(chan string, 1) | ||
go func() { | ||
ch <- compileAndRunWithPreStart(t, ctx, moduleConfig, bin, func(t *testing.T, mod api.Module) { | ||
tcpAddrCh <- requireTCPListenerAddr(t, mod) | ||
}) | ||
}() | ||
tcpAddr := <-tcpAddrCh | ||
|
||
// Give a little time for _start to complete | ||
sleepALittle() | ||
|
||
// Now, send a POST to the address which we had pre-opened. | ||
body := bytes.NewReader([]byte("wazero")) | ||
req, err := http.NewRequest(http.MethodPost, "http://"+tcpAddr.String(), body) | ||
require.NoError(t, err) | ||
|
||
// TODO: test hangs here | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. here's the hang. from the log, the request is never read. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it looks like it's stuck in a loop here: // poll/fd_unix.go
func (fd *FD) Accept() (int, syscall.Sockaddr, string, error) {
...
for {
...
case syscall.EAGAIN:
if fd.pd.pollable() {
if err = fd.pd.waitRead(fd.isFile); err == nil {
continue
}
}
... TBH I don't know if you can actually leave that loop ever 😬 for {
s, rsa, errcall, err := accept(fd.Sysfd)
if err == nil {
return s, rsa, "", err
}
switch err {
case syscall.EINTR:
continue
case syscall.EAGAIN:
if fd.pd.pollable() {
if err = fd.pd.waitRead(fd.isFile); err == nil {
continue
}
}
case syscall.ECONNABORTED:
// This means that a socket on the listen
// queue was closed before we Accept()ed it;
// it's a silly error, so try again.
continue
}
return -1, nil, errcall, err
} I mean, sure. But it doesn't look like there is a way to interrupt gracefully. Stupidest I can think, There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ok I am now calling
|
||
resp, err := http.DefaultClient.Do(req) | ||
require.NoError(t, err) | ||
defer resp.Body.Close() | ||
|
||
require.Equal(t, 200, resp.StatusCode) | ||
b, err := io.ReadAll(resp.Body) | ||
require.NoError(t, err) | ||
require.Equal(t, "wazero\n", string(b)) | ||
|
||
cancelFunc() | ||
|
||
// FIXME: this hangs because the example listens forever; | ||
// however mod.Close() does not result in a clean shudown. | ||
// console := <-ch | ||
// require.Equal(t, "", console) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. solving by serving only 1 request and then exiting the Wasm routine; probably worth investigating what happens if we There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. hah I was thinking about this route also. |
||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
notice something (unrelated?) happened to flags when logging
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
related, probably intended? /cc @codefromthecrypt