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