syntax = "proto3";

package nursery.multicp.netpipe;
option go_package = "code.justin.tv/rhys/nursery/cmd/multicp/netpipe";

import "google/protobuf/duration.proto";
import "google/protobuf/empty.proto";

// 1. What is the API?
// 2. How can clients and servers implement it in a useful and effective way?

// Provides bidirectional pipes usable as stream-mode (TCP) sockets to remote
// callers.
//
// Each pipe has an opaque name that is unique within the service during the
// pipe's lifetime (including timeout grace period). Once a client has
// established a pipe, the remote calls available to act on the pipe are
// idempotent. A client may issue redundant calls for those methods to defend
// against delay or loss of the calls.
//
// Clients may issue multiple calls to write overlapping data segments to defend
// against loss or delay of their remote calls. The server may respond to read
// requests with overlapping data segments as well.
service NetPipe {

    // Opens a new bidirectional pipe on the server, which subsequent calls may
    // reference by opaque name.
    rpc CreatePipe(CreatePipeRequest) returns (Pipe);

    // Closes a pipe.
    rpc DeletePipe(DeletePipeRequest) returns (google.protobuf.Empty) {
        option idempotency_level = IDEMPOTENT;
    }

    // Writes data to a pipe at an offset.
    rpc WriteAt(WriteAtRequest) returns (WriteAtResponse) {
        option idempotency_level = IDEMPOTENT;
    }

    // Reads data from a pipe.
    rpc Read(ReadRequest) returns (ReadResponse) {
        option idempotency_level = NO_SIDE_EFFECTS;
    }

}

// A bidirectional data stream.
message Pipe {
    // Required. The opaque descriptor for the pipe. Max length of 200 bytes.
    string name = 1;
    // Required. The maximum number of bytes that may be written or read in a
    // single operation on this pipe.
    int32 max_message_bytes = 2;
    // Required. The negotiated value for long to keep the pipe active between
    // operations from the client.
    google.protobuf.Duration keepalive_duration = 3;
}

message CreatePipeRequest {
    // Required. The name or address of the pipe's target.
    string target = 1;
    // The client's offer for how long to keep the pipe active between
    // operations from the client.
    google.protobuf.Duration keepalive_duration = 2;
}

message DeletePipeRequest {
    // Required. The opaque descriptor for the pipe.
    string name = 1;
}

message WriteAtRequest {
    // Required. The opaque descriptor for the pipe.
    string name = 1;

    // Data segment to write to the pipe.
    bytes write_data = 2;
    // Offset of the data segment into the write stream, in bytes.
    int64 write_offset = 3;

    // Offset into the read stream for which the client acknowledges receipt, in
    // bytes.
    int64 committed_read_offset = 4;
}

message WriteAtResponse {
    // Offset into the write stream for which the server acknowledges receipt,
    // in bytes.
    int64 committed_write_offset = 1;
}

message ReadRequest {
    // Required. The opaque descriptor for the pipe.
    string name = 1;
    // The maximum number of data bytes that the server may return in response
    // to this call.
    int32 max_read_bytes = 2;
    // The maximum time the server may wait before responding to this request.
    google.protobuf.Duration timeout_duration = 3;
}

message ReadResponse {
    // Data segment read from the pipe.
    bytes read_data = 1;
    // Offset of the data segment into the read stream, in bytes.
    int64 read_offset = 2;
    // The connection is permanently closed and the reader has acknowledged all
    // bytes.
    bool end_of_file = 3;
}
