~ubuntu-branches/ubuntu/karmic/gears/karmic

« back to all changes in this revision

Viewing changes to gears/localserver/ie/http_handler_patch.cc

  • Committer: Bazaar Package Importer
  • Author(s): Stefan Lesicnik
  • Date: 2009-04-30 19:15:25 UTC
  • Revision ID: james.westby@ubuntu.com-20090430191525-0790sb5wzg8ou0xb
Tags: upstream-0.5.21.0~svn3334+dfsg
ImportĀ upstreamĀ versionĀ 0.5.21.0~svn3334+dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// Copyright 2006, Google Inc.
 
2
//
 
3
// Redistribution and use in source and binary forms, with or without
 
4
// modification, are permitted provided that the following conditions are met:
 
5
//
 
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.
 
14
//
 
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.
 
25
 
 
26
#include "gears/localserver/ie/http_handler_patch.h"
 
27
 
 
28
#include <map>
 
29
#include <wininet.h>
 
30
 
 
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"
 
37
 
 
38
namespace {
 
39
 
 
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
 
46
// response.
 
47
//
 
48
// TODO(michaeln): Look into http://code.google.com/p/google-gears/issues/detail?id=182
 
49
// and this new impl?
 
50
//------------------------------------------------------------------------------
 
51
class ActiveHandlers {
 
52
 public:
 
53
  ActiveHandlers() : cached_urlmon_instance_(NULL), cached_handler_(NULL) {
 
54
  }
 
55
 
 
56
  ~ActiveHandlers() {
 
57
    assert(map_.empty());
 
58
  }
 
59
 
 
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;
 
67
    }
 
68
  }
 
69
 
 
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;
 
77
    }
 
78
  }
 
79
 
 
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_;
 
85
    }
 
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_;
 
90
  }
 
91
 
 
92
 private:
 
93
  typedef std::map<IInternetProtocolRoot*, HttpHandlerPatch*> HandlerMap;
 
94
  Mutex mutex_;
 
95
  HandlerMap map_;
 
96
  IInternetProtocolRoot *cached_urlmon_instance_;
 
97
  HttpHandlerPatch *cached_handler_;
 
98
};
 
99
 
 
100
}  // namespace
 
101
 
 
102
static ActiveHandlers active_handlers;
 
103
static bool has_installed_handler = false;
 
104
 
 
105
// static
 
106
HRESULT HttpHandlerPatch::Install() {
 
107
  MutexLock lock(&global_mutex_);
 
108
  if (has_installed_handler)
 
109
    return S_OK;
 
110
  has_installed_handler = true;
 
111
  return InitializeHttpPatches();
 
112
}
 
113
 
 
114
// static
 
115
HttpHandlerPatch *HttpHandlerPatch::Find(
 
116
                      IInternetProtocolRoot *urlmon_instance) {
 
117
  return active_handlers.Lookup(urlmon_instance);
 
118
}
 
119
 
 
120
 
 
121
//------------------------------------------------------------------------------
 
122
// ReplacementSink is used to hijack redirects from URLs not in our cache to
 
123
// URLs that are in our cache.
 
124
//
 
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 {
 
136
 public:
 
137
  BEGIN_COM_MAP(ReplacementSink)
 
138
    COM_INTERFACE_ENTRY(IInternetProtocolSink)
 
139
    COM_INTERFACE_ENTRY_AGGREGATE_BLIND(sink_.p)
 
140
  END_COM_MAP()
 
141
 
 
142
  void Initialize(IInternetProtocolSink *sink) {
 
143
    assert(sink);
 
144
    sink_ = sink;
 
145
  }
 
146
 
 
147
  STDMETHOD(Switch)(PROTOCOLDATA *data) {
 
148
    return sink_->Switch(data);
 
149
  }
 
150
 
 
151
  STDMETHOD(ReportData)(DWORD bscf, ULONG progress, ULONG progress_max) {
 
152
    return sink_->ReportData(bscf, progress, progress_max);
 
153
  }
 
154
 
 
155
  STDMETHOD(ReportResult)(HRESULT result, DWORD error, LPCWSTR result_text) {
 
156
    return sink_->ReportResult(result, error, result_text);
 
157
  }
 
158
 
 
159
  STDMETHOD(ReportProgress)(ULONG status, LPCWSTR text);
 
160
 
 
161
  static void CreateReplacement(IInternetProtocolSink *orig,
 
162
                                IInternetProtocolSink **replacement) {
 
163
    CComObject<ReplacementSink>* replacement_sink = NULL;
 
164
    HRESULT hr = CComObject<ReplacementSink>::CreateInstance(&replacement_sink);
 
165
    if (SUCCEEDED(hr)) {
 
166
      replacement_sink->Initialize(orig);
 
167
      replacement_sink->AddRef();
 
168
      *replacement = replacement_sink;
 
169
    }
 
170
  }
 
171
 
 
172
 private:
 
173
  CComPtr<IInternetProtocolSink> sink_;
 
174
};
 
175
 
 
176
 
 
177
STDMETHODIMP ReplacementSink::ReportProgress(
 
178
    /* [in] */ ULONG status_code,
 
179
    /* [in] */ LPCWSTR status_text) {
 
180
#ifdef DEBUG
 
181
  LOG16((L"ReplacementSink::ReportProgress( %s, %s )\n",
 
182
         GetBindStatusLabel(status_code),
 
183
         status_text ? status_text : L""));
 
184
#endif
 
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,
 
197
                                 status_text);
 
198
    }
 
199
  }
 
200
  return sink_->ReportProgress(status_code, status_text);
 
201
}
 
202
 
 
203
 
 
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
//------------------------------------------------------------------------------
 
212
 
 
213
HttpHandlerPatch::HttpHandlerPatch(IInternetProtocolRoot *urlmon_instance)
 
214
    : urlmon_instance_(urlmon_instance) {
 
215
  active_handlers.Add(urlmon_instance, this);
 
216
}
 
217
 
 
218
HttpHandlerPatch::~HttpHandlerPatch() {
 
219
  LOG16((L"~HttpHandlerPatch\n"));
 
220
  active_handlers.Remove(urlmon_instance_);
 
221
}
 
222
 
 
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) {
 
231
  CComBSTR uri_bstr;
 
232
  HRESULT rv = uri->GetAbsoluteUri(&uri_bstr);
 
233
  if (FAILED(rv)) {
 
234
    return rv;
 
235
  }
 
236
  LOG16((L"HttpHandlerPatch::StartEx( %s )\n", uri_bstr.m_str));
 
237
  Ref();
 
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);
 
242
    }
 
243
    protocol_sink_.Release();
 
244
    http_negotiate_.Release();
 
245
  }
 
246
  if (rv != S_OK) {
 
247
    Unref();
 
248
  }
 
249
  return rv;
 
250
}
 
251
 
 
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));
 
261
  Ref();
 
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);
 
266
    }
 
267
    protocol_sink_.Release();
 
268
    http_negotiate_.Release();
 
269
  }
 
270
  if (rv != S_OK) {
 
271
    Unref();
 
272
  }
 
273
  return rv;
 
274
}
 
275
 
 
276
STDMETHODIMP HttpHandlerPatch::Continue(
 
277
    /* [in] */ PROTOCOLDATA *data) {
 
278
  LOG16((L"HttpHandlerPatch::Continue(data)\n"));
 
279
  return S_OK;
 
280
}
 
281
 
 
282
STDMETHODIMP HttpHandlerPatch::Abort(
 
283
    /* [in] */ HRESULT reason,
 
284
    /* [in] */ DWORD options) {
 
285
  LOG16((L"HttpHandlerPatch::Abort()\n"));
 
286
  was_aborted_ = true;
 
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");
 
290
  return S_OK;
 
291
}
 
292
 
 
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;
 
299
  Unref();
 
300
  return S_OK;
 
301
}
 
302
 
 
303
STDMETHODIMP HttpHandlerPatch::Suspend() {
 
304
  LOG16((L"HttpHandlerPatch::Suspend()\n"));
 
305
  return S_OK;
 
306
}
 
307
 
 
308
STDMETHODIMP HttpHandlerPatch::Resume() {
 
309
  LOG16((L"HttpHandlerPatch::Resume()\n"));
 
310
  return S_OK;
 
311
}
 
312
 
 
313
// IInternetProtocol
 
314
STDMETHODIMP HttpHandlerPatch::Read(
 
315
    /* [in, out] */ void *pv,
 
316
    /* [in] */ ULONG cb,
 
317
    /* [out] */ ULONG *pcbRead) {
 
318
  return ReadImpl(pv, cb, pcbRead);
 
319
}
 
320
 
 
321
STDMETHODIMP HttpHandlerPatch::Seek(
 
322
    /* [in] */ LARGE_INTEGER dlibMove,
 
323
    /* [in] */ DWORD dwOrigin,
 
324
    /* [out] */ ULARGE_INTEGER *plibNewPosition) {
 
325
  LOG16((L"HttpHandlerPatch::Seek()\n"));
 
326
  return S_OK;
 
327
}
 
328
 
 
329
STDMETHODIMP HttpHandlerPatch::LockRequest(
 
330
    /* [in] */ DWORD dwOptions) {
 
331
  LOG16((L"HttpHandlerPatch::LockRequest()\n"));
 
332
  Ref();
 
333
  return S_OK;
 
334
}
 
335
 
 
336
STDMETHODIMP HttpHandlerPatch::UnlockRequest() {
 
337
  LOG16((L"HttpHandlerPatch::UnlockRequest()\n"));
 
338
  Unref();
 
339
  return S_OK;
 
340
}
 
341
 
 
342
// IWinInetInfo
 
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);
 
349
}
 
350
 
 
351
// IWinInetHttpInfo
 
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);
 
360
}
 
361
 
 
362
 
 
363
#ifdef DEBUG
 
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_);
 
375
 
 
376
  HRESULT hr;
 
377
  LONG value;
 
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));
 
386
 
 
387
  if (cache_hints) {
 
388
    WCHAR buffer[1024];
 
389
    DWORD buffer_size = sizeof(buffer);
 
390
    DWORD error = 0;
 
391
    DWORD reserved = 0;
 
392
    hr = cache_hints->SetCacheExtension(L".bla",
 
393
                                        buffer,
 
394
                                        &buffer_size,
 
395
                                        &error,
 
396
                                        &reserved);
 
397
    LOG16((L"IWinInetCacheHints::SetCacheExtension = %d, %d\n", hr, error));
 
398
  }
 
399
  if (cache_hints2) {
 
400
    WCHAR buffer[1024];
 
401
    DWORD buffer_size = sizeof(buffer);
 
402
    DWORD error = 0;
 
403
    DWORD reserved = 0;
 
404
    hr = cache_hints2->SetCacheExtension2(L".bla",
 
405
                                          buffer,
 
406
                                          &buffer_size,
 
407
                                          &error,
 
408
                                          &reserved);
 
409
    LOG16((L"IWinInetCacheHints2::SetCacheExtension2 = %d, %d\n", hr, error));
 
410
  }
 
411
 
 
412
  if (uri_container) {
 
413
    CComPtr<IUri> uri;
 
414
    hr = uri_container->GetIUri(&uri);
 
415
    LOG16((L"IUriContainer::GetIUri = %d\n", hr));
 
416
    if (uri) {
 
417
      CComBSTR url;
 
418
      hr = uri->GetAbsoluteUri(&url);
 
419
      LOG16((L"uri->GetAbsoluteUri = %d, %s\n", hr,
 
420
             url.m_str ? url.m_str : L"null"));
 
421
    }
 
422
  }
 
423
}
 
424
#endif