Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Close http connections. #48

Merged
merged 1 commit into from
Feb 5, 2025
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
23 changes: 3 additions & 20 deletions agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (
"encoding/hex"
"errors"
"fmt"
"net/url"
"reflect"
"time"

Expand All @@ -23,12 +22,6 @@ import (
// DefaultConfig is the default configuration for an Agent.
var DefaultConfig = Config{}

// ic0 is the old (default) host for the Internet Computer.
// var ic0, _ = url.Parse("https://ic0.app/")

// icp0 is the default host for the Internet Computer.
var icp0, _ = url.Parse("https://icp0.io/")

func effectiveCanisterID(canisterID principal.Principal, args []any) principal.Principal {
// If the canisterID is not aaaaa-aa (encoded as empty byte array), return it.
if 0 < len(canisterID.Raw) || len(args) < 1 {
Expand Down Expand Up @@ -171,17 +164,7 @@ func New(cfg Config) (*Agent, error) {
if cfg.Identity != nil {
id = cfg.Identity
}
var logger Logger = new(NoopLogger)
if cfg.Logger != nil {
logger = cfg.Logger
}
ccfg := ClientConfig{
Host: icp0,
}
if cfg.ClientConfig != nil {
ccfg = *cfg.ClientConfig
}
client := NewClientWithLogger(ccfg, logger)
client := NewClient(cfg.ClientConfig...)
rootKey, _ := hex.DecodeString(certification.RootKey)
if cfg.FetchRootKey {
status, err := client.Status()
Expand All @@ -204,7 +187,7 @@ func New(cfg Config) (*Agent, error) {
identity: id,
ingressExpiry: cfg.IngressExpiry,
rootKey: rootKey,
logger: logger,
logger: client.logger,
delay: delay,
timeout: timeout,
verifySignatures: !cfg.DisableSignedQueryVerification,
Expand Down Expand Up @@ -501,7 +484,7 @@ type Config struct {
// The default is set to 5 minutes.
IngressExpiry time.Duration
// ClientConfig is the configuration for the underlying Client.
ClientConfig *ClientConfig
ClientConfig []ClientOption
// FetchRootKey determines whether the root key should be fetched from the IC.
FetchRootKey bool
// Logger is the logger used by the Agent.
Expand Down
84 changes: 57 additions & 27 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,32 +13,30 @@ import (
"github.com/fxamacker/cbor/v2"
)

// ic0 is the old (default) host for the Internet Computer.
// var ic0, _ = url.Parse("https://ic0.app/")

// icp0 is the default host for the Internet Computer.
var icp0, _ = url.Parse("https://icp0.io/")

// Client is a client for the IC agent.
type Client struct {
client http.Client
config ClientConfig
client *http.Client
host *url.URL
logger Logger
}

// NewClient creates a new client based on the given configuration.
func NewClient(cfg ClientConfig) Client {
return Client{
client: http.Client{},
config: cfg,
func NewClient(options ...ClientOption) Client {
c := Client{
client: http.DefaultClient,
host: icp0,
logger: new(NoopLogger),
}
}

// NewClientWithLogger creates a new client based on the given configuration and logger.
func NewClientWithLogger(cfg ClientConfig, logger Logger) Client {
if logger == nil {
logger = new(NoopLogger)
}
return Client{
client: http.Client{},
config: cfg,
logger: logger,
for _, o := range options {
o(&c)
}
return c
}

func (c Client) Call(ctx context.Context, canisterID principal.Principal, data []byte) ([]byte, error) {
Expand All @@ -52,16 +50,20 @@ func (c Client) Call(ctx context.Context, canisterID principal.Principal, data [
if err != nil {
return nil, err
}
defer resp.Body.Close()
switch resp.StatusCode {
case http.StatusAccepted:
return io.ReadAll(resp.Body)
case http.StatusOK:
body, _ := io.ReadAll(resp.Body)
var err preprocessingError
if err := cbor.Unmarshal(body, &err); err != nil {
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
return nil, fmt.Errorf("(%d) %s: %s", err.RejectCode, err.Message, err.ErrorCode)
var pErr preprocessingError
if err := cbor.Unmarshal(body, &pErr); err != nil {
return nil, err
}
return nil, pErr
default:
body, _ := io.ReadAll(resp.Body)
return nil, fmt.Errorf("(%d) %s: %s", resp.StatusCode, resp.Status, body)
Expand Down Expand Up @@ -96,6 +98,7 @@ func (c Client) get(path string) ([]byte, error) {
if err != nil {
return nil, err
}
defer resp.Body.Close()
return io.ReadAll(resp.Body)
}

Expand All @@ -119,11 +122,15 @@ func (c Client) post(ctx context.Context, path string, canisterID principal.Prin
if err != nil {
return nil, err
}
defer resp.Body.Close()
switch resp.StatusCode {
case http.StatusOK:
return io.ReadAll(resp.Body)
default:
body, _ := io.ReadAll(resp.Body)
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
return nil, fmt.Errorf("(%d) %s: %s", resp.StatusCode, resp.Status, body)
}
}
Expand All @@ -139,24 +146,43 @@ func (c Client) postSubnet(ctx context.Context, path string, subnetID principal.
if err != nil {
return nil, err
}
defer resp.Body.Close()
switch resp.StatusCode {
case http.StatusOK:
return io.ReadAll(resp.Body)
default:
body, _ := io.ReadAll(resp.Body)
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
return nil, fmt.Errorf("(%d) %s: %s", resp.StatusCode, resp.Status, body)
}
}

func (c Client) url(p string) string {
u := *c.config.Host
u := *c.host
u.Path = path.Join(u.Path, p)
return u.String()
}

// ClientConfig is the configuration for a client.
type ClientConfig struct {
Host *url.URL
type ClientOption func(c *Client)

func WithHostURL(host *url.URL) ClientOption {
return func(c *Client) {
c.host = host
}
}

func WithHttpClient(client *http.Client) ClientOption {
return func(c *Client) {
c.client = client
}
}

func WithLogger(logger Logger) ClientOption {
return func(c *Client) {
c.logger = logger
}
}

type preprocessingError struct {
Expand All @@ -167,3 +193,7 @@ type preprocessingError struct {
// An optional implementation-specific textual error code.
ErrorCode string `cbor:"error_code"`
}

func (e preprocessingError) Error() string {
return fmt.Sprintf("(%d) %s: %s", e.RejectCode, e.Message, e.ErrorCode)
}
56 changes: 28 additions & 28 deletions clients/registry/proto/v1/operator.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions gen/generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"io/fs"
"strings"
"text/template"
"unicode"

"github.com/aviate-labs/agent-go/candid/did"
)
Expand Down Expand Up @@ -84,6 +85,9 @@ func NewGenerator(agentName, canisterName, packageName string, rawDID []rune) (*
if err != nil {
return nil, err
}
if rs := []rune(agentName); unicode.IsLower(rs[0]) {
agentName = strings.ToUpper(agentName[:1]) + agentName[1:]
}
return &Generator{
AgentName: agentName,
CanisterName: canisterName,
Expand Down
Loading