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

Fix transaction submission when handling large transaction payloads #3643

Merged
merged 5 commits into from
Jun 1, 2021
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
1 change: 1 addition & 0 deletions clients/horizonclient/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ file. This project adheres to [Semantic Versioning](http://semver.org/).
## Unreleased

* Added transaction and operation result codes to the horizonclient.Error string for easy glancing at string only errors for underlying cause.
* Fix bug in the transaction submission where requests with large transaction payloads fail with an HTTP 414 URI Too Long error ([#3643](https://github.com/stellar/go/pull/3643)).

## [v7.0.0](https://github.com/stellar/go/releases/tag/horizonclient-v7.0.0) - 2021-05-15

Expand Down
11 changes: 11 additions & 0 deletions clients/horizonclient/account_request.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package horizonclient

import (
"fmt"
"net/http"
"net/url"

"github.com/stellar/go/support/errors"
Expand Down Expand Up @@ -46,3 +47,13 @@ func (ar AccountRequest) BuildURL() (endpoint string, err error) {

return endpoint, err
}

// HTTPRequest returns the http request for the account endpoint
func (ar AccountRequest) HTTPRequest(horizonURL string) (*http.Request, error) {
endpoint, err := ar.BuildURL()
if err != nil {
return nil, err
}

return http.NewRequest("GET", horizonURL+endpoint, nil)
}
11 changes: 11 additions & 0 deletions clients/horizonclient/accounts_request.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package horizonclient

import (
"fmt"
"net/http"
"net/url"

"github.com/stellar/go/support/errors"
Expand Down Expand Up @@ -54,3 +55,13 @@ func (r AccountsRequest) BuildURL() (endpoint string, err error) {

return endpoint, err
}

// HTTPRequest returns the http request for the accounts endpoint
func (r AccountsRequest) HTTPRequest(horizonURL string) (*http.Request, error) {
endpoint, err := r.BuildURL()
if err != nil {
return nil, err
}

return http.NewRequest("GET", horizonURL+endpoint, nil)
}
11 changes: 11 additions & 0 deletions clients/horizonclient/asset_request.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package horizonclient

import (
"fmt"
"net/http"
"net/url"

"github.com/stellar/go/support/errors"
Expand All @@ -27,3 +28,13 @@ func (ar AssetRequest) BuildURL() (endpoint string, err error) {

return endpoint, err
}

// HTTPRequest returns the http request for the assets endpoint
func (ar AssetRequest) HTTPRequest(horizonURL string) (*http.Request, error) {
endpoint, err := ar.BuildURL()
if err != nil {
return nil, err
}

return http.NewRequest("GET", horizonURL+endpoint, nil)
}
11 changes: 11 additions & 0 deletions clients/horizonclient/claimable_balance_request.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package horizonclient

import (
"fmt"
"net/http"
"net/url"

"github.com/stellar/go/support/errors"
Expand Down Expand Up @@ -40,3 +41,13 @@ func (cbr ClaimableBalanceRequest) BuildURL() (endpoint string, err error) {

return endpoint, err
}

// HTTPRequest returns the http request for the claimable balances endpoint
func (cbr ClaimableBalanceRequest) HTTPRequest(horizonURL string) (*http.Request, error) {
endpoint, err := cbr.BuildURL()
if err != nil {
return nil, err
}

return http.NewRequest("GET", horizonURL+endpoint, nil)
}
84 changes: 37 additions & 47 deletions clients/horizonclient/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,18 +26,13 @@ import (

// sendRequest builds the URL for the given horizon request and sends the url to a horizon server
func (c *Client) sendRequest(hr HorizonRequest, resp interface{}) (err error) {
endpoint, err := hr.BuildURL()
if err != nil {
return
}

c.HorizonURL = c.fixHorizonURL()
_, ok := hr.(submitRequest)
if ok {
return c.sendRequestURL(c.HorizonURL+endpoint, "post", resp)
req, err := hr.HTTPRequest(c.HorizonURL)
if err != nil {
return err
}

return c.sendRequestURL(c.HorizonURL+endpoint, "get", resp)
return c.sendHTTPRequest(req, resp)
}

// checkMemoRequired implements a memo required check as defined in
Expand Down Expand Up @@ -105,36 +100,31 @@ func (c *Client) checkMemoRequired(transaction *txnbuild.Transaction) error {
return nil
}

// sendRequestURL sends a url to a horizon server.
// sendGetRequest sends a HTTP GET request to a horizon server.
// It can be used for requests that do not implement the HorizonRequest interface.
func (c *Client) sendRequestURL(requestURL string, method string, a interface{}) (err error) {
var req *http.Request

if method == "post" || method == "POST" {
req, err = http.NewRequest("POST", requestURL, nil)
req.Header.Set("Content-Type", "application/x-www-form-urlencoded; param=value")
} else {
req, err = http.NewRequest("GET", requestURL, nil)
}

func (c *Client) sendGetRequest(requestURL string, a interface{}) error {
req, err := http.NewRequest("GET", requestURL, nil)
if err != nil {
return errors.Wrap(err, "error creating HTTP request")
}
return c.sendHTTPRequest(req, a)
}

func (c *Client) sendHTTPRequest(req *http.Request, a interface{}) error {
c.setClientAppHeaders(req)
c.setDefaultClient()

if c.horizonTimeout == 0 {
c.horizonTimeout = HorizonTimeout
}
ctx, cancel := context.WithTimeout(context.Background(), c.horizonTimeout)
resp, err := c.HTTP.Do(req.WithContext(ctx))
if err != nil {
cancel()
return
}
defer cancel()

err = decodeResponse(resp, &a, c)
cancel()
return
if resp, err := c.HTTP.Do(req.WithContext(ctx)); err != nil {
return err
} else {
return decodeResponse(resp, &a, c)
}
}

// stream handles connections to endpoints that support streaming on a horizon server
Expand Down Expand Up @@ -587,7 +577,7 @@ func (c *Client) Fund(addr string) (tx hProtocol.Transaction, err error) {
return tx, errors.New("can't fund account from friendbot on production network")
}
friendbotURL := fmt.Sprintf("%sfriendbot?addr=%s", c.fixHorizonURL(), addr)
err = c.sendRequestURL(friendbotURL, "get", &tx)
err = c.sendGetRequest(friendbotURL, &tx)
return
}

Expand Down Expand Up @@ -678,7 +668,7 @@ func (c *Client) FetchTimebounds(seconds int64) (txnbuild.Timebounds, error) {

// Root loads the root endpoint of horizon
func (c *Client) Root() (root hProtocol.Root, err error) {
err = c.sendRequestURL(c.fixHorizonURL(), "get", &root)
err = c.sendGetRequest(c.fixHorizonURL(), &root)
return
}

Expand All @@ -689,67 +679,67 @@ func (c *Client) Version() string {

// NextAccountsPage returns the next page of accounts.
func (c *Client) NextAccountsPage(page hProtocol.AccountsPage) (accounts hProtocol.AccountsPage, err error) {
err = c.sendRequestURL(page.Links.Next.Href, "get", &accounts)
err = c.sendGetRequest(page.Links.Next.Href, &accounts)
return
}

// NextAssetsPage returns the next page of assets.
func (c *Client) NextAssetsPage(page hProtocol.AssetsPage) (assets hProtocol.AssetsPage, err error) {
err = c.sendRequestURL(page.Links.Next.Href, "get", &assets)
err = c.sendGetRequest(page.Links.Next.Href, &assets)
return
}

// PrevAssetsPage returns the previous page of assets.
func (c *Client) PrevAssetsPage(page hProtocol.AssetsPage) (assets hProtocol.AssetsPage, err error) {
err = c.sendRequestURL(page.Links.Prev.Href, "get", &assets)
err = c.sendGetRequest(page.Links.Prev.Href, &assets)
return
}

// NextLedgersPage returns the next page of ledgers.
func (c *Client) NextLedgersPage(page hProtocol.LedgersPage) (ledgers hProtocol.LedgersPage, err error) {
err = c.sendRequestURL(page.Links.Next.Href, "get", &ledgers)
err = c.sendGetRequest(page.Links.Next.Href, &ledgers)
return
}

// PrevLedgersPage returns the previous page of ledgers.
func (c *Client) PrevLedgersPage(page hProtocol.LedgersPage) (ledgers hProtocol.LedgersPage, err error) {
err = c.sendRequestURL(page.Links.Prev.Href, "get", &ledgers)
err = c.sendGetRequest(page.Links.Prev.Href, &ledgers)
return
}

// NextEffectsPage returns the next page of effects.
func (c *Client) NextEffectsPage(page effects.EffectsPage) (efp effects.EffectsPage, err error) {
err = c.sendRequestURL(page.Links.Next.Href, "get", &efp)
err = c.sendGetRequest(page.Links.Next.Href, &efp)
return
}

// PrevEffectsPage returns the previous page of effects.
func (c *Client) PrevEffectsPage(page effects.EffectsPage) (efp effects.EffectsPage, err error) {
err = c.sendRequestURL(page.Links.Prev.Href, "get", &efp)
err = c.sendGetRequest(page.Links.Prev.Href, &efp)
return
}

// NextTransactionsPage returns the next page of transactions.
func (c *Client) NextTransactionsPage(page hProtocol.TransactionsPage) (transactions hProtocol.TransactionsPage, err error) {
err = c.sendRequestURL(page.Links.Next.Href, "get", &transactions)
err = c.sendGetRequest(page.Links.Next.Href, &transactions)
return
}

// PrevTransactionsPage returns the previous page of transactions.
func (c *Client) PrevTransactionsPage(page hProtocol.TransactionsPage) (transactions hProtocol.TransactionsPage, err error) {
err = c.sendRequestURL(page.Links.Prev.Href, "get", &transactions)
err = c.sendGetRequest(page.Links.Prev.Href, &transactions)
return
}

// NextOperationsPage returns the next page of operations.
func (c *Client) NextOperationsPage(page operations.OperationsPage) (operations operations.OperationsPage, err error) {
err = c.sendRequestURL(page.Links.Next.Href, "get", &operations)
err = c.sendGetRequest(page.Links.Next.Href, &operations)
return
}

// PrevOperationsPage returns the previous page of operations.
func (c *Client) PrevOperationsPage(page operations.OperationsPage) (operations operations.OperationsPage, err error) {
err = c.sendRequestURL(page.Links.Prev.Href, "get", &operations)
err = c.sendGetRequest(page.Links.Prev.Href, &operations)
return
}

Expand All @@ -765,25 +755,25 @@ func (c *Client) PrevPaymentsPage(page operations.OperationsPage) (operations.Op

// NextOffersPage returns the next page of offers.
func (c *Client) NextOffersPage(page hProtocol.OffersPage) (offers hProtocol.OffersPage, err error) {
err = c.sendRequestURL(page.Links.Next.Href, "get", &offers)
err = c.sendGetRequest(page.Links.Next.Href, &offers)
return
}

// PrevOffersPage returns the previous page of offers.
func (c *Client) PrevOffersPage(page hProtocol.OffersPage) (offers hProtocol.OffersPage, err error) {
err = c.sendRequestURL(page.Links.Prev.Href, "get", &offers)
err = c.sendGetRequest(page.Links.Prev.Href, &offers)
return
}

// NextTradesPage returns the next page of trades.
func (c *Client) NextTradesPage(page hProtocol.TradesPage) (trades hProtocol.TradesPage, err error) {
err = c.sendRequestURL(page.Links.Next.Href, "get", &trades)
err = c.sendGetRequest(page.Links.Next.Href, &trades)
return
}

// PrevTradesPage returns the previous page of trades.
func (c *Client) PrevTradesPage(page hProtocol.TradesPage) (trades hProtocol.TradesPage, err error) {
err = c.sendRequestURL(page.Links.Prev.Href, "get", &trades)
err = c.sendGetRequest(page.Links.Prev.Href, &trades)
return
}

Expand All @@ -804,14 +794,14 @@ func (c *Client) HomeDomainForAccount(aid string) (string, error) {
// NextTradeAggregationsPage returns the next page of trade aggregations from the current
// trade aggregations response.
func (c *Client) NextTradeAggregationsPage(page hProtocol.TradeAggregationsPage) (ta hProtocol.TradeAggregationsPage, err error) {
err = c.sendRequestURL(page.Links.Next.Href, "get", &ta)
err = c.sendGetRequest(page.Links.Next.Href, &ta)
return
}

// PrevTradeAggregationsPage returns the previous page of trade aggregations from the current
// trade aggregations response.
func (c *Client) PrevTradeAggregationsPage(page hProtocol.TradeAggregationsPage) (ta hProtocol.TradeAggregationsPage, err error) {
err = c.sendRequestURL(page.Links.Prev.Href, "get", &ta)
err = c.sendGetRequest(page.Links.Prev.Href, &ta)
return
}

Expand Down
11 changes: 11 additions & 0 deletions clients/horizonclient/effect_request.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"encoding/json"
"fmt"
"net/http"
"net/url"

"github.com/stellar/go/protocols/horizon/effects"
Expand Down Expand Up @@ -80,3 +81,13 @@ func (er EffectRequest) StreamEffects(ctx context.Context, client *Client, handl
return nil
})
}

// HTTPRequest returns the http request for the effects endpoint
func (er EffectRequest) HTTPRequest(horizonURL string) (*http.Request, error) {
endpoint, err := er.BuildURL()
if err != nil {
return nil, err
}

return http.NewRequest("GET", horizonURL+endpoint, nil)
}
15 changes: 14 additions & 1 deletion clients/horizonclient/fee_stats_request.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package horizonclient

import "github.com/stellar/go/support/errors"
import (
"github.com/stellar/go/support/errors"
"net/http"
)

// BuildURL returns the url for getting fee stats about a running horizon instance
func (fr feeStatsRequest) BuildURL() (endpoint string, err error) {
Expand All @@ -11,3 +14,13 @@ func (fr feeStatsRequest) BuildURL() (endpoint string, err error) {

return
}

// HTTPRequest returns the http request for the fee stats endpoint
func (fr feeStatsRequest) HTTPRequest(horizonURL string) (*http.Request, error) {
endpoint, err := fr.BuildURL()
if err != nil {
return nil, err
}

return http.NewRequest("GET", horizonURL+endpoint, nil)
}
11 changes: 11 additions & 0 deletions clients/horizonclient/ledger_request.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"encoding/json"
"fmt"
"net/http"
"net/url"

hProtocol "github.com/stellar/go/protocols/horizon"
Expand Down Expand Up @@ -40,6 +41,16 @@ func (lr LedgerRequest) BuildURL() (endpoint string, err error) {
return endpoint, err
}

// HTTPRequest returns the http request for the ledger endpoint
func (lr LedgerRequest) HTTPRequest(horizonURL string) (*http.Request, error) {
endpoint, err := lr.BuildURL()
if err != nil {
return nil, err
}

return http.NewRequest("GET", horizonURL+endpoint, nil)
}

// LedgerHandler is a function that is called when a new ledger is received
type LedgerHandler func(hProtocol.Ledger)

Expand Down
2 changes: 2 additions & 0 deletions clients/horizonclient/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -225,8 +225,10 @@ var DefaultPublicNetClient = &Client{
}

// HorizonRequest contains methods implemented by request structs for horizon endpoints.
// Action needed in release: horizonclient-v8.0.0: remove BuildURL()
type HorizonRequest interface {
BuildURL() (string, error)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is BuildURL needed anymore?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BuildURL is not needed anymore but I wanted to avoid a major version bump

HTTPRequest(horizonURL string) (*http.Request, error)
}

// AccountsRequest struct contains data for making requests to the accounts endpoint of a horizon server.
Expand Down
Loading