Compare commits

..

10 Commits

Author SHA1 Message Date
3f3c3a4471 [v3] fix panic on publish methods (#167)
Some checks failed
coverage / build (push) Successful in 3m8s
test / test (push) Failing after 18m47s
* move publish methods to a separate file
* reorder client methods
* fix panic for publish
* refactoring
* go mod tidy
* rename go.micro => micro
* add comment to README about Set-Cookie headers
2025-10-29 09:20:04 +03:00
vtolstov
cee0fde959 Apply Code Coverage Badge 2025-10-18 07:45:28 +00:00
b122fed3cc add compile-time interface compliance check
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2025-10-18 07:45:28 +00:00
vtolstov
fa00663740 Apply Code Coverage Badge 2025-10-17 08:10:31 +00:00
905398dcb6 [v3] improve logs (#166)
* improve logs
2025-10-17 11:08:02 +03:00
a6dd9c0455 fix metrics (#164)
Some checks failed
coverage / build (push) Successful in 2m7s
test / test (push) Failing after 15m22s
2025-10-17 11:03:29 +03:00
c757127453 integrate request builder into HTTP client for googleapis support (#159)
All checks were successful
coverage / build (push) Successful in 2m19s
test / test (push) Successful in 2m27s
2025-10-03 08:39:44 +03:00
vtolstov
b2b24e0a9a Apply Code Coverage Badge 2025-05-05 16:29:54 +00:00
ea64ce0eab [v3] update ci (#151)
All checks were successful
coverage / build (push) Successful in 2m49s
test / test (push) Successful in 20m0s
* update ci

* cleanup
2025-05-05 19:25:08 +03:00
b89246b18c [v3] rename .gitea to .github (#149)
All checks were successful
test / test (push) Successful in 3m16s
* rename .gitea to .github
* attempt to fix lint/test job
2025-04-27 22:32:49 +03:00
20 changed files with 424 additions and 275 deletions

View File

@@ -1,28 +0,0 @@
name: "autoapprove"
on:
pull_request_target:
types: [assigned, opened, synchronize, reopened]
workflow_run:
workflows: ["prbuild"]
types:
- completed
permissions:
pull-requests: write
contents: write
jobs:
autoapprove:
runs-on: ubuntu-latest
steps:
- name: approve
run: [ "curl -o tea https://dl.gitea.com/tea/main/tea-main-linux-amd64",
"chmod +x ./tea",
"./tea login add --name unistack --token ${{ secrets.GITHUB_TOKEN }} --url https://git.unistack.org",
"./tea pr --repo ${{ github.event.repository.name }}"
]
if: github.actor == 'vtolstov'
id: approve
with:
github-token: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -1,94 +0,0 @@
name: sync
on:
schedule:
- cron: '*/5 * * * *'
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
jobs:
sync:
if: github.server_url != 'https://github.com'
runs-on: ubuntu-latest
steps:
- name: init
run: |
git config --global user.email "vtolstov <vtolstov@users.noreply.github.com>"
git config --global user.name "github-actions[bot]"
echo "machine git.unistack.org login vtolstov password ${{ secrets.TOKEN_GITEA }}" >> /root/.netrc
echo "machine github.com login vtolstov password ${{ secrets.TOKEN_GITHUB }}" >> /root/.netrc
- name: check master
id: check_master
run: |
src_hash=$(git ls-remote https://github.com/${GITHUB_REPOSITORY} refs/heads/master | cut -f1)
dst_hash=$(git ls-remote ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY} refs/heads/master | cut -f1)
echo "src_hash=$src_hash"
echo "dst_hash=$dst_hash"
if [ "$src_hash" != "$dst_hash" ]; then
echo "sync_needed=true" >> $GITHUB_OUTPUT
else
echo "sync_needed=false" >> $GITHUB_OUTPUT
fi
- name: sync master
if: steps.check_master.outputs.sync_needed == 'true'
run: |
git clone --filter=blob:none --filter=tree:0 --branch master --single-branch ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY} repo
cd repo
git remote add --no-tags --fetch --track master upstream https://github.com/${GITHUB_REPOSITORY}
git pull --rebase upstream master
git push upstream master --progress
git push origin master --progress
cd ../
rm -rf repo
- name: check v3
id: check_v3
run: |
src_hash=$(git ls-remote https://github.com/${GITHUB_REPOSITORY} refs/heads/v3 | cut -f1)
dst_hash=$(git ls-remote ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY} refs/heads/v3 | cut -f1)
echo "src_hash=$src_hash"
echo "dst_hash=$dst_hash"
if [ "$src_hash" != "$dst_hash" ]; then
echo "sync_needed=true" >> $GITHUB_OUTPUT
else
echo "sync_needed=false" >> $GITHUB_OUTPUT
fi
- name: sync v3
if: steps.check_v3.outputs.sync_needed == 'true'
run: |
git clone --filter=blob:none --filter=tree:0 --branch v3 --single-branch ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY} repo
cd repo
git remote add --no-tags --fetch --track v3 upstream https://github.com/${GITHUB_REPOSITORY}
git pull --rebase upstream v3
git push upstream v3 --progress
git push origin v3 --progress
cd ../
rm -rf repo
- name: check v4
id: check_v4
run: |
src_hash=$(git ls-remote https://github.com/${GITHUB_REPOSITORY} refs/heads/v4 | cut -f1)
dst_hash=$(git ls-remote ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY} refs/heads/v4 | cut -f1)
echo "src_hash=$src_hash"
echo "dst_hash=$dst_hash"
if [ "$src_hash" != "$dst_hash" ]; then
echo "sync_needed=true" >> $GITHUB_OUTPUT
else
echo "sync_needed=false" >> $GITHUB_OUTPUT
fi
- name: sync v4
if: steps.check_v4.outputs.sync_needed == 'true'
run: |
git clone --filter=blob:none --filter=tree:0 --branch v4 --single-branch ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY} repo
cd repo
git remote add --no-tags --fetch --track v4 upstream https://github.com/${GITHUB_REPOSITORY}
git pull --rebase upstream v4
git push upstream v4 --progress
git push origin v4 --progress
cd ../
rm -rf repo

View File

@@ -1,8 +1,8 @@
# HTTP Client
![Coverage](https://img.shields.io/badge/Coverage-42.4%25-yellow)
![Coverage](https://img.shields.io/badge/Coverage-41.7%25-yellow)
This plugin is an HTTP client for [Micro](https://pkg.go.dev/go.unistack.org/micro/v4).
It implements the [micro.Client](https://pkg.go.dev/go.unistack.org/micro/v4/client#Client) interface.
This plugin is an HTTP client for [Micro](https://pkg.go.dev/go.unistack.org/micro/v3).
It implements the [micro.Client](https://pkg.go.dev/go.unistack.org/micro/v3/client#Client) interface.
## Overview
@@ -13,13 +13,14 @@ implements HTTP rules defined in the [google/api/http.proto](https://github.com/
* Streaming is not yet implemented.
* Only protobuf-generated messages are supported.
* In `micro/v3`, metadata is implemented as `map[string]string`, which works for most headers but not for multiple `Set-Cookie` headers. The HTTP specification forbids the use of commas in `Set-Cookie` headers; therefore, their values cannot be parsed reliably. In `micro/v4`, metadata uses `map[string][]string`, resolving this issue.
## Usage
```go
import (
"go.unistack.org/micro/v4"
http "go.unistack.org/micro-client-http/v4"
"go.unistack.org/micro/v3"
http "go.unistack.org/micro-client-http/v3"
)
service := micro.NewService(
@@ -32,9 +33,9 @@ service := micro.NewService(
```go
import (
"go.unistack.org/micro/v4/client"
http "go.unistack.org/micro-client-http/v4"
jsoncodec "go.unistack.org/micro-codec-json/v4"
"go.unistack.org/micro/v3/client"
http "go.unistack.org/micro-client-http/v3"
jsoncodec "go.unistack.org/micro-codec-json/v3"
)
c := http.NewClient(
@@ -60,8 +61,8 @@ err := c.Call(
```go
import (
"go.unistack.org/micro/v4/client"
http "go.unistack.org/micro-client-http/v4"
"go.unistack.org/micro/v3/client"
http "go.unistack.org/micro-client-http/v3"
)
err := c.Call(
@@ -79,8 +80,8 @@ err := c.Call(
```go
import (
"go.unistack.org/micro/v4/metadata"
http "go.unistack.org/micro-client-http/v4"
"go.unistack.org/micro/v3/metadata"
http "go.unistack.org/micro-client-http/v3"
)
ctx := metadata.NewOutgoingContext(ctx, metadata.Pairs(
@@ -100,8 +101,8 @@ err := c.Call(
```go
import (
"go.unistack.org/micro/v4/metadata"
http "go.unistack.org/micro-client-http/v4"
"go.unistack.org/micro/v3/metadata"
http "go.unistack.org/micro-client-http/v3"
)
respMetadata := metadata.Metadata{}
@@ -118,8 +119,8 @@ err := c.Call(
```go
import (
"go.unistack.org/micro/v4/metadata"
http "go.unistack.org/micro-client-http/v4"
"go.unistack.org/micro/v3/metadata"
http "go.unistack.org/micro-client-http/v3"
)
ctx := metadata.NewOutgoingContext(context.Background(), metadata.Pairs(
@@ -138,9 +139,9 @@ err := c.Call(
```go
import (
http "go.unistack.org/micro-client-http/v4"
status "go.unistack.org/micro-client-http/v4/status"
jsoncodec "go.unistack.org/micro-codec-json/v4"
http "go.unistack.org/micro-client-http/v3"
status "go.unistack.org/micro-client-http/v3/status"
jsoncodec "go.unistack.org/micro-codec-json/v3"
)
err := c.Call(

View File

@@ -3745,7 +3745,7 @@ const file_test_messages_proto_rawDesc = "" +
"\fSpecialError\x12\x12\n" +
"\x04code\x18\x01 \x01(\tR\x04code\x12\x10\n" +
"\x03msg\x18\x02 \x01(\tR\x03msg\x12\x18\n" +
"\awarning\x18\x03 \x01(\tR\awarningB2Z0go.unistack.org/micro-client-http/v4/proto;protob\x06proto3"
"\awarning\x18\x03 \x01(\tR\awarningB2Z0go.unistack.org/micro-client-http/v3/proto;protob\x06proto3"
var (
file_test_messages_proto_rawDescOnce sync.Once

View File

@@ -2,7 +2,7 @@ syntax = "proto3";
package proto;
option go_package = "go.unistack.org/micro-client-http/v4/proto;proto";
option go_package = "go.unistack.org/micro-client-http/v3/proto;proto";
message TestRequestBuilder {}

View File

@@ -9,7 +9,7 @@ import (
"google.golang.org/protobuf/proto"
pb "go.unistack.org/micro-client-http/v4/builder/proto"
pb "go.unistack.org/micro-client-http/v3/builder/proto"
)
// sink prevents the compiler from optimizing away parsePathTemplate results.

View File

@@ -6,8 +6,8 @@ import (
"github.com/stretchr/testify/require"
"google.golang.org/protobuf/proto"
"go.unistack.org/micro-client-http/v4/builder"
pb "go.unistack.org/micro-client-http/v4/builder/proto"
"go.unistack.org/micro-client-http/v3/builder"
pb "go.unistack.org/micro-client-http/v3/builder/proto"
)
func TestNewRequestBuilder(t *testing.T) {

View File

@@ -7,21 +7,27 @@ import (
"sync"
"time"
"go.unistack.org/micro/v4/client"
"go.unistack.org/micro/v4/errors"
"go.unistack.org/micro/v4/options"
"go.unistack.org/micro/v4/semconv"
"go.unistack.org/micro/v4/tracer"
"go.unistack.org/micro/v3/client"
"go.unistack.org/micro/v3/errors"
"go.unistack.org/micro/v3/options"
"go.unistack.org/micro/v3/semconv"
"go.unistack.org/micro/v3/tracer"
"go.unistack.org/micro-client-http/v3/status"
)
var _ client.Client = (*Client)(nil)
var DefaultContentType = "application/json"
type Client struct {
funcCall client.FuncCall
funcStream client.FuncStream
httpClient *http.Client
opts client.Options
mu sync.RWMutex
funcPublish client.FuncPublish
funcBatchPublish client.FuncBatchPublish
funcCall client.FuncCall
funcStream client.FuncStream
httpClient *http.Client
opts client.Options
mu sync.RWMutex
}
func NewClient(opts ...client.Option) *Client {
@@ -43,6 +49,8 @@ func NewClient(opts ...client.Option) *Client {
c.httpClient = defaultHTTPClient(dialer, clientOpts.TLSConfig)
}
c.funcPublish = c.fnPublish
c.funcBatchPublish = c.fnBatchPublish
c.funcCall = c.fnCall
c.funcStream = c.fnStream
@@ -64,6 +72,10 @@ func (c *Client) Init(opts ...client.Option) error {
c.funcCall = h(c.funcCall)
case client.HookStream:
c.funcStream = h(c.funcStream)
case client.HookPublish:
c.funcPublish = h(c.funcPublish)
case client.HookBatchPublish:
c.funcBatchPublish = h(c.funcBatchPublish)
}
})
@@ -74,6 +86,20 @@ func (c *Client) Options() client.Options {
return c.opts
}
func (c *Client) NewMessage(topic string, msg interface{}, opts ...client.MessageOption) client.Message {
msgOpts := client.NewMessageOptions(opts...)
if msgOpts.ContentType == "" {
msgOpts.ContentType = c.opts.ContentType
}
return &httpMessage{
topic: topic,
payload: msg,
opts: msgOpts,
}
}
func (c *Client) NewRequest(service, method string, req any, opts ...client.RequestOption) client.Request {
reqOpts := client.NewRequestOptions(opts...)
if reqOpts.ContentType == "" {
@@ -91,25 +117,45 @@ func (c *Client) NewRequest(service, method string, req any, opts ...client.Requ
func (c *Client) Call(ctx context.Context, req client.Request, rsp any, opts ...client.CallOption) error {
ts := time.Now()
c.opts.Meter.Counter(semconv.ClientRequestInflight, "endpoint", req.Endpoint()).Inc()
var sp tracer.Span
ctx, sp = c.opts.Tracer.Start(ctx, req.Endpoint()+" rpc-client",
tracer.WithSpanKind(tracer.SpanKindClient),
tracer.WithSpanLabels("endpoint", req.Endpoint()),
)
defer sp.Finish()
err := c.funcCall(ctx, req, rsp, opts...)
c.opts.Meter.Counter(semconv.ClientRequestInflight, "endpoint", req.Endpoint()).Dec()
te := time.Since(ts)
c.opts.Meter.Summary(semconv.ClientRequestLatencyMicroseconds, "endpoint", req.Endpoint()).Update(te.Seconds())
c.opts.Meter.Histogram(semconv.ClientRequestDurationSeconds, "endpoint", req.Endpoint()).Update(te.Seconds())
if me := errors.FromError(err); me == nil {
sp.Finish()
c.opts.Meter.Counter(semconv.ClientRequestTotal, "endpoint", req.Endpoint(), "status", "success", "code", strconv.Itoa(int(200))).Inc()
} else {
var (
statusCode int
statusLabel string
)
if err == nil {
statusCode = http.StatusOK
statusLabel = "success"
} else if st, ok := status.FromError(err); ok {
statusCode = st.Code()
statusLabel = "failure"
sp.SetStatus(tracer.SpanStatusError, err.Error())
} else if me := errors.FromError(err); me != nil {
statusCode = int(me.Code)
statusLabel = "failure"
sp.SetStatus(tracer.SpanStatusError, err.Error())
} else {
statusCode = http.StatusInternalServerError
statusLabel = "failure"
sp.SetStatus(tracer.SpanStatusError, err.Error())
c.opts.Meter.Counter(semconv.ClientRequestTotal, "endpoint", req.Endpoint(), "status", "failure", "code", strconv.Itoa(int(me.Code))).Inc()
}
c.opts.Meter.Counter(semconv.ClientRequestTotal, "endpoint", req.Endpoint(), "status", statusLabel, "code", strconv.Itoa(statusCode)).Inc()
return err
}
@@ -117,6 +163,14 @@ func (c *Client) Stream(ctx context.Context, req client.Request, opts ...client.
return c.funcStream(ctx, req, opts...)
}
func (c *Client) Publish(ctx context.Context, p client.Message, opts ...client.PublishOption) error {
return c.funcPublish(ctx, p, opts...)
}
func (c *Client) BatchPublish(ctx context.Context, ps []client.Message, opts ...client.PublishOption) error {
return c.funcBatchPublish(ctx, ps, opts...)
}
func (c *Client) String() string {
return "http"
}

View File

@@ -11,14 +11,14 @@ import (
"strings"
"time"
"go.unistack.org/micro/v4/client"
"go.unistack.org/micro/v4/codec"
"go.unistack.org/micro/v4/logger"
"go.unistack.org/micro/v4/metadata"
"go.unistack.org/micro/v3/client"
"go.unistack.org/micro/v3/codec"
"go.unistack.org/micro/v3/logger"
"go.unistack.org/micro/v3/metadata"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/reflect/protoreflect"
"go.unistack.org/micro-client-http/v4/builder"
"go.unistack.org/micro-client-http/v3/builder"
)
func buildHTTPRequest(
@@ -119,10 +119,23 @@ func buildHTTPRequest(
}
if log.V(logger.DebugLevel) {
log.Debug(
ctx,
fmt.Sprintf("request %s to %s with headers %v body %s", method, u.String(), hreq.Header, body),
)
if shouldLogBody(ct) {
log.Debug(
ctx,
fmt.Sprintf(
"micro.client http request: method=%s url=%s headers=%v body=%s",
method, u.String(), hreq.Header, body,
),
)
} else {
log.Debug(
ctx,
fmt.Sprintf(
"micro.client http request: method=%s url=%s headers=%v",
method, u.String(), hreq.Header,
),
)
}
}
return hreq, nil
@@ -205,7 +218,7 @@ func setHeadersAndCookies(ctx context.Context, r *http.Request, ct string, opts
applyCookies(r, v)
continue
}
r.Header[k] = append(r.Header[k], v...)
r.Header[k] = append(r.Header[k], v)
}
}
@@ -215,20 +228,18 @@ func setHeadersAndCookies(ctx context.Context, r *http.Request, ct string, opts
applyCookies(r, v)
continue
}
r.Header[k] = append(r.Header[k], v...)
r.Header[k] = append(r.Header[k], v)
}
}
}
func applyCookies(r *http.Request, rawCookies []string) {
func applyCookies(r *http.Request, rawCookies string) {
if len(rawCookies) == 0 {
return
}
raw := strings.Join(rawCookies, "; ")
tmp := http.Request{Header: http.Header{}}
tmp.Header.Set("Cookie", raw)
tmp.Header.Set("Cookie", rawCookies)
for _, c := range tmp.Cookies() {
r.AddCookie(c)
@@ -261,3 +272,18 @@ func validateHeadersAndCookies(r *http.Request, parameters map[string]map[string
return nil
}
func shouldLogBody(contentType string) bool {
ct := strings.ToLower(strings.Split(contentType, ";")[0])
switch {
case strings.HasPrefix(ct, "text/"): // => text/html, text/plain, text/csv etc.
return true
case ct == "application/json",
ct == "application/xml",
ct == "application/x-www-form-urlencoded",
ct == "application/yaml":
return true
default:
return false
}
}

View File

@@ -6,10 +6,10 @@ import (
"testing"
"github.com/stretchr/testify/require"
jsoncodec "go.unistack.org/micro-codec-json/v4"
jsoncodec "go.unistack.org/micro-codec-json/v3"
"google.golang.org/protobuf/proto"
pb "go.unistack.org/micro-client-http/v4/builder/proto"
pb "go.unistack.org/micro-client-http/v3/builder/proto"
)
func TestJoinURL(t *testing.T) {
@@ -232,24 +232,24 @@ func TestMarshallMsg(t *testing.T) {
func TestApplyCookies(t *testing.T) {
tests := []struct {
name string
rawCookies []string
rawCookies string
want []*http.Cookie
}{
{
name: "empty",
rawCookies: []string{},
rawCookies: "",
want: []*http.Cookie{},
},
{
name: "single cookie",
rawCookies: []string{"session=abc123"},
rawCookies: "session=abc123",
want: []*http.Cookie{
{Name: "session", Value: "abc123"},
},
},
{
name: "multiple cookies separate items",
rawCookies: []string{"session=abc123", "user=john"},
rawCookies: "session=abc123; user=john",
want: []*http.Cookie{
{Name: "session", Value: "abc123"},
{Name: "user", Value: "john"},
@@ -257,7 +257,7 @@ func TestApplyCookies(t *testing.T) {
},
{
name: "multiple cookies in one item",
rawCookies: []string{"a=1; b=2"},
rawCookies: "a=1; b=2",
want: []*http.Cookie{
{Name: "a", Value: "1"},
{Name: "b", Value: "2"},
@@ -265,7 +265,7 @@ func TestApplyCookies(t *testing.T) {
},
{
name: "mix of combined and separate cookies",
rawCookies: []string{"a=1; b=2", "c=3"},
rawCookies: "a=1; b=2; c=3",
want: []*http.Cookie{
{Name: "a", Value: "1"},
{Name: "b", Value: "2"},
@@ -274,7 +274,7 @@ func TestApplyCookies(t *testing.T) {
},
{
name: "duplicate cookies",
rawCookies: []string{"session=abc123", "session=xyz"},
rawCookies: "session=abc123; session=xyz",
want: []*http.Cookie{
{Name: "session", Value: "abc123"},
{Name: "session", Value: "xyz"},
@@ -282,7 +282,7 @@ func TestApplyCookies(t *testing.T) {
},
{
name: "cookie with spaces",
rawCookies: []string{"token=abc 123"},
rawCookies: "token=abc 123",
want: []*http.Cookie{
{Name: "token", Value: "abc 123", Quoted: true},
},
@@ -399,3 +399,49 @@ func TestValidateHeadersAndCookies(t *testing.T) {
})
}
}
func TestShouldLogBody(t *testing.T) {
tests := []struct {
name string
contentType string
want bool
}{
// --- text/*
{"plain text", "text/plain", true},
{"html", "text/html", true},
{"csv", "text/csv", true},
{"yaml text", "text/yaml", true},
// --- application/*
{"json", "application/json", true},
{"xml", "application/xml", true},
{"form-urlencoded", "application/x-www-form-urlencoded", true},
{"yaml", "application/yaml", true},
// --- with parameters
{"json with charset", "application/json; charset=utf-8", true},
{"binary with charset", "application/octet-stream; charset=utf-8", false},
// --- binary
{"multipart form", "multipart/form-data", false},
{"binary stream", "application/octet-stream", false},
{"pdf", "application/pdf", false},
{"protobuf", "application/protobuf", false},
{"image", "image/png", false},
// --- edge cases
{"upper case type", "APPLICATION/JSON", true},
{"mixed case type", "Text/HTML", true},
{"unknown text prefix", "TEXT/FOO", true},
{"weird semicolon only", ";", false},
{"spaces only", " ", false},
{"empty content-type", "", false},
{"missing main type", "/plain", false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
require.Equal(t, tt.want, shouldLogBody(tt.contentType))
})
}
}

84
client_publish.go Normal file
View File

@@ -0,0 +1,84 @@
package http
import (
"context"
"os"
"go.unistack.org/micro/v3/broker"
"go.unistack.org/micro/v3/client"
"go.unistack.org/micro/v3/codec"
"go.unistack.org/micro/v3/errors"
"go.unistack.org/micro/v3/metadata"
)
func (c *Client) publish(ctx context.Context, ps []client.Message, opts ...client.PublishOption) error {
var body []byte
options := client.NewPublishOptions(opts...)
// get proxy
exchange := ""
if v, ok := os.LookupEnv("MICRO_PROXY"); ok {
exchange = v
}
// get the exchange
if len(options.Exchange) > 0 {
exchange = options.Exchange
}
msgs := make([]*broker.Message, 0, len(ps))
omd, ok := metadata.FromOutgoingContext(ctx)
if !ok {
omd = metadata.New(2)
}
for _, p := range ps {
md := metadata.Copy(omd)
topic := p.Topic()
if len(exchange) > 0 {
topic = exchange
}
md.Set(metadata.HeaderTopic, topic)
iter := p.Metadata().Iterator()
var k, v string
for iter.Next(&k, &v) {
md.Set(k, v)
}
md[metadata.HeaderContentType] = p.ContentType()
// passed in raw data
if d, ok := p.Payload().(*codec.Frame); ok {
body = d.Data
} else {
// use codec for payload
cf, err := c.newCodec(p.ContentType())
if err != nil {
return errors.InternalServerError("go.micro.client", "%+v", err)
}
// set the body
b, err := cf.Marshal(p.Payload())
if err != nil {
return errors.InternalServerError("go.micro.client", "%+v", err)
}
body = b
}
msgs = append(msgs, &broker.Message{Header: md, Body: body})
}
return c.opts.Broker.BatchPublish(
ctx,
msgs,
broker.PublishContext(options.Context),
broker.PublishBodyOnly(options.BodyOnly),
)
}
func (c *Client) fnPublish(ctx context.Context, p client.Message, opts ...client.PublishOption) error {
return c.publish(ctx, []client.Message{p}, opts...)
}
func (c *Client) fnBatchPublish(ctx context.Context, ps []client.Message, opts ...client.PublishOption) error {
return c.publish(ctx, ps, opts...)
}

View File

@@ -3,7 +3,7 @@ package http
import (
"context"
"go.unistack.org/micro/v4/client"
"go.unistack.org/micro/v3/client"
)
// TODO: Add stream support in the future.

View File

@@ -10,14 +10,14 @@ import (
"strings"
"time"
"go.unistack.org/micro/v4/client"
"go.unistack.org/micro/v4/codec"
"go.unistack.org/micro/v4/errors"
"go.unistack.org/micro/v4/logger"
"go.unistack.org/micro/v4/metadata"
"go.unistack.org/micro/v4/selector"
"go.unistack.org/micro/v3/client"
"go.unistack.org/micro/v3/codec"
"go.unistack.org/micro/v3/errors"
"go.unistack.org/micro/v3/logger"
"go.unistack.org/micro/v3/metadata"
"go.unistack.org/micro/v3/selector"
"go.unistack.org/micro-client-http/v4/status"
"go.unistack.org/micro-client-http/v3/status"
)
func (c *Client) fnCall(ctx context.Context, req client.Request, rsp any, opts ...client.CallOption) error {
@@ -207,8 +207,6 @@ func (c *Client) parseRsp(ctx context.Context, hrsp *http.Response, rsp any, opt
default:
}
var buf []byte
if opts.ResponseMetadata != nil {
for k, v := range hrsp.Header {
opts.ResponseMetadata.Set(k, strings.Join(v, ","))
@@ -224,6 +222,8 @@ func (c *Client) parseRsp(ctx context.Context, hrsp *http.Response, rsp any, opt
ct = htype
}
var buf []byte
if hrsp.Body != nil {
var err error
buf, err = io.ReadAll(hrsp.Body)
@@ -232,15 +232,31 @@ func (c *Client) parseRsp(ctx context.Context, hrsp *http.Response, rsp any, opt
}
}
if log.V(logger.DebugLevel) {
if shouldLogBody(ct) {
log.Debug(
ctx,
fmt.Sprintf(
"micro.client http response: status=%s headers=%v body=%s",
hrsp.Status, hrsp.Header, buf,
),
)
} else {
log.Debug(
ctx,
fmt.Sprintf(
"micro.client http response: status=%s headers=%v",
hrsp.Status, hrsp.Header,
),
)
}
}
cf, err := c.newCodec(ct)
if err != nil {
return errors.InternalServerError("go.micro.client", "unknown content-type %s: %v", ct, err)
}
if log.V(logger.DebugLevel) {
log.Debug(ctx, fmt.Sprintf("response with headers: %v and body: %s", hrsp.Header, buf))
}
if hrsp.StatusCode < http.StatusBadRequest {
if err = cf.Unmarshal(buf, rsp); err != nil {
return errors.InternalServerError("go.micro.client", "unmarshal response: %v", err)

View File

@@ -9,15 +9,15 @@ import (
"time"
"github.com/stretchr/testify/require"
jsoncodec "go.unistack.org/micro-codec-json/v4"
"go.unistack.org/micro/v4/client"
microerr "go.unistack.org/micro/v4/errors"
"go.unistack.org/micro/v4/metadata"
jsoncodec "go.unistack.org/micro-codec-json/v3"
"go.unistack.org/micro/v3/client"
microerr "go.unistack.org/micro/v3/errors"
"go.unistack.org/micro/v3/metadata"
"google.golang.org/protobuf/proto"
httpcli "go.unistack.org/micro-client-http/v4"
pb "go.unistack.org/micro-client-http/v4/builder/proto"
"go.unistack.org/micro-client-http/v4/status"
httpcli "go.unistack.org/micro-client-http/v3"
pb "go.unistack.org/micro-client-http/v3/builder/proto"
"go.unistack.org/micro-client-http/v3/status"
)
func TestClient_Call_Get(t *testing.T) {
@@ -130,9 +130,10 @@ func TestClient_Call_Get(t *testing.T) {
)
var (
ctx = metadata.NewOutgoingContext(
md, _ = metadata.Pairs("Authorization", "Bearer token", "My-Header", "My-Header-Value")
ctx = metadata.NewOutgoingContext(
context.Background(),
metadata.Pairs("Authorization", "Bearer token", "My-Header", "My-Header-Value"),
md,
)
rsp = &response{}
respMetadata = metadata.Metadata{}
@@ -162,8 +163,8 @@ func TestClient_Call_Get(t *testing.T) {
} else {
require.NoError(t, err)
require.True(t, proto.Equal(tt.wantRsp, rsp))
require.Equal(t, "application/json", respMetadata.GetJoined("Content-Type"))
require.Equal(t, "My-Header-Value", respMetadata.GetJoined("My-Header"))
require.Equal(t, "application/json", respMetadata.MustGet("Content-Type"))
require.Equal(t, "My-Header-Value", respMetadata.MustGet("My-Header"))
}
})
}
@@ -278,9 +279,10 @@ func TestClient_Call_Head(t *testing.T) {
)
var (
ctx = metadata.NewOutgoingContext(
md, _ = metadata.Pairs("Authorization", "Bearer token", "My-Header", "My-Header-Value")
ctx = metadata.NewOutgoingContext(
context.Background(),
metadata.Pairs("Authorization", "Bearer token", "My-Header", "My-Header-Value"),
md,
)
rsp = &response{}
respMetadata = metadata.Metadata{}
@@ -310,8 +312,8 @@ func TestClient_Call_Head(t *testing.T) {
} else {
require.NoError(t, err)
require.True(t, proto.Equal(tt.wantRsp, rsp))
require.Equal(t, "application/json", respMetadata.GetJoined("Content-Type"))
require.Equal(t, "My-Header-Value", respMetadata.GetJoined("My-Header"))
require.Equal(t, "application/json", respMetadata.MustGet("Content-Type"))
require.Equal(t, "My-Header-Value", respMetadata.MustGet("My-Header"))
}
})
}
@@ -462,9 +464,10 @@ func TestClient_Call_Post(t *testing.T) {
)
var (
ctx = metadata.NewOutgoingContext(
md, _ = metadata.Pairs("Authorization", "Bearer token", "My-Header", "My-Header-Value")
ctx = metadata.NewOutgoingContext(
context.Background(),
metadata.Pairs("Authorization", "Bearer token", "My-Header", "My-Header-Value"),
md,
)
rsp = &response{}
respMetadata = metadata.Metadata{}
@@ -494,8 +497,8 @@ func TestClient_Call_Post(t *testing.T) {
} else {
require.NoError(t, err)
require.True(t, proto.Equal(tt.wantRsp, rsp))
require.Equal(t, "application/json", respMetadata.GetJoined("Content-Type"))
require.Equal(t, "My-Header-Value", respMetadata.GetJoined("My-Header"))
require.Equal(t, "application/json", respMetadata.MustGet("Content-Type"))
require.Equal(t, "My-Header-Value", respMetadata.MustGet("My-Header"))
}
})
}
@@ -611,9 +614,10 @@ func TestClient_Call_Delete(t *testing.T) {
)
var (
ctx = metadata.NewOutgoingContext(
md, _ = metadata.Pairs("Authorization", "Bearer token", "My-Header", "My-Header-Value")
ctx = metadata.NewOutgoingContext(
context.Background(),
metadata.Pairs("Authorization", "Bearer token", "My-Header", "My-Header-Value"),
md,
)
rsp = &response{}
respMetadata = metadata.Metadata{}
@@ -643,8 +647,8 @@ func TestClient_Call_Delete(t *testing.T) {
} else {
require.NoError(t, err)
require.True(t, proto.Equal(tt.wantRsp, rsp))
require.Equal(t, "application/json", respMetadata.GetJoined("Content-Type"))
require.Equal(t, "My-Header-Value", respMetadata.GetJoined("My-Header"))
require.Equal(t, "application/json", respMetadata.MustGet("Content-Type"))
require.Equal(t, "My-Header-Value", respMetadata.MustGet("My-Header"))
}
})
}
@@ -767,9 +771,10 @@ func TestClient_Call_APIError_WithErrorsMap(t *testing.T) {
)
var (
ctx = metadata.NewOutgoingContext(
md, _ = metadata.Pairs("Authorization", "Bearer token", "My-Header", "My-Header-Value")
ctx = metadata.NewOutgoingContext(
context.Background(),
metadata.Pairs("Authorization", "Bearer token", "My-Header", "My-Header-Value"),
md,
)
req = &request{UserId: "123", OrderId: 456}
rsp = &response{}
@@ -803,8 +808,8 @@ func TestClient_Call_APIError_WithErrorsMap(t *testing.T) {
require.Equal(t, tt.expectedStatus, s)
require.Empty(t, rsp)
require.Equal(t, "application/json", respMetadata.GetJoined("Content-Type"))
require.Equal(t, "My-Header-Value", respMetadata.GetJoined("My-Header"))
require.Equal(t, "application/json", respMetadata.MustGet("Content-Type"))
require.Equal(t, "My-Header-Value", respMetadata.MustGet("My-Header"))
})
}
}
@@ -858,9 +863,10 @@ func TestClient_Call_APIError_WithoutErrorsMap(t *testing.T) {
)
var (
ctx = metadata.NewOutgoingContext(
md, _ = metadata.Pairs("Authorization", "Bearer token", "My-Header", "My-Header-Value")
ctx = metadata.NewOutgoingContext(
context.Background(),
metadata.Pairs("Authorization", "Bearer token", "My-Header", "My-Header-Value"),
md,
)
req = &request{UserId: "123", OrderId: 456}
rsp = &response{}
@@ -890,8 +896,8 @@ func TestClient_Call_APIError_WithoutErrorsMap(t *testing.T) {
require.Equal(t, expectedStatus, s)
require.Empty(t, rsp)
require.Equal(t, "application/json", respMetadata.GetJoined("Content-Type"))
require.Equal(t, "My-Header-Value", respMetadata.GetJoined("My-Header"))
require.Equal(t, "application/json", respMetadata.MustGet("Content-Type"))
require.Equal(t, "My-Header-Value", respMetadata.MustGet("My-Header"))
}
func TestClient_Call_HeadersAndCookies(t *testing.T) {
@@ -948,7 +954,8 @@ func TestClient_Call_HeadersAndCookies(t *testing.T) {
}))
},
prepareMetadata: func() metadata.Metadata {
return metadata.Pairs("Authorization", "Bearer token", "My-Header", "My-Header-Value")
md, _ := metadata.Pairs("Authorization", "Bearer token", "My-Header", "My-Header-Value")
return md
},
headerOption: httpcli.Header("Authorization", "true", "My-Header", "true"),
expectedRsp: &response{Id: "product-id", Name: "product-name"},
@@ -992,7 +999,8 @@ func TestClient_Call_HeadersAndCookies(t *testing.T) {
}))
},
prepareMetadata: func() metadata.Metadata {
return metadata.Pairs("Authorization", "Bearer token")
md, _ := metadata.Pairs("Authorization", "Bearer token")
return md
},
headerOption: httpcli.Header("Authorization", "true", "My-Header", "true"),
wantErr: true,
@@ -1035,7 +1043,8 @@ func TestClient_Call_HeadersAndCookies(t *testing.T) {
}))
},
prepareMetadata: func() metadata.Metadata {
return metadata.Pairs("Cookie", "session_id=abc123; theme=dark")
md, _ := metadata.Pairs("Cookie", "session_id=abc123; theme=dark")
return md
},
cookieOption: httpcli.Cookie("session_id", "true", "theme", "true"),
expectedRsp: &response{Id: "product-id", Name: "product-name"},
@@ -1078,7 +1087,8 @@ func TestClient_Call_HeadersAndCookies(t *testing.T) {
}))
},
prepareMetadata: func() metadata.Metadata {
return metadata.Pairs("Cookie", "session_id=abc123")
md, _ := metadata.Pairs("Cookie", "session_id=abc123")
return md
},
cookieOption: httpcli.Cookie("session_id", "true", "theme", "true"),
wantErr: true,
@@ -1123,11 +1133,12 @@ func TestClient_Call_HeadersAndCookies(t *testing.T) {
}))
},
prepareMetadata: func() metadata.Metadata {
return metadata.Pairs(
md, _ := metadata.Pairs(
"Authorization", "Bearer token",
"My-Header", "My-Header-Value",
"Cookie", "session_id=abc123; theme=dark",
)
return md
},
headerOption: httpcli.Header("Authorization", "true", "My-Header", "true"),
cookieOption: httpcli.Cookie("session_id", "true", "theme", "true"),
@@ -1179,8 +1190,8 @@ func TestClient_Call_HeadersAndCookies(t *testing.T) {
} else {
require.NoError(t, err)
require.True(t, proto.Equal(tt.expectedRsp, rsp))
require.Equal(t, "application/json", respMetadata.GetJoined("Content-Type"))
require.Equal(t, "My-Header-Value", respMetadata.GetJoined("My-Header"))
require.Equal(t, "application/json", respMetadata.MustGet("Content-Type"))
require.Equal(t, "My-Header-Value", respMetadata.MustGet("My-Header"))
}
})
}
@@ -1228,9 +1239,10 @@ func TestClient_Call_NoContent(t *testing.T) {
)
var (
ctx = metadata.NewOutgoingContext(
md, _ = metadata.Pairs("Authorization", "Bearer token", "My-Header", "My-Header-Value")
ctx = metadata.NewOutgoingContext(
context.Background(),
metadata.Pairs("Authorization", "Bearer token", "My-Header", "My-Header-Value"),
md,
)
req = &request{UserId: "123", OrderId: 456}
rsp = &response{}
@@ -1255,8 +1267,8 @@ func TestClient_Call_NoContent(t *testing.T) {
require.NoError(t, err)
require.Empty(t, rsp)
require.Equal(t, "application/json", respMetadata.GetJoined("Content-Type"))
require.Equal(t, "My-Header-Value", respMetadata.GetJoined("My-Header"))
require.Equal(t, "application/json", respMetadata.MustGet("Content-Type"))
require.Equal(t, "My-Header-Value", respMetadata.MustGet("My-Header"))
}
func TestClient_Call_RequestTimeoutError(t *testing.T) {

23
go.mod
View File

@@ -1,25 +1,26 @@
module go.unistack.org/micro-client-http/v4
module go.unistack.org/micro-client-http/v3
go 1.23.0
go 1.24.0
toolchain go1.24.2
toolchain go1.24.3
require (
github.com/stretchr/testify v1.11.1
go.unistack.org/micro-codec-json/v4 v4.1.0
go.unistack.org/micro/v4 v4.1.19
google.golang.org/protobuf v1.36.9
go.unistack.org/micro-codec-json/v3 v3.10.3
go.unistack.org/micro/v3 v3.11.48
google.golang.org/protobuf v1.36.10
)
require (
github.com/ash3in/uuidv8 v1.2.0 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/matoous/go-nanoid v1.5.1 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/spf13/cast v1.9.2 // indirect
go.unistack.org/micro-proto/v4 v4.1.0 // indirect
golang.org/x/sys v0.35.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250818200422-3122310a409c // indirect
google.golang.org/grpc v1.75.0 // indirect
go.unistack.org/micro-proto/v3 v3.4.1 // indirect
golang.org/x/sys v0.36.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250929231259-57b25ae835d4 // indirect
google.golang.org/grpc v1.75.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

39
go.sum
View File

@@ -1,7 +1,10 @@
github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU=
github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU=
github.com/ash3in/uuidv8 v1.2.0 h1:2oogGdtCPwaVtyvPPGin4TfZLtOGE5F+W++E880G6SI=
github.com/ash3in/uuidv8 v1.2.0/go.mod h1:BnU0wJBxnzdEKmVg4xckBkD+VZuecTFTUP3M0dWgyY4=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
@@ -14,32 +17,32 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/matoous/go-nanoid v1.5.1 h1:aCjdvTyO9LLnTIi0fgdXhOPPvOHjpXN6Ik9DaNjIct4=
github.com/matoous/go-nanoid v1.5.1/go.mod h1:zyD2a71IubI24efhpvkJz+ZwfwagzgSO6UNiFsZKN7U=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
github.com/spf13/cast v1.9.2 h1:SsGfm7M8QOFtEzumm7UZrZdLLquNdzFYfIbEXntcFbE=
github.com/spf13/cast v1.9.2/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
go.unistack.org/micro-codec-json/v4 v4.1.0 h1:iydeSkt3ee7IPU0dHHKlGN97lw+YFQasBk9rdv0woYA=
go.unistack.org/micro-codec-json/v4 v4.1.0/go.mod h1:aUg86elSlURSynTAetDAAXj/VzFDwwcg92QNrRzcvrM=
go.unistack.org/micro-proto/v4 v4.1.0 h1:qPwL2n/oqh9RE3RTTDgt28XK3QzV597VugQPaw9lKUk=
go.unistack.org/micro-proto/v4 v4.1.0/go.mod h1:ArmK7o+uFvxSY3dbJhKBBX4Pm1rhWdLEFf3LxBrMtec=
go.unistack.org/micro/v4 v4.1.19 h1:LKpmSPYvX5B9AkFD7JqMU/U06v5yEWn2bsCG/YKZtZI=
go.unistack.org/micro/v4 v4.1.19/go.mod h1:xleO2M5Yxh4s6I+RUcLrEpUjobefh+71ctrdIfn7TUs=
go.unistack.org/micro-codec-json/v3 v3.10.3 h1:FwSBfJswov30Dyqxp1XfQW1EG4h77uTEe/VGflg6XlY=
go.unistack.org/micro-codec-json/v3 v3.10.3/go.mod h1:26OK5MizMNKhspGC6PRVwpDIp5w1GmRb0nE5eRWWDxA=
go.unistack.org/micro-proto/v3 v3.4.1 h1:UTjLSRz2YZuaHk9iSlVqqsA50JQNAEK2ZFboGqtEa9Q=
go.unistack.org/micro-proto/v3 v3.4.1/go.mod h1:okx/cnOhzuCX0ggl/vToatbCupi0O44diiiLLsZ93Zo=
go.unistack.org/micro/v3 v3.11.48 h1:lHJYSHU2z1TTcuswItGwG7cZXN6n04EFqY7lk/0gA7w=
go.unistack.org/micro/v3 v3.11.48/go.mod h1:fDQ8Mu9wubaFP0L8hNQlpzHiEnWN0wbOlawN9HYo0N4=
golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250818200422-3122310a409c h1:qXWI/sQtv5UKboZ/zUk7h+mrf/lXORyI+n9DKDAusdg=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250818200422-3122310a409c/go.mod h1:gw1tLEfykwDz2ET4a12jcXt4couGAm7IwsVaTy0Sflo=
google.golang.org/grpc v1.75.0 h1:+TW+dqTd2Biwe6KKfhE5JpiYIBWq865PhKGSXiivqt4=
google.golang.org/grpc v1.75.0/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ=
google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw=
google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250929231259-57b25ae835d4 h1:i8QOKZfYg6AbGVZzUAY3LrNWCKF8O6zFisU9Wl9RER4=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250929231259-57b25ae835d4/go.mod h1:HSkG/KdJWusxU1F6CNrwNDjBMgisKxGnc5dAZfT0mjQ=
google.golang.org/grpc v1.75.1 h1:/ODCNEuf9VghjgO3rqLcfg8fiOP0nSluljWFlDxELLI=
google.golang.org/grpc v1.75.1/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ=
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=

28
message.go Normal file
View File

@@ -0,0 +1,28 @@
package http
import (
"go.unistack.org/micro/v3/client"
"go.unistack.org/micro/v3/metadata"
)
type httpMessage struct {
topic string
payload interface{}
opts client.MessageOptions
}
func (m *httpMessage) Topic() string {
return m.topic
}
func (m *httpMessage) Payload() interface{} {
return m.payload
}
func (m *httpMessage) ContentType() string {
return m.opts.ContentType
}
func (m *httpMessage) Metadata() metadata.Metadata {
return m.opts.Metadata
}

View File

@@ -7,7 +7,7 @@ import (
"net/http"
"time"
"go.unistack.org/micro/v4/client"
"go.unistack.org/micro/v3/client"
)
// --------------------------------------------- HTTPClient option -----------------------------------------------------

View File

@@ -1,8 +1,8 @@
package http
import (
"go.unistack.org/micro/v4/client"
"go.unistack.org/micro/v4/codec"
"go.unistack.org/micro/v3/client"
"go.unistack.org/micro/v3/codec"
)
type httpRequest struct {

View File

@@ -8,7 +8,7 @@ import (
"github.com/stretchr/testify/require"
"go.unistack.org/micro-client-http/v4/status"
"go.unistack.org/micro-client-http/v3/status"
)
type fakeError struct{ s *status.Status }