blob: e5c9b7a53cf15926a0dedfc5466e249176793575 [file] [log] [blame]
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001// Copyright 2012 the V8 project authors. All rights reserved.
Steve Blocka7e24c12009-10-30 11:49:00 +00002// 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
Ben Murdochb8a8cc12014-11-26 15:28:44 +000028#include <include/v8.h>
Steve Blocka7e24c12009-10-30 11:49:00 +000029
Ben Murdochb8a8cc12014-11-26 15:28:44 +000030#include <include/libplatform/libplatform.h>
31
Steve Blocka7e24c12009-10-30 11:49:00 +000032#include <map>
Ben Murdochb8a8cc12014-11-26 15:28:44 +000033#include <string>
Steve Blocka7e24c12009-10-30 11:49:00 +000034
Ben Murdoch257744e2011-11-30 15:57:28 +000035#ifdef COMPRESS_STARTUP_DATA_BZ2
36#error Using compressed startup data is not supported for this sample
37#endif
38
Steve Blocka7e24c12009-10-30 11:49:00 +000039using namespace std;
40using namespace v8;
41
42// These interfaces represent an existing request processing interface.
43// The idea is to imagine a real application that uses these interfaces
44// and then add scripting capabilities that allow you to interact with
45// the objects through JavaScript.
46
47/**
48 * A simplified http request.
49 */
50class HttpRequest {
51 public:
52 virtual ~HttpRequest() { }
53 virtual const string& Path() = 0;
54 virtual const string& Referrer() = 0;
55 virtual const string& Host() = 0;
56 virtual const string& UserAgent() = 0;
57};
58
Ben Murdochb8a8cc12014-11-26 15:28:44 +000059
Steve Blocka7e24c12009-10-30 11:49:00 +000060/**
61 * The abstract superclass of http request processors.
62 */
63class HttpRequestProcessor {
64 public:
65 virtual ~HttpRequestProcessor() { }
66
67 // Initialize this processor. The map contains options that control
68 // how requests should be processed.
69 virtual bool Initialize(map<string, string>* options,
70 map<string, string>* output) = 0;
71
72 // Process a single request.
73 virtual bool Process(HttpRequest* req) = 0;
74
75 static void Log(const char* event);
76};
77
Ben Murdochb8a8cc12014-11-26 15:28:44 +000078
Steve Blocka7e24c12009-10-30 11:49:00 +000079/**
80 * An http request processor that is scriptable using JavaScript.
81 */
82class JsHttpRequestProcessor : public HttpRequestProcessor {
83 public:
Steve Blocka7e24c12009-10-30 11:49:00 +000084 // Creates a new processor that processes requests by invoking the
85 // Process function of the JavaScript script given as an argument.
Ben Murdochb8a8cc12014-11-26 15:28:44 +000086 JsHttpRequestProcessor(Isolate* isolate, Handle<String> script)
87 : isolate_(isolate), script_(script) { }
Steve Blocka7e24c12009-10-30 11:49:00 +000088 virtual ~JsHttpRequestProcessor();
89
90 virtual bool Initialize(map<string, string>* opts,
91 map<string, string>* output);
92 virtual bool Process(HttpRequest* req);
93
94 private:
Steve Blocka7e24c12009-10-30 11:49:00 +000095 // Execute the script associated with this processor and extract the
96 // Process function. Returns true if this succeeded, otherwise false.
97 bool ExecuteScript(Handle<String> script);
98
99 // Wrap the options and output map in a JavaScript objects and
100 // install it in the global namespace as 'options' and 'output'.
101 bool InstallMaps(map<string, string>* opts, map<string, string>* output);
102
103 // Constructs the template that describes the JavaScript wrapper
104 // type for requests.
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000105 static Handle<ObjectTemplate> MakeRequestTemplate(Isolate* isolate);
106 static Handle<ObjectTemplate> MakeMapTemplate(Isolate* isolate);
Steve Blocka7e24c12009-10-30 11:49:00 +0000107
108 // Callbacks that access the individual fields of request objects.
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000109 static void GetPath(Local<String> name,
110 const PropertyCallbackInfo<Value>& info);
111 static void GetReferrer(Local<String> name,
112 const PropertyCallbackInfo<Value>& info);
113 static void GetHost(Local<String> name,
114 const PropertyCallbackInfo<Value>& info);
115 static void GetUserAgent(Local<String> name,
116 const PropertyCallbackInfo<Value>& info);
Steve Blocka7e24c12009-10-30 11:49:00 +0000117
118 // Callbacks that access maps
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000119 static void MapGet(Local<String> name,
120 const PropertyCallbackInfo<Value>& info);
121 static void MapSet(Local<String> name,
122 Local<Value> value,
123 const PropertyCallbackInfo<Value>& info);
Steve Blocka7e24c12009-10-30 11:49:00 +0000124
125 // Utility methods for wrapping C++ objects as JavaScript objects,
126 // and going back again.
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000127 Handle<Object> WrapMap(map<string, string>* obj);
Steve Blocka7e24c12009-10-30 11:49:00 +0000128 static map<string, string>* UnwrapMap(Handle<Object> obj);
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000129 Handle<Object> WrapRequest(HttpRequest* obj);
Steve Blocka7e24c12009-10-30 11:49:00 +0000130 static HttpRequest* UnwrapRequest(Handle<Object> obj);
131
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000132 Isolate* GetIsolate() { return isolate_; }
133
134 Isolate* isolate_;
Steve Blocka7e24c12009-10-30 11:49:00 +0000135 Handle<String> script_;
136 Persistent<Context> context_;
137 Persistent<Function> process_;
138 static Persistent<ObjectTemplate> request_template_;
139 static Persistent<ObjectTemplate> map_template_;
140};
141
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000142
Steve Blocka7e24c12009-10-30 11:49:00 +0000143// -------------------------
144// --- P r o c e s s o r ---
145// -------------------------
146
147
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000148static void LogCallback(const v8::FunctionCallbackInfo<v8::Value>& args) {
149 if (args.Length() < 1) return;
150 HandleScope scope(args.GetIsolate());
Steve Blocka7e24c12009-10-30 11:49:00 +0000151 Handle<Value> arg = args[0];
152 String::Utf8Value value(arg);
153 HttpRequestProcessor::Log(*value);
Steve Blocka7e24c12009-10-30 11:49:00 +0000154}
155
156
157// Execute the script and fetch the Process method.
158bool JsHttpRequestProcessor::Initialize(map<string, string>* opts,
159 map<string, string>* output) {
160 // Create a handle scope to hold the temporary references.
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000161 HandleScope handle_scope(GetIsolate());
Steve Blocka7e24c12009-10-30 11:49:00 +0000162
163 // Create a template for the global object where we set the
164 // built-in global functions.
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000165 Handle<ObjectTemplate> global = ObjectTemplate::New(GetIsolate());
166 global->Set(String::NewFromUtf8(GetIsolate(), "log"),
167 FunctionTemplate::New(GetIsolate(), LogCallback));
Steve Blocka7e24c12009-10-30 11:49:00 +0000168
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -0800169 // Each processor gets its own context so different processors don't
170 // affect each other. Context::New returns a persistent handle which
171 // is what we need for the reference to remain after we return from
172 // this method. That persistent handle has to be disposed in the
173 // destructor.
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000174 v8::Handle<v8::Context> context = Context::New(GetIsolate(), NULL, global);
175 context_.Reset(GetIsolate(), context);
Steve Blocka7e24c12009-10-30 11:49:00 +0000176
177 // Enter the new context so all the following operations take place
178 // within it.
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000179 Context::Scope context_scope(context);
Steve Blocka7e24c12009-10-30 11:49:00 +0000180
181 // Make the options mapping available within the context
182 if (!InstallMaps(opts, output))
183 return false;
184
185 // Compile and run the script
186 if (!ExecuteScript(script_))
187 return false;
188
189 // The script compiled and ran correctly. Now we fetch out the
190 // Process function from the global object.
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000191 Handle<String> process_name = String::NewFromUtf8(GetIsolate(), "Process");
192 Handle<Value> process_val = context->Global()->Get(process_name);
Steve Blocka7e24c12009-10-30 11:49:00 +0000193
194 // If there is no Process function, or if it is not a function,
195 // bail out
196 if (!process_val->IsFunction()) return false;
197
198 // It is a function; cast it to a Function
199 Handle<Function> process_fun = Handle<Function>::Cast(process_val);
200
201 // Store the function in a Persistent handle, since we also want
202 // that to remain after this call returns
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000203 process_.Reset(GetIsolate(), process_fun);
Steve Blocka7e24c12009-10-30 11:49:00 +0000204
205 // All done; all went well
206 return true;
207}
208
209
210bool JsHttpRequestProcessor::ExecuteScript(Handle<String> script) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000211 HandleScope handle_scope(GetIsolate());
Steve Blocka7e24c12009-10-30 11:49:00 +0000212
213 // We're just about to compile the script; set up an error handler to
214 // catch any exceptions the script might throw.
215 TryCatch try_catch;
216
217 // Compile the script and check for errors.
218 Handle<Script> compiled_script = Script::Compile(script);
219 if (compiled_script.IsEmpty()) {
220 String::Utf8Value error(try_catch.Exception());
221 Log(*error);
222 // The script failed to compile; bail out.
223 return false;
224 }
225
226 // Run the script!
227 Handle<Value> result = compiled_script->Run();
228 if (result.IsEmpty()) {
229 // The TryCatch above is still in effect and will have caught the error.
230 String::Utf8Value error(try_catch.Exception());
231 Log(*error);
232 // Running the script failed; bail out.
233 return false;
234 }
235 return true;
236}
237
238
239bool JsHttpRequestProcessor::InstallMaps(map<string, string>* opts,
240 map<string, string>* output) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000241 HandleScope handle_scope(GetIsolate());
Steve Blocka7e24c12009-10-30 11:49:00 +0000242
243 // Wrap the map object in a JavaScript wrapper
244 Handle<Object> opts_obj = WrapMap(opts);
245
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000246 v8::Local<v8::Context> context =
247 v8::Local<v8::Context>::New(GetIsolate(), context_);
248
Steve Blocka7e24c12009-10-30 11:49:00 +0000249 // Set the options object as a property on the global object.
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000250 context->Global()->Set(String::NewFromUtf8(GetIsolate(), "options"),
251 opts_obj);
Steve Blocka7e24c12009-10-30 11:49:00 +0000252
253 Handle<Object> output_obj = WrapMap(output);
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000254 context->Global()->Set(String::NewFromUtf8(GetIsolate(), "output"),
255 output_obj);
Steve Blocka7e24c12009-10-30 11:49:00 +0000256
257 return true;
258}
259
260
261bool JsHttpRequestProcessor::Process(HttpRequest* request) {
262 // Create a handle scope to keep the temporary object references.
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000263 HandleScope handle_scope(GetIsolate());
264
265 v8::Local<v8::Context> context =
266 v8::Local<v8::Context>::New(GetIsolate(), context_);
Steve Blocka7e24c12009-10-30 11:49:00 +0000267
268 // Enter this processor's context so all the remaining operations
269 // take place there
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000270 Context::Scope context_scope(context);
Steve Blocka7e24c12009-10-30 11:49:00 +0000271
272 // Wrap the C++ request object in a JavaScript wrapper
273 Handle<Object> request_obj = WrapRequest(request);
274
275 // Set up an exception handler before calling the Process function
276 TryCatch try_catch;
277
278 // Invoke the process function, giving the global object as 'this'
279 // and one argument, the request.
280 const int argc = 1;
281 Handle<Value> argv[argc] = { request_obj };
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000282 v8::Local<v8::Function> process =
283 v8::Local<v8::Function>::New(GetIsolate(), process_);
284 Handle<Value> result = process->Call(context->Global(), argc, argv);
Steve Blocka7e24c12009-10-30 11:49:00 +0000285 if (result.IsEmpty()) {
286 String::Utf8Value error(try_catch.Exception());
287 Log(*error);
288 return false;
289 } else {
290 return true;
291 }
292}
293
294
295JsHttpRequestProcessor::~JsHttpRequestProcessor() {
296 // Dispose the persistent handles. When noone else has any
297 // references to the objects stored in the handles they will be
298 // automatically reclaimed.
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000299 context_.Reset();
300 process_.Reset();
Steve Blocka7e24c12009-10-30 11:49:00 +0000301}
302
303
304Persistent<ObjectTemplate> JsHttpRequestProcessor::request_template_;
305Persistent<ObjectTemplate> JsHttpRequestProcessor::map_template_;
306
307
308// -----------------------------------
309// --- A c c e s s i n g M a p s ---
310// -----------------------------------
311
312// Utility function that wraps a C++ http request object in a
313// JavaScript object.
314Handle<Object> JsHttpRequestProcessor::WrapMap(map<string, string>* obj) {
315 // Handle scope for temporary handles.
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000316 EscapableHandleScope handle_scope(GetIsolate());
Steve Blocka7e24c12009-10-30 11:49:00 +0000317
318 // Fetch the template for creating JavaScript map wrappers.
319 // It only has to be created once, which we do on demand.
Kristian Monsen25f61362010-05-21 11:50:48 +0100320 if (map_template_.IsEmpty()) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000321 Handle<ObjectTemplate> raw_template = MakeMapTemplate(GetIsolate());
322 map_template_.Reset(GetIsolate(), raw_template);
Steve Blocka7e24c12009-10-30 11:49:00 +0000323 }
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000324 Handle<ObjectTemplate> templ =
325 Local<ObjectTemplate>::New(GetIsolate(), map_template_);
Steve Blocka7e24c12009-10-30 11:49:00 +0000326
327 // Create an empty map wrapper.
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000328 Local<Object> result = templ->NewInstance();
Steve Blocka7e24c12009-10-30 11:49:00 +0000329
330 // Wrap the raw C++ pointer in an External so it can be referenced
331 // from within JavaScript.
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000332 Handle<External> map_ptr = External::New(GetIsolate(), obj);
Steve Blocka7e24c12009-10-30 11:49:00 +0000333
334 // Store the map pointer in the JavaScript wrapper.
335 result->SetInternalField(0, map_ptr);
336
337 // Return the result through the current handle scope. Since each
338 // of these handles will go away when the handle scope is deleted
339 // we need to call Close to let one, the result, escape into the
340 // outer handle scope.
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000341 return handle_scope.Escape(result);
Steve Blocka7e24c12009-10-30 11:49:00 +0000342}
343
344
345// Utility function that extracts the C++ map pointer from a wrapper
346// object.
347map<string, string>* JsHttpRequestProcessor::UnwrapMap(Handle<Object> obj) {
348 Handle<External> field = Handle<External>::Cast(obj->GetInternalField(0));
349 void* ptr = field->Value();
350 return static_cast<map<string, string>*>(ptr);
351}
352
353
354// Convert a JavaScript string to a std::string. To not bother too
355// much with string encodings we just use ascii.
356string ObjectToString(Local<Value> value) {
357 String::Utf8Value utf8_value(value);
358 return string(*utf8_value);
359}
360
361
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000362void JsHttpRequestProcessor::MapGet(Local<String> name,
363 const PropertyCallbackInfo<Value>& info) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000364 // Fetch the map wrapped by this object.
365 map<string, string>* obj = UnwrapMap(info.Holder());
366
367 // Convert the JavaScript string to a std::string.
368 string key = ObjectToString(name);
369
370 // Look up the value if it exists using the standard STL ideom.
371 map<string, string>::iterator iter = obj->find(key);
372
373 // If the key is not present return an empty handle as signal
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000374 if (iter == obj->end()) return;
Steve Blocka7e24c12009-10-30 11:49:00 +0000375
376 // Otherwise fetch the value and wrap it in a JavaScript string
377 const string& value = (*iter).second;
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000378 info.GetReturnValue().Set(String::NewFromUtf8(
379 info.GetIsolate(), value.c_str(), String::kNormalString,
380 static_cast<int>(value.length())));
Steve Blocka7e24c12009-10-30 11:49:00 +0000381}
382
383
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000384void JsHttpRequestProcessor::MapSet(Local<String> name,
385 Local<Value> value_obj,
386 const PropertyCallbackInfo<Value>& info) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000387 // Fetch the map wrapped by this object.
388 map<string, string>* obj = UnwrapMap(info.Holder());
389
390 // Convert the key and value to std::strings.
391 string key = ObjectToString(name);
392 string value = ObjectToString(value_obj);
393
394 // Update the map.
395 (*obj)[key] = value;
396
397 // Return the value; any non-empty handle will work.
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000398 info.GetReturnValue().Set(value_obj);
Steve Blocka7e24c12009-10-30 11:49:00 +0000399}
400
401
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000402Handle<ObjectTemplate> JsHttpRequestProcessor::MakeMapTemplate(
403 Isolate* isolate) {
404 EscapableHandleScope handle_scope(isolate);
Steve Blocka7e24c12009-10-30 11:49:00 +0000405
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000406 Local<ObjectTemplate> result = ObjectTemplate::New(isolate);
Steve Blocka7e24c12009-10-30 11:49:00 +0000407 result->SetInternalFieldCount(1);
408 result->SetNamedPropertyHandler(MapGet, MapSet);
409
410 // Again, return the result through the current handle scope.
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000411 return handle_scope.Escape(result);
Steve Blocka7e24c12009-10-30 11:49:00 +0000412}
413
414
415// -------------------------------------------
416// --- A c c e s s i n g R e q u e s t s ---
417// -------------------------------------------
418
419/**
420 * Utility function that wraps a C++ http request object in a
421 * JavaScript object.
422 */
423Handle<Object> JsHttpRequestProcessor::WrapRequest(HttpRequest* request) {
424 // Handle scope for temporary handles.
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000425 EscapableHandleScope handle_scope(GetIsolate());
Steve Blocka7e24c12009-10-30 11:49:00 +0000426
427 // Fetch the template for creating JavaScript http request wrappers.
428 // It only has to be created once, which we do on demand.
429 if (request_template_.IsEmpty()) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000430 Handle<ObjectTemplate> raw_template = MakeRequestTemplate(GetIsolate());
431 request_template_.Reset(GetIsolate(), raw_template);
Steve Blocka7e24c12009-10-30 11:49:00 +0000432 }
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000433 Handle<ObjectTemplate> templ =
434 Local<ObjectTemplate>::New(GetIsolate(), request_template_);
Steve Blocka7e24c12009-10-30 11:49:00 +0000435
436 // Create an empty http request wrapper.
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000437 Local<Object> result = templ->NewInstance();
Steve Blocka7e24c12009-10-30 11:49:00 +0000438
439 // Wrap the raw C++ pointer in an External so it can be referenced
440 // from within JavaScript.
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000441 Handle<External> request_ptr = External::New(GetIsolate(), request);
Steve Blocka7e24c12009-10-30 11:49:00 +0000442
443 // Store the request pointer in the JavaScript wrapper.
444 result->SetInternalField(0, request_ptr);
445
446 // Return the result through the current handle scope. Since each
447 // of these handles will go away when the handle scope is deleted
448 // we need to call Close to let one, the result, escape into the
449 // outer handle scope.
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000450 return handle_scope.Escape(result);
Steve Blocka7e24c12009-10-30 11:49:00 +0000451}
452
453
454/**
455 * Utility function that extracts the C++ http request object from a
456 * wrapper object.
457 */
458HttpRequest* JsHttpRequestProcessor::UnwrapRequest(Handle<Object> obj) {
459 Handle<External> field = Handle<External>::Cast(obj->GetInternalField(0));
460 void* ptr = field->Value();
461 return static_cast<HttpRequest*>(ptr);
462}
463
464
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000465void JsHttpRequestProcessor::GetPath(Local<String> name,
466 const PropertyCallbackInfo<Value>& info) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000467 // Extract the C++ request object from the JavaScript wrapper.
468 HttpRequest* request = UnwrapRequest(info.Holder());
469
470 // Fetch the path.
471 const string& path = request->Path();
472
473 // Wrap the result in a JavaScript string and return it.
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000474 info.GetReturnValue().Set(String::NewFromUtf8(
475 info.GetIsolate(), path.c_str(), String::kNormalString,
476 static_cast<int>(path.length())));
Steve Blocka7e24c12009-10-30 11:49:00 +0000477}
478
479
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000480void JsHttpRequestProcessor::GetReferrer(
481 Local<String> name,
482 const PropertyCallbackInfo<Value>& info) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000483 HttpRequest* request = UnwrapRequest(info.Holder());
484 const string& path = request->Referrer();
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000485 info.GetReturnValue().Set(String::NewFromUtf8(
486 info.GetIsolate(), path.c_str(), String::kNormalString,
487 static_cast<int>(path.length())));
Steve Blocka7e24c12009-10-30 11:49:00 +0000488}
489
490
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000491void JsHttpRequestProcessor::GetHost(Local<String> name,
492 const PropertyCallbackInfo<Value>& info) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000493 HttpRequest* request = UnwrapRequest(info.Holder());
494 const string& path = request->Host();
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000495 info.GetReturnValue().Set(String::NewFromUtf8(
496 info.GetIsolate(), path.c_str(), String::kNormalString,
497 static_cast<int>(path.length())));
Steve Blocka7e24c12009-10-30 11:49:00 +0000498}
499
500
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000501void JsHttpRequestProcessor::GetUserAgent(
502 Local<String> name,
503 const PropertyCallbackInfo<Value>& info) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000504 HttpRequest* request = UnwrapRequest(info.Holder());
505 const string& path = request->UserAgent();
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000506 info.GetReturnValue().Set(String::NewFromUtf8(
507 info.GetIsolate(), path.c_str(), String::kNormalString,
508 static_cast<int>(path.length())));
Steve Blocka7e24c12009-10-30 11:49:00 +0000509}
510
511
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000512Handle<ObjectTemplate> JsHttpRequestProcessor::MakeRequestTemplate(
513 Isolate* isolate) {
514 EscapableHandleScope handle_scope(isolate);
Steve Blocka7e24c12009-10-30 11:49:00 +0000515
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000516 Local<ObjectTemplate> result = ObjectTemplate::New(isolate);
Steve Blocka7e24c12009-10-30 11:49:00 +0000517 result->SetInternalFieldCount(1);
518
519 // Add accessors for each of the fields of the request.
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000520 result->SetAccessor(
521 String::NewFromUtf8(isolate, "path", String::kInternalizedString),
522 GetPath);
523 result->SetAccessor(
524 String::NewFromUtf8(isolate, "referrer", String::kInternalizedString),
525 GetReferrer);
526 result->SetAccessor(
527 String::NewFromUtf8(isolate, "host", String::kInternalizedString),
528 GetHost);
529 result->SetAccessor(
530 String::NewFromUtf8(isolate, "userAgent", String::kInternalizedString),
531 GetUserAgent);
Steve Blocka7e24c12009-10-30 11:49:00 +0000532
533 // Again, return the result through the current handle scope.
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000534 return handle_scope.Escape(result);
Steve Blocka7e24c12009-10-30 11:49:00 +0000535}
536
537
538// --- Test ---
539
540
541void HttpRequestProcessor::Log(const char* event) {
542 printf("Logged: %s\n", event);
543}
544
545
546/**
547 * A simplified http request.
548 */
549class StringHttpRequest : public HttpRequest {
550 public:
551 StringHttpRequest(const string& path,
552 const string& referrer,
553 const string& host,
554 const string& user_agent);
555 virtual const string& Path() { return path_; }
556 virtual const string& Referrer() { return referrer_; }
557 virtual const string& Host() { return host_; }
558 virtual const string& UserAgent() { return user_agent_; }
559 private:
560 string path_;
561 string referrer_;
562 string host_;
563 string user_agent_;
564};
565
566
567StringHttpRequest::StringHttpRequest(const string& path,
568 const string& referrer,
569 const string& host,
570 const string& user_agent)
571 : path_(path),
572 referrer_(referrer),
573 host_(host),
574 user_agent_(user_agent) { }
575
576
577void ParseOptions(int argc,
578 char* argv[],
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000579 map<string, string>* options,
Steve Blocka7e24c12009-10-30 11:49:00 +0000580 string* file) {
581 for (int i = 1; i < argc; i++) {
582 string arg = argv[i];
Ben Murdoch3fb3ca82011-12-02 17:19:32 +0000583 size_t index = arg.find('=', 0);
Steve Blocka7e24c12009-10-30 11:49:00 +0000584 if (index == string::npos) {
585 *file = arg;
586 } else {
587 string key = arg.substr(0, index);
588 string value = arg.substr(index+1);
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000589 (*options)[key] = value;
Steve Blocka7e24c12009-10-30 11:49:00 +0000590 }
591 }
592}
593
594
595// Reads a file into a v8 string.
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000596Handle<String> ReadFile(Isolate* isolate, const string& name) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000597 FILE* file = fopen(name.c_str(), "rb");
598 if (file == NULL) return Handle<String>();
599
600 fseek(file, 0, SEEK_END);
601 int size = ftell(file);
602 rewind(file);
603
604 char* chars = new char[size + 1];
605 chars[size] = '\0';
606 for (int i = 0; i < size;) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000607 int read = static_cast<int>(fread(&chars[i], 1, size - i, file));
Steve Blocka7e24c12009-10-30 11:49:00 +0000608 i += read;
609 }
610 fclose(file);
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000611 Handle<String> result =
612 String::NewFromUtf8(isolate, chars, String::kNormalString, size);
Steve Blocka7e24c12009-10-30 11:49:00 +0000613 delete[] chars;
614 return result;
615}
616
617
618const int kSampleSize = 6;
619StringHttpRequest kSampleRequests[kSampleSize] = {
620 StringHttpRequest("/process.cc", "localhost", "google.com", "firefox"),
621 StringHttpRequest("/", "localhost", "google.net", "firefox"),
622 StringHttpRequest("/", "localhost", "google.org", "safari"),
623 StringHttpRequest("/", "localhost", "yahoo.com", "ie"),
624 StringHttpRequest("/", "localhost", "yahoo.com", "safari"),
625 StringHttpRequest("/", "localhost", "yahoo.com", "firefox")
626};
627
628
629bool ProcessEntries(HttpRequestProcessor* processor, int count,
630 StringHttpRequest* reqs) {
631 for (int i = 0; i < count; i++) {
632 if (!processor->Process(&reqs[i]))
633 return false;
634 }
635 return true;
636}
637
638
639void PrintMap(map<string, string>* m) {
640 for (map<string, string>::iterator i = m->begin(); i != m->end(); i++) {
641 pair<string, string> entry = *i;
642 printf("%s: %s\n", entry.first.c_str(), entry.second.c_str());
643 }
644}
645
646
647int main(int argc, char* argv[]) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000648 v8::V8::InitializeICU();
649 v8::Platform* platform = v8::platform::CreateDefaultPlatform();
650 v8::V8::InitializePlatform(platform);
651 v8::V8::Initialize();
Steve Blocka7e24c12009-10-30 11:49:00 +0000652 map<string, string> options;
653 string file;
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000654 ParseOptions(argc, argv, &options, &file);
Steve Blocka7e24c12009-10-30 11:49:00 +0000655 if (file.empty()) {
656 fprintf(stderr, "No script was specified.\n");
657 return 1;
658 }
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000659 Isolate* isolate = Isolate::New();
660 Isolate::Scope isolate_scope(isolate);
661 HandleScope scope(isolate);
662 Handle<String> source = ReadFile(isolate, file);
Steve Blocka7e24c12009-10-30 11:49:00 +0000663 if (source.IsEmpty()) {
664 fprintf(stderr, "Error reading '%s'.\n", file.c_str());
665 return 1;
666 }
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000667 JsHttpRequestProcessor processor(isolate, source);
Steve Blocka7e24c12009-10-30 11:49:00 +0000668 map<string, string> output;
669 if (!processor.Initialize(&options, &output)) {
670 fprintf(stderr, "Error initializing processor.\n");
671 return 1;
672 }
673 if (!ProcessEntries(&processor, kSampleSize, kSampleRequests))
674 return 1;
675 PrintMap(&output);
676}