summaryrefslogtreecommitdiffstats
path: root/third_party/content_analysis_sdk/agent/src/agent_win.h
blob: a6a0c7331166120c80474d663d7a9e57c13f2b55 (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
// Copyright 2022 The Chromium Authors.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CONTENT_ANALYSIS_AGENT_SRC_AGENT_WIN_H_
#define CONTENT_ANALYSIS_AGENT_SRC_AGENT_WIN_H_

#include <windows.h>

#include <memory>
#include <sstream>
#include <string>
#include <vector>

#include "agent_base.h"

namespace content_analysis {
namespace sdk {

// Agent implementaton for Windows.
class AgentWin : public AgentBase {
 public:
  // Creates a new agent given the specific configuration and handler.
  // If an error occurs during creation, `rc` is set to the specific
  // error.  Otherwise `rc` is ResultCode::OK.
  AgentWin(Config config, std::unique_ptr<AgentEventHandler> handler,
           ResultCode* rc);
  ~AgentWin() override;

  // Agent:
  ResultCode HandleEvents() override;
  ResultCode Stop() override;
  std::string DebugString() const override;

  // Handles one pipe event and returns.  Used only in tests.
  ResultCode HandleOneEventForTesting();

  // Returns true if there is at least one client connected to this agent.
  bool IsAClientConnectedForTesting();

private:
  // Represents one connection to a Google Chrome browser, or one pipe
  // listening for a Google Chrome browser to connect.
  class Connection {
   public:
    // Starts listening on a pipe with the given name. `is_first_pipe` should
    // be true only for the first pipe created by the agent.  If an error
    // occurs while creating the connction object it is returned in `rc`.
    // If no errors occur then rc is set to ResultCode::OK.
    //
    // When `user_specific` is true there is a different agent instance per OS
    // user.
    //
    // `Connection` objects cannot be copied or moved because the OVERLAPPED
    // structure cannot be changed or moved in memory while an I/O operation
    // is in progress.
    Connection(const std::string& pipename,
               bool user_specific,
               AgentEventHandler* handler,
               bool is_first_pipe,
               ResultCode* rc);
    Connection(const Connection& other) = delete;
    Connection(Connection&& other) = delete;
    Connection& operator=(const Connection& other) = delete;
    Connection& operator=(Connection&& other) = delete;
    ~Connection();

    bool IsValid() const { return handle_ != INVALID_HANDLE_VALUE; }
    bool IsConnected() const { return is_connected_; }
    HANDLE GetWaitHandle() const { return overlapped_.hEvent; }

    // Resets this connection object to listen for a new Google Chrome browser.
    // When `user_specific` is true there is a different agent instance per OS
    // user.
    ResultCode Reset(const std::string& pipename, bool user_specific);

    // Hnadles an event for this connection.  `wait_handle` corresponds to
    // this connections wait handle.
    ResultCode HandleEvent(HANDLE wait_handle);

    // Append debuf information to the string stream.
    void AppendDebugString(std::stringstream& state) const;

   private:
    // Listens for a new connection from Google Chrome.
    ResultCode ConnectPipe();

    // Resets this connection object to listen for a new Google Chrome browser.
    // When `user_specific` is true there is a different agent instance per OS
    // user.
    ResultCode ResetInternal(const std::string& pipename,
                             bool user_specific,
                             bool is_first_pipe);

    // Cleans up this connection object so that it can be reused with a new
    // Google Chroem browser instance.  The handles assocated with this object
    // are not closed.  On return, this object is neither connected nor
    // listening and any buffer used to hold browser messages are cleared.
    void Cleanup();

    // Queues a read on the pipe to receive a message from Google Chrome.
    // ERROR_SUCCESS, ERROR_IO_PENDING, and ERROR_MORE_DATA are successful
    // return values. Other values represent an error with the connection.
    // If `reset_cursor` is true the internal read buffer cursor is reset to
    // the start of the buffer, otherwise it is unchanged.
    ResultCode QueueReadFile(bool reset_cursor);

    // Called when data from Google Chrome is available for reading from the
    // pipe. ERROR_SUCCESS and ERROR_MORE_DATA are both successful return
    // values.  Other values represent an error with the connection.
    //
    // `done_reading` is true if the code has finished reading an entire message
    // from chrome.  Regardless of whether reading is done, `count` contains
    // the number of bytes read.
    //
    // If `done_reading` is true, the data received from the browser is parsed
    // as if it were a `ChromeToAgent` proto message and the handler is called
    // as needed.
    //
    // If `done_reading` is false, the data received from the browser is
    // appended to the data already received from the browser.  `buffer_` is
    // resized to allow reading more data from the browser.
    //
    // In all cases the caller is expected to use QueueReadFile() to continue
    // reading data from the browser.
    ResultCode OnReadFile(BOOL done_reading, DWORD count);

    // Calls the appropriate method the handler depending on the message
    // received from Google Chrome.
    ResultCode CallHandler();

    // Fills in the browser_info_ member of this Connection.  Assumes
    // IsConnected() is true.
    ResultCode BuildBrowserInfo();

    // Notifies the handler of the given error iff `rc` is not equal to
    // ResultCode::OK.  Appends the Google Chrome browser process id to the
    // context before calling the handler.  Also append `err` to the context
    // if it is not ERROR_SUCCESS.
    //
    // Returns the error passed into the method.
    ResultCode NotifyIfError(const char* context,
                             ResultCode rc,
                             DWORD err=ERROR_SUCCESS);

    // The handler to call for various agent events.
    AgentEventHandler* handler_ = nullptr;

    // Members used to communicate with Google Chrome.
    HANDLE handle_ = INVALID_HANDLE_VALUE;
    OVERLAPPED overlapped_;

    // True if this connection is assigned to a specific Google Chrome browser,
    // otherwise this connection is listening for a new browser.
    bool is_connected_ = false;

    // Information about the Google Chrome browser process.
    BrowserInfo browser_info_;

    // Members used to read messages from Google Chrome.
    std::vector<char> buffer_;
    char* cursor_ = nullptr;
    DWORD read_size_ = 0;
    DWORD final_size_ = 0;
  };

  // Returns handles that can be used to wait for events from all handles
  // managed by this agent.  This includes all connection objects and the
  // stop event.  The stop event is always last in the list.
  void GetHandles(std::vector<HANDLE>& wait_handles) const;

  // Handles one pipe event and returns.  If the return value is
  // ResultCode::OK, the `stopped` argument is set to true if the agent
  // should stop handling more events.  If the return value is not
  // ResultCode::OK, `stopped` is undefined.
  ResultCode HandleOneEvent(std::vector<HANDLE>& wait_handles, bool* stopped);

  // Performs a clean shutdown of the agent.
  void Shutdown();

  // Name used to create the pipes between the agent and Google Chrome browsers.
  std::string pipename_;

  // A list of pipes to already connected Google Chrome browsers.
  // The first kMinNumListeningPipeInstances pipes in the list correspond to
  // listening pipes.
  std::vector<std::unique_ptr<Connection>> connections_;

  // An event that is set when the agent should stop.  Set in Stop().
  HANDLE stop_event_ = nullptr;
};

}  // namespace sdk
}  // namespace content_analysis

#endif  // CONTENT_ANALYSIS_AGENT_SRC_AGENT_WIN_H_