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
38using namespace std;
39using 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 */
49class HttpRequest {
50 public:
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 */
62class HttpRequestProcessor {
63 public:
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 */
81class 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.
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
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
145static 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.
155bool 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
212bool 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
243bool 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
273bool 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
307JsHttpRequestProcessor::~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
316Global<ObjectTemplate> JsHttpRequestProcessor::request_template_;
317Global<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.
326Local<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.
360map<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.
369string ObjectToString(Local<Value> value) {
370  String::Utf8Value utf8_value(value);
371  return string(*utf8_value);
372}
373
374
375void 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
400void 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
419Local<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 */
440Local<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 */
476HttpRequest* 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
483void 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
499void 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
511void 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
522void 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
534Local<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
567void HttpRequestProcessor::Log(const char* event) {
568  printf("Logged: %s\n", event);
569}
570
571
572/**
573 * A simplified http request.
574 */
575class StringHttpRequest : public HttpRequest {
576 public:
577  StringHttpRequest(const string& path,
578                    const string& referrer,
579                    const string& host,
580                    const string& user_agent);
581  virtual const string& Path() { return path_; }
582  virtual const string& Referrer() { return referrer_; }
583  virtual const string& Host() { return host_; }
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
593StringHttpRequest::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
603void 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.
622MaybeLocal<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
647const int kSampleSize = 6;
648StringHttpRequest 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
658bool 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
670void 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
678int 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