Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions .github/actions/setup-tarantool/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
name: Setup Tarantool Test Environment
description: 'Sets up Tarantool and tt'

inputs:
tarantool-version:
description: 'Tarantool version to install'
required: true
default: '2.11'

runs:
using: "composite"
steps:
- name: setup tarantool
uses: tarantool/setup-tarantool@v3
with:
tarantool-version: ${{ inputs.tarantool-version }}
- name: add tarantool/modules repo
shell: bash
run: |
os=$(. /etc/os-release && echo $ID)
dist=$(. /etc/os-release && echo $VERSION_CODENAME)
curl -L "https://download.tarantool.org/tarantool/modules/gpgkey" | sudo apt-key add -
apt_source_path="/etc/apt/sources.list.d/tarantool.list"
echo "deb https://download.tarantool.org/tarantool/modules/${os}/ ${dist} main" | sudo tee ${apt_source_path}
- name: install tt
shell: bash
run: sudo apt-get update && sudo apt-get install -y tt
18 changes: 18 additions & 0 deletions .github/actions/setup-test/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
name: Setup Tarantool Test Environment
description: 'Sets up Tarantool and dependencies for testing'

inputs:
tarantool-version:
description: 'Tarantool version to install'
required: true

runs:
using: "composite"
steps:
- name: setup tarantool
uses: ./.github/actions/setup-tarantool
with:
tarantool-version: ${{ inputs.tarantool-version }}
- name: install luatest scm-1
shell: bash
run: tt rocks install luatest 1.1.0
16 changes: 16 additions & 0 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
name: Linting with luacheck

on:
- push
- pull_request

jobs:
run-luacheck-linter:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/setup-tarantool
with:
tarantool-version: '2.11'
- name: install tarantool/luacheck and execute it
run: tt rocks install luacheck && .rocks/bin/luacheck .
58 changes: 58 additions & 0 deletions .github/workflows/push-rockspec.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
name: Create and push rockspec for moonlibs/connection

on:
workflow_run:
workflows:
- "Linting with luacheck"
- "Testing with unit tests"
types:
- completed
push:
tags:
- '*'

env:
ROCK_NAME: connection

jobs:
pack-and-push-tagged-rockspec:
runs-on: ubuntu-latest
if: ${{ github.event.workflow_run.conclusion == 'success' }} && startsWith(github.ref, 'refs/tags/') }}
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/setup-tarantool

# https://stackoverflow.com/questions/58177786/get-the-current-pushed-tag-in-github-actions
- name: Set env
run: echo "TAG=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV

- run: tt rocks new_version --tag=${{ env.TAG }} ${{ env.ROCK_NAME }}-dev-1.rockspec ${{ env.TAG }} "git+https://github.com/${{ github.repository }}.git"
- run: tt rocks install ${{ env.ROCK_NAME }}-${{ env.TAG }}-1.rockspec
- run: tt rocks pack ${{ env.ROCK_NAME }}-${{ env.TAG }}-1.rockspec

- uses: unfor19/install-aws-cli-action@v1.0.3
- run:
|
mkdir .build
cp ${{ env.ROCK_NAME }}-dev-1.rockspec ${{ env.ROCK_NAME }}-${{ env.TAG }}-1.rockspec \
.build/
cp *.src.rock .build/
- name: rebuild and publish s3 luarocks server
env:
AWS_ACCESS_KEY_ID: ${{ secrets.MOONLIBS_S3_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.MOONLIBS_S3_SECRET_KEY}}
AWS_EC2_METADATA_DISABLED: true
run: |
cd .build && \
aws s3 sync s3://moonlibs/ ./ && \
tt rocks admin make_manifest . && \
aws s3 sync --acl public-read ./ s3://moonlibs/;
- uses: "marvinpinto/action-automatic-releases@latest"
with:
repo_token: "${{ secrets.GITHUB_TOKEN }}"
prerelease: false
files: |
README.md
${{env.ROCK_NAME}}-dev-1.rockspec
${{env.ROCK_NAME}}-${{env.TAG}}-1.rockspec
${{env.ROCK_NAME}}-${{env.TAG}}-1.src.rock
21 changes: 21 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
name: Testing with unit tests

on:
- push
- pull_request

jobs:
run-unit-tests:
runs-on: ubuntu-22.04
strategy:
matrix:
version: ["1.10.15", "2.10.7", "2.11.2", "3.0.1"]
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/setup-test
with:
tarantool-version: ${{matrix.version}}
- name: build lib
run: tt rocks make connection-dev-1.rockspec
- name: run tests
run: .rocks/bin/luatest --no-capture -v
12 changes: 12 additions & 0 deletions .luacheckrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
std="tarantool"

codes=true
max_line_length=140
include_files = {"connection.lua"}

ignore = {
"212", -- unused argument
"431", -- shadwing upvalue
"432", -- shadowing upvalue argument
"542", -- empty if branch
}
21 changes: 21 additions & 0 deletions .luacov
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
runreport = false
deletestats = false

exclude = {
"%.rocks/",
"builtin/",
"t/.+%.test",
}

pathcorrect = {
{ "^/source/connection/", "" },
}

coveralls = {
root = "/",
debug = true,
pathcorrect = {
{ "^/home/runner/work/connection/connection/", "" },
{ "^/source/connection", "" },
},
}
6 changes: 6 additions & 0 deletions .rocks/config-5.1.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
rocks_servers = {
"https://moonlibs.org",
"https://moonlibs.github.io/rocks",
"https://rocks.tarantool.org",
"https://luarocks.org",
}
210 changes: 210 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
# Connection - Base Class for TCP Connections

A Lua module providing a foundation for managing TCP socket connections in Tarantool applications. This library offers an object-oriented approach to handling network connections with support for asynchronous operations, automatic reconnection, and customizable event handling.

## Features

- Asynchronous TCP connection management
- Automatic reconnection with configurable timeout
- Buffer management for both reading and writing
- Event-based architecture with callbacks
- Non-blocking I/O

## Installation

### Using Tarantool Rocks

```bash
tt rocks --server https://moonlibs.org install connection
```

### Manual Installation

Clone the repository and install using the rockspec:

```bash
git clone https://github.com/moonlibs/connection
cd connection
tt rocks make
```

## Usage

### Basic Connection

```lua
local connection = require('connection')

-- Create a new connection
local conn = connection.new('example.com', 8080)

-- Wait for connection to be established (if needed)
conn.connwait:get(timeout)

-- Send data
conn:push_write("Hello, server!")
conn:flush()

-- Close connection when done
conn:close()
```

### Connection Options

```lua
local default_options = {
timeout = 1/3, -- Connection and request timeout in seconds
autoconnect = true, -- Connect immediately when created
reconnect = 1/3, -- Reconnect after 1/3 seconds if connection fails
maxbuf = 2 * 1024 * 1024 -- 2MB read buffer size for read
}

local conn = connection.new('example.com', 8080, {
timeout = 1, -- Connection and request timeout in seconds
autoconnect = true, -- Connect immediately when created
reconnect = 0.5, -- Reconnect after 0.5 seconds if connection fails
maxbuf = 2 * 1024 * 1024 -- 2MB read buffer size for read
})
```

### Custom Event Handlers

```lua
local obj = require('obj')
local connection = require('connection')

local MyConnection = obj.class({}, 'MyConnection', connection)

function MyConnection:on_connected()
self:log('I', 'Successfully connected!')
-- Initialize session, authenticate, etc.
end

function MyConnection:on_disconnect(err)
self:log('W', 'Disconnected: %s', err)
-- Clean up resources
end

function MyConnection:on_read(is_last)
-- Process received data
local data = ffi.string(self.rbuf, self.avail)
self:log('D', 'Received data: %s', data)

-- Important: reset buffer position after processing
self.avail = 0
end

-- Create instance of your connection
local conn = MyConnection:new('example.com', 8080)
```

## Connection States

The connection can be in one of the following states:

- `NOTCONNECTED` (0) - Initial state, not connected
- `CONNECTING` (1) - Connection attempt in progress
- `CONNECTED` (2) - Successfully connected
- `RECONNECTING` (3) - Attempting to reconnect after failure

You can check the current state:

```lua
if conn.state == connection.S2S.CONNECTED then
-- Connection is established
end
```

## API Reference

### Methods (all methods never yields)

- `new(host, port, options)` - Create a new connection
- `connect()` - Initiate connection
- `push_write(buf, len)` - Queue data to be sent, reallocates write buffer
- `flush()` - Send queued data
- `close()` - Close the connection, connection can be reused
- `destroy()` - Clean up resources, makes connection unusable
- `fdno()` - Returns file descriptor (or -1)

### Callbacks (executed in separate fiber)

- `on_connected()` - Called when connection is established, executed in separate fiber
- `on_disconnect(err: string)` - Called when connection is closed, executed in separate fiber
- `on_connect_failed(errno_code)` - Called when connection attempt fails, reconnection logic is performed here
- `on_connect_reset(errno_code)` - Called when connection is reset (closed due to an error)

### Reading data

- `on_read(is_last)` - Called when data is available for reading, from rw (read worker) fiber.
- **Important**: `on_read` is called directly from the read worker fiber. If your callback yields (using `fiber.sleep()` or other yielding operations), reading from the socket is stopped until the callback returns.
- If an exception is raised inside `on_read`, the connection will be reset. The connection can be reestablished automatically if the `reconnect` option is set.

### Protected methods

- `_cleanup(errno_code)` - Executes cleanup of all resources, closes socket, cancels fibers, drains buffers

## Example: Echo Client

Here's a simple echo client implementation that demonstrates basic usage:

```lua
local connection = require('connection')
local fiber = require('fiber')
local ffi = require('ffi')
local obj = require('obj')

local EchoClient = obj.class({}, 'EchoClient', connection)

function EchoClient:on_connected()
self:log('I', 'Connected to echo server')
-- Send a message when connected
self:push_write("Hello, Echo Server!\n")
self:flush()
end

function EchoClient:on_read(is_last)
-- Process the received echo response
local data = ffi.string(self.rbuf, self.avail)
self:log('I', 'Received echo: %s', data)

-- Clear the buffer after processing
self.avail = 0

-- Send another message
if not is_last then
-- yielding in on_read callback stops read from socket.
fiber.sleep(1)
self:push_write("Another message!\n")
self:flush()
end
end

function EchoClient:on_disconnect(err)
self:log('W', 'Disconnected from echo server: %s', err)
end

-- Usage example
local function test_echo_client()
local client = EchoClient:new('localhost', 7777)
client.connwait:get(2) -- Wait up to 2 seconds for connection

-- Keep the client running for a while
fiber.sleep(5)

-- Close the connection
client:close()
end

fiber.create(test_echo_client)
```

## Real-World Examples

Some real world examples

[connection-legacy](https://github.com/moonlibs/connection-legacy) repository, which implements a backward compatible API on top of this module.

[connection-scribe](https://github.com/moonlibs/connection-scribe) repository, which implements Scribe protocol

[tarantool1.5-replica](https://github.com/ochaton/migrate/blob/master/migrate/replica.lua) repository, which implements replication protocol of Tarantool 1.5
Loading
Loading