00001
package com.quadcap.http.server22;
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
import java.util.Date;
00042
import java.util.Enumeration;
00043
import java.util.Hashtable;
00044
import java.util.Properties;
00045
import java.util.Random;
00046
import java.util.Vector;
00047
00048
import java.io.File;
00049
import java.io.FileInputStream;
00050
import java.io.FileOutputStream;
00051
import java.io.InputStream;
00052
import java.io.IOException;
00053
import java.io.PrintWriter;
00054
00055
import java.text.SimpleDateFormat;
00056
00057
import java.net.InetAddress;
00058
import java.net.MalformedURLException;
00059
import java.net.Socket;
00060
import java.net.URL;
00061
import java.net.URLConnection;
00062
00063
import javax.servlet.RequestDispatcher;
00064
import javax.servlet.Servlet;
00065
import javax.servlet.ServletContext;
00066
import javax.servlet.ServletException;
00067
import javax.servlet.ServletRequest;
00068
import javax.servlet.ServletResponse;
00069
00070
import javax.servlet.http.HttpServlet;
00071
import javax.servlet.http.HttpServletRequest;
00072
import javax.servlet.http.HttpSession;
00073
import javax.servlet.http.HttpSessionContext;
00074
00075
import com.quadcap.net.server.Server;
00076
import com.quadcap.net.server.Worker;
00077
00078
import com.quadcap.util.threads.Command;
00079
import com.quadcap.util.threads.PeriodicScheduler;
00080
00081
import com.quadcap.util.text.OctetMap;
00082
import com.quadcap.util.text.Scanner;
00083
00084
import com.quadcap.io.dir.Directory;
00085
00086
import com.quadcap.util.Config;
00087
import com.quadcap.util.Debug;
00088
import com.quadcap.util.Util;
00089
00090
00091
00092
00093
00094
00095 public class WebServer {
00096 Random
random =
new Random();
00097
00098
00099
00100
00101 String
hostName = null;
00102
00103
00104
00105
00106 Hashtable
contexts =
new Hashtable();
00107
00108
00109
00110
00111 Hashtable
mimeTypes =
new Hashtable();
00112
00113 WebApplication defaultContext = null;
00114
00115 File
tmpDir;
00116
00117 long sessionCount = 0;
00118 static final int defaultInactiveInterval = 60;
00119 static final int expireCheckerInterval = 60;
00120
00121 PeriodicScheduler
expireChecker;
00122
00123 PrintWriter
reqStream;
00124
00125 Server
server;
00126
00127
00128
00129
00130
00131
00132
00133
00134
00135
00136
00137
00138
00139
00140
00141
00142
00143
00144
00145
00146
00147
00148
00149
00150
00151
00152
00153
00154
00155
00156
00157
00158
00159
00160
00161
00162
00163
00164
00165
00166
00167
00168
00169
00170
00171
00172
00173
00174
00175
00176
00177
00178
00179
00180
00181
00182
00183 public void init(Properties p)
00184
throws Exception
00185 {
00186 p.put(
"workerClass",
"com.quadcap.http.server22.WebWorker");
00187
server =
new Server(p,
this);
00188
00189 String tmp = p.getProperty(
"tempDir",
"repository");
00190
tmpDir =
new File(tmp);
00191
if (!
tmpDir.isDirectory() && !
tmpDir.mkdirs()) {
00192
throw new IOException(
"Can't create temp directory: " + tmp);
00193 }
00194
00195 tmp = p.getProperty(
"requestLog",
"web.log");
00196
reqStream =
new PrintWriter(
new FileOutputStream(tmp));
00197
00198 InputStream dis = ClassLoader.getSystemResourceAsStream(
00199
"com/quadcap/http/server22/mime.types");
00200
if (dis != null) {
00201
try {
00202
parseMimeTypes(dis);
00203 } finally {
00204 dis.close();
00205 }
00206 }
00207
00208 Enumeration e =
Config.getMatchingProps(p,
"context.*.root");
00209
while (e.hasMoreElements()) {
00210 String context = e.nextElement().toString();
00211 String c =
"context." + context;
00212 String root = p.getProperty(c +
".root");
00213 String docBase = p.getProperty(c +
".docBase");
00214
if (docBase == null) {
00215
Debug.println(0,
"No docBase specified for context " +
00216 context);
00217 }
else {
00218
try {
00219
WebApplication app =
addWebApplication(root, docBase);
00220
if (context.equals(
"default")) {
00221
defaultContext = app;
00222 }
00223 }
catch (Throwable t) {
00224
Debug.print(t);
00225 }
00226 }
00227 }
00228
00229
boolean found =
false;
00230 e =
Config.getMatchingProps(p,
"acceptor.*.port");
00231
while (e.hasMoreElements()) {
00232 found =
true;
00233 String acceptor = e.nextElement().toString();
00234 String a =
"acceptor." + acceptor;
00235 Properties ap =
new Properties();
00236 ap.put(
"port", p.getProperty(a +
".port"));
00237 ap.put(
"queueDepth", p.getProperty(a +
".queueDepth",
"32"));
00238
Debug.println(
"startAcceptor: " + ap.get(
"port"));
00239
server.startAcceptor(ap);
00240 }
00241
00242
expireChecker =
new PeriodicScheduler(
server.getThreadGroup(),
00243
"expire checker",
00244
this);
00245
expireChecker.add(
"Check for expired sessions",
00246
new ExpireChecker(),
00247
expireCheckerInterval * 1000);
00248
expireChecker.start();
00249
if (!found) {
00250
Debug.println(0,
"No acceptor.<name>.port properties found!!");
00251 }
00252 }
00253
00254 public void stop() {
00255
server.stop();
00256 }
00257
00258 final void parseMimeTypes(InputStream is)
throws IOException {
00259
Scanner s =
new Scanner(is);
00260 s.
skipWhile(
OctetMap.crlfChars);
00261
int c;
00262
while ((c = s.
peek()) >= 0) {
00263
if (c ==
'#') {
00264 s.
skipUntil(
OctetMap.crlfChars);
00265 }
else {
00266 String mimeType = s.
parseWhile(
OctetMap.uriChars);
00267 s.
skipWhile(
OctetMap.wsChars);
00268
while ((c = s.
peek()) >= 0 &&
OctetMap.tokenChars.has(c)) {
00269 String ext = s.
parseWhile(
OctetMap.tokenChars);
00270
mimeTypes.put(ext, mimeType);
00271 s.
skipWhile(
OctetMap.wsChars);
00272 }
00273 s.
skipUntil(
OctetMap.crlfChars);
00274 }
00275 s.
skipWhile(
OctetMap.crlfChars);
00276 }
00277 is.close();
00278 }
00279
00280 public void removeWebApplication(String root) {
00281
WebApplication app =
getContextForRoot(root);
00282
if (app != null) {
00283
contexts.remove(root);
00284 app.
shutdown();
00285 }
00286 }
00287
00288 public WebApplication addWebApplication(String root, String docBase)
00289
throws IOException, ServletException
00290 {
00291
00292
int len = root.length();
00293
if (len > 0 && root.charAt(len-1) ==
'/') {
00294 root = root.substring(0, len-1);
00295 }
00296
00297
if (
Trace.level() > 1) {
00298
Debug.println(
"addWebApplication(" + root +
", " + docBase +
")");
00299 }
00300
00301
00302
WebApplication oldApp = (
WebApplication)
contexts.get(root);
00303
if (oldApp != null) {
00304
try {
00305
Debug.println(
"[Shutting down old app for " + root +
": " +
00306 oldApp);
00307 oldApp.shutdown();
00308 }
catch (Throwable t) {
00309 }
00310 }
00311
00312 File dir =
new File(docBase);
00313
Directory d =
Directory.getDirectory(dir);
00314
WebApplication app =
new WebApplication();
00315 app.
init(
this, root, d);
00316
00317
contexts.put(root, app);
00318
00319
00320
return app;
00321 }
00322
00323
00324
00325
00326
00327
00328
00329 public String
getServerInfo() {
00330
00331
00332
return "Quadcap Web Server 3.4";
00333
00334 }
00335
00336
00337
00338
00339 public String
getMimeTypeForExt(String ext) {
00340 String type = (String)
mimeTypes.get(ext.toLowerCase());
00341
return type;
00342 }
00343
00344
00345
00346
00347 public WebApplication getContext(String path) {
00348
WebApplication ret = (
WebApplication)
contexts.get(path);
00349
for (
int i = path.length() - 1; ret == null && i >= 0; i--) {
00350
if (path.charAt(i) ==
'/') {
00351 String subPath = path.substring(0, i);
00352 ret = (
WebApplication)
contexts.get(subPath);
00353 }
00354 }
00355
if (ret == null) ret =
defaultContext;
00356
Debug.println(
"defaultContext = " + defaultContext);
00357
return ret;
00358 }
00359
00360 String
makeSessionId() {
00361
int r =
random.nextInt() & 0xfffffff;
00362
return "" + (
sessionCount++) +
"." + r +
"." +
00363 System.currentTimeMillis();
00364 }
00365
00366 public void expireSessions() {
00367 Enumeration e =
contexts.elements();
00368
while (e.hasMoreElements()) {
00369
WebApplication app = (
WebApplication)e.nextElement();
00370 app.
expireSessions();
00371 }
00372 }
00373
00374 final File
getTempDir() {
00375
return tmpDir;
00376 }
00377
00378 public Enumeration
getContextRoots() {
00379
return contexts.keys();
00380 }
00381
00382 public WebApplication getContextForRoot(String root) {
00383
return (
WebApplication)
contexts.get(root);
00384 }
00385
00386 public static void main(String args[]) {
00387 Properties p =
Config.getProperties();
00388
WebServer w =
new WebServer();
00389
try {
00390 w.
init(p);
00391 }
catch (Throwable t) {
00392
Debug.print(t);
00393 }
00394 }
00395
00396 static SimpleDateFormat
df;
00397
static {
00398
df =
new SimpleDateFormat(
"yyyy/MM/dd HH:mm:ss.SSS");
00399 }
00400 public void requestLog(String s) {
00401
reqStream.println(
df.format(
new Date()) +
" " + s);
00402
reqStream.flush();
00403 }
00404 }
00405
00406
00407 class ExpireChecker implements Command {
00408 public void execute(Object context) {
00409 ((
WebServer)context).expireSessions();
00410 }
00411 }
00412