aboutsummaryrefslogtreecommitdiff
path: root/osp/public/presentation/presentation_connection.h
blob: ee0cff1b47902871e944a3b3fa89816bcf4ff581 (plain)
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
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef OSP_PUBLIC_PRESENTATION_PRESENTATION_CONNECTION_H_
#define OSP_PUBLIC_PRESENTATION_PRESENTATION_CONNECTION_H_

#include <cstdint>
#include <map>
#include <memory>
#include <string>
#include <vector>

#include "absl/strings/string_view.h"
#include "absl/types/optional.h"
#include "osp/public/message_demuxer.h"
#include "platform/api/time.h"
#include "platform/base/error.h"
#include "platform/base/ip_address.h"
#include "platform/base/macros.h"
#include "util/osp_logging.h"

namespace openscreen {
namespace osp {

class ProtocolConnection;

enum class TerminationReason {
  kReceiverTerminateCalled = 0,
  kReceiverUserTerminated,
  kControllerTerminateCalled,
  kControllerUserTerminated,
  kReceiverPresentationReplaced,
  kReceiverIdleTooLong,
  kReceiverPresentationUnloaded,
  kReceiverShuttingDown,
  kReceiverError,
};

class Connection {
 public:
  enum class CloseReason {
    kClosed = 0,
    kDiscarded,
    kError,
  };

  enum class State {
    // The library is currently attempting to connect to the presentation.
    kConnecting,
    // The connection to the presentation is open and communication is possible.
    kConnected,
    // The connection is closed or could not be opened.  No communication is
    // possible but it may be possible to reopen the connection via
    // ReconnectPresentation.
    kClosed,
    // The connection is closed and the receiver has been terminated.
    kTerminated,
  };

  // An object to receive callbacks related to a single Connection.
  class Delegate {
   public:
    Delegate() = default;
    virtual ~Delegate() = default;

    // State changes.
    virtual void OnConnected() = 0;

    // Explicit close by other endpoint.
    virtual void OnClosedByRemote() = 0;

    // Closed because the script connection object was discarded.
    virtual void OnDiscarded() = 0;

    // Closed because of an error.
    virtual void OnError(const absl::string_view message) = 0;

    // Terminated through a different connection.
    virtual void OnTerminated() = 0;

    // A UTF-8 string message was received.
    virtual void OnStringMessage(const absl::string_view message) = 0;

    // A binary message was received.
    virtual void OnBinaryMessage(const std::vector<uint8_t>& data) = 0;

   private:
    OSP_DISALLOW_COPY_AND_ASSIGN(Delegate);
  };

  // Allows different close, termination, and destruction behavior for both
  // possible parents: controller and receiver.  This is different from the
  // normal delegate above, which would be supplied by the embedder to link it's
  // presentation connection functionality.
  class ParentDelegate {
   public:
    ParentDelegate() = default;
    virtual ~ParentDelegate() = default;

    virtual Error CloseConnection(Connection* connection,
                                  CloseReason reason) = 0;
    virtual Error OnPresentationTerminated(const std::string& presentation_id,
                                           TerminationReason reason) = 0;
    virtual void OnConnectionDestroyed(Connection* connection) = 0;

   private:
    OSP_DISALLOW_COPY_AND_ASSIGN(ParentDelegate);
  };

  struct PresentationInfo {
    std::string id;
    std::string url;
  };

  // Constructs a new connection using |delegate| for callbacks.
  Connection(const PresentationInfo& info,
             Delegate* delegate,
             ParentDelegate* parent_delegate);
  ~Connection();

  // Returns the ID and URL of this presentation.
  const PresentationInfo& presentation_info() const { return presentation_; }

  State state() const { return state_; }

  ProtocolConnection* get_protocol_connection() const {
    return protocol_connection_.get();
  }

  // These methods should only be called when we are connected.
  uint64_t endpoint_id() const {
    OSP_CHECK(endpoint_id_);
    return endpoint_id_.value();
  }
  uint64_t connection_id() const {
    OSP_CHECK(connection_id_);
    return connection_id_.value();
  }

  // Sends a UTF-8 string message.
  Error SendString(absl::string_view message);

  // Sends a binary message.
  Error SendBinary(std::vector<uint8_t>&& data);

  // Closes the connection.  This can be based on an explicit request from the
  // embedder or because the connection object is being discarded (page
  // navigated, object GC'd, etc.).
  Error Close(CloseReason reason);

  // Terminates the presentation associated with this connection.
  void Terminate(TerminationReason reason);

  void OnConnecting();

  // Called by the receiver when the OnPresentationStarted logic happens. This
  // notifies the delegate and updates our internal stream and ids.
  void OnConnected(uint64_t connection_id,
                   uint64_t endpoint_id,
                   std::unique_ptr<ProtocolConnection> stream);

  void OnClosedByError(Error cause);
  void OnClosedByRemote();
  void OnTerminated();

  Delegate* get_delegate() { return delegate_; }

 private:
  // Helper method that handles closing down our internal state.
  // Returns whether or not the connection state changed (and thus
  // whether or not delegates should be informed).
  bool OnClosed();

  PresentationInfo presentation_;
  State state_ = State::kConnecting;
  Delegate* delegate_;
  ParentDelegate* parent_delegate_;
  absl::optional<uint64_t> connection_id_;
  absl::optional<uint64_t> endpoint_id_;
  std::unique_ptr<ProtocolConnection> protocol_connection_;

  OSP_DISALLOW_COPY_AND_ASSIGN(Connection);
};

class ConnectionManager final : public MessageDemuxer::MessageCallback {
 public:
  explicit ConnectionManager(MessageDemuxer* demuxer);

  void AddConnection(Connection* connection);
  void RemoveConnection(Connection* connection);

  // MessasgeDemuxer::MessageCallback overrides.
  ErrorOr<size_t> OnStreamMessage(uint64_t endpoint_id,
                                  uint64_t connection_id,
                                  msgs::Type message_type,
                                  const uint8_t* buffer,
                                  size_t buffer_size,
                                  Clock::time_point now) override;

  Connection* GetConnection(uint64_t connection_id);

 private:
  // TODO(btolsch): Connection IDs were changed to be per-endpoint, but this
  // table then needs to be <endpoint id, connection id> since connection id is
  // still not unique globally.
  std::map<uint64_t, Connection*> connections_;

  MessageDemuxer::MessageWatch message_watch_;
  MessageDemuxer::MessageWatch close_request_watch_;
  MessageDemuxer::MessageWatch close_event_watch_;

  OSP_DISALLOW_COPY_AND_ASSIGN(ConnectionManager);
};

}  // namespace osp
}  // namespace openscreen

#endif  // OSP_PUBLIC_PRESENTATION_PRESENTATION_CONNECTION_H_