tcp-server-jruby

License

This is a small tcp server for JRuby available as a ruby gem.

It is based on the Netty project. Netty is written in java, but I wanted to write ruby.

Quick-start

Follow these instructions to get a tcp server echo program running.

Container

You may run the websocket server in a container. Using [colima] for a container runtime is recommended.

colima start
docker build --tag tcp-server-jruby .
docker compose up --detach
nc localhost 4000
docker compose down

Building the image or running the container:

docker build --tag tcp-server-jruby .
docker run --detach --publish 4000:4000 --name tcp-server-jruby tcp-server-jruby

Manually

If one insists, one may run everything directly with the required dependencies installed.

Install mise-en-place

The mise CLI tool used to manage multiple runtime versions.

See: https://mise.jdx.dev/getting-started.html

curl https://mise.jdx.dev/install.sh | sh
~/.local/bin/mise --version

Enable mise activation in future zsh sessions.

echo 'eval "$(~/.local/bin/mise activate zsh)"' >> ~/.zshrc

Install required runtime software

Use mise to install the runtime software defined as requirements in the .tool-versions file.

mise install

Install the project dependencies.

gem install bundler
bundle install

Run

The entrypoint for the TCP server may now be invoked from a command line interface terminal shell.

bundle exec exe/tcp_server

Build the gem

To run linting, unit tests, build the gem file, execute:

bundle exec rake

Publish the gem

To publish the gem after first verifying that the built gem works, execute:

version=$(ruby -r lib/tcp_server/version -I . -e 'puts TcpServer::VERSION')
git tag --annotate --message "Release ${version}" "${version}-release"
git push origin --tags

Or manually, if necessary:

bundle exec rake clean clobber test package verify
bundle exec rake publish

Clean up gem artifacts

To clean the project, execute:

bundle exec rake clean clobber

Project file tree

Here is a bird's-eye view of the project layout.

$ date; tree --gitignore
Sun Feb  8 09:29:34 CST 2026
.
├── docker-compose.yaml
├── Dockerfile
├── exe
│   └── tcp_server
├── Gemfile
├── lib
│   ├── tcp_server
│   │   ├── arguments_parser.rb
│   │   ├── channelizer.rb
│   │   ├── client.rb
│   │   ├── config.rb
│   │   ├── default_handler.rb
│   │   ├── demo_listener.rb
│   │   ├── instance_methods.rb
│   │   ├── listenable.rb
│   │   ├── logging.rb
│   │   ├── message_handler.rb
│   │   ├── modular_handler.rb
│   │   ├── server.rb
│   │   └── version.rb
│   └── tcp_server.rb
├── LICENSE
├── Rakefile
├── README.md
├── spec
│   ├── server_arguments_parser_spec.rb
│   ├── server_default_handler_spec.rb
│   ├── server_listenable_spec.rb
│   ├── server_message_handler_spec.rb
│   ├── spec_helper.rb
│   ├── test_spec.rb
│   └── verify
│       └── verify_spec.rb
├── tcp_server.png
├── tcp_server.rb
└── tcp-server-jruby.gemspec

6 directories, 31 files

CI linting

Use the GitLab CI Linting API to validate the syntax of a CI definition file.

jq --null-input --arg yaml "$(<.gitlab/ci/gem.gitlab-ci.yml)" '.content=$yaml' | curl --silent --location https://gitlab.com/api/v4/ci/lint --header "PRIVATE-TOKEN: ${GITLAB_COM_API_PRIVATE_TOKEN}" --header "Content-Type: application/json" --data @- | jq --raw-output '.errors[0]'

CI configuration

Generate a deploy key.

ssh-keygen -t ed25519 -P '' -C deploy_key -f deploy_key_ed25519

Use the GitLab Project-level Variables API to add the deploy key as a ssh private key variable.

project_path="nelsnelson/$(basename $(pwd))"

# Test auth token validity
curl --silent --show-error --location "https://gitlab.com/api/v4/projects" --header "PRIVATE-TOKEN: ${GITLAB_COM_API_PRIVATE_TOKEN}" | jq '.[0]["id"]'

project=$(curl --silent --show-error --location "https://gitlab.com/api/v4/search?scope=projects&search=${project_path}" --header "PRIVATE-TOKEN: ${GITLAB_COM_API_PRIVATE_TOKEN}" | jq --arg project_path "${project_path}" '.[] | select(.path_with_namespace == $project_path)')

project_id=$(curl --silent --show-error --location "https://gitlab.com/api/v4/search?scope=projects&search=${project_path}" --header "PRIVATE-TOKEN: ${GITLAB_COM_API_PRIVATE_TOKEN}" | jq --arg project_path "${project_path}" '.[] | select(.path_with_namespace == $project_path) | .id')

# Add the deploy_token as a CI variable:
curl --silent --show-error --location --request POST "https://gitlab.com/api/v4/projects/${project_id}/variables" --header "PRIVATE-TOKEN: ${GITLAB_COM_API_PRIVATE_TOKEN}" --form "key=SSH_PRIVATE_KEY" --form "value=$(cat ./deploy_key_ed25519)" --form "protected=true" | jq

Use the Deploy keys API to add a the public deploy key as a deploy key for the project.

curl --silent --show-error --location --request POST "https://gitlab.com/api/v4/projects/${project_id}/deploy_keys" --header "PRIVATE-TOKEN: ${GITLAB_COM_API_PRIVATE_TOKEN}" --form "title=deploy_key"  --form "key=$(cat ./deploy_key_ed25519.pub)" --form "can_push=true" | jq