Skip to content
Open

wip #18

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
37 changes: 20 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -150,28 +150,29 @@ host-verified connection for another session.

### SFTP

Called directly on a fresh connection, each SFTP method is one-shot: it opens
its own channel and subsystem, runs the single operation, and closes. Downloads
and uploads use `xstring` end to end:
Use `zif_oassh_sftp_one_shot` on a fresh connection for one operation. It opens
its own channel and subsystem, runs the operation, and closes the channel.
Downloads and uploads use `xstring` end to end:

```abap
DATA lv_file TYPE xstring.
DATA li_sftp TYPE REF TO zif_oassh_sftp_one_shot.

lo_ssh = zcl_oassh=>connect(
li_sftp = zcl_oassh=>connect(
iv_host = 'ssh.example.com'
iv_port = '22'
iv_user = 'deploy'
iv_password = 'secret'
ii_host_verifier = lo_host_verifier ).

TRY.
lv_file = lo_ssh->sftp_download(
lv_file = li_sftp->sftp_download(
iv_path = '/incoming/data.bin'
iv_timeout_seconds = 60 ).
CLEANUP.
lo_ssh->close( ).
li_sftp->close( ).
ENDTRY.
lo_ssh->close( ).
li_sftp->close( ).
```

The public SFTP methods are:
Expand All @@ -190,24 +191,26 @@ file.

#### Multi-operation SFTP sessions

To run several SFTP operations over a single authenticated connection, call
`sftp_open` first. It performs the channel, subsystem, and INIT/VERSION
To run several SFTP operations over a single authenticated connection, use
`zif_oassh_sftp_session` and call `sftp_open` first. It performs the channel,
subsystem, and INIT/VERSION
handshake once; the same `sftp_*` methods then run inside that session until
`sftp_close`. This avoids repeating the full SSH handshake — and, on APC, the
one-byte-per-frame cost of every handshake — for each operation:

```abap
lo_ssh = zcl_oassh=>connect( ... ).
DATA li_sftp TYPE REF TO zif_oassh_sftp_session.
li_sftp = zcl_oassh=>connect( ... ).
TRY.
lo_ssh->sftp_open( ).
lt_names = lo_ssh->sftp_list( '/incoming' ).
lv_file = lo_ssh->sftp_download( '/incoming/data.bin' ).
ls_attrs = lo_ssh->sftp_stat( '/incoming/data.bin' ).
lo_ssh->sftp_close( ).
li_sftp->sftp_open( ).
lt_names = li_sftp->sftp_list( '/incoming' ).
lv_file = li_sftp->sftp_download( '/incoming/data.bin' ).
ls_attrs = li_sftp->sftp_stat( '/incoming/data.bin' ).
li_sftp->sftp_close( ).
CLEANUP.
lo_ssh->close( ).
li_sftp->close( ).
ENDTRY.
lo_ssh->close( ).
li_sftp->close( ).
```

One SFTP operation runs at a time, and the session is not reused across
Expand Down
3 changes: 3 additions & 0 deletions abaplint.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@
"begin_end_names": true,
"begin_single_include": true,
"call_transaction_authority_check": true,
"add_test_attributes": true,
"aff_and_xml": true,
"align_pseudo_comments": true,
"cds_parser_error": true,
"chain_mainly_declarations": true,
"check_abstract": true,
Expand Down
23 changes: 13 additions & 10 deletions docs/interactive-exec.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ response, decide, and write the next request while the command keeps
running. The canonical example is `git-upload-pack`, which GitHub exposes
to deploy keys: ref advertisement down, `want`/`done` up, packfile down.

The interactive exec API keeps the session channel open and hands the
byte streams to the caller:
The `zif_oassh_interactive_exec` interface keeps the session channel open and
hands the byte streams to the caller:

| Method | Purpose |
| --- | --- |
Expand All @@ -31,28 +31,31 @@ command's output stream has ended (the peer sent `CHANNEL_EOF` or the
connection closed). Callers therefore loop:

```abap
DATA(lo_ssh) = zcl_oassh=>connect(
DATA li_exec TYPE REF TO zif_oassh_interactive_exec.

li_exec = zcl_oassh=>connect(
iv_host = 'github.com'
iv_port = '22'
iv_user = 'git'
iv_private_seed = lv_ed25519_seed " the deploy key
ii_host_verifier = li_verifier ).

lo_ssh->exec_open( |git-upload-pack 'owner/repo.git'| ).
li_exec->exec_open( |git-upload-pack 'owner/repo.git'| ).

DATA(lv_advertisement) = VALUE xstring( ).
WHILE lo_ssh->exec_is_closed( ) = abap_false.
DATA(lv_chunk) = lo_ssh->exec_read( ).
WHILE li_exec->exec_is_closed( ) = abap_false.
DATA(lv_chunk) = li_exec->exec_read( ).
IF lv_chunk IS INITIAL.
EXIT. " timeout: decide whether the protocol layer has a complete unit
ENDIF.
CONCATENATE lv_advertisement lv_chunk INTO lv_advertisement IN BYTE MODE.
" ... hand to a pkt-line parser, stop when it has the full advertisement
ENDWHILE.

lo_ssh->exec_write( lv_want_request ). " pkt-lines: want, deepen, flush, done
li_exec->exec_write( lv_want_request ). " pkt-lines: want, deepen, flush, done
" ... read the packfile with the same exec_read loop
lo_ssh->exec_close( ).
li_exec->exec_close( ).
li_exec->close( ).
```

## Flow control
Expand All @@ -63,8 +66,8 @@ that do not fit are queued and sent automatically when the server's
`exec_eof( )` refuses to half-close while stdin is still queued, because
those bytes could never be delivered afterwards.

Like every other operation on `zcl_oassh`, an interactive exec is the one
operation of its connection object: `exec_open( )` on a connection that
Like every other workflow, an interactive exec is the one operation of its
connection object: `exec_open( )` on a connection that
already ran an operation raises the usual typed error.

## Scope
Expand Down
35 changes: 18 additions & 17 deletions docs/sftp-sessions.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# Multi-operation SFTP sessions

Each `sftp_*` method on `zcl_oassh` is one-shot by default: it authenticates,
opens a session channel, starts the `sftp` subsystem, runs a single operation,
and closes the channel. Every operation therefore pays for a full SSH handshake
Each method on `zif_oassh_sftp_one_shot` authenticates, opens a session channel,
starts the `sftp` subsystem, runs a single operation, and closes the channel.
Every operation therefore pays for a full SSH handshake
and a fresh subsystem — expensive when a caller needs several operations in a
row, and especially so on APC, where each handshake is exchanged one byte per
frame.
Expand All @@ -12,31 +12,31 @@ the channel, subsystem, and INIT/VERSION handshake once; the same `sftp_*`
methods then run inside that single subsystem channel until `sftp_close`.

```abap
DATA lo_ssh TYPE REF TO zcl_oassh.
DATA lt_names TYPE zcl_oassh_sftp=>ty_names.
DATA li_sftp TYPE REF TO zif_oassh_sftp_session.
DATA lt_names TYPE zif_oassh_sftp_one_shot=>ty_names.
DATA lv_data TYPE xstring.
DATA ls_attrs TYPE zcl_oassh_sftp=>ty_attrs.
DATA ls_attrs TYPE zif_oassh_sftp_one_shot=>ty_attrs.

lo_ssh = zcl_oassh=>connect(
li_sftp = zcl_oassh=>connect(
iv_host = 'ssh.example.com'
iv_port = '22'
iv_user = 'deploy'
iv_password = 'secret'
ii_host_verifier = lo_host_verifier ).

TRY.
lo_ssh->sftp_open( ). " channel + subsystem + INIT/VERSION
lt_names = lo_ssh->sftp_list( '/incoming' ).
lv_data = lo_ssh->sftp_download( '/incoming/data.bin' ).
ls_attrs = lo_ssh->sftp_stat( '/incoming/data.bin' ).
lo_ssh->sftp_rename(
li_sftp->sftp_open( ). " channel + subsystem + INIT/VERSION
lt_names = li_sftp->sftp_list( '/incoming' ).
lv_data = li_sftp->sftp_download( '/incoming/data.bin' ).
ls_attrs = li_sftp->sftp_stat( '/incoming/data.bin' ).
li_sftp->sftp_rename(
iv_old_path = '/incoming/data.bin'
iv_new_path = '/incoming/data.bin.done' ).
lo_ssh->sftp_close( ). " channel close handshake
li_sftp->sftp_close( ). " channel close handshake
CLEANUP.
lo_ssh->close( ).
li_sftp->close( ).
ENDTRY.
lo_ssh->close( ).
li_sftp->close( ).
```

## API
Expand All @@ -52,8 +52,9 @@ lo_ssh->close( ).
usual typed error, and a session is never reused across those other kinds or a
second channel.

Without `sftp_open`, every `sftp_*` method behaves exactly as before — one-shot,
one connection per operation — so existing callers are unaffected.
Use `zif_oassh_sftp_one_shot` when no reusable session is needed. The concrete
SFTP methods on `zcl_oassh` are implementation details; callers select one of
the two SFTP interfaces explicitly.

## Semantics

Expand Down
18 changes: 9 additions & 9 deletions examples/zoassh_example_sftp.prog.abap
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ START-OF-SELECTION.
FORM run.

DATA lo_host_verifier TYPE REF TO zif_oassh_host_verifier.
DATA lo_ssh TYPE REF TO zcl_oassh.
DATA lt_names TYPE zcl_oassh_sftp=>ty_names.
DATA li_sftp TYPE REF TO zif_oassh_sftp_session.
DATA lt_names TYPE zif_oassh_sftp_one_shot=>ty_names.
DATA lv_filename TYPE string.
DATA lv_data TYPE xstring.
DATA lv_preview TYPE string.
Expand All @@ -59,30 +59,30 @@ FORM run.
TRY.
* ii_random is optional on SAP: zcl_oassh defaults to zcl_oassh_random_secure,
* which draws from the kernel-backed secure random source.
lo_ssh = zcl_oassh=>connect(
li_sftp = zcl_oassh=>connect(
iv_host = p_host
iv_port = p_port
iv_user = p_user
iv_password = p_pass
ii_host_verifier = lo_host_verifier ).
TRY.
lo_ssh->sftp_open( p_tmout ).
li_sftp->sftp_open( p_tmout ).

* --- list a directory -----------------------------------------------------
lt_names = lo_ssh->sftp_list(
lt_names = li_sftp->sftp_list(
iv_path = p_dir
iv_timeout_seconds = p_tmout ).

* --- download a file ------------------------------------------------------
lv_data = lo_ssh->sftp_download(
lv_data = li_sftp->sftp_download(
iv_path = p_file
iv_timeout_seconds = p_tmout ).

lo_ssh->sftp_close( p_tmout ).
li_sftp->sftp_close( p_tmout ).
CLEANUP.
lo_ssh->close( ).
li_sftp->close( ).
ENDTRY.
lo_ssh->close( ).
li_sftp->close( ).

WRITE: / 'listing of', p_dir.
SKIP.
Expand Down
3 changes: 3 additions & 0 deletions scripts/check-public-api.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import test from "node:test";

const publicSources = [
"../src/zcl_oassh.clas.abap",
"../src/zif_oassh_interactive_exec.intf.abap",
"../src/zif_oassh_sftp_one_shot.intf.abap",
"../src/zif_oassh_sftp_session.intf.abap",
"../src/platform/zif_oassh_socket.intf.abap",
];

Expand Down
Loading
Loading