1
// Copyright 2006, Google Inc.
3
// Redistribution and use in source and binary forms, with or without
4
// modification, are permitted provided that the following conditions are met:
6
// 1. Redistributions of source code must retain the above copyright notice,
7
// this list of conditions and the following disclaimer.
8
// 2. Redistributions in binary form must reproduce the above copyright notice,
9
// this list of conditions and the following disclaimer in the documentation
10
// and/or other materials provided with the distribution.
11
// 3. Neither the name of Google Inc. nor the names of its contributors may be
12
// used to endorse or promote products derived from this software without
13
// specific prior written permission.
15
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
16
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
17
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
18
// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
19
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
21
// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
22
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23
// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
24
// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
#include "gears/localserver/ie/http_handler_patch.h"
31
#include "gears/base/common/mutex.h"
32
#include "gears/base/common/string_utils.h"
33
#include "gears/localserver/common/http_constants.h"
34
#include "gears/localserver/ie/http_intercept.h"
35
#include "gears/localserver/ie/http_patches.h"
36
#include "gears/localserver/ie/urlmon_utils.h"
40
//------------------------------------------------------------------------------
41
// ActiveHandlers is a collection of live HttpHandlerPatch instances in the
42
// process. This map is used to quickly lookup the HttpHandlerPatch associated
43
// with an instance of the URLMON's default handler. This is frequently NULL.
44
// Each patched method call involves a lookup. For this reason, we cache the
45
// most recent lookup to avoid calling map find repeadly while streaming in a
48
// TODO(michaeln): Look into http://code.google.com/p/google-gears/issues/detail?id=182
50
//------------------------------------------------------------------------------
51
class ActiveHandlers {
53
ActiveHandlers() : cached_urlmon_instance_(NULL), cached_handler_(NULL) {
60
void Add(IInternetProtocolRoot *urlmon_instance, HttpHandlerPatch *handler) {
61
assert(urlmon_instance && handler);
62
MutexLock lock(&mutex_);
63
assert(map_.find(urlmon_instance) == map_.end());
64
map_[urlmon_instance] = handler;
65
if (cached_urlmon_instance_ == urlmon_instance) {
66
cached_handler_ = handler;
70
void Remove(IInternetProtocolRoot *urlmon_instance) {
71
assert(urlmon_instance);
72
MutexLock lock(&mutex_);
73
assert(map_.find(urlmon_instance) != map_.end());
74
map_.erase(urlmon_instance);
75
if (cached_urlmon_instance_ == urlmon_instance) {
76
cached_handler_ = NULL;
80
HttpHandlerPatch* Lookup(IInternetProtocolRoot *urlmon_instance) {
81
assert(urlmon_instance);
82
MutexLock lock(&mutex_);
83
if (urlmon_instance == cached_urlmon_instance_) {
84
return cached_handler_;
86
HandlerMap::iterator iter = map_.find(urlmon_instance);
87
cached_urlmon_instance_ = urlmon_instance;
88
cached_handler_ = (iter == map_.end()) ? NULL : iter->second;
89
return cached_handler_;
93
typedef std::map<IInternetProtocolRoot*, HttpHandlerPatch*> HandlerMap;
96
IInternetProtocolRoot *cached_urlmon_instance_;
97
HttpHandlerPatch *cached_handler_;
102
static ActiveHandlers active_handlers;
103
static bool has_installed_handler = false;
106
HRESULT HttpHandlerPatch::Install() {
107
MutexLock lock(&global_mutex_);
108
if (has_installed_handler)
110
has_installed_handler = true;
111
return InitializeHttpPatches();
115
HttpHandlerPatch *HttpHandlerPatch::Find(
116
IInternetProtocolRoot *urlmon_instance) {
117
return active_handlers.Lookup(urlmon_instance);
121
//------------------------------------------------------------------------------
122
// ReplacementSink is used to hijack redirects from URLs not in our cache to
123
// URLs that are in our cache.
125
// When redirection occurs, generally a new handler is not started, a chain of
126
// redirects are handled within the context of a single URLMON instance.
127
// This presents a problem for us since Start or StartEx will not be called
128
// for the new location of the redirect. When we defer to the default handler,
129
// we wrap the caller's "sink" with an instance of our ReplacementSink. Our
130
// code monitors the ReportProgress calls and detects when a 302 back into
131
// our cache occurs and intervenes to satisfy the request from our cache.
132
//------------------------------------------------------------------------------
133
class ATL_NO_VTABLE ReplacementSink :
134
public CComObjectRootEx<CComMultiThreadModel>,
135
public IInternetProtocolSink {
137
BEGIN_COM_MAP(ReplacementSink)
138
COM_INTERFACE_ENTRY(IInternetProtocolSink)
139
COM_INTERFACE_ENTRY_AGGREGATE_BLIND(sink_.p)
142
void Initialize(IInternetProtocolSink *sink) {
147
STDMETHOD(Switch)(PROTOCOLDATA *data) {
148
return sink_->Switch(data);
151
STDMETHOD(ReportData)(DWORD bscf, ULONG progress, ULONG progress_max) {
152
return sink_->ReportData(bscf, progress, progress_max);
155
STDMETHOD(ReportResult)(HRESULT result, DWORD error, LPCWSTR result_text) {
156
return sink_->ReportResult(result, error, result_text);
159
STDMETHOD(ReportProgress)(ULONG status, LPCWSTR text);
161
static void CreateReplacement(IInternetProtocolSink *orig,
162
IInternetProtocolSink **replacement) {
163
CComObject<ReplacementSink>* replacement_sink = NULL;
164
HRESULT hr = CComObject<ReplacementSink>::CreateInstance(&replacement_sink);
166
replacement_sink->Initialize(orig);
167
replacement_sink->AddRef();
168
*replacement = replacement_sink;
173
CComPtr<IInternetProtocolSink> sink_;
177
STDMETHODIMP ReplacementSink::ReportProgress(
178
/* [in] */ ULONG status_code,
179
/* [in] */ LPCWSTR status_text) {
181
LOG16((L"ReplacementSink::ReportProgress( %s, %s )\n",
182
GetBindStatusLabel(status_code),
183
status_text ? status_text : L""));
185
if (status_code == BINDSTATUS_REDIRECTING) {
186
// status_text contains the redirect url in this case
187
WebCacheDB* db = WebCacheDB::GetDB();
188
if (db && db->CanService(status_text, NULL)) {
189
// Here we detect redirect into our cache here and intervene to hijack
190
// handling of the request. Reporting a result of INET_E_REDIRECT_FAILED
191
// causes URMLON to abandon the current handler instance and create a new
192
// instance to follow the redirect. When that new instance of our handler
193
// is created, it will satisfy the request locally.
194
LOG16((L"ReplacementSink::ReportProgress - hijacking redirect\n"));
195
return sink_->ReportResult(INET_E_REDIRECT_FAILED,
196
HttpConstants::HTTP_FOUND,
200
return sink_->ReportProgress(status_code, status_text);
204
//------------------------------------------------------------------------------
205
// HttpHandlerPatch is used to retrieve resources from our cache. When
206
// StartMaybe or StartExMaybe is called, we determine whether
207
// to satisfy the request or to defer to the default handler. If we're handling
208
// the request subsequent method calls on the default handler will be delivered
209
// to our instance, otherwise our instance will be deleted upon return from
210
// StartMaybe or StartExMaybe.
211
//------------------------------------------------------------------------------
213
HttpHandlerPatch::HttpHandlerPatch(IInternetProtocolRoot *urlmon_instance)
214
: urlmon_instance_(urlmon_instance) {
215
active_handlers.Add(urlmon_instance, this);
218
HttpHandlerPatch::~HttpHandlerPatch() {
219
LOG16((L"~HttpHandlerPatch\n"));
220
active_handlers.Remove(urlmon_instance_);
223
// IInternetProtocolEx
224
STDMETHODIMP HttpHandlerPatch::StartExMaybe(
225
/* [in] */ IUri *uri,
226
/* [in] */ IInternetProtocolSink *protocol_sink,
227
/* [in] */ IInternetBindInfo *bind_info,
228
/* [in] */ DWORD flags,
229
/* [in] */ HANDLE_PTR reserved,
230
/* [out] */IInternetProtocolSink **replacement_sink) {
232
HRESULT rv = uri->GetAbsoluteUri(&uri_bstr);
236
LOG16((L"HttpHandlerPatch::StartEx( %s )\n", uri_bstr.m_str));
238
rv = StartImpl(uri_bstr.m_str, protocol_sink, bind_info, flags, reserved);
239
if (rv == INET_E_USE_DEFAULT_PROTOCOLHANDLER) {
240
if (is_passingthru_) {
241
ReplacementSink::CreateReplacement(protocol_sink, replacement_sink);
243
protocol_sink_.Release();
244
http_negotiate_.Release();
252
// IInternetProtocolRoot
253
STDMETHODIMP HttpHandlerPatch::StartMaybe(
254
/* [in] */ LPCWSTR url,
255
/* [in] */ IInternetProtocolSink *protocol_sink,
256
/* [in] */ IInternetBindInfo *bind_info,
257
/* [in] */ DWORD flags,
258
/* [in] */ HANDLE_PTR reserved,
259
/* [out] */IInternetProtocolSink **replacement_sink) {
260
LOG16((L"HttpHandlerPatch::Start( %s )\n", url));
262
HRESULT rv = StartImpl(url, protocol_sink, bind_info, flags, reserved);
263
if (rv == INET_E_USE_DEFAULT_PROTOCOLHANDLER) {
264
if (is_passingthru_) {
265
ReplacementSink::CreateReplacement(protocol_sink, replacement_sink);
267
protocol_sink_.Release();
268
http_negotiate_.Release();
276
STDMETHODIMP HttpHandlerPatch::Continue(
277
/* [in] */ PROTOCOLDATA *data) {
278
LOG16((L"HttpHandlerPatch::Continue(data)\n"));
282
STDMETHODIMP HttpHandlerPatch::Abort(
283
/* [in] */ HRESULT reason,
284
/* [in] */ DWORD options) {
285
LOG16((L"HttpHandlerPatch::Abort()\n"));
287
// We intentionally don't propogate the return value from this method,
288
// our handler is aborted regardless of the sink's return value.
289
CallReportResult(reason, E_ABORT, L"Aborted");
293
STDMETHODIMP HttpHandlerPatch::Terminate(
294
/* [in] */ DWORD options) {
295
LOG16((L"HttpHandlerPatch::Terminate()\n"));
296
protocol_sink_.Release();
297
http_negotiate_.Release();
298
was_terminated_ = true;
303
STDMETHODIMP HttpHandlerPatch::Suspend() {
304
LOG16((L"HttpHandlerPatch::Suspend()\n"));
308
STDMETHODIMP HttpHandlerPatch::Resume() {
309
LOG16((L"HttpHandlerPatch::Resume()\n"));
314
STDMETHODIMP HttpHandlerPatch::Read(
315
/* [in, out] */ void *pv,
317
/* [out] */ ULONG *pcbRead) {
318
return ReadImpl(pv, cb, pcbRead);
321
STDMETHODIMP HttpHandlerPatch::Seek(
322
/* [in] */ LARGE_INTEGER dlibMove,
323
/* [in] */ DWORD dwOrigin,
324
/* [out] */ ULARGE_INTEGER *plibNewPosition) {
325
LOG16((L"HttpHandlerPatch::Seek()\n"));
329
STDMETHODIMP HttpHandlerPatch::LockRequest(
330
/* [in] */ DWORD dwOptions) {
331
LOG16((L"HttpHandlerPatch::LockRequest()\n"));
336
STDMETHODIMP HttpHandlerPatch::UnlockRequest() {
337
LOG16((L"HttpHandlerPatch::UnlockRequest()\n"));
343
STDMETHODIMP HttpHandlerPatch::QueryOption(
344
/* [in] */ DWORD option,
345
/* [in, out] */ LPVOID buffer,
346
/* [in, out] */ DWORD *len) {
347
LOG16((L"HttpHandlerPatch::QueryOption(%d)\n", option));
348
return QueryOptionImpl(option, buffer, len);
352
STDMETHODIMP HttpHandlerPatch::QueryInfo(
353
/* [in] */ DWORD option,
354
/* [in, out] */ LPVOID buffer,
355
/* [in, out] */ DWORD *len,
356
/* [in, out] */ DWORD *flags,
357
/* [in, out] */ DWORD *reserved) {
358
LOG16((L"HttpHandlerPatch::QueryInfo(%d)\n", option));
359
return QueryInfoImpl(option, buffer, len, flags, reserved);
364
void HttpHandlerPatch::SelfTest() {
365
// We probe the behavior of these interfaces that we have not patched.
366
// The start method of the underlying urlmon instance has not been called,
367
// and we want to observe their behavior when these methods get called while
368
// the object is in that state. This is invoked when handling a request for
369
// http://gears_self_test/
370
CComQIPtr<IInternetPriority> priority(urlmon_instance_);
371
CComQIPtr<IInternetThreadSwitch> thread_switch(urlmon_instance_);
372
CComQIPtr<IWinInetCacheHints> cache_hints(urlmon_instance_);
373
CComQIPtr<IWinInetCacheHints2> cache_hints2(urlmon_instance_);
374
CComQIPtr<IUriContainer> uri_container(urlmon_instance_);
378
hr = priority->GetPriority(&value);
379
LOG16((L"IInternetPriority::GetPrioriy = %d\n", hr));
380
hr = priority->SetPriority(value);
381
LOG16((L"IInternetPriority::SetPriority = %d\n", hr));
382
hr = thread_switch->Prepare();
383
LOG16((L"IInternetThreadSwitch::Prepare = %d\n", hr));
384
hr = thread_switch->Continue();
385
LOG16((L"IInternetThreadSwitch::Continue = %d\n", hr));
389
DWORD buffer_size = sizeof(buffer);
392
hr = cache_hints->SetCacheExtension(L".bla",
397
LOG16((L"IWinInetCacheHints::SetCacheExtension = %d, %d\n", hr, error));
401
DWORD buffer_size = sizeof(buffer);
404
hr = cache_hints2->SetCacheExtension2(L".bla",
409
LOG16((L"IWinInetCacheHints2::SetCacheExtension2 = %d, %d\n", hr, error));
414
hr = uri_container->GetIUri(&uri);
415
LOG16((L"IUriContainer::GetIUri = %d\n", hr));
418
hr = uri->GetAbsoluteUri(&url);
419
LOG16((L"uri->GetAbsoluteUri = %d, %s\n", hr,
420
url.m_str ? url.m_str : L"null"));