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::New("log"), FunctionTemplate::New(LogCallback));
    165 
    166   // Each processor gets its own context so different processors don't
    167   // affect each other. Context::New returns a persistent handle which
    168   // is what we need for the reference to remain after we return from
    169   // this method. That persistent handle has to be disposed in the
    170   // destructor.
    171   v8::Handle<v8::Context> context = Context::New(GetIsolate(), NULL, global);
    172   context_.Reset(GetIsolate(), context);
    173 
    174   // Enter the new context so all the following operations take place
    175   // within it.
    176   Context::Scope context_scope(context);
    177 
    178   // Make the options mapping available within the context
    179   if (!InstallMaps(opts, output))
    180     return false;
    181 
    182   // Compile and run the script
    183   if (!ExecuteScript(script_))
    184     return false;
    185 
    186   // The script compiled and ran correctly.  Now we fetch out the
    187   // Process function from the global object.
    188   Handle<String> process_name = String::New("Process");
    189   Handle<Value> process_val = context->Global()->Get(process_name);
    190 
    191   // If there is no Process function, or if it is not a function,
    192   // bail out
    193   if (!process_val->IsFunction()) return false;
    194 
    195   // It is a function; cast it to a Function
    196   Handle<Function> process_fun = Handle<Function>::Cast(process_val);
    197 
    198   // Store the function in a Persistent handle, since we also want
    199   // that to remain after this call returns
    200   process_.Reset(GetIsolate(), process_fun);
    201 
    202   // All done; all went well
    203   return true;
    204 }
    205 
    206 
    207 bool JsHttpRequestProcessor::ExecuteScript(Handle<String> script) {
    208   HandleScope handle_scope(GetIsolate());
    209 
    210   // We're just about to compile the script; set up an error handler to
    211   // catch any exceptions the script might throw.
    212   TryCatch try_catch;
    213 
    214   // Compile the script and check for errors.
    215   Handle<Script> compiled_script = Script::Compile(script);
    216   if (compiled_script.IsEmpty()) {
    217     String::Utf8Value error(try_catch.Exception());
    218     Log(*error);
    219     // The script failed to compile; bail out.
    220     return false;
    221   }
    222 
    223   // Run the script!
    224   Handle<Value> result = compiled_script->Run();
    225   if (result.IsEmpty()) {
    226     // The TryCatch above is still in effect and will have caught the error.
    227     String::Utf8Value error(try_catch.Exception());
    228     Log(*error);
    229     // Running the script failed; bail out.
    230     return false;
    231   }
    232   return true;
    233 }
    234 
    235 
    236 bool JsHttpRequestProcessor::InstallMaps(map<string, string>* opts,
    237                                          map<string, string>* output) {
    238   HandleScope handle_scope(GetIsolate());
    239 
    240   // Wrap the map object in a JavaScript wrapper
    241   Handle<Object> opts_obj = WrapMap(opts);
    242 
    243   v8::Local<v8::Context> context =
    244       v8::Local<v8::Context>::New(GetIsolate(), context_);
    245 
    246   // Set the options object as a property on the global object.
    247   context->Global()->Set(String::New("options"), opts_obj);
    248 
    249   Handle<Object> output_obj = WrapMap(output);
    250   context->Global()->Set(String::New("output"), output_obj);
    251 
    252   return true;
    253 }
    254 
    255 
    256 bool JsHttpRequestProcessor::Process(HttpRequest* request) {
    257   // Create a handle scope to keep the temporary object references.
    258   HandleScope handle_scope(GetIsolate());
    259 
    260   v8::Local<v8::Context> context =
    261       v8::Local<v8::Context>::New(GetIsolate(), context_);
    262 
    263   // Enter this processor's context so all the remaining operations
    264   // take place there
    265   Context::Scope context_scope(context);
    266 
    267   // Wrap the C++ request object in a JavaScript wrapper
    268   Handle<Object> request_obj = WrapRequest(request);
    269 
    270   // Set up an exception handler before calling the Process function
    271   TryCatch try_catch;
    272 
    273   // Invoke the process function, giving the global object as 'this'
    274   // and one argument, the request.
    275   const int argc = 1;
    276   Handle<Value> argv[argc] = { request_obj };
    277   v8::Local<v8::Function> process =
    278       v8::Local<v8::Function>::New(GetIsolate(), process_);
    279   Handle<Value> result = process->Call(context->Global(), argc, argv);
    280   if (result.IsEmpty()) {
    281     String::Utf8Value error(try_catch.Exception());
    282     Log(*error);
    283     return false;
    284   } else {
    285     return true;
    286   }
    287 }
    288 
    289 
    290 JsHttpRequestProcessor::~JsHttpRequestProcessor() {
    291   // Dispose the persistent handles.  When noone else has any
    292   // references to the objects stored in the handles they will be
    293   // automatically reclaimed.
    294   Isolate* isolate = GetIsolate();
    295   context_.Dispose(isolate);
    296   process_.Dispose(isolate);
    297 }
    298 
    299 
    300 Persistent<ObjectTemplate> JsHttpRequestProcessor::request_template_;
    301 Persistent<ObjectTemplate> JsHttpRequestProcessor::map_template_;
    302 
    303 
    304 // -----------------------------------
    305 // --- A c c e s s i n g   M a p s ---
    306 // -----------------------------------
    307 
    308 // Utility function that wraps a C++ http request object in a
    309 // JavaScript object.
    310 Handle<Object> JsHttpRequestProcessor::WrapMap(map<string, string>* obj) {
    311   // Handle scope for temporary handles.
    312   HandleScope handle_scope(GetIsolate());
    313 
    314   // Fetch the template for creating JavaScript map wrappers.
    315   // It only has to be created once, which we do on demand.
    316   if (map_template_.IsEmpty()) {
    317     Handle<ObjectTemplate> raw_template = MakeMapTemplate(GetIsolate());
    318     map_template_.Reset(GetIsolate(), raw_template);
    319   }
    320   Handle<ObjectTemplate> templ =
    321       Local<ObjectTemplate>::New(GetIsolate(), map_template_);
    322 
    323   // Create an empty map wrapper.
    324   Handle<Object> result = templ->NewInstance();
    325 
    326   // Wrap the raw C++ pointer in an External so it can be referenced
    327   // from within JavaScript.
    328   Handle<External> map_ptr = External::New(obj);
    329 
    330   // Store the map pointer in the JavaScript wrapper.
    331   result->SetInternalField(0, map_ptr);
    332 
    333   // Return the result through the current handle scope.  Since each
    334   // of these handles will go away when the handle scope is deleted
    335   // we need to call Close to let one, the result, escape into the
    336   // outer handle scope.
    337   return handle_scope.Close(result);
    338 }
    339 
    340 
    341 // Utility function that extracts the C++ map pointer from a wrapper
    342 // object.
    343 map<string, string>* JsHttpRequestProcessor::UnwrapMap(Handle<Object> obj) {
    344   Handle<External> field = Handle<External>::Cast(obj->GetInternalField(0));
    345   void* ptr = field->Value();
    346   return static_cast<map<string, string>*>(ptr);
    347 }
    348 
    349 
    350 // Convert a JavaScript string to a std::string.  To not bother too
    351 // much with string encodings we just use ascii.
    352 string ObjectToString(Local<Value> value) {
    353   String::Utf8Value utf8_value(value);
    354   return string(*utf8_value);
    355 }
    356 
    357 
    358 void JsHttpRequestProcessor::MapGet(Local<String> name,
    359                                     const PropertyCallbackInfo<Value>& info) {
    360   // Fetch the map wrapped by this object.
    361   map<string, string>* obj = UnwrapMap(info.Holder());
    362 
    363   // Convert the JavaScript string to a std::string.
    364   string key = ObjectToString(name);
    365 
    366   // Look up the value if it exists using the standard STL ideom.
    367   map<string, string>::iterator iter = obj->find(key);
    368 
    369   // If the key is not present return an empty handle as signal
    370   if (iter == obj->end()) return;
    371 
    372   // Otherwise fetch the value and wrap it in a JavaScript string
    373   const string& value = (*iter).second;
    374   info.GetReturnValue().Set(
    375       String::New(value.c_str(), static_cast<int>(value.length())));
    376 }
    377 
    378 
    379 void JsHttpRequestProcessor::MapSet(Local<String> name,
    380                                     Local<Value> value_obj,
    381                                     const PropertyCallbackInfo<Value>& info) {
    382   // Fetch the map wrapped by this object.
    383   map<string, string>* obj = UnwrapMap(info.Holder());
    384 
    385   // Convert the key and value to std::strings.
    386   string key = ObjectToString(name);
    387   string value = ObjectToString(value_obj);
    388 
    389   // Update the map.
    390   (*obj)[key] = value;
    391 
    392   // Return the value; any non-empty handle will work.
    393   info.GetReturnValue().Set(value_obj);
    394 }
    395 
    396 
    397 Handle<ObjectTemplate> JsHttpRequestProcessor::MakeMapTemplate(
    398     Isolate* isolate) {
    399   HandleScope handle_scope(isolate);
    400 
    401   Handle<ObjectTemplate> result = ObjectTemplate::New();
    402   result->SetInternalFieldCount(1);
    403   result->SetNamedPropertyHandler(MapGet, MapSet);
    404 
    405   // Again, return the result through the current handle scope.
    406   return handle_scope.Close(result);
    407 }
    408 
    409 
    410 // -------------------------------------------
    411 // --- A c c e s s i n g   R e q u e s t s ---
    412 // -------------------------------------------
    413 
    414 /**
    415  * Utility function that wraps a C++ http request object in a
    416  * JavaScript object.
    417  */
    418 Handle<Object> JsHttpRequestProcessor::WrapRequest(HttpRequest* request) {
    419   // Handle scope for temporary handles.
    420   HandleScope handle_scope(GetIsolate());
    421 
    422   // Fetch the template for creating JavaScript http request wrappers.
    423   // It only has to be created once, which we do on demand.
    424   if (request_template_.IsEmpty()) {
    425     Handle<ObjectTemplate> raw_template = MakeRequestTemplate(GetIsolate());
    426     request_template_.Reset(GetIsolate(), raw_template);
    427   }
    428   Handle<ObjectTemplate> templ =
    429       Local<ObjectTemplate>::New(GetIsolate(), request_template_);
    430 
    431   // Create an empty http request wrapper.
    432   Handle<Object> result = templ->NewInstance();
    433 
    434   // Wrap the raw C++ pointer in an External so it can be referenced
    435   // from within JavaScript.
    436   Handle<External> request_ptr = External::New(request);
    437 
    438   // Store the request pointer in the JavaScript wrapper.
    439   result->SetInternalField(0, request_ptr);
    440 
    441   // Return the result through the current handle scope.  Since each
    442   // of these handles will go away when the handle scope is deleted
    443   // we need to call Close to let one, the result, escape into the
    444   // outer handle scope.
    445   return handle_scope.Close(result);
    446 }
    447 
    448 
    449 /**
    450  * Utility function that extracts the C++ http request object from a
    451  * wrapper object.
    452  */
    453 HttpRequest* JsHttpRequestProcessor::UnwrapRequest(Handle<Object> obj) {
    454   Handle<External> field = Handle<External>::Cast(obj->GetInternalField(0));
    455   void* ptr = field->Value();
    456   return static_cast<HttpRequest*>(ptr);
    457 }
    458 
    459 
    460 void JsHttpRequestProcessor::GetPath(Local<String> name,
    461                                      const PropertyCallbackInfo<Value>& info) {
    462   // Extract the C++ request object from the JavaScript wrapper.
    463   HttpRequest* request = UnwrapRequest(info.Holder());
    464 
    465   // Fetch the path.
    466   const string& path = request->Path();
    467 
    468   // Wrap the result in a JavaScript string and return it.
    469   info.GetReturnValue().Set(
    470       String::New(path.c_str(), static_cast<int>(path.length())));
    471 }
    472 
    473 
    474 void JsHttpRequestProcessor::GetReferrer(
    475     Local<String> name,
    476     const PropertyCallbackInfo<Value>& info) {
    477   HttpRequest* request = UnwrapRequest(info.Holder());
    478   const string& path = request->Referrer();
    479   info.GetReturnValue().Set(
    480       String::New(path.c_str(), static_cast<int>(path.length())));
    481 }
    482 
    483 
    484 void JsHttpRequestProcessor::GetHost(Local<String> name,
    485                                      const PropertyCallbackInfo<Value>& info) {
    486   HttpRequest* request = UnwrapRequest(info.Holder());
    487   const string& path = request->Host();
    488   info.GetReturnValue().Set(
    489       String::New(path.c_str(), static_cast<int>(path.length())));
    490 }
    491 
    492 
    493 void JsHttpRequestProcessor::GetUserAgent(
    494     Local<String> name,
    495     const PropertyCallbackInfo<Value>& info) {
    496   HttpRequest* request = UnwrapRequest(info.Holder());
    497   const string& path = request->UserAgent();
    498   info.GetReturnValue().Set(
    499       String::New(path.c_str(), static_cast<int>(path.length())));
    500 }
    501 
    502 
    503 Handle<ObjectTemplate> JsHttpRequestProcessor::MakeRequestTemplate(
    504     Isolate* isolate) {
    505   HandleScope handle_scope(isolate);
    506 
    507   Handle<ObjectTemplate> result = ObjectTemplate::New();
    508   result->SetInternalFieldCount(1);
    509 
    510   // Add accessors for each of the fields of the request.
    511   result->SetAccessor(String::NewSymbol("path"), GetPath);
    512   result->SetAccessor(String::NewSymbol("referrer"), GetReferrer);
    513   result->SetAccessor(String::NewSymbol("host"), GetHost);
    514   result->SetAccessor(String::NewSymbol("userAgent"), GetUserAgent);
    515 
    516   // Again, return the result through the current handle scope.
    517   return handle_scope.Close(result);
    518 }
    519 
    520 
    521 // --- Test ---
    522 
    523 
    524 void HttpRequestProcessor::Log(const char* event) {
    525   printf("Logged: %s\n", event);
    526 }
    527 
    528 
    529 /**
    530  * A simplified http request.
    531  */
    532 class StringHttpRequest : public HttpRequest {
    533  public:
    534   StringHttpRequest(const string& path,
    535                     const string& referrer,
    536                     const string& host,
    537                     const string& user_agent);
    538   virtual const string& Path() { return path_; }
    539   virtual const string& Referrer() { return referrer_; }
    540   virtual const string& Host() { return host_; }
    541   virtual const string& UserAgent() { return user_agent_; }
    542  private:
    543   string path_;
    544   string referrer_;
    545   string host_;
    546   string user_agent_;
    547 };
    548 
    549 
    550 StringHttpRequest::StringHttpRequest(const string& path,
    551                                      const string& referrer,
    552                                      const string& host,
    553                                      const string& user_agent)
    554     : path_(path),
    555       referrer_(referrer),
    556       host_(host),
    557       user_agent_(user_agent) { }
    558 
    559 
    560 void ParseOptions(int argc,
    561                   char* argv[],
    562                   map<string, string>& options,
    563                   string* file) {
    564   for (int i = 1; i < argc; i++) {
    565     string arg = argv[i];
    566     size_t index = arg.find('=', 0);
    567     if (index == string::npos) {
    568       *file = arg;
    569     } else {
    570       string key = arg.substr(0, index);
    571       string value = arg.substr(index+1);
    572       options[key] = value;
    573     }
    574   }
    575 }
    576 
    577 
    578 // Reads a file into a v8 string.
    579 Handle<String> ReadFile(const string& name) {
    580   FILE* file = fopen(name.c_str(), "rb");
    581   if (file == NULL) return Handle<String>();
    582 
    583   fseek(file, 0, SEEK_END);
    584   int size = ftell(file);
    585   rewind(file);
    586 
    587   char* chars = new char[size + 1];
    588   chars[size] = '\0';
    589   for (int i = 0; i < size;) {
    590     int read = static_cast<int>(fread(&chars[i], 1, size - i, file));
    591     i += read;
    592   }
    593   fclose(file);
    594   Handle<String> result = String::New(chars, size);
    595   delete[] chars;
    596   return result;
    597 }
    598 
    599 
    600 const int kSampleSize = 6;
    601 StringHttpRequest kSampleRequests[kSampleSize] = {
    602   StringHttpRequest("/process.cc", "localhost", "google.com", "firefox"),
    603   StringHttpRequest("/", "localhost", "google.net", "firefox"),
    604   StringHttpRequest("/", "localhost", "google.org", "safari"),
    605   StringHttpRequest("/", "localhost", "yahoo.com", "ie"),
    606   StringHttpRequest("/", "localhost", "yahoo.com", "safari"),
    607   StringHttpRequest("/", "localhost", "yahoo.com", "firefox")
    608 };
    609 
    610 
    611 bool ProcessEntries(HttpRequestProcessor* processor, int count,
    612                     StringHttpRequest* reqs) {
    613   for (int i = 0; i < count; i++) {
    614     if (!processor->Process(&reqs[i]))
    615       return false;
    616   }
    617   return true;
    618 }
    619 
    620 
    621 void PrintMap(map<string, string>* m) {
    622   for (map<string, string>::iterator i = m->begin(); i != m->end(); i++) {
    623     pair<string, string> entry = *i;
    624     printf("%s: %s\n", entry.first.c_str(), entry.second.c_str());
    625   }
    626 }
    627 
    628 
    629 int main(int argc, char* argv[]) {
    630   v8::V8::InitializeICU();
    631   map<string, string> options;
    632   string file;
    633   ParseOptions(argc, argv, options, &file);
    634   if (file.empty()) {
    635     fprintf(stderr, "No script was specified.\n");
    636     return 1;
    637   }
    638   Isolate* isolate = Isolate::GetCurrent();
    639   HandleScope scope(isolate);
    640   Handle<String> source = ReadFile(file);
    641   if (source.IsEmpty()) {
    642     fprintf(stderr, "Error reading '%s'.\n", file.c_str());
    643     return 1;
    644   }
    645   JsHttpRequestProcessor processor(isolate, source);
    646   map<string, string> output;
    647   if (!processor.Initialize(&options, &output)) {
    648     fprintf(stderr, "Error initializing processor.\n");
    649     return 1;
    650   }
    651   if (!ProcessEntries(&processor, kSampleSize, kSampleRequests))
    652     return 1;
    653   PrintMap(&output);
    654 }
    655