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