Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code

Delta Between Two Patch Sets: AdblockPlusEngine/main.cpp

Issue 10580043: Run a single FilterEngine instance in a separate process (Closed)
Left Patch Set: Created May 16, 2013, 5:29 a.m.
Right Patch Set: Addressed review issues Created May 23, 2013, 7:10 p.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
Left: Side by side diff | Download
Right: Side by side diff | Download
« no previous file with change/comment | « AdblockPlusEngine/AdblockPlusEngine.vcxproj.filters ('k') | Shared/AdblockPlusClient.h » ('j') | no next file with change/comment »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
LEFTRIGHT
1 #include <AdblockPlus.h> 1 #include <AdblockPlus.h>
2 #include <iostream> 2 #include <iostream>
3 #include <Lmcons.h>
3 #include <ShlObj.h> 4 #include <ShlObj.h>
4 #include <sstream> 5 #include <sstream>
5 #include <vector> 6 #include <vector>
6 #include <Windows.h> 7 #include <Windows.h>
7 #include <Sddl.h> 8 #include <Sddl.h>
Wladimir Palant 2013/05/16 11:52:16 I think that the usual order of includes is from g
Felix Dahlke 2013/05/17 09:11:36 We're using alphabetical in most places, but it's
8 9
9 namespace 10 namespace
10 { 11 {
11 const std::wstring pipeName = L"\\\\.\\pipe\\adblockplusengine"; 12 std::wstring GetUserName()
13 {
14 const DWORD maxLength = UNLEN + 1;
15 std::auto_ptr<wchar_t> buffer(new wchar_t[maxLength]);
16 DWORD length = maxLength;
17 if (!::GetUserName(buffer.get(), &length))
18 {
19 std::stringstream stream;
20 stream << "Failed to get the current user's name (Error code: " << GetLast Error() << ")";
21 throw std::runtime_error("Failed to get the current user's name");
22 }
23 return std::wstring(buffer.get(), length);
24 }
25
26 const std::wstring pipeName = L"\\\\.\\pipe\\adblockplusengine_" + GetUserName ();
12 const int bufferSize = 1024; 27 const int bufferSize = 1024;
13 std::auto_ptr<AdblockPlus::FilterEngine> filterEngine; 28 std::auto_ptr<AdblockPlus::FilterEngine> filterEngine;
14 HANDLE filterEngineMutex;
15 29
16 class AutoHandle 30 class AutoHandle
17 { 31 {
18 public: 32 public:
19 AutoHandle() 33 AutoHandle()
20 { 34 {
21 } 35 }
22 36
23 AutoHandle(HANDLE handle) : handle(handle) 37 AutoHandle(HANDLE handle) : handle(handle)
24 { 38 {
25 } 39 }
26 40
27 ~AutoHandle() 41 ~AutoHandle()
28 { 42 {
29 CloseHandle(handle); 43 CloseHandle(handle);
30 } 44 }
31 45
32 HANDLE get() 46 HANDLE get()
33 { 47 {
34 return handle; 48 return handle;
35 } 49 }
36 50
37 private: 51 private:
38 HANDLE handle; 52 HANDLE handle;
39 static int references;
Wladimir Palant 2013/05/16 11:52:16 Unused member?
Felix Dahlke 2013/05/23 12:31:20 Done.
40 53
41 AutoHandle(const AutoHandle& autoHandle); 54 AutoHandle(const AutoHandle& autoHandle);
42 AutoHandle& operator=(const AutoHandle& autoHandle); 55 AutoHandle& operator=(const AutoHandle& autoHandle);
43 }; 56 };
44 57
45 void Log(const std::string& message) 58 void Log(const std::string& message)
46 { 59 {
47 // TODO: Log to a log file 60 // TODO: Log to a log file
48 MessageBoxA(0, ("AdblockPlusEngine: " + message).c_str(), "", MB_OK); 61 MessageBoxA(0, ("AdblockPlusEngine: " + message).c_str(), "", MB_OK);
49 } 62 }
50 63
51 void LogLastError(const std::string& message) 64 void LogLastError(const std::string& message)
52 { 65 {
53 std::stringstream stream; 66 std::stringstream stream;
54 stream << message << " (Error code: " << GetLastError() << ")"; 67 stream << message << " (Error code: " << GetLastError() << ")";
55 Log(stream.str()); 68 Log(stream.str());
56 } 69 }
57 70
58 void LogException(const std::exception& exception) 71 void LogException(const std::exception& exception)
59 { 72 {
60 Log(std::string("An exception occurred: ") + exception.what()); 73 Log(std::string("An exception occurred: ") + exception.what());
61 } 74 }
62 75
63 std::string MarshalStrings(const std::vector<std::string>& strings) 76 std::string MarshalStrings(const std::vector<std::string>& strings)
64 { 77 {
65 // TODO: This is some pretty hacky marshalling, replace it with something mo re robust 78 // TODO: This is some pretty hacky marshalling, replace it with something mo re robust
Wladimir Palant 2013/05/16 11:52:16 That's an understatement :)
66 std::string marshalledStrings; 79 std::string marshalledStrings;
67 for (std::vector<std::string>::const_iterator it = strings.begin(); it != st rings.end(); it++) 80 for (std::vector<std::string>::const_iterator it = strings.begin(); it != st rings.end(); it++)
68 marshalledStrings += *it + ';'; 81 marshalledStrings += *it + ';';
69 return marshalledStrings; 82 return marshalledStrings;
70 } 83 }
71 84
72 std::vector<std::string> UnmarshalStrings(const std::string& message) 85 std::vector<std::string> UnmarshalStrings(const std::string& message)
73 { 86 {
74 std::stringstream stream(message); 87 std::stringstream stream(message);
75 std::vector<std::string> strings; 88 std::vector<std::string> strings;
76 std::string string; 89 std::string string;
77 while (std::getline(stream, string, ';')) 90 while (std::getline(stream, string, ';'))
78 strings.push_back(string); 91 strings.push_back(string);
79 return strings; 92 return strings;
80 } 93 }
81 94
82 std::string ToString(std::wstring value) 95 std::string ToUtf8String(std::wstring str)
83 { 96 {
84 int size = WideCharToMultiByte(CP_UTF8, 0, value.c_str(), -1, 0, 0, 0, 0); 97 size_t length = str.size();
Wladimir Palant 2013/05/16 11:52:16 WideCharToMultiByte() will return 0 on error, this
Felix Dahlke 2013/05/23 12:31:20 Done. Learnt something here: I thought std::string
85 char* converted = new char[size]; 98 if (length == 0)
Wladimir Palant 2013/05/16 11:52:16 std::auto_ptr please, don't delete manually.
Felix Dahlke 2013/05/23 12:31:20 Done.
86 WideCharToMultiByte(CP_UTF8, 0, value.c_str(), -1, converted, size, 0, 0); 99 return std::string();
87 std::string string(converted); 100
Wladimir Palant 2013/05/16 11:52:16 string(converted, size) please - you have an expli
Felix Dahlke 2013/05/17 09:11:36 Writing directly to the string buffer is still not
Felix Dahlke 2013/05/23 12:31:20 Decided to keep the function after all. ToUTF8Stri
Wladimir Palant 2013/05/23 14:25:01 No real objections but the function name should at
Felix Dahlke 2013/05/23 19:10:44 Just copied the function from libadblockplus, alth
88 delete converted; 101 DWORD utf8StringLength = WideCharToMultiByte(CP_UTF8, 0, str.c_str(), length , 0, 0, 0, 0);
89 return string; 102 if (utf8StringLength == 0)
103 throw std::runtime_error("Failed to determine the required buffer size");
104
105 std::string utf8String(utf8StringLength, '\0');
106 WideCharToMultiByte(CP_UTF8, 0, str.c_str(), length, &utf8String[0], utf8Str ingLength, 0, 0);
107 return utf8String;
90 } 108 }
91 109
92 std::string ReadMessage(HANDLE pipe) 110 std::string ReadMessage(HANDLE pipe)
93 { 111 {
94 std::stringstream stream; 112 std::stringstream stream;
95 std::auto_ptr<char> buffer(new char[bufferSize]); 113 std::auto_ptr<char> buffer(new char[bufferSize]);
96 bool hasError; 114 bool doneReading = false;
97 do 115 while (!doneReading)
98 { 116 {
99 DWORD bytesRead; 117 DWORD bytesRead;
100 hasError = !ReadFile(pipe, buffer.get(), bufferSize * sizeof(char), &bytes Read, 0); 118 if (ReadFile(pipe, buffer.get(), bufferSize * sizeof(char), &bytesRead, 0) )
101 if (hasError && GetLastError() != ERROR_MORE_DATA) 119 doneReading = true;
120 else if (GetLastError() != ERROR_MORE_DATA)
102 { 121 {
103 std::stringstream stream; 122 std::stringstream stream;
104 stream << "Error reading from pipe: " << GetLastError(); 123 stream << "Error reading from pipe: " << GetLastError();
105 throw std::runtime_error(stream.str()); 124 throw std::runtime_error(stream.str());
106 } 125 }
107 stream << std::string(buffer.get(), bytesRead); 126 stream << std::string(buffer.get(), bytesRead);
108 } while (hasError); 127 }
Wladimir Palant 2013/05/16 11:52:16 The flow is rather hard to follow here, particular
Felix Dahlke 2013/05/23 12:31:20 Done.
109 return stream.str(); 128 return stream.str();
110 } 129 }
111 130
112 void WriteMessage(HANDLE pipe, const std::string& message) 131 void WriteMessage(HANDLE pipe, const std::string& message)
113 { 132 {
114 DWORD bytesWritten; 133 DWORD bytesWritten;
115 if (!WriteFile(pipe, message.c_str(), message.length(), &bytesWritten, 0)) 134 if (!WriteFile(pipe, message.c_str(), message.length(), &bytesWritten, 0))
116 throw std::runtime_error("Failed to write to pipe"); 135 throw std::runtime_error("Failed to write to pipe");
Wladimir Palant 2013/05/16 11:52:16 What if bytesWritten < message.length()? According
Felix Dahlke 2013/05/17 09:11:36 Yes, it's blocking. And writing long strings works
117 } 136 }
118 137
119 std::string HandleRequest(const std::vector<std::string>& strings) 138 std::string HandleRequest(const std::vector<std::string>& strings)
120 { 139 {
121 std::string procedureName = strings[0]; 140 std::string procedureName = strings[0];
122 if (procedureName == "Matches") 141 if (procedureName == "Matches")
123 return filterEngine->Matches(strings[1], strings[2], strings[3]) ? "1" : " 0"; 142 return filterEngine->Matches(strings[1], strings[2], strings[3]) ? "1" : " 0";
124 if (procedureName == "GetElementHidingSelectors") 143 if (procedureName == "GetElementHidingSelectors")
125 return MarshalStrings(filterEngine->GetElementHidingSelectors(strings[1])) ; 144 return MarshalStrings(filterEngine->GetElementHidingSelectors(strings[1])) ;
126 return ""; 145 return "";
127 } 146 }
128 147
129 DWORD WINAPI ClientThread(LPVOID param) 148 DWORD WINAPI ClientThread(LPVOID param)
130 { 149 {
131 HANDLE pipe = static_cast<HANDLE>(param); 150 HANDLE pipe = static_cast<HANDLE>(param);
132 151
133 try 152 try
134 { 153 {
135 std::string message = ReadMessage(pipe); 154 std::string message = ReadMessage(pipe);
136 std::vector<std::string> strings = UnmarshalStrings(message); 155 std::vector<std::string> strings = UnmarshalStrings(message);
137 WaitForSingleObject(filterEngineMutex, INFINITE);
Wladimir Palant 2013/05/16 11:52:16 Doesn't this duplicate the functionality of mutexe
Felix Dahlke 2013/05/23 12:31:20 Done.
138 std::string response = HandleRequest(strings); 156 std::string response = HandleRequest(strings);
139 ReleaseMutex(filterEngineMutex);
140 WriteMessage(pipe, response); 157 WriteMessage(pipe, response);
141 } 158 }
142 catch (const std::exception& e) 159 catch (const std::exception& e)
143 { 160 {
144 LogException(e); 161 LogException(e);
145 } 162 }
146 163
164 // TODO: Keep the pipe open until the client disconnects
147 FlushFileBuffers(pipe); 165 FlushFileBuffers(pipe);
148 DisconnectNamedPipe(pipe); 166 DisconnectNamedPipe(pipe);
149 CloseHandle(pipe); 167 CloseHandle(pipe);
Wladimir Palant 2013/05/16 11:52:16 Opening a new connection for each calls sounds lik
Felix Dahlke 2013/05/17 09:11:36 I previously read that there is a limit to the num
Felix Dahlke 2013/05/23 12:31:20 Began to work on this, but it's a bit tricky and I
150 return 0; 168 return 0;
151 } 169 }
152 } 170
153 171 bool IsWindowsVistaOrLater()
154 bool IsWindowsVistaOrLater() 172 {
Wladimir Palant 2013/05/16 11:52:16 Any reason why some functions are inside an empty
Felix Dahlke 2013/05/17 09:11:36 Forgot to move it there.
155 { 173 OSVERSIONINFOEX osvi;
156 OSVERSIONINFOEX osvi; 174 ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
157 ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX)); 175 osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
158 osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); 176 GetVersionEx(reinterpret_cast<LPOSVERSIONINFO>(&osvi));
159 GetVersionEx(reinterpret_cast<LPOSVERSIONINFO>(&osvi)); 177 return osvi.dwMajorVersion >= 6;
160 return osvi.dwMajorVersion >= 6; 178 }
161 } 179 }
162 180
163 std::wstring GetAppDataPath() 181 std::wstring GetAppDataPath()
164 { 182 {
165 wchar_t appDataPath[MAX_PATH]; 183 std::wstring appDataPath;
166 if (IsWindowsVistaOrLater()) 184 if (IsWindowsVistaOrLater())
167 { 185 {
168 WCHAR* dataPath; 186 WCHAR* pathBuffer;
169 if (FAILED(SHGetKnownFolderPath(FOLDERID_LocalAppDataLow, 0, 0, &dataPath))) 187 if (FAILED(SHGetKnownFolderPath(FOLDERID_LocalAppDataLow, 0, 0, &pathBuffer) ))
Wladimir Palant 2013/05/16 11:52:16 I don't think that statically compiling against SH
Felix Dahlke 2013/05/23 12:31:20 I'll leave this to Oleksandr, since he's working o
Oleksandr 2013/05/23 12:38:11 We are not statically compiling against SHGetKnown
170 throw std::runtime_error("Unable to find app data directory"); 188 throw std::runtime_error("Unable to find app data directory");
171 wcscpy_s(appDataPath, dataPath); 189 appDataPath.assign(pathBuffer);
Wladimir Palant 2013/05/16 11:52:16 Why do we need two copy operations here? Convertin
Felix Dahlke 2013/05/17 09:11:36 I think that's because SHGetKnownFolderPath wants
Wladimir Palant 2013/05/17 10:35:02 Then maybe get rid of that assumption? :) Declare
Felix Dahlke 2013/05/23 12:31:20 Done.
172 CoTaskMemFree(dataPath); 190 CoTaskMemFree(pathBuffer);
173 } 191 }
174 else 192 else
175 { 193 {
176 if (!SHGetSpecialFolderPath(0, appDataPath, CSIDL_LOCAL_APPDATA, true)) 194 std::auto_ptr<wchar_t> pathBuffer(new wchar_t[MAX_PATH]);
195 if (!SHGetSpecialFolderPath(0, pathBuffer.get(), CSIDL_LOCAL_APPDATA, true))
177 throw std::runtime_error("Unable to find app data directory"); 196 throw std::runtime_error("Unable to find app data directory");
178 } 197 appDataPath.assign(pathBuffer.get());
179 return std::wstring(appDataPath) + L"\\AdblockPlus"; 198 }
199 return appDataPath + L"\\AdblockPlus";
180 } 200 }
181 201
182 std::auto_ptr<AdblockPlus::FilterEngine> CreateFilterEngine() 202 std::auto_ptr<AdblockPlus::FilterEngine> CreateFilterEngine()
183 { 203 {
184 // TODO: Pass appInfo in, which should be sent by the client 204 // TODO: Pass appInfo in, which should be sent by the client
185 AdblockPlus::JsEnginePtr jsEngine = AdblockPlus::JsEngine::New(); 205 AdblockPlus::JsEnginePtr jsEngine = AdblockPlus::JsEngine::New();
186 std::string dataPath = ToString(GetAppDataPath()); 206 std::string dataPath = ToUtf8String(GetAppDataPath());
187 dynamic_cast<AdblockPlus::DefaultFileSystem*>(jsEngine->GetFileSystem().get()) ->SetBasePath(dataPath); 207 dynamic_cast<AdblockPlus::DefaultFileSystem*>(jsEngine->GetFileSystem().get()) ->SetBasePath(dataPath);
188 std::auto_ptr<AdblockPlus::FilterEngine> filterEngine(new AdblockPlus::FilterE ngine(jsEngine)); 208 std::auto_ptr<AdblockPlus::FilterEngine> filterEngine(new AdblockPlus::FilterE ngine(jsEngine));
189 int timeout = 5000;
190 while (!filterEngine->IsInitialized())
Wladimir Palant 2013/05/16 11:52:16 TODO comment here please - we don't really want to
Felix Dahlke 2013/05/17 09:11:36 I presume that's not necessary anymore, now that y
Wladimir Palant 2013/05/17 10:35:02 Not pushed yet but - yes, once that review is done
Felix Dahlke 2013/05/23 12:31:20 Done.
191 {
192 const int step = 10;
193 Sleep(step);
194 timeout -= step;
195 if (timeout <= 0)
196 throw std::runtime_error("Timeout while waiting for FilterEngine initializ ation");
197 }
198 std::vector<AdblockPlus::SubscriptionPtr> subscriptions = filterEngine->FetchA vailableSubscriptions(); 209 std::vector<AdblockPlus::SubscriptionPtr> subscriptions = filterEngine->FetchA vailableSubscriptions();
199 // TODO: Select a subscription based on the language, not just the first one. 210 // TODO: Select a subscription based on the language, not just the first one.
200 // This should ideally be done in libadblockplus. 211 // This should ideally be done in libadblockplus.
201 AdblockPlus::SubscriptionPtr subscription = subscriptions[0]; 212 AdblockPlus::SubscriptionPtr subscription = subscriptions[0];
202 subscription->AddToList(); 213 subscription->AddToList();
203 return filterEngine; 214 return filterEngine;
204 } 215 }
205 216
206 HANDLE CreatePipe(const std::wstring& pipeName) 217 HANDLE CreatePipe(const std::wstring& pipeName)
207 { 218 {
208 SECURITY_ATTRIBUTES sa; 219 SECURITY_ATTRIBUTES sa;
209 memset(&sa, 0, sizeof(SECURITY_ATTRIBUTES)); 220 memset(&sa, 0, sizeof(SECURITY_ATTRIBUTES));
210 sa.nLength = sizeof(SECURITY_ATTRIBUTES); 221 sa.nLength = sizeof(SECURITY_ATTRIBUTES);
211 222
212 // Low mandatory label. See http://msdn.microsoft.com/en-us/library/bb625958.a spx 223 // Low mandatory label. See http://msdn.microsoft.com/en-us/library/bb625958.a spx
213 LPCWSTR LOW_INTEGRITY_SDDL_SACL_W = L"S:(ML;;NW;;;LW)"; 224 LPCWSTR accessControlEntry = L"S:(ML;;NW;;;LW)";
214 PSECURITY_DESCRIPTOR securitydescriptor; 225 PSECURITY_DESCRIPTOR securitydescriptor;
215 ConvertStringSecurityDescriptorToSecurityDescriptor( 226 ConvertStringSecurityDescriptorToSecurityDescriptor(accessControlEntry, SDDL_R EVISION_1, &securitydescriptor, 0);
216 LOW_INTEGRITY_SDDL_SACL_W, SDDL_REVISION_1, &securitydescriptor, 0);
Wladimir Palant 2013/05/16 11:52:16 You are leaking this security descriptor, it needs
Felix Dahlke 2013/05/23 12:31:20 Done.
217 227
218 sa.lpSecurityDescriptor = securitydescriptor; 228 sa.lpSecurityDescriptor = securitydescriptor;
219 sa.bInheritHandle = TRUE; 229 sa.bInheritHandle = TRUE;
220 230
221 return CreateNamedPipe(pipeName.c_str(), PIPE_ACCESS_DUPLEX, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, 231 HANDLE pipe = CreateNamedPipe(pipeName.c_str(), PIPE_ACCESS_DUPLEX, PIPE_TYPE_ MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
222 PIPE_UNLIMITED_INSTANCES, bufferSize, bufferSize, 0, &s a); 232 PIPE_UNLIMITED_INSTANCES, bufferSize, bufferSize , 0, &sa);
233 LocalFree(securitydescriptor);
234 return pipe;
223 } 235 }
224 236
225 int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int) 237 int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
Wladimir Palant 2013/05/16 11:52:16 Second HINSTANCE parameter is hPrevInstance - you
Felix Dahlke 2013/05/23 12:31:20 Seems to be always 0 in Win32: http://blogs.msdn.c
Wladimir Palant 2013/05/23 14:25:01 Right, a bit of outdated knowledge on my end :-( F
Felix Dahlke 2013/05/23 19:10:44 How about we just try to create the pipe initially
Wladimir Palant 2013/05/23 20:10:15 Yes, that's something I thought about as well. Thi
226 { 238 {
227 filterEngine = CreateFilterEngine(); 239 filterEngine = CreateFilterEngine();
228 filterEngineMutex = CreateMutex(0, false, 0);
229 240
230 for (;;) 241 for (;;)
231 { 242 {
232 HANDLE pipe = CreatePipe(pipeName); 243 HANDLE pipe = CreatePipe(pipeName);
233 if (pipe == INVALID_HANDLE_VALUE) 244 if (pipe == INVALID_HANDLE_VALUE)
234 { 245 {
235 LogLastError("CreateNamedPipe failed"); 246 LogLastError("CreateNamedPipe failed");
236 return 1; 247 return 1;
237 } 248 }
238 249
239 if (!ConnectNamedPipe(pipe, 0)) 250 if (!ConnectNamedPipe(pipe, 0))
240 { 251 {
241 LogLastError("Client failed to connect"); 252 LogLastError("Client failed to connect");
242 CloseHandle(pipe); 253 CloseHandle(pipe);
243 continue; 254 continue;
244 } 255 }
245 256
246 // TODO: Count established connections, kill the engine when none are left 257 // TODO: Count established connections, kill the engine when none are left
247 258
248 AutoHandle thread(CreateThread(0, 0, ClientThread, static_cast<LPVOID>(pipe) , 0, 0)); 259 AutoHandle thread(CreateThread(0, 0, ClientThread, static_cast<LPVOID>(pipe) , 0, 0));
249 if (!thread.get()) 260 if (!thread.get())
250 { 261 {
251 LogLastError("CreateThread failed"); 262 LogLastError("CreateThread failed");
252 return 1; 263 return 1;
253 } 264 }
254 } 265 }
255 266
256 return 0; 267 return 0;
257 } 268 }
Wladimir Palant 2013/05/16 11:52:16 IMHO, ReadMessage(), WriteMessage(), MarshalString
Felix Dahlke 2013/05/17 09:11:36 Same here, there's a TODO for that :)
LEFTRIGHT

Powered by Google App Engine
This is Rietveld