| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "chrome/test/chrome_plugin/test_chrome_plugin.h" | |
| 6 | |
| 7 #include "base/at_exit.h" | |
| 8 #include "base/basictypes.h" | |
| 9 #include "base/logging.h" | |
| 10 #include "base/message_loop.h" | |
| 11 #include "base/string_util.h" | |
| 12 #include "chrome/common/chrome_plugin_api.h" | |
| 13 #include "googleurl/src/gurl.h" | |
| 14 | |
| 15 static CPID g_cpid; | |
| 16 static CPBrowserFuncs g_cpbrowser_funcs; | |
| 17 static CPRequestFuncs g_cprequest_funcs; | |
| 18 static CPResponseFuncs g_cpresponse_funcs; | |
| 19 static TestFuncParams::BrowserFuncs g_cptest_funcs; | |
| 20 | |
| 21 // Create a global AtExitManager so that our code can use code from base that | |
| 22 // uses Singletons, for example. We don't care about static constructors here. | |
| 23 static base::AtExitManager global_at_exit_manager; | |
| 24 | |
| 25 const TestResponsePayload* FindPayload(const char* url) { | |
| 26 for (size_t i = 0; i < arraysize(kChromeTestPluginPayloads); ++i) { | |
| 27 if (strcmp(kChromeTestPluginPayloads[i].url, url) == 0) | |
| 28 return &kChromeTestPluginPayloads[i]; | |
| 29 } | |
| 30 return NULL; | |
| 31 } | |
| 32 | |
| 33 std::string GetPayloadHeaders(const TestResponsePayload* payload) { | |
| 34 return StringPrintf( | |
| 35 "HTTP/1.1 200 OK%c" | |
| 36 "Content-type: %s%c" | |
| 37 "%c", 0, payload->mime_type, 0, 0); | |
| 38 } | |
| 39 | |
| 40 void STDCALL InvokeLaterCallback(void* data) { | |
| 41 Task* task = static_cast<Task*>(data); | |
| 42 task->Run(); | |
| 43 delete task; | |
| 44 } | |
| 45 | |
| 46 // ResponseStream: Manages the streaming of the payload data. | |
| 47 | |
| 48 class ResponseStream : public base::RefCounted<ResponseStream> { | |
| 49 public: | |
| 50 ResponseStream(const TestResponsePayload* payload, CPRequest* request); | |
| 51 | |
| 52 void Init(); | |
| 53 int GetResponseInfo(CPResponseInfoType type, void* buf, uint32 buf_size); | |
| 54 int ReadData(void* buf, uint32 buf_size); | |
| 55 | |
| 56 private: | |
| 57 friend class base::RefCounted<ResponseStream>; | |
| 58 | |
| 59 ~ResponseStream() { | |
| 60 request_->pdata = NULL; | |
| 61 } | |
| 62 | |
| 63 // Called asynchronously via InvokeLater. | |
| 64 void ResponseStarted(); | |
| 65 int ReadCompleted(void* buf, uint32 buf_size); | |
| 66 | |
| 67 enum ReadyStates { | |
| 68 READY_INVALID = 0, | |
| 69 READY_WAITING = 1, | |
| 70 READY_GOT_HEADERS = 2, | |
| 71 READY_GOT_DATA = 3, | |
| 72 }; | |
| 73 const TestResponsePayload* payload_; | |
| 74 uint32 offset_; | |
| 75 int ready_state_; | |
| 76 CPRequest* request_; | |
| 77 }; | |
| 78 | |
| 79 ResponseStream::ResponseStream(const TestResponsePayload* payload, | |
| 80 CPRequest* request) | |
| 81 : payload_(payload), offset_(0), ready_state_(READY_INVALID), | |
| 82 request_(request) { | |
| 83 } | |
| 84 | |
| 85 void ResponseStream::Init() { | |
| 86 if (payload_->async) { | |
| 87 // simulate an asynchronous start complete | |
| 88 ready_state_ = READY_WAITING; | |
| 89 g_cptest_funcs.invoke_later( | |
| 90 InvokeLaterCallback, | |
| 91 // downcast to Task before void, since we upcast from void to Task. | |
| 92 static_cast<Task*>( | |
| 93 NewRunnableMethod(this, &ResponseStream::ResponseStarted)), | |
| 94 500); | |
| 95 } else { | |
| 96 ready_state_ = READY_GOT_DATA; | |
| 97 } | |
| 98 } | |
| 99 | |
| 100 int ResponseStream::GetResponseInfo(CPResponseInfoType type, void* buf, | |
| 101 uint32 buf_size) { | |
| 102 if (ready_state_ < READY_GOT_HEADERS) | |
| 103 return CPERR_FAILURE; | |
| 104 | |
| 105 switch (type) { | |
| 106 case CPRESPONSEINFO_HTTP_STATUS: | |
| 107 if (buf) | |
| 108 memcpy(buf, &payload_->status, buf_size); | |
| 109 break; | |
| 110 case CPRESPONSEINFO_HTTP_RAW_HEADERS: { | |
| 111 std::string headers = GetPayloadHeaders(payload_); | |
| 112 if (buf_size < headers.size()+1) | |
| 113 return static_cast<int>(headers.size()+1); | |
| 114 if (buf) | |
| 115 memcpy(buf, headers.c_str(), headers.size()+1); | |
| 116 break; | |
| 117 } | |
| 118 default: | |
| 119 return CPERR_INVALID_VERSION; | |
| 120 } | |
| 121 | |
| 122 return CPERR_SUCCESS; | |
| 123 } | |
| 124 | |
| 125 int ResponseStream::ReadData(void* buf, uint32 buf_size) { | |
| 126 if (ready_state_ < READY_GOT_DATA) { | |
| 127 // simulate an asynchronous read complete | |
| 128 g_cptest_funcs.invoke_later( | |
| 129 InvokeLaterCallback, | |
| 130 // downcast to Task before void, since we upcast from void to Task. | |
| 131 static_cast<Task*>( | |
| 132 NewRunnableMethod(this, &ResponseStream::ReadCompleted, | |
| 133 buf, buf_size)), | |
| 134 500); | |
| 135 return CPERR_IO_PENDING; | |
| 136 } | |
| 137 | |
| 138 // synchronously complete the read | |
| 139 return ReadCompleted(buf, buf_size); | |
| 140 } | |
| 141 | |
| 142 void ResponseStream::ResponseStarted() { | |
| 143 ready_state_ = READY_GOT_HEADERS; | |
| 144 g_cpresponse_funcs.start_completed(request_, CPERR_SUCCESS); | |
| 145 } | |
| 146 | |
| 147 int ResponseStream::ReadCompleted(void* buf, uint32 buf_size) { | |
| 148 uint32 size = static_cast<uint32>(strlen(payload_->body)); | |
| 149 uint32 avail = size - offset_; | |
| 150 uint32 count = buf_size; | |
| 151 if (count > avail) | |
| 152 count = avail; | |
| 153 | |
| 154 if (count) { | |
| 155 memcpy(buf, payload_->body + offset_, count); | |
| 156 } | |
| 157 | |
| 158 offset_ += count; | |
| 159 | |
| 160 if (ready_state_ < READY_GOT_DATA) { | |
| 161 ready_state_ = READY_GOT_DATA; | |
| 162 g_cpresponse_funcs.read_completed(request_, static_cast<int>(count)); | |
| 163 } | |
| 164 | |
| 165 return count; | |
| 166 } | |
| 167 | |
| 168 // CPP Funcs | |
| 169 | |
| 170 CPError STDCALL CPP_Shutdown() { | |
| 171 return CPERR_SUCCESS; | |
| 172 } | |
| 173 | |
| 174 CPBool STDCALL CPP_ShouldInterceptRequest(CPRequest* request) { | |
| 175 DCHECK(base::strncasecmp(request->url, kChromeTestPluginProtocol, | |
| 176 arraysize(kChromeTestPluginProtocol) - 1) == 0); | |
| 177 return FindPayload(request->url) != NULL; | |
| 178 } | |
| 179 | |
| 180 CPError STDCALL CPR_StartRequest(CPRequest* request) { | |
| 181 const TestResponsePayload* payload = FindPayload(request->url); | |
| 182 DCHECK(payload); | |
| 183 ResponseStream* stream = new ResponseStream(payload, request); | |
| 184 stream->AddRef(); // Released in CPR_EndRequest | |
| 185 stream->Init(); | |
| 186 request->pdata = stream; | |
| 187 return payload->async ? CPERR_IO_PENDING : CPERR_SUCCESS; | |
| 188 } | |
| 189 | |
| 190 void STDCALL CPR_EndRequest(CPRequest* request, CPError reason) { | |
| 191 ResponseStream* stream = static_cast<ResponseStream*>(request->pdata); | |
| 192 request->pdata = NULL; | |
| 193 stream->Release(); // balances AddRef in CPR_StartRequest | |
| 194 } | |
| 195 | |
| 196 void STDCALL CPR_SetExtraRequestHeaders(CPRequest* request, | |
| 197 const char* headers) { | |
| 198 // doesn't affect us | |
| 199 } | |
| 200 | |
| 201 void STDCALL CPR_SetRequestLoadFlags(CPRequest* request, uint32 flags) { | |
| 202 // doesn't affect us | |
| 203 } | |
| 204 | |
| 205 void STDCALL CPR_AppendDataToUpload(CPRequest* request, const char* bytes, | |
| 206 int bytes_len) { | |
| 207 // doesn't affect us | |
| 208 } | |
| 209 | |
| 210 CPError STDCALL CPR_AppendFileToUpload(CPRequest* request, const char* filepath, | |
| 211 uint64 offset, uint64 length) { | |
| 212 // doesn't affect us | |
| 213 return CPERR_FAILURE; | |
| 214 } | |
| 215 | |
| 216 int STDCALL CPR_GetResponseInfo(CPRequest* request, CPResponseInfoType type, | |
| 217 void* buf, uint32 buf_size) { | |
| 218 ResponseStream* stream = static_cast<ResponseStream*>(request->pdata); | |
| 219 return stream->GetResponseInfo(type, buf, buf_size); | |
| 220 } | |
| 221 | |
| 222 int STDCALL CPR_Read(CPRequest* request, void* buf, uint32 buf_size) { | |
| 223 ResponseStream* stream = static_cast<ResponseStream*>(request->pdata); | |
| 224 return stream->ReadData(buf, buf_size); | |
| 225 } | |
| 226 | |
| 227 // RequestResponse: manages the retrieval of response data from the host | |
| 228 | |
| 229 class RequestResponse { | |
| 230 public: | |
| 231 explicit RequestResponse(const std::string& raw_headers) | |
| 232 : raw_headers_(raw_headers), offset_(0) {} | |
| 233 void StartReading(CPRequest* request); | |
| 234 void ReadCompleted(CPRequest* request, int bytes_read); | |
| 235 | |
| 236 private: | |
| 237 std::string raw_headers_; | |
| 238 std::string body_; | |
| 239 int offset_; | |
| 240 }; | |
| 241 | |
| 242 void RequestResponse::StartReading(CPRequest* request) { | |
| 243 int rv = 0; | |
| 244 const uint32 kReadSize = 4096; | |
| 245 do { | |
| 246 body_.resize(offset_ + kReadSize); | |
| 247 rv = g_cprequest_funcs.read(request, &body_[offset_], kReadSize); | |
| 248 if (rv > 0) | |
| 249 offset_ += rv; | |
| 250 } while (rv > 0); | |
| 251 | |
| 252 if (rv != CPERR_IO_PENDING) { | |
| 253 // Either an error occurred, or we are done. | |
| 254 ReadCompleted(request, rv); | |
| 255 } | |
| 256 } | |
| 257 | |
| 258 void RequestResponse::ReadCompleted(CPRequest* request, int bytes_read) { | |
| 259 if (bytes_read > 0) { | |
| 260 offset_ += bytes_read; | |
| 261 StartReading(request); | |
| 262 return; | |
| 263 } | |
| 264 | |
| 265 body_.resize(offset_); | |
| 266 bool success = (bytes_read == 0); | |
| 267 g_cptest_funcs.test_complete(request, success, raw_headers_, body_); | |
| 268 g_cprequest_funcs.end_request(request, CPERR_CANCELLED); | |
| 269 delete this; | |
| 270 } | |
| 271 | |
| 272 void STDCALL CPRR_ReceivedRedirect(CPRequest* request, const char* new_url) { | |
| 273 } | |
| 274 | |
| 275 void STDCALL CPRR_StartCompleted(CPRequest* request, CPError result) { | |
| 276 DCHECK(!request->pdata); | |
| 277 | |
| 278 std::string raw_headers; | |
| 279 int size = g_cprequest_funcs.get_response_info( | |
| 280 request, CPRESPONSEINFO_HTTP_RAW_HEADERS, NULL, 0); | |
| 281 int rv = size < 0 ? size : g_cprequest_funcs.get_response_info( | |
| 282 request, CPRESPONSEINFO_HTTP_RAW_HEADERS, | |
| 283 WriteInto(&raw_headers, size+1), size); | |
| 284 if (rv != CPERR_SUCCESS) { | |
| 285 g_cptest_funcs.test_complete(request, false, std::string(), std::string()); | |
| 286 g_cprequest_funcs.end_request(request, CPERR_CANCELLED); | |
| 287 return; | |
| 288 } | |
| 289 | |
| 290 RequestResponse* response = new RequestResponse(raw_headers); | |
| 291 request->pdata = response; | |
| 292 response->StartReading(request); | |
| 293 } | |
| 294 | |
| 295 void STDCALL CPRR_ReadCompleted(CPRequest* request, int bytes_read) { | |
| 296 RequestResponse* response = | |
| 297 reinterpret_cast<RequestResponse*>(request->pdata); | |
| 298 response->ReadCompleted(request, bytes_read); | |
| 299 } | |
| 300 | |
| 301 int STDCALL CPT_MakeRequest(const char* method, const GURL& url) { | |
| 302 CPRequest* request = NULL; | |
| 303 if (g_cpbrowser_funcs.create_request(g_cpid, NULL, method, url.spec().c_str(), | |
| 304 &request) != CPERR_SUCCESS || | |
| 305 !request) { | |
| 306 return CPERR_FAILURE; | |
| 307 } | |
| 308 | |
| 309 g_cprequest_funcs.set_request_load_flags(request, | |
| 310 CPREQUESTLOAD_DISABLE_INTERCEPT); | |
| 311 | |
| 312 if (strcmp(method, "POST") == 0) { | |
| 313 g_cprequest_funcs.set_extra_request_headers( | |
| 314 request, "Content-Type: text/plain"); | |
| 315 g_cprequest_funcs.append_data_to_upload( | |
| 316 request, kChromeTestPluginPostData, | |
| 317 arraysize(kChromeTestPluginPostData) - 1); | |
| 318 } | |
| 319 | |
| 320 int rv = g_cprequest_funcs.start_request(request); | |
| 321 if (rv == CPERR_SUCCESS) { | |
| 322 CPRR_StartCompleted(request, CPERR_SUCCESS); | |
| 323 } else if (rv != CPERR_IO_PENDING) { | |
| 324 g_cprequest_funcs.end_request(request, CPERR_CANCELLED); | |
| 325 return CPERR_FAILURE; | |
| 326 } | |
| 327 | |
| 328 return CPERR_SUCCESS; | |
| 329 } | |
| 330 | |
| 331 // DLL entry points | |
| 332 | |
| 333 CPError STDCALL CP_Initialize(CPID id, const CPBrowserFuncs* bfuncs, | |
| 334 CPPluginFuncs* pfuncs) { | |
| 335 if (bfuncs == NULL || pfuncs == NULL) | |
| 336 return CPERR_FAILURE; | |
| 337 | |
| 338 if (CP_GET_MAJOR_VERSION(bfuncs->version) > CP_MAJOR_VERSION) | |
| 339 return CPERR_INVALID_VERSION; | |
| 340 | |
| 341 if (bfuncs->size < sizeof(CPBrowserFuncs) || | |
| 342 pfuncs->size < sizeof(CPPluginFuncs)) | |
| 343 return CPERR_INVALID_VERSION; | |
| 344 | |
| 345 pfuncs->version = CP_VERSION; | |
| 346 pfuncs->shutdown = CPP_Shutdown; | |
| 347 pfuncs->should_intercept_request = CPP_ShouldInterceptRequest; | |
| 348 | |
| 349 static CPRequestFuncs request_funcs; | |
| 350 request_funcs.start_request = CPR_StartRequest; | |
| 351 request_funcs.end_request = CPR_EndRequest; | |
| 352 request_funcs.set_extra_request_headers = CPR_SetExtraRequestHeaders; | |
| 353 request_funcs.set_request_load_flags = CPR_SetRequestLoadFlags; | |
| 354 request_funcs.append_data_to_upload = CPR_AppendDataToUpload; | |
| 355 request_funcs.get_response_info = CPR_GetResponseInfo; | |
| 356 request_funcs.read = CPR_Read; | |
| 357 request_funcs.append_file_to_upload = CPR_AppendFileToUpload; | |
| 358 pfuncs->request_funcs = &request_funcs; | |
| 359 | |
| 360 static CPResponseFuncs response_funcs; | |
| 361 response_funcs.received_redirect = CPRR_ReceivedRedirect; | |
| 362 response_funcs.start_completed = CPRR_StartCompleted; | |
| 363 response_funcs.read_completed = CPRR_ReadCompleted; | |
| 364 pfuncs->response_funcs = &response_funcs; | |
| 365 | |
| 366 g_cpid = id; | |
| 367 g_cpbrowser_funcs = *bfuncs; | |
| 368 g_cprequest_funcs = *bfuncs->request_funcs; | |
| 369 g_cpresponse_funcs = *bfuncs->response_funcs; | |
| 370 g_cpbrowser_funcs = *bfuncs; | |
| 371 | |
| 372 const char* protocols[] = {kChromeTestPluginProtocol}; | |
| 373 g_cpbrowser_funcs.enable_request_intercept(g_cpid, protocols, 1); | |
| 374 return CPERR_SUCCESS; | |
| 375 } | |
| 376 | |
| 377 int STDCALL CP_Test(void* vparam) { | |
| 378 TestFuncParams* param = reinterpret_cast<TestFuncParams*>(vparam); | |
| 379 param->pfuncs.test_make_request = CPT_MakeRequest; | |
| 380 | |
| 381 g_cptest_funcs = param->bfuncs; | |
| 382 return CPERR_SUCCESS; | |
| 383 } | |
| OLD | NEW |