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

Side by Side Diff: AdblockPlusEngine/main.cpp

Issue 10580043: Run a single FilterEngine instance in a separate process (Closed)
Patch Set: Created May 16, 2013, 5:29 a.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 #include <AdblockPlus.h>
2 #include <iostream>
3 #include <ShlObj.h>
4 #include <sstream>
5 #include <vector>
6 #include <Windows.h>
7 #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 namespace
10 {
11 const std::wstring pipeName = L"\\\\.\\pipe\\adblockplusengine";
12 const int bufferSize = 1024;
13 std::auto_ptr<AdblockPlus::FilterEngine> filterEngine;
14 HANDLE filterEngineMutex;
15
16 class AutoHandle
17 {
18 public:
19 AutoHandle()
20 {
21 }
22
23 AutoHandle(HANDLE handle) : handle(handle)
24 {
25 }
26
27 ~AutoHandle()
28 {
29 CloseHandle(handle);
30 }
31
32 HANDLE get()
33 {
34 return handle;
35 }
36
37 private:
38 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
41 AutoHandle(const AutoHandle& autoHandle);
42 AutoHandle& operator=(const AutoHandle& autoHandle);
43 };
44
45 void Log(const std::string& message)
46 {
47 // TODO: Log to a log file
48 MessageBoxA(0, ("AdblockPlusEngine: " + message).c_str(), "", MB_OK);
49 }
50
51 void LogLastError(const std::string& message)
52 {
53 std::stringstream stream;
54 stream << message << " (Error code: " << GetLastError() << ")";
55 Log(stream.str());
56 }
57
58 void LogException(const std::exception& exception)
59 {
60 Log(std::string("An exception occurred: ") + exception.what());
61 }
62
63 std::string MarshalStrings(const std::vector<std::string>& strings)
64 {
65 // 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;
67 for (std::vector<std::string>::const_iterator it = strings.begin(); it != st rings.end(); it++)
68 marshalledStrings += *it + ';';
69 return marshalledStrings;
70 }
71
72 std::vector<std::string> UnmarshalStrings(const std::string& message)
73 {
74 std::stringstream stream(message);
75 std::vector<std::string> strings;
76 std::string string;
77 while (std::getline(stream, string, ';'))
78 strings.push_back(string);
79 return strings;
80 }
81
82 std::string ToString(std::wstring value)
83 {
84 int size = WideCharToMultiByte(CP_UTF8, 0, value.c_str(), -1, 0, 0, 0, 0);
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];
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);
87 std::string string(converted);
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;
89 return string;
90 }
91
92 std::string ReadMessage(HANDLE pipe)
93 {
94 std::stringstream stream;
95 std::auto_ptr<char> buffer(new char[bufferSize]);
96 bool hasError;
97 do
98 {
99 DWORD bytesRead;
100 hasError = !ReadFile(pipe, buffer.get(), bufferSize * sizeof(char), &bytes Read, 0);
101 if (hasError && GetLastError() != ERROR_MORE_DATA)
102 {
103 std::stringstream stream;
104 stream << "Error reading from pipe: " << GetLastError();
105 throw std::runtime_error(stream.str());
106 }
107 stream << std::string(buffer.get(), bytesRead);
108 } while (hasError);
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();
110 }
111
112 void WriteMessage(HANDLE pipe, const std::string& message)
113 {
114 DWORD bytesWritten;
115 if (!WriteFile(pipe, message.c_str(), message.length(), &bytesWritten, 0))
116 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 }
118
119 std::string HandleRequest(const std::vector<std::string>& strings)
120 {
121 std::string procedureName = strings[0];
122 if (procedureName == "Matches")
123 return filterEngine->Matches(strings[1], strings[2], strings[3]) ? "1" : " 0";
124 if (procedureName == "GetElementHidingSelectors")
125 return MarshalStrings(filterEngine->GetElementHidingSelectors(strings[1])) ;
126 return "";
127 }
128
129 DWORD WINAPI ClientThread(LPVOID param)
130 {
131 HANDLE pipe = static_cast<HANDLE>(param);
132
133 try
134 {
135 std::string message = ReadMessage(pipe);
136 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);
139 ReleaseMutex(filterEngineMutex);
140 WriteMessage(pipe, response);
141 }
142 catch (const std::exception& e)
143 {
144 LogException(e);
145 }
146
147 FlushFileBuffers(pipe);
148 DisconnectNamedPipe(pipe);
149 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;
151 }
152 }
153
154 bool IsWindowsVistaOrLater()
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 {
156 OSVERSIONINFOEX osvi;
157 ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
158 osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
159 GetVersionEx(reinterpret_cast<LPOSVERSIONINFO>(&osvi));
160 return osvi.dwMajorVersion >= 6;
161 }
162
163 std::wstring GetAppDataPath()
164 {
165 wchar_t appDataPath[MAX_PATH];
166 if (IsWindowsVistaOrLater())
167 {
168 WCHAR* dataPath;
169 if (FAILED(SHGetKnownFolderPath(FOLDERID_LocalAppDataLow, 0, 0, &dataPath)))
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");
171 wcscpy_s(appDataPath, dataPath);
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);
173 }
174 else
175 {
176 if (!SHGetSpecialFolderPath(0, appDataPath, CSIDL_LOCAL_APPDATA, true))
177 throw std::runtime_error("Unable to find app data directory");
178 }
179 return std::wstring(appDataPath) + L"\\AdblockPlus";
180 }
181
182 std::auto_ptr<AdblockPlus::FilterEngine> CreateFilterEngine()
183 {
184 // TODO: Pass appInfo in, which should be sent by the client
185 AdblockPlus::JsEnginePtr jsEngine = AdblockPlus::JsEngine::New();
186 std::string dataPath = ToString(GetAppDataPath());
187 dynamic_cast<AdblockPlus::DefaultFileSystem*>(jsEngine->GetFileSystem().get()) ->SetBasePath(dataPath);
188 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();
199 // TODO: Select a subscription based on the language, not just the first one.
200 // This should ideally be done in libadblockplus.
201 AdblockPlus::SubscriptionPtr subscription = subscriptions[0];
202 subscription->AddToList();
203 return filterEngine;
204 }
205
206 HANDLE CreatePipe(const std::wstring& pipeName)
207 {
208 SECURITY_ATTRIBUTES sa;
209 memset(&sa, 0, sizeof(SECURITY_ATTRIBUTES));
210 sa.nLength = sizeof(SECURITY_ATTRIBUTES);
211
212 // 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)";
214 PSECURITY_DESCRIPTOR securitydescriptor;
215 ConvertStringSecurityDescriptorToSecurityDescriptor(
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
218 sa.lpSecurityDescriptor = securitydescriptor;
219 sa.bInheritHandle = TRUE;
220
221 return CreateNamedPipe(pipeName.c_str(), PIPE_ACCESS_DUPLEX, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
222 PIPE_UNLIMITED_INSTANCES, bufferSize, bufferSize, 0, &s a);
223 }
224
225 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 {
227 filterEngine = CreateFilterEngine();
228 filterEngineMutex = CreateMutex(0, false, 0);
229
230 for (;;)
231 {
232 HANDLE pipe = CreatePipe(pipeName);
233 if (pipe == INVALID_HANDLE_VALUE)
234 {
235 LogLastError("CreateNamedPipe failed");
236 return 1;
237 }
238
239 if (!ConnectNamedPipe(pipe, 0))
240 {
241 LogLastError("Client failed to connect");
242 CloseHandle(pipe);
243 continue;
244 }
245
246 // TODO: Count established connections, kill the engine when none are left
247
248 AutoHandle thread(CreateThread(0, 0, ClientThread, static_cast<LPVOID>(pipe) , 0, 0));
249 if (!thread.get())
250 {
251 LogLastError("CreateThread failed");
252 return 1;
253 }
254 }
255
256 return 0;
257 }
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 :)
OLDNEW

Powered by Google App Engine
This is Rietveld