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