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 |