Skip to content

revenge: Add ch_listen() function for socket server support#19231

Open
mattn wants to merge 11 commits intovim:masterfrom
mattn:ch_listen
Open

revenge: Add ch_listen() function for socket server support#19231
mattn wants to merge 11 commits intovim:masterfrom
mattn:ch_listen

Conversation

@mattn
Copy link
Member

@mattn mattn commented Jan 21, 2026

Summary

#3639

This patch adds ch_listen() function to Vim, allowing scripts to create listening sockets and accept incoming network connections. This enables Vim to act as a simple network server for various use cases.

Changes

New Functions

  • ch_listen(address [, options]) - Listen on a socket and accept connections

    • Returns a channel representing the listening socket
    • Calls a callback function when a new client connects
    • Supports both IPv4 and Unix domain sockets
  • channel_listen_unix(path, callback) - Unix domain socket support

  • channel_listen_func(argvars) - Vim script interface

Implementation Details

  • Added ch_listen field to struct channel_S to mark listening sockets
  • Modified channel_read() to handle accept() on listening sockets
  • When a connection is accepted, a new channel is created and passed to the callback
  • The callback receives the accepted channel and client address

API Usage

function! OnConnect(channel, address)
    echomsg 'Client connected from: ' . a:address
    call ch_setoptions(a:channel, {
        \ 'callback': function('OnData'),
    \ })
endfunction

function! OnData(channel, data)
    echomsg 'Received: ' . a:data
endfunction

let server = ch_listen('127.0.0.1:8080', {
    \ 'callback': function('OnConnect'),
\ })

Use Cases

  • Simple HTTP server for serving static files
  • Network protocol implementation for testing
  • Local IPC between Vim and other processes
  • Learning network programming concepts

Testing

  • Implemented test case Test_listen() in test_channel.vim
  • Tested with multiple concurrent connections
  • Verified callback invocation and data transmission

Related Features

This complements the existing ch_open() function:

  • ch_open() - connect to a server
  • ch_listen() - accept connections as a server

Limitations

  • No SSL/TLS support
  • Single-threaded (blocks Vim event loop during processing)
  • No built-in HTTP parsing (simple callbacks only)

This is demonstration of HTTP server written in Vim script.

image

@mattn
Copy link
Member Author

mattn commented Jan 21, 2026

I'll fix test fail in later.

@chrisbra
Copy link
Member

Thanks that is nice. But I'll move this after the Vim 9.2 release.
Can a few people try it out? I'd also think that we should have a very simple practical example for this in the help.

@bfrg
Copy link
Contributor

bfrg commented Jan 24, 2026

It is not clear from the docs how ch_listen({address} [, {options}]) differs from the already existing function ch_open({address} [, {options}])? See :h channel-address for possible values of address.

@mattn
Copy link
Member Author

mattn commented Jan 24, 2026

@chrisbra This is simple practice example working as HTTP file server.

https://gist.github.com/mattn/3e691419c37fe7ce501dc37e2a1bb545

@chrisbra
Copy link
Member

Thanks.

ch_listen(address [, options]) - Listen on a socket and accept connections
Returns a channel representing the listening socket
Calls a callback function when a new client connects
Supports both IPv4 and Unix domain sockets
channel_listen_unix(path, callback) - Unix domain socket support

Why do we need channel_list_unix()? It looks like this is already supported via ch_listen() anyhow, no?

@habamax
Copy link
Contributor

habamax commented Jan 27, 2026

I wonder if this would help @yegappan lsp to support lsp servers without stdio, e.g. godot lsp server that works only over http?

@mattn
Copy link
Member Author

mattn commented Jan 28, 2026

Call tree is below. Should we rename channel_listen to channel_listen_tcp ?

f_ch_listen
  channel_listen_func
    channel_listen (TCP)
    channel_listen_unix (Unix Domain Socket)

@chrisbra
Copy link
Member

I think we should have one single ch_listen() function, that accepts either a TCP address or a Unix domain socket, similar to ch_open(). I don't think we need separate vim script functions for tcp and unix domain sockets.

@bfrg
Copy link
Contributor

bfrg commented Jan 28, 2026

I wonder if this would help @yegappan lsp to support lsp servers without stdio, e.g. godot lsp server that works only over http?

@habamax This is already possible, see :h channel-address. You need to open a channel with ch_open() and then pass that channel to job_start() using its channel option (explained under :h job-options [below :h job-term] ). There's also an example in the docs under :h channel-demo using a Python server.

@mattn
Copy link
Member Author

mattn commented Jan 29, 2026

@chrisbra
The current implementation already follows this approach. ch_listen() accepts both TCP addresses and Unix domain sockets using the same pattern as ch_open():

  • TCP: ch_listen('127.0.0.1:12345', ...)
  • Unix socket: ch_listen('unix:/path/to/socket', ...)

The channel_listen_func() detects the unix: prefix and internally calls either channel_listen_unix() or channel_listen() accordingly. There is no separate ch_listen_tcp() or ch_listen_unix() function exposed to Vim script.

@dkearns
Copy link
Contributor

dkearns commented Jan 29, 2026

I think we should have one single ch_listen() function, that accepts either a TCP address or a Unix domain socket, similar to ch_open(). I don't think we need separate vim script functions for tcp and unix domain sockets.

ch_listen() is the only builtin Vim script function added, the others are implementation functions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants