The data contained in this repository can be downloaded to your computer using one of several clients.
Please see the documentation of your version control software client for more information.

Please select the desired protocol below to get the URL.

This URL has Read-Only access.

Statistics
| Branch: | Revision:

main_repo / deps / v8 / samples / process.cc @ 40c0f755

History | View | Annotate | Download (20.2 KB)

1
// Copyright 2008 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
using namespace std;
34
using namespace v8;
35

    
36
// These interfaces represent an existing request processing interface.
37
// The idea is to imagine a real application that uses these interfaces
38
// and then add scripting capabilities that allow you to interact with
39
// the objects through JavaScript.
40

    
41
/**
42
 * A simplified http request.
43
 */
44
class HttpRequest {
45
 public:
46
  virtual ~HttpRequest() { }
47
  virtual const string& Path() = 0;
48
  virtual const string& Referrer() = 0;
49
  virtual const string& Host() = 0;
50
  virtual const string& UserAgent() = 0;
51
};
52

    
53
/**
54
 * The abstract superclass of http request processors.
55
 */
56
class HttpRequestProcessor {
57
 public:
58
  virtual ~HttpRequestProcessor() { }
59

    
60
  // Initialize this processor.  The map contains options that control
61
  // how requests should be processed.
62
  virtual bool Initialize(map<string, string>* options,
63
                          map<string, string>* output) = 0;
64

    
65
  // Process a single request.
66
  virtual bool Process(HttpRequest* req) = 0;
67

    
68
  static void Log(const char* event);
69
};
70

    
71
/**
72
 * An http request processor that is scriptable using JavaScript.
73
 */
74
class JsHttpRequestProcessor : public HttpRequestProcessor {
75
 public:
76

    
77
  // Creates a new processor that processes requests by invoking the
78
  // Process function of the JavaScript script given as an argument.
79
  explicit JsHttpRequestProcessor(Handle<String> script) : script_(script) { }
80
  virtual ~JsHttpRequestProcessor();
81

    
82
  virtual bool Initialize(map<string, string>* opts,
83
                          map<string, string>* output);
84
  virtual bool Process(HttpRequest* req);
85

    
86
 private:
87

    
88
  // Execute the script associated with this processor and extract the
89
  // Process function.  Returns true if this succeeded, otherwise false.
90
  bool ExecuteScript(Handle<String> script);
91

    
92
  // Wrap the options and output map in a JavaScript objects and
93
  // install it in the global namespace as 'options' and 'output'.
94
  bool InstallMaps(map<string, string>* opts, map<string, string>* output);
95

    
96
  // Constructs the template that describes the JavaScript wrapper
97
  // type for requests.
98
  static Handle<ObjectTemplate> MakeRequestTemplate();
99
  static Handle<ObjectTemplate> MakeMapTemplate();
100

    
101
  // Callbacks that access the individual fields of request objects.
102
  static Handle<Value> GetPath(Local<String> name, const AccessorInfo& info);
103
  static Handle<Value> GetReferrer(Local<String> name,
104
                                   const AccessorInfo& info);
105
  static Handle<Value> GetHost(Local<String> name, const AccessorInfo& info);
106
  static Handle<Value> GetUserAgent(Local<String> name,
107
                                    const AccessorInfo& info);
108

    
109
  // Callbacks that access maps
110
  static Handle<Value> MapGet(Local<String> name, const AccessorInfo& info);
111
  static Handle<Value> MapSet(Local<String> name,
112
                              Local<Value> value,
113
                              const AccessorInfo& info);
114

    
115
  // Utility methods for wrapping C++ objects as JavaScript objects,
116
  // and going back again.
117
  static Handle<Object> WrapMap(map<string, string>* obj);
118
  static map<string, string>* UnwrapMap(Handle<Object> obj);
119
  static Handle<Object> WrapRequest(HttpRequest* obj);
120
  static HttpRequest* UnwrapRequest(Handle<Object> obj);
121

    
122
  Handle<String> script_;
123
  Persistent<Context> context_;
124
  Persistent<Function> process_;
125
  static Persistent<ObjectTemplate> request_template_;
126
  static Persistent<ObjectTemplate> map_template_;
127
};
128

    
129
// -------------------------
130
// --- P r o c e s s o r ---
131
// -------------------------
132

    
133

    
134
static Handle<Value> LogCallback(const Arguments& args) {
135
  if (args.Length() < 1) return v8::Undefined();
136
  HandleScope scope;
137
  Handle<Value> arg = args[0];
138
  String::Utf8Value value(arg);
139
  HttpRequestProcessor::Log(*value);
140
  return v8::Undefined();
141
}
142

    
143

    
144
// Execute the script and fetch the Process method.
145
bool JsHttpRequestProcessor::Initialize(map<string, string>* opts,
146
                                        map<string, string>* output) {
147
  // Create a handle scope to hold the temporary references.
148
  HandleScope handle_scope;
149

    
150
  // Create a template for the global object where we set the
151
  // built-in global functions.
152
  Handle<ObjectTemplate> global = ObjectTemplate::New();
153
  global->Set(String::New("log"), FunctionTemplate::New(LogCallback));
154

    
155
  // Each processor gets its own context so different processors
156
  // don't affect each other (ignore the first three lines).
157
  Handle<Context> context = Context::New(NULL, global);
158

    
159
  // Store the context in the processor object in a persistent handle,
160
  // since we want the reference to remain after we return from this
161
  // method.
162
  context_ = Persistent<Context>::New(context);
163

    
164
  // Enter the new context so all the following operations take place
165
  // within it.
166
  Context::Scope context_scope(context);
167

    
168
  // Make the options mapping available within the context
169
  if (!InstallMaps(opts, output))
170
    return false;
171

    
172
  // Compile and run the script
173
  if (!ExecuteScript(script_))
174
    return false;
175

    
176
  // The script compiled and ran correctly.  Now we fetch out the
177
  // Process function from the global object.
178
  Handle<String> process_name = String::New("Process");
179
  Handle<Value> process_val = context->Global()->Get(process_name);
180

    
181
  // If there is no Process function, or if it is not a function,
182
  // bail out
183
  if (!process_val->IsFunction()) return false;
184

    
185
  // It is a function; cast it to a Function
186
  Handle<Function> process_fun = Handle<Function>::Cast(process_val);
187

    
188
  // Store the function in a Persistent handle, since we also want
189
  // that to remain after this call returns
190
  process_ = Persistent<Function>::New(process_fun);
191

    
192
  // All done; all went well
193
  return true;
194
}
195

    
196

    
197
bool JsHttpRequestProcessor::ExecuteScript(Handle<String> script) {
198
  HandleScope handle_scope;
199

    
200
  // We're just about to compile the script; set up an error handler to
201
  // catch any exceptions the script might throw.
202
  TryCatch try_catch;
203

    
204
  // Compile the script and check for errors.
205
  Handle<Script> compiled_script = Script::Compile(script);
206
  if (compiled_script.IsEmpty()) {
207
    String::Utf8Value error(try_catch.Exception());
208
    Log(*error);
209
    // The script failed to compile; bail out.
210
    return false;
211
  }
212

    
213
  // Run the script!
214
  Handle<Value> result = compiled_script->Run();
215
  if (result.IsEmpty()) {
216
    // The TryCatch above is still in effect and will have caught the error.
217
    String::Utf8Value error(try_catch.Exception());
218
    Log(*error);
219
    // Running the script failed; bail out.
220
    return false;
221
  }
222
  return true;
223
}
224

    
225

    
226
bool JsHttpRequestProcessor::InstallMaps(map<string, string>* opts,
227
                                         map<string, string>* output) {
228
  HandleScope handle_scope;
229

    
230
  // Wrap the map object in a JavaScript wrapper
231
  Handle<Object> opts_obj = WrapMap(opts);
232

    
233
  // Set the options object as a property on the global object.
234
  context_->Global()->Set(String::New("options"), opts_obj);
235

    
236
  Handle<Object> output_obj = WrapMap(output);
237
  context_->Global()->Set(String::New("output"), output_obj);
238

    
239
  return true;
240
}
241

    
242

    
243
bool JsHttpRequestProcessor::Process(HttpRequest* request) {
244
  // Create a handle scope to keep the temporary object references.
245
  HandleScope handle_scope;
246

    
247
  // Enter this processor's context so all the remaining operations
248
  // take place there
249
  Context::Scope context_scope(context_);
250

    
251
  // Wrap the C++ request object in a JavaScript wrapper
252
  Handle<Object> request_obj = WrapRequest(request);
253

    
254
  // Set up an exception handler before calling the Process function
255
  TryCatch try_catch;
256

    
257
  // Invoke the process function, giving the global object as 'this'
258
  // and one argument, the request.
259
  const int argc = 1;
260
  Handle<Value> argv[argc] = { request_obj };
261
  Handle<Value> result = process_->Call(context_->Global(), argc, argv);
262
  if (result.IsEmpty()) {
263
    String::Utf8Value error(try_catch.Exception());
264
    Log(*error);
265
    return false;
266
  } else {
267
    return true;
268
  }
269
}
270

    
271

    
272
JsHttpRequestProcessor::~JsHttpRequestProcessor() {
273
  // Dispose the persistent handles.  When noone else has any
274
  // references to the objects stored in the handles they will be
275
  // automatically reclaimed.
276
  context_.Dispose();
277
  process_.Dispose();
278
}
279

    
280

    
281
Persistent<ObjectTemplate> JsHttpRequestProcessor::request_template_;
282
Persistent<ObjectTemplate> JsHttpRequestProcessor::map_template_;
283

    
284

    
285
// -----------------------------------
286
// --- A c c e s s i n g   M a p s ---
287
// -----------------------------------
288

    
289
// Utility function that wraps a C++ http request object in a
290
// JavaScript object.
291
Handle<Object> JsHttpRequestProcessor::WrapMap(map<string, string>* obj) {
292
  // Handle scope for temporary handles.
293
  HandleScope handle_scope;
294

    
295
  // Fetch the template for creating JavaScript map wrappers.
296
  // It only has to be created once, which we do on demand.
297
  if (request_template_.IsEmpty()) {
298
    Handle<ObjectTemplate> raw_template = MakeMapTemplate();
299
    map_template_ = Persistent<ObjectTemplate>::New(raw_template);
300
  }
301
  Handle<ObjectTemplate> templ = map_template_;
302

    
303
  // Create an empty map wrapper.
304
  Handle<Object> result = templ->NewInstance();
305

    
306
  // Wrap the raw C++ pointer in an External so it can be referenced
307
  // from within JavaScript.
308
  Handle<External> map_ptr = External::New(obj);
309

    
310
  // Store the map pointer in the JavaScript wrapper.
311
  result->SetInternalField(0, map_ptr);
312

    
313
  // Return the result through the current handle scope.  Since each
314
  // of these handles will go away when the handle scope is deleted
315
  // we need to call Close to let one, the result, escape into the
316
  // outer handle scope.
317
  return handle_scope.Close(result);
318
}
319

    
320

    
321
// Utility function that extracts the C++ map pointer from a wrapper
322
// object.
323
map<string, string>* JsHttpRequestProcessor::UnwrapMap(Handle<Object> obj) {
324
  Handle<External> field = Handle<External>::Cast(obj->GetInternalField(0));
325
  void* ptr = field->Value();
326
  return static_cast<map<string, string>*>(ptr);
327
}
328

    
329

    
330
// Convert a JavaScript string to a std::string.  To not bother too
331
// much with string encodings we just use ascii.
332
string ObjectToString(Local<Value> value) {
333
  String::Utf8Value utf8_value(value);
334
  return string(*utf8_value);
335
}
336

    
337

    
338
Handle<Value> JsHttpRequestProcessor::MapGet(Local<String> name,
339
                                             const AccessorInfo& info) {
340
  // Fetch the map wrapped by this object.
341
  map<string, string>* obj = UnwrapMap(info.Holder());
342

    
343
  // Convert the JavaScript string to a std::string.
344
  string key = ObjectToString(name);
345

    
346
  // Look up the value if it exists using the standard STL ideom.
347
  map<string, string>::iterator iter = obj->find(key);
348

    
349
  // If the key is not present return an empty handle as signal
350
  if (iter == obj->end()) return Handle<Value>();
351

    
352
  // Otherwise fetch the value and wrap it in a JavaScript string
353
  const string& value = (*iter).second;
354
  return String::New(value.c_str(), value.length());
355
}
356

    
357

    
358
Handle<Value> JsHttpRequestProcessor::MapSet(Local<String> name,
359
                                             Local<Value> value_obj,
360
                                             const AccessorInfo& info) {
361
  // Fetch the map wrapped by this object.
362
  map<string, string>* obj = UnwrapMap(info.Holder());
363

    
364
  // Convert the key and value to std::strings.
365
  string key = ObjectToString(name);
366
  string value = ObjectToString(value_obj);
367

    
368
  // Update the map.
369
  (*obj)[key] = value;
370

    
371
  // Return the value; any non-empty handle will work.
372
  return value_obj;
373
}
374

    
375

    
376
Handle<ObjectTemplate> JsHttpRequestProcessor::MakeMapTemplate() {
377
  HandleScope handle_scope;
378

    
379
  Handle<ObjectTemplate> result = ObjectTemplate::New();
380
  result->SetInternalFieldCount(1);
381
  result->SetNamedPropertyHandler(MapGet, MapSet);
382

    
383
  // Again, return the result through the current handle scope.
384
  return handle_scope.Close(result);
385
}
386

    
387

    
388
// -------------------------------------------
389
// --- A c c e s s i n g   R e q u e s t s ---
390
// -------------------------------------------
391

    
392
/**
393
 * Utility function that wraps a C++ http request object in a
394
 * JavaScript object.
395
 */
396
Handle<Object> JsHttpRequestProcessor::WrapRequest(HttpRequest* request) {
397
  // Handle scope for temporary handles.
398
  HandleScope handle_scope;
399

    
400
  // Fetch the template for creating JavaScript http request wrappers.
401
  // It only has to be created once, which we do on demand.
402
  if (request_template_.IsEmpty()) {
403
    Handle<ObjectTemplate> raw_template = MakeRequestTemplate();
404
    request_template_ = Persistent<ObjectTemplate>::New(raw_template);
405
  }
406
  Handle<ObjectTemplate> templ = request_template_;
407

    
408
  // Create an empty http request wrapper.
409
  Handle<Object> result = templ->NewInstance();
410

    
411
  // Wrap the raw C++ pointer in an External so it can be referenced
412
  // from within JavaScript.
413
  Handle<External> request_ptr = External::New(request);
414

    
415
  // Store the request pointer in the JavaScript wrapper.
416
  result->SetInternalField(0, request_ptr);
417

    
418
  // Return the result through the current handle scope.  Since each
419
  // of these handles will go away when the handle scope is deleted
420
  // we need to call Close to let one, the result, escape into the
421
  // outer handle scope.
422
  return handle_scope.Close(result);
423
}
424

    
425

    
426
/**
427
 * Utility function that extracts the C++ http request object from a
428
 * wrapper object.
429
 */
430
HttpRequest* JsHttpRequestProcessor::UnwrapRequest(Handle<Object> obj) {
431
  Handle<External> field = Handle<External>::Cast(obj->GetInternalField(0));
432
  void* ptr = field->Value();
433
  return static_cast<HttpRequest*>(ptr);
434
}
435

    
436

    
437
Handle<Value> JsHttpRequestProcessor::GetPath(Local<String> name,
438
                                              const AccessorInfo& info) {
439
  // Extract the C++ request object from the JavaScript wrapper.
440
  HttpRequest* request = UnwrapRequest(info.Holder());
441

    
442
  // Fetch the path.
443
  const string& path = request->Path();
444

    
445
  // Wrap the result in a JavaScript string and return it.
446
  return String::New(path.c_str(), path.length());
447
}
448

    
449

    
450
Handle<Value> JsHttpRequestProcessor::GetReferrer(Local<String> name,
451
                                                  const AccessorInfo& info) {
452
  HttpRequest* request = UnwrapRequest(info.Holder());
453
  const string& path = request->Referrer();
454
  return String::New(path.c_str(), path.length());
455
}
456

    
457

    
458
Handle<Value> JsHttpRequestProcessor::GetHost(Local<String> name,
459
                                              const AccessorInfo& info) {
460
  HttpRequest* request = UnwrapRequest(info.Holder());
461
  const string& path = request->Host();
462
  return String::New(path.c_str(), path.length());
463
}
464

    
465

    
466
Handle<Value> JsHttpRequestProcessor::GetUserAgent(Local<String> name,
467
                                                   const AccessorInfo& info) {
468
  HttpRequest* request = UnwrapRequest(info.Holder());
469
  const string& path = request->UserAgent();
470
  return String::New(path.c_str(), path.length());
471
}
472

    
473

    
474
Handle<ObjectTemplate> JsHttpRequestProcessor::MakeRequestTemplate() {
475
  HandleScope handle_scope;
476

    
477
  Handle<ObjectTemplate> result = ObjectTemplate::New();
478
  result->SetInternalFieldCount(1);
479

    
480
  // Add accessors for each of the fields of the request.
481
  result->SetAccessor(String::NewSymbol("path"), GetPath);
482
  result->SetAccessor(String::NewSymbol("referrer"), GetReferrer);
483
  result->SetAccessor(String::NewSymbol("host"), GetHost);
484
  result->SetAccessor(String::NewSymbol("userAgent"), GetUserAgent);
485

    
486
  // Again, return the result through the current handle scope.
487
  return handle_scope.Close(result);
488
}
489

    
490

    
491
// --- Test ---
492

    
493

    
494
void HttpRequestProcessor::Log(const char* event) {
495
  printf("Logged: %s\n", event);
496
}
497

    
498

    
499
/**
500
 * A simplified http request.
501
 */
502
class StringHttpRequest : public HttpRequest {
503
 public:
504
  StringHttpRequest(const string& path,
505
                    const string& referrer,
506
                    const string& host,
507
                    const string& user_agent);
508
  virtual const string& Path() { return path_; }
509
  virtual const string& Referrer() { return referrer_; }
510
  virtual const string& Host() { return host_; }
511
  virtual const string& UserAgent() { return user_agent_; }
512
 private:
513
  string path_;
514
  string referrer_;
515
  string host_;
516
  string user_agent_;
517
};
518

    
519

    
520
StringHttpRequest::StringHttpRequest(const string& path,
521
                                     const string& referrer,
522
                                     const string& host,
523
                                     const string& user_agent)
524
    : path_(path),
525
      referrer_(referrer),
526
      host_(host),
527
      user_agent_(user_agent) { }
528

    
529

    
530
void ParseOptions(int argc,
531
                  char* argv[],
532
                  map<string, string>& options,
533
                  string* file) {
534
  for (int i = 1; i < argc; i++) {
535
    string arg = argv[i];
536
    int index = arg.find('=', 0);
537
    if (index == string::npos) {
538
      *file = arg;
539
    } else {
540
      string key = arg.substr(0, index);
541
      string value = arg.substr(index+1);
542
      options[key] = value;
543
    }
544
  }
545
}
546

    
547

    
548
// Reads a file into a v8 string.
549
Handle<String> ReadFile(const string& name) {
550
  FILE* file = fopen(name.c_str(), "rb");
551
  if (file == NULL) return Handle<String>();
552

    
553
  fseek(file, 0, SEEK_END);
554
  int size = ftell(file);
555
  rewind(file);
556

    
557
  char* chars = new char[size + 1];
558
  chars[size] = '\0';
559
  for (int i = 0; i < size;) {
560
    int read = fread(&chars[i], 1, size - i, file);
561
    i += read;
562
  }
563
  fclose(file);
564
  Handle<String> result = String::New(chars, size);
565
  delete[] chars;
566
  return result;
567
}
568

    
569

    
570
const int kSampleSize = 6;
571
StringHttpRequest kSampleRequests[kSampleSize] = {
572
  StringHttpRequest("/process.cc", "localhost", "google.com", "firefox"),
573
  StringHttpRequest("/", "localhost", "google.net", "firefox"),
574
  StringHttpRequest("/", "localhost", "google.org", "safari"),
575
  StringHttpRequest("/", "localhost", "yahoo.com", "ie"),
576
  StringHttpRequest("/", "localhost", "yahoo.com", "safari"),
577
  StringHttpRequest("/", "localhost", "yahoo.com", "firefox")
578
};
579

    
580

    
581
bool ProcessEntries(HttpRequestProcessor* processor, int count,
582
                    StringHttpRequest* reqs) {
583
  for (int i = 0; i < count; i++) {
584
    if (!processor->Process(&reqs[i]))
585
      return false;
586
  }
587
  return true;
588
}
589

    
590

    
591
void PrintMap(map<string, string>* m) {
592
  for (map<string, string>::iterator i = m->begin(); i != m->end(); i++) {
593
    pair<string, string> entry = *i;
594
    printf("%s: %s\n", entry.first.c_str(), entry.second.c_str());
595
  }
596
}
597

    
598

    
599
int main(int argc, char* argv[]) {
600
  map<string, string> options;
601
  string file;
602
  ParseOptions(argc, argv, options, &file);
603
  if (file.empty()) {
604
    fprintf(stderr, "No script was specified.\n");
605
    return 1;
606
  }
607
  HandleScope scope;
608
  Handle<String> source = ReadFile(file);
609
  if (source.IsEmpty()) {
610
    fprintf(stderr, "Error reading '%s'.\n", file.c_str());
611
    return 1;
612
  }
613
  JsHttpRequestProcessor processor(source);
614
  map<string, string> output;
615
  if (!processor.Initialize(&options, &output)) {
616
    fprintf(stderr, "Error initializing processor.\n");
617
    return 1;
618
  }
619
  if (!ProcessEntries(&processor, kSampleSize, kSampleRequests))
620
    return 1;
621
  PrintMap(&output);
622
}