Parent Directory
|
Revision Log
precompile many modules at server start time. this allows more compiled code (parse trees, bytecode, etc.) to be shared among child processes, and speeds child start time, since that compilation has already been done in the master process. you may want to turn this off for development, since it makes the server take a really long time to start.
1 ################################################################################ 2 # WeBWorK Online Homework Delivery System 3 # Copyright © 2000-2006 The WeBWorK Project, http://openwebwork.sf.net/ 4 # $CVSHeader: webwork2/lib/WeBWorK.pm,v 1.92 2006/08/14 18:14:22 sh002i Exp $ 5 # 6 # This program is free software; you can redistribute it and/or modify it under 7 # the terms of either: (a) the GNU General Public License as published by the 8 # Free Software Foundation; either version 2, or (at your option) any later 9 # version, or (b) the "Artistic License" which comes with this package. 10 # 11 # This program is distributed in the hope that it will be useful, but WITHOUT 12 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 13 # FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the 14 # Artistic License for more details. 15 ################################################################################ 16 17 package WeBWorK; 18 19 =head1 NAME 20 21 WeBWorK - Dispatch requests to the appropriate content generator. 22 23 =head1 SYNOPSIS 24 25 my $r = Apache->request; 26 my $result = eval { WeBWorK::dispatch($r) }; 27 die "something bad happened: $@" if $@; 28 29 =head1 DESCRIPTION 30 31 C<WeBWorK> is the dispatcher for the WeBWorK system. Given an Apache request 32 object, it performs authentication and determines which subclass of 33 C<WeBWorK::ContentGenerator> to call. 34 35 =cut 36 37 BEGIN { $main::VERSION = "2.x"; } 38 39 use strict; 40 use warnings; 41 use Time::HiRes qw/time/; 42 43 # load WeBWorK::Constants before anything else 44 # this sets package variables in several packages 45 use WeBWorK::Constants; 46 47 use WeBWorK::Authen; 48 use WeBWorK::Authz; 49 use WeBWorK::CourseEnvironment; 50 use WeBWorK::DB; 51 use WeBWorK::Debug; 52 use WeBWorK::Request; 53 use WeBWorK::Upload; 54 use WeBWorK::URLPath; 55 use WeBWorK::Utils qw(runtime_use writeTimingLogEntry); 56 57 use mod_perl; 58 use constant MP2 => ( exists $ENV{MOD_PERL_API_VERSION} and $ENV{MOD_PERL_API_VERSION} >= 2 ); 59 60 # Apache2 needs upload class 61 BEGIN { 62 if (MP2) { 63 require Apache2::Upload; 64 Apache2::Upload->import(); 65 require Apache2::RequestUtil; 66 Apache2::RequestUtil->import(); 67 } 68 } 69 70 use constant LOGIN_MODULE => "WeBWorK::ContentGenerator::Login"; 71 use constant PROCTOR_LOGIN_MODULE => "WeBWorK::ContentGenerator::LoginProctor"; 72 73 BEGIN { 74 # pre-compile all content generators 75 # Login and LoginProctor need to be handled separately, since they don't have paths 76 map { eval "require $_" } 77 WeBWorK::URLPath->all_modules, 78 LOGIN_MODULE, 79 PROCTOR_LOGIN_MODULE; 80 # other candidates for preloading: 81 # - DB Record, Schema, and Driver classes (esp. Driver::SQL as it loads DBI) 82 # - CourseManagement subclasses (ditto. sql_single.pm) 83 # - WeBWorK::PG::Local, which loads WeBWorK::PG::Translator 84 # - Authen subclasses 85 } 86 87 our %SeedCE; 88 89 sub dispatch($) { 90 my ($apache) = @_; 91 my $r = new WeBWorK::Request($apache); 92 93 my $method = $r->method; 94 my $location = $r->location; 95 my $uri = $r->uri; 96 my $path_info = $r->path_info | ""; 97 my $args = $r->args || ""; 98 #my $webwork_root = $r->dir_config("webwork_root"); 99 #my $pg_root = $r->dir_config("pg_root"); 100 101 debug("\n\n===> Begin " . __PACKAGE__ . "::dispatch() <===\n\n"); 102 debug("Hi, I'm the new dispatcher!\n"); 103 debug(("-" x 80) . "\n"); 104 105 debug("Okay, I got some basic information:\n"); 106 debug("The apache location is $location\n"); 107 debug("The request method is $method\n"); 108 debug("The URI is $uri\n"); 109 debug("The path-info is $path_info\n"); 110 debug("The argument string is $args\n"); 111 #debug("The WeBWorK root directory is $webwork_root\n"); 112 #debug("The PG root directory is $pg_root\n"); 113 debug(("-" x 80) . "\n"); 114 115 debug("The first thing we need to do is munge the path a little:\n"); 116 117 my ($path) = $uri =~ m/$location(.*)/; 118 $path = "/" if $path eq ""; # no path at all 119 120 debug("We can't trust the path-info, so we make our own path.\n"); 121 debug("path-info claims: $path_info\n"); 122 debug("but it's really: $path\n"); 123 debug("(if it's empty, we set it to \"/\".)\n"); 124 125 $path =~ s|/+|/|g; 126 debug("...and here it is without repeated slashes: $path\n"); 127 128 # lookbehind assertion for "not a slash" 129 # matches the boundary after the last char 130 $path =~ s|(?<=[^/])$|/|; 131 debug("...and here it is with a trailing slash: $path\n"); 132 133 debug(("-" x 80) . "\n"); 134 135 debug("Now we need to look at the path a little to figure out where we are\n"); 136 137 debug("-------------------- call to WeBWorK::URLPath::newFromPath\n"); 138 my $urlPath = WeBWorK::URLPath->newFromPath($path); 139 debug("-------------------- call to WeBWorK::URLPath::newFromPath\n"); 140 141 unless ($urlPath) { 142 debug("This path is invalid... see you later!\n"); 143 die "The path '$path' is not valid.\n"; 144 } 145 146 my $displayModule = $urlPath->module; 147 my %displayArgs = $urlPath->args; 148 149 unless ($displayModule) { 150 debug("The display module is empty, so we can DECLINE here.\n"); 151 die "No display module found for path '$path'."; 152 } 153 154 debug("The display module for this path is: $displayModule\n"); 155 debug("...and here are the arguments we'll pass to it:\n"); 156 foreach my $key (keys %displayArgs) { 157 debug("\t$key => $displayArgs{$key}\n"); 158 } 159 160 my $selfPath = $urlPath->path; 161 my $parent = $urlPath->parent; 162 my $parentPath = $parent ? $parent->path : "<no parent>"; 163 164 debug("Reconstructing the original path gets us: $selfPath\n"); 165 debug("And we can generate the path to our parent, too: $parentPath\n"); 166 debug("(We could also figure out who our children are, but we'd need to supply additional arguments.)\n"); 167 debug(("-" x 80) . "\n"); 168 169 debug("The URLPath looks good, we'll add it to the request.\n"); 170 $r->urlpath($urlPath); 171 172 debug("Now we want to look at the parameters we got.\n"); 173 174 debug("The raw params:\n"); 175 foreach my $key ($r->param) { 176 my @vals = $r->param($key); 177 my $vals = join(", ", map { "'$_'" } @vals); 178 debug("\t$key => $vals\n"); 179 } 180 181 #mungeParams($r); 182 # 183 #debug("The munged params:\n"); 184 #foreach my $key ($r->param) { 185 # debug("\t$key\n"); 186 # debug("\t\t$_\n") foreach $r->param($key); 187 #} 188 189 debug(("-" x 80) . "\n"); 190 191 my $apache_hostname = $r->hostname; 192 my $apache_port = $r->get_server_port; 193 my $apache_is_ssl = ($r->subprocess_env('https') ? 1 : ""); 194 my $apache_root_url; 195 if ($r->subprocess_env('https')) { 196 $apache_root_url = "https://$apache_hostname"; 197 $apache_root_url .= ":$apache_port" if $apache_port != 443; 198 } else { 199 $apache_root_url = "http://$apache_hostname"; 200 $apache_root_url .= ":$apache_port" if $apache_port != 80; 201 } 202 203 debug("We need to get a course environment (with or without a courseID!)\n"); 204 my $ce = eval { new WeBWorK::CourseEnvironment({ 205 %SeedCE, 206 courseName => $displayArgs{courseID}, 207 # this is kind of a hack, but it's really the only sane way to get this 208 # server information into the PG box 209 apache_hostname => $apache_hostname, 210 apache_port => $apache_port, 211 apache_is_ssl => $apache_is_ssl, 212 apache_root_url => $apache_root_url, 213 }) }; 214 $@ and die "Failed to initialize course environment: $@\n"; 215 debug("Here's the course environment: $ce\n"); 216 $r->ce($ce); 217 218 my @uploads; 219 if (MP2) { 220 my $upload_table = $r->upload; 221 @uploads = values %$upload_table if defined $upload_table; 222 } else { 223 @uploads = $r->upload; 224 } 225 foreach my $u (@uploads) { 226 # make sure it's a "real" upload 227 next unless $u->filename; 228 229 # store the upload 230 my $upload = WeBWorK::Upload->store($u, 231 dir => $ce->{webworkDirs}->{uploadCache} 232 ); 233 234 # store the upload ID and hash in the file upload field 235 my $id = $upload->id; 236 my $hash = $upload->hash; 237 $r->param($u->name => "$id $hash"); 238 } 239 240 # create these out here. they should fail if they don't have the right information 241 # this lets us not be so careful about whether these objects are defined when we use them. 242 # instead, we just create the behavior that if they don't have a valid $db they fail. 243 my $authz = new WeBWorK::Authz($r); 244 $r->authz($authz); 245 246 # figure out which authentication modules to use 247 #my $user_authen_module; 248 #my $proctor_authen_module; 249 #if (ref $ce->{authen}{user_module} eq "HASH") { 250 # if (exists $ce->{authen}{user_module}{$ce->{dbLayoutName}}) { 251 # $user_authen_module = $ce->{authen}{user_module}{$ce->{dbLayoutName}}; 252 # } else { 253 # $user_authen_module = $ce->{authen}{user_module}{"*"}; 254 # } 255 #} else { 256 # $user_authen_module = $ce->{authen}{user_module}; 257 #} 258 #if (ref $ce->{authen}{proctor_module} eq "HASH") { 259 # if (exists $ce->{authen}{proctor_module}{$ce->{dbLayoutName}}) { 260 # $proctor_authen_module = $ce->{authen}{proctor_module}{$ce->{dbLayoutName}}; 261 # } else { 262 # $proctor_authen_module = $ce->{authen}{proctor_module}{"*"}; 263 # } 264 #} else { 265 # $proctor_authen_module = $ce->{authen}{proctor_module}; 266 #} 267 268 my $user_authen_module = WeBWorK::Authen::class($ce, "user_module"); 269 270 runtime_use $user_authen_module; 271 my $authen = $user_authen_module->new($r); 272 debug("Using user_authen_module $user_authen_module: $authen\n"); 273 $r->authen($authen); 274 275 my $db; 276 277 if ($displayArgs{courseID}) { 278 debug("We got a courseID from the URLPath, now we can do some stuff:\n"); 279 280 unless (-e $ce->{courseDirs}->{root}) { 281 die "Course '$displayArgs{courseID}' not found: $!"; 282 } 283 284 debug("...we can create a database object...\n"); 285 $db = new WeBWorK::DB($ce->{dbLayout}); 286 debug("(here's the DB handle: $db)\n"); 287 $r->db($db); 288 289 my $authenOK = $authen->verify; 290 if ($authenOK) { 291 my $userID = $r->param("user"); 292 debug("Hi, $userID, glad you made it.\n"); 293 294 # tell authorizer to cache this user's permission level 295 $authz->setCachedUser($userID); 296 297 debug("Now we deal with the effective user:\n"); 298 my $eUserID = $r->param("effectiveUser") || $userID; 299 debug("userID=$userID eUserID=$eUserID\n"); 300 if ($userID ne $eUserID) { 301 debug("userID and eUserID differ... seeing if userID has 'become_student' permission.\n"); 302 my $su_authorized = $authz->hasPermissions($userID, "become_student"); 303 if ($su_authorized) { 304 debug("Ok, looks like you're allowed to become $eUserID. Whoopie!\n"); 305 } else { 306 debug("Uh oh, you're not allowed to become $eUserID. Nice try!\n"); 307 die "You are not allowed to act as another user.\n"; 308 } 309 } 310 311 # set effectiveUser in case it was changed or not set to begin with 312 $r->param("effectiveUser" => $eUserID); 313 314 # if we're doing a proctored test, after the user has been authenticated 315 # we need to also check on the proctor. note that in the gateway quiz 316 # module we double check this, to be sure that someone isn't taking a 317 # proctored quiz but calling the unproctored ContentGenerator 318 my $urlProducedPath = $urlPath->path(); 319 if ( $urlProducedPath =~ /proctored_quiz_mode/i ) { 320 my $proctor_authen_module = WeBWorK::Authen::class($ce, "proctor_module"); 321 runtime_use $proctor_authen_module; 322 my $authenProctor = $proctor_authen_module->new($r); 323 debug("Using proctor_authen_module $proctor_authen_module: $authenProctor\n"); 324 my $procAuthOK = $authenProctor->verify(); 325 326 if (not $procAuthOK) { 327 $displayModule = PROCTOR_LOGIN_MODULE; 328 } 329 } 330 } else { 331 debug("Bad news: authentication failed!\n"); 332 $displayModule = LOGIN_MODULE; 333 debug("set displayModule to $displayModule\n"); 334 } 335 } 336 337 # store the time before we invoke the content generator 338 my $cg_start = time; # this is Time::HiRes's time, which gives floating point values 339 340 debug(("-" x 80) . "\n"); 341 debug("Finally, we'll load the display module...\n"); 342 343 runtime_use($displayModule); 344 345 debug("...instantiate it...\n"); 346 347 my $instance = $displayModule->new($r); 348 349 debug("...and call it:\n"); 350 debug("-------------------- call to ${displayModule}::go\n"); 351 352 my $result = $instance->go(); 353 354 debug("-------------------- call to ${displayModule}::go\n"); 355 356 my $cg_end = time; 357 my $cg_duration = $cg_end - $cg_start; 358 writeTimingLogEntry($ce, "[".$r->uri."]", sprintf("runTime = %.3f sec", $cg_duration)." ".$ce->{dbLayoutName}, ""); 359 360 debug("returning result: " . (defined $result ? $result : "UNDEF") . "\n"); 361 362 return $result; 363 } 364 365 sub mungeParams { 366 my ($r) = @_; 367 368 my @paramQueue; 369 370 # remove all the params from the request, and store them in the param queue 371 foreach my $key ($r->param) { 372 push @paramQueue, [ $key => [ $r->param($key) ] ]; 373 $r->parms->unset($key) 374 } 375 376 # exhaust the param queue, decoding encoded params 377 while (@paramQueue) { 378 my ($key, $values) = @{ shift @paramQueue }; 379 380 if ($key =~ m/\,/) { 381 # we have multiple params encoded in a single param 382 # split them up and add them to the end of the queue 383 push @paramQueue, map { [ $_, $values ] } split m/\,/, $key; 384 } elsif ($key =~ m/\:/) { 385 # we have a whole param encoded in a key 386 # split it up and add it to the end of the queue 387 my ($newKey, $newValue) = split m/\:/, $key; 388 push @paramQueue, [ $newKey, [ $newValue ] ]; 389 } else { 390 # this is a "normal" param 391 # add it to the param list 392 if (defined $r->param($key)) { 393 # the param already exists -- append the values we have 394 $r->param($key => [ $r->param($key), @$values ]); 395 } else { 396 # the param doesn't exist -- create it with the values we have 397 $r->param($key => $values); 398 } 399 } 400 } 401 } 402 403 =head1 AUTHOR 404 405 Written by Dennis Lambe, malsyned at math.rochester.edu. Modified by Sam 406 Hathaway, sh002i at math.rochester.edu. 407 408 =cut 409 410 1;
| aubreyja at gmail dot com | ViewVC Help |
| Powered by ViewVC 1.0.9 |