Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(57)

Side by Side Diff: chrome/browser/extensions/api/messaging/native_message_process_host.cc

Issue 11968028: Remove connect message from Native Messaging API (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 7 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/extensions/api/messaging/native_message_process_host.h" 5 #include "chrome/browser/extensions/api/messaging/native_message_process_host.h"
6 6
7 #include "base/bind.h"
7 #include "base/command_line.h" 8 #include "base/command_line.h"
8 #include "base/file_path.h" 9 #include "base/file_path.h"
9 #include "base/logging.h" 10 #include "base/logging.h"
10 #include "base/path_service.h" 11 #include "base/path_service.h"
11 #include "base/pickle.h" 12 #include "base/pickle.h"
13 #include "base/platform_file.h"
12 #include "base/process_util.h" 14 #include "base/process_util.h"
13 #include "base/values.h" 15 #include "base/values.h"
14 #include "chrome/browser/extensions/api/messaging/native_process_launcher.h" 16 #include "chrome/browser/extensions/api/messaging/native_process_launcher.h"
15 #include "chrome/common/chrome_paths.h" 17 #include "chrome/common/chrome_paths.h"
16 #include "chrome/common/chrome_switches.h" 18 #include "chrome/common/chrome_switches.h"
17 #include "chrome/common/chrome_version_info.h" 19 #include "chrome/common/chrome_version_info.h"
18 #include "chrome/common/extensions/features/feature.h" 20 #include "chrome/common/extensions/features/feature.h"
19 #include "content/public/common/result_codes.h" 21 #include "content/public/common/result_codes.h"
20 22
21 namespace { 23 namespace {
22 24
23 const int kExitTimeoutMS = 5000; 25 const int kExitTimeoutMS = 5000;
24 const uint32 kMaxMessageDataLength = 10 * 1024 * 1024; 26 const uint32 kMaxMessageDataLength = 10 * 1024 * 1024;
25 const char kNativeHostsDirectoryName[] = "native_hosts"; 27 const char kNativeHostsDirectoryName[] = "native_hosts";
26 28
27 } // namespace 29 } // namespace
28 30
29 namespace extensions { 31 namespace extensions {
30 32
31 NativeMessageProcessHost::NativeMessageProcessHost( 33 NativeMessageProcessHost::NativeMessageProcessHost(
32 base::WeakPtr<Client> weak_client_ui, 34 base::WeakPtr<Client> weak_client_ui,
35 const std::string& native_host_name,
33 int destination_port, 36 int destination_port,
34 base::ProcessHandle native_process_handle, 37 scoped_ptr<NativeProcessLauncher> launcher)
35 FileHandle read_file,
36 FileHandle write_file,
37 bool is_send_message)
38 : weak_client_ui_(weak_client_ui), 38 : weak_client_ui_(weak_client_ui),
39 native_host_name_(native_host_name),
39 destination_port_(destination_port), 40 destination_port_(destination_port),
40 native_process_handle_(native_process_handle), 41 native_process_handle_(base::kNullProcessHandle),
41 read_file_(read_file), 42 read_file_(base::kInvalidPlatformFileValue),
42 write_file_(write_file), 43 write_file_(base::kInvalidPlatformFileValue) {
43 scoped_read_file_(&read_file_), 44 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
44 scoped_write_file_(&write_file_), 45
45 is_send_message_(is_send_message) { 46 // It's safe to use base::Unretained() here because NativeMessagePort always
46 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE)); 47 // deletes us on the FILE thread.
47 InitIO(); 48 content::BrowserThread::PostTask(content::BrowserThread::FILE, FROM_HERE,
49 base::Bind(&NativeMessageProcessHost::LaunchHostProcess,
50 base::Unretained(this), base::Passed(&launcher)));
48 } 51 }
49 52
50 NativeMessageProcessHost::~NativeMessageProcessHost() { 53 NativeMessageProcessHost::~NativeMessageProcessHost() {
51 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE)); 54 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
55
52 // Give the process some time to shutdown, then try and kill it. 56 // Give the process some time to shutdown, then try and kill it.
53 content::BrowserThread::PostDelayedTask( 57 content::BrowserThread::PostDelayedTask(
54 content::BrowserThread::FILE, 58 content::BrowserThread::FILE,
55 FROM_HERE, 59 FROM_HERE,
56 base::Bind(base::IgnoreResult(&base::KillProcess), 60 base::Bind(base::IgnoreResult(&base::KillProcess),
57 native_process_handle_, 61 native_process_handle_,
58 content::RESULT_CODE_NORMAL_EXIT, 62 content::RESULT_CODE_NORMAL_EXIT,
59 false /* don't wait for exit */), 63 false /* don't wait for exit */),
60 base::TimeDelta::FromMilliseconds(kExitTimeoutMS)); 64 base::TimeDelta::FromMilliseconds(kExitTimeoutMS));
61 } 65 }
62 66
63 // static 67 // static
64 void NativeMessageProcessHost::Create(base::WeakPtr<Client> weak_client_ui, 68 scoped_ptr<NativeMessageProcessHost> NativeMessageProcessHost::Create(
65 const std::string& native_app_name, 69 base::WeakPtr<Client> weak_client_ui,
66 const std::string& connection_message, 70 const std::string& native_host_name,
67 int destination_port, 71 int destination_port) {
68 MessageType type, 72 scoped_ptr<NativeProcessLauncher> launcher(new NativeProcessLauncher());
69 CreateCallback callback) { 73 return CreateWithLauncher(weak_client_ui, native_host_name, destination_port,
70 NativeProcessLauncher launcher; 74 launcher.Pass());
71 CreateWithLauncher(weak_client_ui, native_app_name, connection_message,
72 destination_port, type, callback, launcher);
73 } 75 }
74 76
75 // static 77 // static
76 void NativeMessageProcessHost::CreateWithLauncher( 78 scoped_ptr<NativeMessageProcessHost>
79 NativeMessageProcessHost::CreateWithLauncher(
77 base::WeakPtr<Client> weak_client_ui, 80 base::WeakPtr<Client> weak_client_ui,
78 const std::string& native_app_name, 81 const std::string& native_host_name,
79 const std::string& connection_message,
80 int destination_port, 82 int destination_port,
81 MessageType type, 83 scoped_ptr<NativeProcessLauncher> launcher) {
82 CreateCallback callback, 84 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
83 const NativeProcessLauncher& launcher) {
84 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
85 DCHECK(type == TYPE_SEND_MESSAGE_REQUEST || type == TYPE_CONNECT);
86 85
87 ScopedHost process; 86 scoped_ptr<NativeMessageProcessHost> process;
88 if (Feature::GetCurrentChannel() > chrome::VersionInfo::CHANNEL_DEV || 87 if (Feature::GetCurrentChannel() > chrome::VersionInfo::CHANNEL_DEV ||
89 !CommandLine::ForCurrentProcess()->HasSwitch( 88 !CommandLine::ForCurrentProcess()->HasSwitch(
90 switches::kEnableNativeMessaging)) { 89 switches::kEnableNativeMessaging)) {
91 content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, 90 return process.Pass();
92 base::Bind(callback,
93 base::Passed(&process)));
94 return;
95 } 91 }
96 92
93 process.reset(new NativeMessageProcessHost(
94 weak_client_ui, native_host_name, destination_port, launcher.Pass()));
95
96 return process.Pass();
97 }
98
99 void NativeMessageProcessHost::LaunchHostProcess(
100 scoped_ptr<NativeProcessLauncher> launcher) {
101 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
102
97 FilePath native_host_program; 103 FilePath native_host_program;
98 FilePath native_host_registry; 104 FilePath native_host_registry;
99 CHECK(PathService::Get(chrome::DIR_USER_DATA, &native_host_registry)); 105 CHECK(PathService::Get(chrome::DIR_USER_DATA, &native_host_registry));
100 native_host_registry = 106 native_host_registry =
101 native_host_registry.AppendASCII(kNativeHostsDirectoryName); 107 native_host_registry.AppendASCII(kNativeHostsDirectoryName);
102 native_host_program = native_host_registry.AppendASCII(native_app_name); 108 native_host_program = native_host_registry.AppendASCII(native_host_name_);
103 109
104 // Make sure that the client is not trying to invoke something outside of the 110 // Make sure that the client is not trying to invoke something outside of the
105 // proper directory. Eg. '../../dangerous_something.exe'. 111 // proper directory. Eg. '../../dangerous_something.exe'.
106 if (!file_util::ContainsPath(native_host_registry, native_host_program)) { 112 if (!file_util::ContainsPath(native_host_registry, native_host_program)) {
107 LOG(ERROR) << "Could not find native host: " << native_app_name; 113 LOG(ERROR) << "Could not find native host: " << native_host_name_;
108 content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, 114 content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
109 base::Bind(callback, 115 base::Bind(&Client::CloseChannel, weak_client_ui_,
110 base::Passed(&process))); 116 destination_port_, true));
111 return; 117 return;
112 } 118 }
113 119
114 FileHandle read_handle; 120 if (!launcher->LaunchNativeProcess(native_host_program,
115 FileHandle write_handle; 121 &native_process_handle_,
116 base::ProcessHandle native_process_handle; 122 &read_file_,
117 123 &write_file_)) {
118 if (!launcher.LaunchNativeProcess(native_host_program,
119 &native_process_handle,
120 &read_handle,
121 &write_handle)) {
122 content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, 124 content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
123 base::Bind(callback, 125 base::Bind(&Client::CloseChannel, weak_client_ui_,
124 base::Passed(&process))); 126 destination_port_, true));
125 return; 127 return;
126 } 128 }
127 129
128 process.reset(new NativeMessageProcessHost( 130 #if defined(OS_POSIX)
129 weak_client_ui, destination_port, native_process_handle, read_handle, 131 scoped_read_file_.reset(&read_file_);
130 write_handle, type == TYPE_SEND_MESSAGE_REQUEST)); 132 scoped_write_file_.reset(&write_file_);
133 #endif // defined(OS_POSIX)
131 134
132 process->SendImpl(type, connection_message); 135 InitIO();
133
134 content::BrowserThread::PostTask(
135 content::BrowserThread::UI, FROM_HERE,
136 base::Bind(callback, base::Passed(&process)));
137 } 136 }
138 137
139 void NativeMessageProcessHost::SendImpl(MessageType type, 138 void NativeMessageProcessHost::Send(const std::string& json) {
140 const std::string& json) {
141 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE)); 139 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
142 140
141 if (native_process_handle_ == base::kNullProcessHandle) {
142 // Don't need to do anything if LaunchHostProcess() didn't succeed.
143 return;
144 }
145
143 // Make sure that the process has not died. 146 // Make sure that the process has not died.
144 if (base::GetTerminationStatus(native_process_handle_, NULL) != 147 if (base::GetTerminationStatus(native_process_handle_, NULL) !=
145 base::TERMINATION_STATUS_STILL_RUNNING) { 148 base::TERMINATION_STATUS_STILL_RUNNING) {
146 // Notify the message service that the channel should close. 149 // Notify the message service that the channel should close.
147 content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, 150 content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
148 base::Bind(&Client::CloseChannel, weak_client_ui_, 151 base::Bind(&Client::CloseChannel, weak_client_ui_,
149 destination_port_, true)); 152 destination_port_, true));
150 } 153 }
151 154
152 WriteMessage(type, json); 155 WriteMessage(json);
153 } 156 }
154 157
155 bool NativeMessageProcessHost::WriteMessage(MessageType type, 158 bool NativeMessageProcessHost::WriteMessage(const std::string& message) {
156 const std::string& message) {
157 Pickle pickle; 159 Pickle pickle;
158 160
159 // Pickles will always pad bytes to 32-bit alignment, so just use a unit32.
160 pickle.WriteUInt32(type);
161
162 // Pickles write the length of a string before it as a uint32. 161 // Pickles write the length of a string before it as a uint32.
163 pickle.WriteString(message); 162 pickle.WriteString(message);
164 163
165 // Make sure that the pickle doesn't do any unexpected padding. 164 // Make sure that the pickle doesn't do any unexpected padding.
166 CHECK(8 + message.length() == pickle.payload_size()); 165 CHECK_EQ(4 + message.length(), pickle.payload_size());
167 166
168 if (!WriteData(write_file_, const_cast<const Pickle*>(&pickle)->payload(), 167 if (!WriteData(write_file_, const_cast<const Pickle*>(&pickle)->payload(),
169 pickle.payload_size())) { 168 pickle.payload_size())) {
170 LOG(ERROR) << "Error writing message to the native client."; 169 LOG(ERROR) << "Error writing message to the native client.";
171 return false; 170 return false;
172 } 171 }
173 172
174 return true; 173 return true;
175 } 174 }
176 175
177 bool NativeMessageProcessHost::ReadMessage(MessageType* type, 176 bool NativeMessageProcessHost::ReadMessage(std::string* message) {
178 std::string* message) { 177 // Read the length (uint32).
179 // Read the type (uint32) and length (uint32). 178 char message_meta_data[4];
180 char message_meta_data[8]; 179 if (!ReadData(read_file_, message_meta_data, sizeof(message_meta_data))) {
181 if (!ReadData(read_file_, message_meta_data, 8)) { 180 LOG(ERROR) << "Error reading the message length.";
182 LOG(ERROR) << "Error reading the message type and length.";
183 return false; 181 return false;
184 } 182 }
185 183
186 Pickle pickle; 184 Pickle pickle;
187 pickle.WriteBytes(message_meta_data, 8); 185 pickle.WriteBytes(message_meta_data, 8);
jln (very slow on Chromium) 2013/01/23 01:28:13 Oops ? That's why sizeof is good ;)
188 PickleIterator pickle_it(pickle); 186 PickleIterator pickle_it(pickle);
189 uint32 uint_type;
190 uint32 data_length; 187 uint32 data_length;
191 if (!pickle_it.ReadUInt32(&uint_type) || 188 if (!pickle_it.ReadUInt32(&data_length)) {
192 !pickle_it.ReadUInt32(&data_length)) { 189 LOG(ERROR) << "Error getting the message length from the pickle.";
193 LOG(ERROR) << "Error getting the message type and length from the pickle.";
194 return false; 190 return false;
195 } 191 }
196 192
197 if (uint_type >= NUM_MESSAGE_TYPES) {
198 LOG(ERROR) << type << " is not a valid message type.";
199 return false;
200 }
201
202 if ((is_send_message_ && (uint_type != TYPE_SEND_MESSAGE_RESPONSE)) ||
203 (!is_send_message_ && (uint_type != TYPE_CONNECT_MESSAGE))) {
204 LOG(ERROR) << "Recieved a message of type " << uint_type << ". "
205 << "Expecting a message of type "
206 << (is_send_message_ ? TYPE_SEND_MESSAGE_RESPONSE :
207 TYPE_CONNECT_MESSAGE);
208 return false;
209 }
210 *type = static_cast<MessageType>(uint_type);
211
212 if (data_length > kMaxMessageDataLength) { 193 if (data_length > kMaxMessageDataLength) {
213 LOG(ERROR) << data_length << " is too large for the length of a message. " 194 LOG(ERROR) << data_length << " is too large for the length of a message. "
214 << "Max message size is " << kMaxMessageDataLength; 195 << "Max message size is " << kMaxMessageDataLength;
215 return false; 196 return false;
216 } 197 }
217 198
218 message->resize(data_length, '\0'); 199 message->resize(data_length, '\0');
219 if (!ReadData(read_file_, &(*message)[0], data_length)) { 200 if (!ReadData(read_file_, &(*message)[0], data_length)) {
220 LOG(ERROR) << "Error reading the json data."; 201 LOG(ERROR) << "Error reading the json data.";
221 return false; 202 return false;
222 } 203 }
223 204
224 return true; 205 return true;
225 } 206 }
226 207
227 } // namespace extensions 208 } // namespace extensions
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698