1
// Copyright 2007, 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.
28
#import "gears/blob/blob_interface.h"
29
#import "gears/localserver/common/http_constants.h"
30
#import "gears/localserver/common/http_cookies.h"
31
#import "gears/localserver/safari/async_task_sf.h"
33
const char16 *AsyncTask::kCookieRequiredErrorMessage =
34
STRING16(L"Required cookie is not present");
36
//------------------------------------------------------------------------------
38
//------------------------------------------------------------------------------
39
AsyncTask::AsyncTask(BrowsingContext *browsing_context) :
41
is_initialized_(false),
42
browsing_context_(browsing_context),
43
is_destructing_(false),
44
delete_when_done_(false),
50
//------------------------------------------------------------------------------
52
//------------------------------------------------------------------------------
53
AsyncTask::~AsyncTask() {
55
LOG(("~AsyncTask - thread is still running: %p\n", this));
56
// extra scope to lock our monitor
58
CritSecLock locker(lock_);
59
is_destructing_ = true;
63
pthread_join(thread_, NULL);
68
//------------------------------------------------------------------------------
70
//------------------------------------------------------------------------------
71
bool AsyncTask::Init() {
72
if (is_initialized_) {
73
assert(!is_initialized_);
78
is_initialized_ = true;
80
// Create a mach port to communicate between the threads
81
CFMachPortContext context;
84
context.retain = NULL;
85
context.release = NULL;
86
context.copyDescription = NULL;
87
Boolean should_free = false; // Don't free context.info on dealloc
88
msg_port_.reset(CFMachPortCreate(kCFAllocatorDefault, OnAsyncCall,
89
&context, &should_free));
91
if (!msg_port_.get() || should_free) {
92
LOG(("Couldn't make mach port\n"));
96
// Create a RL source and add it to the current runloop. This is so that
97
// the thread can send a message to the main thread via OnAsyncCall()
98
scoped_CFRunLoopSource
99
msg_source(CFMachPortCreateRunLoopSource(kCFAllocatorDefault,
100
msg_port_.get(), 0));
101
CFRunLoopAddSource(CFRunLoopGetCurrent(), msg_source.get(),
102
kCFRunLoopCommonModes);
107
//------------------------------------------------------------------------------
109
//------------------------------------------------------------------------------
110
void AsyncTask::SetListener(Listener *listener) {
111
listener_ = listener;
114
//------------------------------------------------------------------------------
116
//------------------------------------------------------------------------------
117
bool AsyncTask::Start() {
118
if (!is_initialized_) {
119
assert(is_initialized_);
124
if (pthread_create(&thread_, NULL, ThreadEntry, this))
127
return (thread_ != NULL);
130
//------------------------------------------------------------------------------
132
//------------------------------------------------------------------------------
133
void AsyncTask::Abort() {
134
CritSecLock locker(lock_);
135
if (thread_ && !is_aborted_) {
140
//------------------------------------------------------------------------------
142
//------------------------------------------------------------------------------
143
void AsyncTask::DeleteWhenDone() {
144
CritSecLock locker(lock_);
145
assert(!delete_when_done_);
146
if (!delete_when_done_) {
148
// In this particular code path, we have to call unlock prior to delete
149
// otherwise the locker would try to access deleted memory, &lock_,
150
// after it's been freed.
154
delete_when_done_ = true;
159
//------------------------------------------------------------------------------
160
// ThreadEntry - Our worker thread's entry procedure
161
//------------------------------------------------------------------------------
162
void *AsyncTask::ThreadEntry(void *task) {
163
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
165
AsyncTask *asyncTask = reinterpret_cast<AsyncTask*>(task);
167
asyncTask->NotifyListener(kThreadDoneMessageCode, 0);
174
//------------------------------------------------------------------------------
176
//------------------------------------------------------------------------------
177
void AsyncTask::NotifyListener(int code, int param) {
178
ThreadMessage msg = {0};
180
// Post a Mach message to be recieved by the main thread via OnAsyncCall()
181
msg.header.msgh_size = sizeof(ThreadMessage);
182
msg.header.msgh_remote_port = CFMachPortGetPort(msg_port_.get());
183
msg.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND,
184
MACH_MSG_TYPE_MAKE_SEND_ONCE);
187
mach_msg(&(msg.header),
188
MACH_SEND_MSG | MACH_SEND_TIMEOUT,
189
msg.header.msgh_size, 0, 0, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
192
//------------------------------------------------------------------------------
194
//------------------------------------------------------------------------------
195
void AsyncTask::OnThreadDone() {
196
CritSecLock locker(lock_);
197
if (delete_when_done_) {
199
// In this particular code path, we have to call unlock prior to delete
200
// otherwise the locker would try to access deleted memory, &lock_,
201
// after it's been freed.
207
//------------------------------------------------------------------------------
209
//------------------------------------------------------------------------------
210
bool AsyncTask::HttpGet(const char16 *full_url,
212
const char16 *reason_header_value,
213
const char16 *if_mod_since_date,
214
const char16 *required_cookie,
215
WebCacheDB::PayloadInfo *payload,
216
scoped_refptr<BlobInterface> *payload_data,
217
bool *was_redirected,
218
std::string16 *full_redirect_url,
219
std::string16 *error_message) {
220
return MakeHttpRequest(HttpConstants::kHttpGET,
224
NULL, // content_type_header_value
236
//------------------------------------------------------------------------------
238
//------------------------------------------------------------------------------
239
bool AsyncTask::HttpPost(const char16 *full_url,
241
const char16 *reason_header_value,
242
const char16 *content_type_header_value,
243
const char16 *if_mod_since_date,
244
const char16 *required_cookie,
245
bool disable_browser_cookies,
246
BlobInterface *post_body,
247
WebCacheDB::PayloadInfo *payload,
248
scoped_refptr<BlobInterface> *payload_data,
249
bool *was_redirected,
250
std::string16 *full_redirect_url,
251
std::string16 *error_message) {
252
return MakeHttpRequest(HttpConstants::kHttpPOST,
256
content_type_header_value,
259
disable_browser_cookies,
268
//------------------------------------------------------------------------------
270
//------------------------------------------------------------------------------
271
bool AsyncTask::MakeHttpRequest(const char16 *method,
272
const char16 *full_url,
274
const char16 *reason_header_value,
275
const char16 *content_type_header_value,
276
const char16 *if_mod_since_date,
277
const char16 *required_cookie,
278
bool disable_browser_cookies,
279
BlobInterface *post_body,
280
WebCacheDB::PayloadInfo *payload,
281
scoped_refptr<BlobInterface> *payload_data,
282
bool *was_redirected,
283
std::string16 *full_redirect_url,
284
std::string16 *error_message) {
286
if (was_redirected) {
287
*was_redirected = false;
289
if (full_redirect_url) {
290
full_redirect_url->clear();
293
error_message->clear();
296
if (required_cookie && required_cookie[0]) {
297
std::string16 required_cookie_str(required_cookie);
298
std::string16 name, value;
299
ParseCookieNameAndValue(required_cookie_str, &name, &value);
300
if (value != kNegatedRequiredCookieValue) {
301
CookieMap cookie_map;
302
cookie_map.LoadMapForUrl(full_url, browsing_context_.get());
303
if (!cookie_map.HasLocalServerRequiredCookie(required_cookie_str)) {
305
*(error_message) = kCookieRequiredErrorMessage;
312
scoped_refptr<HttpRequest> http_request;
313
if (!HttpRequest::Create(&http_request))
316
http_request->SetCachingBehavior(HttpRequest::BYPASS_ALL_CACHES);
318
if (!http_request->Open(method, full_url, true, browsing_context_.get())) {
323
http_request->SetRedirectBehavior(HttpRequest::FOLLOW_NONE);
324
if (!http_request->SetRequestHeader(HttpConstants::kXGoogleGearsHeader,
330
if (!http_request->SetRequestHeader(HttpConstants::kCacheControlHeader,
331
HttpConstants::kNoCache)) {
335
if (!http_request->SetRequestHeader(HttpConstants::kPragmaHeader,
336
HttpConstants::kNoCache)) {
340
if (reason_header_value && reason_header_value[0]) {
341
if (!http_request->SetRequestHeader(HttpConstants::kXGearsReasonHeader,
342
reason_header_value)) {
347
if (content_type_header_value && content_type_header_value[0]) {
348
if (!http_request->SetRequestHeader(HttpConstants::kContentTypeHeader,
349
content_type_header_value)) {
354
if (if_mod_since_date && if_mod_since_date[0]) {
355
if (!http_request->SetRequestHeader(HttpConstants::kIfModifiedSinceHeader,
356
if_mod_since_date)) {
361
if (disable_browser_cookies) {
362
if (!http_request->SetCookieBehavior(
363
HttpRequest::DO_NOT_SEND_BROWSER_COOKIES)) {
368
payload->data.reset();
370
// Rely on logic inside HttpRequest to check for valid combinations of
371
// method and presence of body.
372
bool result = http_request->Send(post_body);
377
// Wait for the data to come back from the HTTP request
379
HttpRequest::ReadyState ready_state;
380
http_request->GetReadyState(&ready_state);
382
if (ready_state == HttpRequest::COMPLETE) {
387
http_request->Abort();
391
// Run for a second in the run loop
392
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1, true);
395
// Extract the status & data
397
if (http_request->GetStatus(&status)) {
398
payload->status_code = status;
399
if (http_request->GetStatusLine(&payload->status_line)) {
400
if (http_request->GetAllResponseHeaders(&payload->headers)) {
401
// TODO(bgarcia): Make WebCacheDB::PayloadInfo.data a Blob.
402
http_request->GetResponseBody(payload_data);
407
// If we were redirected during the load, setup the return variables
408
if (http_request->WasRedirected()) {
410
*was_redirected = true;
412
if (full_redirect_url)
413
http_request->GetFinalUrl(full_redirect_url);
416
return !is_aborted_ && payload_data->get();
419
//------------------------------------------------------------------------------
420
// static OnAsyncCall
421
//------------------------------------------------------------------------------
422
void AsyncTask::OnAsyncCall(CFMachPortRef port, void *mach_msg, CFIndex size,
424
ThreadMessage *msg = (ThreadMessage *)mach_msg;
425
AsyncTask *asyncTask = (AsyncTask *)info;
427
if (msg->code == kThreadDoneMessageCode) {
428
asyncTask->OnThreadDone();
432
if (asyncTask->listener_)
433
asyncTask->listener_->HandleAsyncTaskEvent(
434
msg->code, msg->param, asyncTask);