-
Notifications
You must be signed in to change notification settings - Fork 302
/
io.go
157 lines (136 loc) · 6.13 KB
/
io.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
// Copyright © 2019 The Things Network Foundation, The Things Industries B.V.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package io
import (
"context"
"net/http"
"go.thethings.network/lorawan-stack/v3/pkg/component"
"go.thethings.network/lorawan-stack/v3/pkg/config"
"go.thethings.network/lorawan-stack/v3/pkg/errorcontext"
"go.thethings.network/lorawan-stack/v3/pkg/errors"
"go.thethings.network/lorawan-stack/v3/pkg/ratelimit"
"go.thethings.network/lorawan-stack/v3/pkg/ttnpb"
)
const bufferSize = 32
// PubSub represents the Application Server Pub/Sub capabilities to application frontends.
type PubSub interface {
// Publish publishes upstream traffic to the Application Server.
Publish(ctx context.Context, up *ttnpb.ApplicationUp) error
// Subscribe subscribes an application or integration by its identifiers to the Application Server, and returns a
// Subscription for traffic and control. If the cluster parameter is true, the subscription receives all of the
// traffic of the application. Otherwise, only traffic that was processed locally is sent.
Subscribe(ctx context.Context, protocol string, ids *ttnpb.ApplicationIdentifiers, cluster bool) (*Subscription, error)
}
// DownlinkQueueOperator represents the Application Server downlink queue operations to application frontends.
type DownlinkQueueOperator interface {
// DownlinkQueuePush pushes the given downlink messages to the end device's application downlink queue.
DownlinkQueuePush(context.Context, ttnpb.EndDeviceIdentifiers, []*ttnpb.ApplicationDownlink) error
// DownlinkQueueReplace replaces the end device's application downlink queue with the given downlink messages.
DownlinkQueueReplace(context.Context, ttnpb.EndDeviceIdentifiers, []*ttnpb.ApplicationDownlink) error
// DownlinkQueueList lists the application downlink queue of the given end device.
DownlinkQueueList(context.Context, ttnpb.EndDeviceIdentifiers) ([]*ttnpb.ApplicationDownlink, error)
}
// UplinkStorage represents the Application Server uplink storage to application frontends.
type UplinkStorage interface {
// RangeUplinks ranges the application uplinks and calls the callback function, until false is returned.
RangeUplinks(ctx context.Context, ids ttnpb.EndDeviceIdentifiers, paths []string, f func(ctx context.Context, up *ttnpb.ApplicationUplink) bool) error
}
// Server represents the Application Server to application frontends.
type Server interface {
component.TaskStarter
PubSub
DownlinkQueueOperator
UplinkStorage
// GetBaseConfig returns the component configuration.
GetBaseConfig(ctx context.Context) config.ServiceBase
// FillContext fills the given context.
// This method should only be used for request contexts.
FillContext(ctx context.Context) context.Context
// HTTPClient returns a configured *http.Client.
HTTPClient(context.Context) (*http.Client, error)
// RateLimiter returns the rate limiter instance.
RateLimiter() ratelimit.Interface
}
// ContextualApplicationUp represents an ttnpb.ApplicationUp with its context.
type ContextualApplicationUp struct {
context.Context
*ttnpb.ApplicationUp
}
// Subscription is a subscription to an application or integration managed by a frontend.
type Subscription struct {
ctx context.Context
cancelCtx errorcontext.CancelFunc
protocol string
ids *ttnpb.ApplicationIdentifiers
upCh chan *ContextualApplicationUp
}
// NewSubscription instantiates a new application or integration subscription.
func NewSubscription(ctx context.Context, protocol string, ids *ttnpb.ApplicationIdentifiers) *Subscription {
ctx, cancelCtx := errorcontext.New(ctx)
return &Subscription{
ctx: ctx,
cancelCtx: cancelCtx,
protocol: protocol,
ids: ids,
upCh: make(chan *ContextualApplicationUp, bufferSize),
}
}
// Context returns the subscription context.
func (s *Subscription) Context() context.Context { return s.ctx }
// Disconnect marks the subscription as disconnected and cancels the context.
func (s *Subscription) Disconnect(err error) {
s.cancelCtx(err)
}
// Protocol returns the protocol used for the subscription, i.e. grpc, mqtt or http.
func (s *Subscription) Protocol() string { return s.protocol }
// ApplicationIDs returns the application identifiers, if the subscription represents any specific.
func (s *Subscription) ApplicationIDs() *ttnpb.ApplicationIdentifiers { return s.ids }
var errBufferFull = errors.DefineResourceExhausted("buffer_full", "buffer is full")
// Publish publishes an upstream message.
// This method returns immediately, returning nil if the message is buffered, or with an error when the buffer is full.
func (s *Subscription) Publish(ctx context.Context, up *ttnpb.ApplicationUp) error {
ctxUp := &ContextualApplicationUp{
Context: ctx,
ApplicationUp: up,
}
select {
case <-s.ctx.Done():
return s.ctx.Err()
case s.upCh <- ctxUp:
default:
return errBufferFull.New()
}
return nil
}
// Up returns the upstream channel.
func (s *Subscription) Up() <-chan *ContextualApplicationUp {
return s.upCh
}
// CleanDownlinks returns a copy of the given downlink items with only the fields that can be set by the application.
func CleanDownlinks(items []*ttnpb.ApplicationDownlink) []*ttnpb.ApplicationDownlink {
res := make([]*ttnpb.ApplicationDownlink, 0, len(items))
for _, item := range items {
res = append(res, &ttnpb.ApplicationDownlink{
FPort: item.FPort,
FCnt: item.FCnt, // FCnt must be set when skipping application payload crypto.
FRMPayload: item.FRMPayload,
DecodedPayload: item.DecodedPayload,
ClassBC: item.ClassBC,
Priority: item.Priority,
Confirmed: item.Confirmed,
CorrelationIDs: item.CorrelationIDs,
})
}
return res
}