1 // Copyright 2012 the V8 project authors. All rights reserved.
2 // Redistribution and use in source and binary forms, with or without
3 // modification, are permitted provided that the following conditions are
4 // met:
5 //
6 // * Redistributions of source code must retain the above copyright
7 // notice, this list of conditions and the following disclaimer.
8 // * Redistributions in binary form must reproduce the above
9 // copyright notice, this list of conditions and the following
10 // disclaimer in the documentation and/or other materials provided
11 // with the distribution.
12 // * Neither the name of Google Inc. nor the names of its
13 // contributors may be used to endorse or promote products derived
14 // from this software without specific prior written permission.
15 //
16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28 #include <include/v8.h>
29
30 #include <include/libplatform/libplatform.h>
31
32 #include <stdlib.h>
33 #include <string.h>
34
35 #include <map>
36 #include <string>
37
38 using std::map;
39 using std::pair;
40 using std::string;
41
42 using v8::Context;
43 using v8::EscapableHandleScope;
44 using v8::External;
45 using v8::Function;
46 using v8::FunctionTemplate;
47 using v8::Global;
48 using v8::HandleScope;
49 using v8::Isolate;
50 using v8::Local;
51 using v8::MaybeLocal;
52 using v8::Name;
53 using v8::NamedPropertyHandlerConfiguration;
54 using v8::NewStringType;
55 using v8::Object;
56 using v8::ObjectTemplate;
57 using v8::PropertyCallbackInfo;
58 using v8::Script;
59 using v8::String;
60 using v8::TryCatch;
61 using v8::Value;
62
63 // These interfaces represent an existing request processing interface.
64 // The idea is to imagine a real application that uses these interfaces
65 // and then add scripting capabilities that allow you to interact with
66 // the objects through JavaScript.
67
68 /**
69 * A simplified http request.
70 */
71 class HttpRequest {
72 public:
~HttpRequest()73 virtual ~HttpRequest() { }
74 virtual const string& Path() = 0;
75 virtual const string& Referrer() = 0;
76 virtual const string& Host() = 0;
77 virtual const string& UserAgent() = 0;
78 };
79
80
81 /**
82 * The abstract superclass of http request processors.
83 */
84 class HttpRequestProcessor {
85 public:
~HttpRequestProcessor()86 virtual ~HttpRequestProcessor() { }
87
88 // Initialize this processor. The map contains options that control
89 // how requests should be processed.
90 virtual bool Initialize(map<string, string>* options,
91 map<string, string>* output) = 0;
92
93 // Process a single request.
94 virtual bool Process(HttpRequest* req) = 0;
95
96 static void Log(const char* event);
97 };
98
99
100 /**
101 * An http request processor that is scriptable using JavaScript.
102 */
103 class JsHttpRequestProcessor : public HttpRequestProcessor {
104 public:
105 // Creates a new processor that processes requests by invoking the
106 // Process function of the JavaScript script given as an argument.
JsHttpRequestProcessor(Isolate * isolate,Local<String> script)107 JsHttpRequestProcessor(Isolate* isolate, Local<String> script)
108 : isolate_(isolate), script_(script) {}
109 virtual ~JsHttpRequestProcessor();
110
111 virtual bool Initialize(map<string, string>* opts,
112 map<string, string>* output);
113 virtual bool Process(HttpRequest* req);
114
115 private:
116 // Execute the script associated with this processor and extract the
117 // Process function. Returns true if this succeeded, otherwise false.
118 bool ExecuteScript(Local<String> script);
119
120 // Wrap the options and output map in a JavaScript objects and
121 // install it in the global namespace as 'options' and 'output'.
122 bool InstallMaps(map<string, string>* opts, map<string, string>* output);
123
124 // Constructs the template that describes the JavaScript wrapper
125 // type for requests.
126 static Local<ObjectTemplate> MakeRequestTemplate(Isolate* isolate);
127 static Local<ObjectTemplate> MakeMapTemplate(Isolate* isolate);
128
129 // Callbacks that access the individual fields of request objects.
130 static void GetPath(Local<String> name,
131 const PropertyCallbackInfo<Value>& info);
132 static void GetReferrer(Local<String> name,
133 const PropertyCallbackInfo<Value>& info);
134 static void GetHost(Local<String> name,
135 const PropertyCallbackInfo<Value>& info);
136 static void GetUserAgent(Local<String> name,
137 const PropertyCallbackInfo<Value>& info);
138
139 // Callbacks that access maps
140 static void MapGet(Local<Name> name, const PropertyCallbackInfo<Value>& info);
141 static void MapSet(Local<Name> name, Local<Value> value,
142 const PropertyCallbackInfo<Value>& info);
143
144 // Utility methods for wrapping C++ objects as JavaScript objects,
145 // and going back again.
146 Local<Object> WrapMap(map<string, string>* obj);
147 static map<string, string>* UnwrapMap(Local<Object> obj);
148 Local<Object> WrapRequest(HttpRequest* obj);
149 static HttpRequest* UnwrapRequest(Local<Object> obj);
150
GetIsolate()151 Isolate* GetIsolate() { return isolate_; }
152
153 Isolate* isolate_;
154 Local<String> script_;
155 Global<Context> context_;
156 Global<Function> process_;
157 static Global<ObjectTemplate> request_template_;
158 static Global<ObjectTemplate> map_template_;
159 };
160
161
162 // -------------------------
163 // --- P r o c e s s o r ---
164 // -------------------------
165
166
LogCallback(const v8::FunctionCallbackInfo<v8::Value> & args)167 static void LogCallback(const v8::FunctionCallbackInfo<v8::Value>& args) {
168 if (args.Length() < 1) return;
169 Isolate* isolate = args.GetIsolate();
170 HandleScope scope(isolate);
171 Local<Value> arg = args[0];
172 String::Utf8Value value(isolate, arg);
173 HttpRequestProcessor::Log(*value);
174 }
175
176
177 // Execute the script and fetch the Process method.
Initialize(map<string,string> * opts,map<string,string> * output)178 bool JsHttpRequestProcessor::Initialize(map<string, string>* opts,
179 map<string, string>* output) {
180 // Create a handle scope to hold the temporary references.
181 HandleScope handle_scope(GetIsolate());
182
183 // Create a template for the global object where we set the
184 // built-in global functions.
185 Local<ObjectTemplate> global = ObjectTemplate::New(GetIsolate());
186 global->Set(String::NewFromUtf8(GetIsolate(), "log", NewStringType::kNormal)
187 .ToLocalChecked(),
188 FunctionTemplate::New(GetIsolate(), LogCallback));
189
190 // Each processor gets its own context so different processors don't
191 // affect each other. Context::New returns a persistent handle which
192 // is what we need for the reference to remain after we return from
193 // this method. That persistent handle has to be disposed in the
194 // destructor.
195 v8::Local<v8::Context> context = Context::New(GetIsolate(), NULL, global);
196 context_.Reset(GetIsolate(), context);
197
198 // Enter the new context so all the following operations take place
199 // within it.
200 Context::Scope context_scope(context);
201
202 // Make the options mapping available within the context
203 if (!InstallMaps(opts, output))
204 return false;
205
206 // Compile and run the script
207 if (!ExecuteScript(script_))
208 return false;
209
210 // The script compiled and ran correctly. Now we fetch out the
211 // Process function from the global object.
212 Local<String> process_name =
213 String::NewFromUtf8(GetIsolate(), "Process", NewStringType::kNormal)
214 .ToLocalChecked();
215 Local<Value> process_val;
216 // If there is no Process function, or if it is not a function,
217 // bail out
218 if (!context->Global()->Get(context, process_name).ToLocal(&process_val) ||
219 !process_val->IsFunction()) {
220 return false;
221 }
222
223 // It is a function; cast it to a Function
224 Local<Function> process_fun = Local<Function>::Cast(process_val);
225
226 // Store the function in a Global handle, since we also want
227 // that to remain after this call returns
228 process_.Reset(GetIsolate(), process_fun);
229
230 // All done; all went well
231 return true;
232 }
233
234
ExecuteScript(Local<String> script)235 bool JsHttpRequestProcessor::ExecuteScript(Local<String> script) {
236 HandleScope handle_scope(GetIsolate());
237
238 // We're just about to compile the script; set up an error handler to
239 // catch any exceptions the script might throw.
240 TryCatch try_catch(GetIsolate());
241
242 Local<Context> context(GetIsolate()->GetCurrentContext());
243
244 // Compile the script and check for errors.
245 Local<Script> compiled_script;
246 if (!Script::Compile(context, script).ToLocal(&compiled_script)) {
247 String::Utf8Value error(GetIsolate(), try_catch.Exception());
248 Log(*error);
249 // The script failed to compile; bail out.
250 return false;
251 }
252
253 // Run the script!
254 Local<Value> result;
255 if (!compiled_script->Run(context).ToLocal(&result)) {
256 // The TryCatch above is still in effect and will have caught the error.
257 String::Utf8Value error(GetIsolate(), try_catch.Exception());
258 Log(*error);
259 // Running the script failed; bail out.
260 return false;
261 }
262
263 return true;
264 }
265
266
InstallMaps(map<string,string> * opts,map<string,string> * output)267 bool JsHttpRequestProcessor::InstallMaps(map<string, string>* opts,
268 map<string, string>* output) {
269 HandleScope handle_scope(GetIsolate());
270
271 // Wrap the map object in a JavaScript wrapper
272 Local<Object> opts_obj = WrapMap(opts);
273
274 v8::Local<v8::Context> context =
275 v8::Local<v8::Context>::New(GetIsolate(), context_);
276
277 // Set the options object as a property on the global object.
278 context->Global()
279 ->Set(context,
280 String::NewFromUtf8(GetIsolate(), "options", NewStringType::kNormal)
281 .ToLocalChecked(),
282 opts_obj)
283 .FromJust();
284
285 Local<Object> output_obj = WrapMap(output);
286 context->Global()
287 ->Set(context,
288 String::NewFromUtf8(GetIsolate(), "output", NewStringType::kNormal)
289 .ToLocalChecked(),
290 output_obj)
291 .FromJust();
292
293 return true;
294 }
295
296
Process(HttpRequest * request)297 bool JsHttpRequestProcessor::Process(HttpRequest* request) {
298 // Create a handle scope to keep the temporary object references.
299 HandleScope handle_scope(GetIsolate());
300
301 v8::Local<v8::Context> context =
302 v8::Local<v8::Context>::New(GetIsolate(), context_);
303
304 // Enter this processor's context so all the remaining operations
305 // take place there
306 Context::Scope context_scope(context);
307
308 // Wrap the C++ request object in a JavaScript wrapper
309 Local<Object> request_obj = WrapRequest(request);
310
311 // Set up an exception handler before calling the Process function
312 TryCatch try_catch(GetIsolate());
313
314 // Invoke the process function, giving the global object as 'this'
315 // and one argument, the request.
316 const int argc = 1;
317 Local<Value> argv[argc] = {request_obj};
318 v8::Local<v8::Function> process =
319 v8::Local<v8::Function>::New(GetIsolate(), process_);
320 Local<Value> result;
321 if (!process->Call(context, context->Global(), argc, argv).ToLocal(&result)) {
322 String::Utf8Value error(GetIsolate(), try_catch.Exception());
323 Log(*error);
324 return false;
325 }
326 return true;
327 }
328
329
~JsHttpRequestProcessor()330 JsHttpRequestProcessor::~JsHttpRequestProcessor() {
331 // Dispose the persistent handles. When no one else has any
332 // references to the objects stored in the handles they will be
333 // automatically reclaimed.
334 context_.Reset();
335 process_.Reset();
336 }
337
338
339 Global<ObjectTemplate> JsHttpRequestProcessor::request_template_;
340 Global<ObjectTemplate> JsHttpRequestProcessor::map_template_;
341
342
343 // -----------------------------------
344 // --- A c c e s s i n g M a p s ---
345 // -----------------------------------
346
347 // Utility function that wraps a C++ http request object in a
348 // JavaScript object.
WrapMap(map<string,string> * obj)349 Local<Object> JsHttpRequestProcessor::WrapMap(map<string, string>* obj) {
350 // Local scope for temporary handles.
351 EscapableHandleScope handle_scope(GetIsolate());
352
353 // Fetch the template for creating JavaScript map wrappers.
354 // It only has to be created once, which we do on demand.
355 if (map_template_.IsEmpty()) {
356 Local<ObjectTemplate> raw_template = MakeMapTemplate(GetIsolate());
357 map_template_.Reset(GetIsolate(), raw_template);
358 }
359 Local<ObjectTemplate> templ =
360 Local<ObjectTemplate>::New(GetIsolate(), map_template_);
361
362 // Create an empty map wrapper.
363 Local<Object> result =
364 templ->NewInstance(GetIsolate()->GetCurrentContext()).ToLocalChecked();
365
366 // Wrap the raw C++ pointer in an External so it can be referenced
367 // from within JavaScript.
368 Local<External> map_ptr = External::New(GetIsolate(), obj);
369
370 // Store the map pointer in the JavaScript wrapper.
371 result->SetInternalField(0, map_ptr);
372
373 // Return the result through the current handle scope. Since each
374 // of these handles will go away when the handle scope is deleted
375 // we need to call Close to let one, the result, escape into the
376 // outer handle scope.
377 return handle_scope.Escape(result);
378 }
379
380
381 // Utility function that extracts the C++ map pointer from a wrapper
382 // object.
UnwrapMap(Local<Object> obj)383 map<string, string>* JsHttpRequestProcessor::UnwrapMap(Local<Object> obj) {
384 Local<External> field = Local<External>::Cast(obj->GetInternalField(0));
385 void* ptr = field->Value();
386 return static_cast<map<string, string>*>(ptr);
387 }
388
389
390 // Convert a JavaScript string to a std::string. To not bother too
391 // much with string encodings we just use ascii.
ObjectToString(v8::Isolate * isolate,Local<Value> value)392 string ObjectToString(v8::Isolate* isolate, Local<Value> value) {
393 String::Utf8Value utf8_value(isolate, value);
394 return string(*utf8_value);
395 }
396
397
MapGet(Local<Name> name,const PropertyCallbackInfo<Value> & info)398 void JsHttpRequestProcessor::MapGet(Local<Name> name,
399 const PropertyCallbackInfo<Value>& info) {
400 if (name->IsSymbol()) return;
401
402 // Fetch the map wrapped by this object.
403 map<string, string>* obj = UnwrapMap(info.Holder());
404
405 // Convert the JavaScript string to a std::string.
406 string key = ObjectToString(info.GetIsolate(), Local<String>::Cast(name));
407
408 // Look up the value if it exists using the standard STL ideom.
409 map<string, string>::iterator iter = obj->find(key);
410
411 // If the key is not present return an empty handle as signal
412 if (iter == obj->end()) return;
413
414 // Otherwise fetch the value and wrap it in a JavaScript string
415 const string& value = (*iter).second;
416 info.GetReturnValue().Set(
417 String::NewFromUtf8(info.GetIsolate(), value.c_str(),
418 NewStringType::kNormal,
419 static_cast<int>(value.length())).ToLocalChecked());
420 }
421
422
MapSet(Local<Name> name,Local<Value> value_obj,const PropertyCallbackInfo<Value> & info)423 void JsHttpRequestProcessor::MapSet(Local<Name> name, Local<Value> value_obj,
424 const PropertyCallbackInfo<Value>& info) {
425 if (name->IsSymbol()) return;
426
427 // Fetch the map wrapped by this object.
428 map<string, string>* obj = UnwrapMap(info.Holder());
429
430 // Convert the key and value to std::strings.
431 string key = ObjectToString(info.GetIsolate(), Local<String>::Cast(name));
432 string value = ObjectToString(info.GetIsolate(), value_obj);
433
434 // Update the map.
435 (*obj)[key] = value;
436
437 // Return the value; any non-empty handle will work.
438 info.GetReturnValue().Set(value_obj);
439 }
440
441
MakeMapTemplate(Isolate * isolate)442 Local<ObjectTemplate> JsHttpRequestProcessor::MakeMapTemplate(
443 Isolate* isolate) {
444 EscapableHandleScope handle_scope(isolate);
445
446 Local<ObjectTemplate> result = ObjectTemplate::New(isolate);
447 result->SetInternalFieldCount(1);
448 result->SetHandler(NamedPropertyHandlerConfiguration(MapGet, MapSet));
449
450 // Again, return the result through the current handle scope.
451 return handle_scope.Escape(result);
452 }
453
454
455 // -------------------------------------------
456 // --- A c c e s s i n g R e q u e s t s ---
457 // -------------------------------------------
458
459 /**
460 * Utility function that wraps a C++ http request object in a
461 * JavaScript object.
462 */
WrapRequest(HttpRequest * request)463 Local<Object> JsHttpRequestProcessor::WrapRequest(HttpRequest* request) {
464 // Local scope for temporary handles.
465 EscapableHandleScope handle_scope(GetIsolate());
466
467 // Fetch the template for creating JavaScript http request wrappers.
468 // It only has to be created once, which we do on demand.
469 if (request_template_.IsEmpty()) {
470 Local<ObjectTemplate> raw_template = MakeRequestTemplate(GetIsolate());
471 request_template_.Reset(GetIsolate(), raw_template);
472 }
473 Local<ObjectTemplate> templ =
474 Local<ObjectTemplate>::New(GetIsolate(), request_template_);
475
476 // Create an empty http request wrapper.
477 Local<Object> result =
478 templ->NewInstance(GetIsolate()->GetCurrentContext()).ToLocalChecked();
479
480 // Wrap the raw C++ pointer in an External so it can be referenced
481 // from within JavaScript.
482 Local<External> request_ptr = External::New(GetIsolate(), request);
483
484 // Store the request pointer in the JavaScript wrapper.
485 result->SetInternalField(0, request_ptr);
486
487 // Return the result through the current handle scope. Since each
488 // of these handles will go away when the handle scope is deleted
489 // we need to call Close to let one, the result, escape into the
490 // outer handle scope.
491 return handle_scope.Escape(result);
492 }
493
494
495 /**
496 * Utility function that extracts the C++ http request object from a
497 * wrapper object.
498 */
UnwrapRequest(Local<Object> obj)499 HttpRequest* JsHttpRequestProcessor::UnwrapRequest(Local<Object> obj) {
500 Local<External> field = Local<External>::Cast(obj->GetInternalField(0));
501 void* ptr = field->Value();
502 return static_cast<HttpRequest*>(ptr);
503 }
504
505
GetPath(Local<String> name,const PropertyCallbackInfo<Value> & info)506 void JsHttpRequestProcessor::GetPath(Local<String> name,
507 const PropertyCallbackInfo<Value>& info) {
508 // Extract the C++ request object from the JavaScript wrapper.
509 HttpRequest* request = UnwrapRequest(info.Holder());
510
511 // Fetch the path.
512 const string& path = request->Path();
513
514 // Wrap the result in a JavaScript string and return it.
515 info.GetReturnValue().Set(
516 String::NewFromUtf8(info.GetIsolate(), path.c_str(),
517 NewStringType::kNormal,
518 static_cast<int>(path.length())).ToLocalChecked());
519 }
520
521
GetReferrer(Local<String> name,const PropertyCallbackInfo<Value> & info)522 void JsHttpRequestProcessor::GetReferrer(
523 Local<String> name,
524 const PropertyCallbackInfo<Value>& info) {
525 HttpRequest* request = UnwrapRequest(info.Holder());
526 const string& path = request->Referrer();
527 info.GetReturnValue().Set(
528 String::NewFromUtf8(info.GetIsolate(), path.c_str(),
529 NewStringType::kNormal,
530 static_cast<int>(path.length())).ToLocalChecked());
531 }
532
533
GetHost(Local<String> name,const PropertyCallbackInfo<Value> & info)534 void JsHttpRequestProcessor::GetHost(Local<String> name,
535 const PropertyCallbackInfo<Value>& info) {
536 HttpRequest* request = UnwrapRequest(info.Holder());
537 const string& path = request->Host();
538 info.GetReturnValue().Set(
539 String::NewFromUtf8(info.GetIsolate(), path.c_str(),
540 NewStringType::kNormal,
541 static_cast<int>(path.length())).ToLocalChecked());
542 }
543
544
GetUserAgent(Local<String> name,const PropertyCallbackInfo<Value> & info)545 void JsHttpRequestProcessor::GetUserAgent(
546 Local<String> name,
547 const PropertyCallbackInfo<Value>& info) {
548 HttpRequest* request = UnwrapRequest(info.Holder());
549 const string& path = request->UserAgent();
550 info.GetReturnValue().Set(
551 String::NewFromUtf8(info.GetIsolate(), path.c_str(),
552 NewStringType::kNormal,
553 static_cast<int>(path.length())).ToLocalChecked());
554 }
555
556
MakeRequestTemplate(Isolate * isolate)557 Local<ObjectTemplate> JsHttpRequestProcessor::MakeRequestTemplate(
558 Isolate* isolate) {
559 EscapableHandleScope handle_scope(isolate);
560
561 Local<ObjectTemplate> result = ObjectTemplate::New(isolate);
562 result->SetInternalFieldCount(1);
563
564 // Add accessors for each of the fields of the request.
565 result->SetAccessor(
566 String::NewFromUtf8(isolate, "path", NewStringType::kInternalized)
567 .ToLocalChecked(),
568 GetPath);
569 result->SetAccessor(
570 String::NewFromUtf8(isolate, "referrer", NewStringType::kInternalized)
571 .ToLocalChecked(),
572 GetReferrer);
573 result->SetAccessor(
574 String::NewFromUtf8(isolate, "host", NewStringType::kInternalized)
575 .ToLocalChecked(),
576 GetHost);
577 result->SetAccessor(
578 String::NewFromUtf8(isolate, "userAgent", NewStringType::kInternalized)
579 .ToLocalChecked(),
580 GetUserAgent);
581
582 // Again, return the result through the current handle scope.
583 return handle_scope.Escape(result);
584 }
585
586
587 // --- Test ---
588
589
Log(const char * event)590 void HttpRequestProcessor::Log(const char* event) {
591 printf("Logged: %s\n", event);
592 }
593
594
595 /**
596 * A simplified http request.
597 */
598 class StringHttpRequest : public HttpRequest {
599 public:
600 StringHttpRequest(const string& path,
601 const string& referrer,
602 const string& host,
603 const string& user_agent);
Path()604 virtual const string& Path() { return path_; }
Referrer()605 virtual const string& Referrer() { return referrer_; }
Host()606 virtual const string& Host() { return host_; }
UserAgent()607 virtual const string& UserAgent() { return user_agent_; }
608 private:
609 string path_;
610 string referrer_;
611 string host_;
612 string user_agent_;
613 };
614
615
StringHttpRequest(const string & path,const string & referrer,const string & host,const string & user_agent)616 StringHttpRequest::StringHttpRequest(const string& path,
617 const string& referrer,
618 const string& host,
619 const string& user_agent)
620 : path_(path),
621 referrer_(referrer),
622 host_(host),
623 user_agent_(user_agent) { }
624
625
ParseOptions(int argc,char * argv[],map<string,string> * options,string * file)626 void ParseOptions(int argc,
627 char* argv[],
628 map<string, string>* options,
629 string* file) {
630 for (int i = 1; i < argc; i++) {
631 string arg = argv[i];
632 size_t index = arg.find('=', 0);
633 if (index == string::npos) {
634 *file = arg;
635 } else {
636 string key = arg.substr(0, index);
637 string value = arg.substr(index+1);
638 (*options)[key] = value;
639 }
640 }
641 }
642
643
644 // Reads a file into a v8 string.
ReadFile(Isolate * isolate,const string & name)645 MaybeLocal<String> ReadFile(Isolate* isolate, const string& name) {
646 FILE* file = fopen(name.c_str(), "rb");
647 if (file == NULL) return MaybeLocal<String>();
648
649 fseek(file, 0, SEEK_END);
650 size_t size = ftell(file);
651 rewind(file);
652
653 std::unique_ptr<char> chars(new char[size + 1]);
654 chars.get()[size] = '\0';
655 for (size_t i = 0; i < size;) {
656 i += fread(&chars.get()[i], 1, size - i, file);
657 if (ferror(file)) {
658 fclose(file);
659 return MaybeLocal<String>();
660 }
661 }
662 fclose(file);
663 MaybeLocal<String> result = String::NewFromUtf8(
664 isolate, chars.get(), NewStringType::kNormal, static_cast<int>(size));
665 return result;
666 }
667
668
669 const int kSampleSize = 6;
670 StringHttpRequest kSampleRequests[kSampleSize] = {
671 StringHttpRequest("/process.cc", "localhost", "google.com", "firefox"),
672 StringHttpRequest("/", "localhost", "google.net", "firefox"),
673 StringHttpRequest("/", "localhost", "google.org", "safari"),
674 StringHttpRequest("/", "localhost", "yahoo.com", "ie"),
675 StringHttpRequest("/", "localhost", "yahoo.com", "safari"),
676 StringHttpRequest("/", "localhost", "yahoo.com", "firefox")
677 };
678
679
ProcessEntries(v8::Platform * platform,HttpRequestProcessor * processor,int count,StringHttpRequest * reqs)680 bool ProcessEntries(v8::Platform* platform, HttpRequestProcessor* processor,
681 int count, StringHttpRequest* reqs) {
682 for (int i = 0; i < count; i++) {
683 bool result = processor->Process(&reqs[i]);
684 while (v8::platform::PumpMessageLoop(platform, Isolate::GetCurrent()))
685 continue;
686 if (!result) return false;
687 }
688 return true;
689 }
690
691
PrintMap(map<string,string> * m)692 void PrintMap(map<string, string>* m) {
693 for (map<string, string>::iterator i = m->begin(); i != m->end(); i++) {
694 pair<string, string> entry = *i;
695 printf("%s: %s\n", entry.first.c_str(), entry.second.c_str());
696 }
697 }
698
699
main(int argc,char * argv[])700 int main(int argc, char* argv[]) {
701 v8::V8::InitializeICUDefaultLocation(argv[0]);
702 v8::V8::InitializeExternalStartupData(argv[0]);
703 std::unique_ptr<v8::Platform> platform = v8::platform::NewDefaultPlatform();
704 v8::V8::InitializePlatform(platform.get());
705 v8::V8::Initialize();
706 map<string, string> options;
707 string file;
708 ParseOptions(argc, argv, &options, &file);
709 if (file.empty()) {
710 fprintf(stderr, "No script was specified.\n");
711 return 1;
712 }
713 Isolate::CreateParams create_params;
714 create_params.array_buffer_allocator =
715 v8::ArrayBuffer::Allocator::NewDefaultAllocator();
716 Isolate* isolate = Isolate::New(create_params);
717 Isolate::Scope isolate_scope(isolate);
718 HandleScope scope(isolate);
719 Local<String> source;
720 if (!ReadFile(isolate, file).ToLocal(&source)) {
721 fprintf(stderr, "Error reading '%s'.\n", file.c_str());
722 return 1;
723 }
724 JsHttpRequestProcessor processor(isolate, source);
725 map<string, string> output;
726 if (!processor.Initialize(&options, &output)) {
727 fprintf(stderr, "Error initializing processor.\n");
728 return 1;
729 }
730 if (!ProcessEntries(platform.get(), &processor, kSampleSize, kSampleRequests))
731 return 1;
732 PrintMap(&output);
733 }
734