00001
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 module mango.http.server.HttpRequest;
00037
00038 private import mango.utils.Text;
00039
00040 private import mango.io.Uri,
00041 mango.io.Token,
00042 mango.io.Buffer,
00043 mango.io.Reader,
00044 mango.io.Socket,
00045 mango.io.Exception,
00046 mango.io.Tokenizer;
00047
00048 private import mango.io.model.IBuffer,
00049 mango.io.model.IWriter;
00050
00051 private import mango.http.HttpReader;
00052
00053 private import mango.http.server.HttpParams,
00054 mango.http.server.HttpCookies,
00055 mango.http.server.HttpHeaders,
00056 mango.http.server.HttpMessage;
00057
00058 private import mango.http.server.model.IProviderBridge;
00059
00060
00061
00062
00063
00064
00065
00066
00067 class HttpRequest : HttpMessage, IWritable
00068 {
00069 private int port;
00070 private char[] host;
00071 private bool mimed,
00072 uried,
00073 gulped;
00074
00075
00076 private MutableUri uri;
00077 private CompositeToken line;
00078 private HttpReader reader;
00079 private HttpParams params;
00080 private HttpCookies cookies;
00081 private StartLine startLine;
00082
00083 static private InvalidStateException InvalidState;
00084
00085
00086
00087
00088
00089
00090
00091 static this()
00092 {
00093 InvalidState = new InvalidStateException("Invalid request state");
00094 }
00095
00096
00097
00098
00099
00100
00101
00102
00103
00104
00105 this (IProviderBridge bridge)
00106 {
00107 IBuffer buffer;
00108
00109 super (bridge, null);
00110 buffer = super.getBuffer();
00111
00112
00113 uri = new MutableUri();
00114
00115
00116 startLine = new StartLine();
00117
00118
00119 params = new HttpParams ();
00120
00121
00122 reader = new HttpReader (buffer);
00123
00124
00125 cookies = new HttpCookies (getHeader());
00126
00127
00128 line = new CompositeToken (Tokenizers.line, buffer);
00129 }
00130
00131
00132
00133
00134
00135
00136
00137 void reset()
00138 {
00139 port = Uri.InvalidPort;
00140 host = null;
00141 uried = false;
00142 mimed = false;
00143 gulped = false;
00144
00145 uri.reset();
00146 super.reset();
00147 params.reset();
00148 cookies.reset();
00149 }
00150
00151
00152
00153
00154
00155
00156
00157 StartLine getStartLine()
00158 {
00159 return startLine;
00160 }
00161
00162
00163
00164
00165
00166
00167
00168 Uri getRequestUri()
00169 {
00170 if (! uried)
00171 {
00172 uri.parse (startLine.getPath());
00173 if (uri.getScheme() is null)
00174 uri.setScheme (getServerScheme());
00175 uried = true;
00176 }
00177 return uri;
00178 }
00179
00180
00181
00182
00183
00184
00185
00186 Uri getExplicitUri()
00187 {
00188 getRequestUri();
00189
00190 if (uri.getHost is null)
00191 uri.setHost (getHost);
00192 return uri;
00193 }
00194
00195
00196
00197
00198
00199
00200
00201
00202
00203 HttpReader getReader()
00204 {
00205
00206 gulped = true;
00207 return reader;
00208 }
00209
00210
00211
00212
00213
00214
00215
00216 HttpCookies getInputCookies()
00217 {
00218 if (gulped)
00219 throw InvalidState;
00220 return cookies;
00221 }
00222
00223
00224
00225
00226
00227
00228
00229 HttpHeaders getInputHeaders()
00230 {
00231 if (gulped)
00232 throw InvalidState;
00233 return getHeader();
00234 }
00235
00236
00237
00238
00239
00240
00241
00242
00243 HttpParams getInputParameters()
00244 {
00245
00246 if (! params.isParsed ())
00247 {
00248 char[] query = getRequestUri().getQuery();
00249
00250
00251 if (query.length)
00252
00253 params.parse (query);
00254 else
00255
00256 if (startLine.getMethod() == "POST" &&
00257 super.getContentType() == "application/x-www-form-urlencoded")
00258 {
00259
00260 int length = getHeader.getInt (HttpHeader.ContentLength);
00261 if (length < 0)
00262 throw new IOException ("No params supplied for http POST");
00263 else
00264 if (length > HttpHeader.MaxPostParamSize)
00265 throw new IOException ("Post parameters exceed maximum length");
00266 else
00267 {
00268 char[] c = cast(char[]) getBuffer.get (length);
00269 if (c)
00270 params.parse (uri.decode (Text.replace(c, '+', ' ')));
00271 else
00272 throw new IOException ("Post parameters exceed buffer size");
00273 }
00274 }
00275 }
00276 return params;
00277 }
00278
00279
00280
00281
00282
00283
00284
00285
00286
00287 IBuffer getInputBuffer()
00288 {
00289
00290 gulped = true;
00291 return super.getBuffer();
00292 }
00293
00294
00295
00296
00297
00298
00299
00300
00301 void write (IWriter writer)
00302 {
00303 startLine.write (writer);
00304 super.write (writer);
00305 }
00306
00307
00308
00309
00310
00311
00312
00313 void readHeaders()
00314 {
00315 IBuffer input = super.getBuffer();
00316
00317
00318 while (line.next() && line.getLength() == 0)
00319 {}
00320
00321
00322 if (input.readable() == 0)
00323 throw new IOException ("truncated request");
00324
00325
00326 startLine.parse (line.toString());
00327
00328
00329 getHeader().parse (input);
00330
00331 version (ShowHeaders)
00332 {
00333 Stdout.cr().put(">>>> request Headers:").cr();
00334 getHeader().write (Stdout);
00335 }
00336 }
00337
00338
00339
00340
00341
00342
00343
00344 char[] getRemoteAddr()
00345 {
00346 return getBridge().getServer().getRemoteAddress(getConduit());
00347 }
00348
00349
00350
00351
00352
00353
00354
00355 char[] getRemoteHost()
00356 {
00357 return getBridge().getServer().getRemoteHost(getConduit());
00358 }
00359
00360
00361
00362
00363
00364
00365
00366 char[] getServerScheme()
00367 {
00368 return getBridge().getServer().getProtocol();
00369 }
00370
00371
00372
00373
00374
00375
00376
00377 char[] getEncoding()
00378 {
00379 getMimeType();
00380 return super.getEncoding();
00381 }
00382
00383
00384
00385
00386
00387
00388
00389 char[] getMimeType()
00390 {
00391 if (! mimed)
00392 {
00393 setMimeAndEncoding (super.getContentType());
00394 mimed = true;
00395 }
00396 return super.getMimeType();
00397 }
00398
00399
00400
00401
00402
00403
00404
00405 int getPort()
00406 {
00407 if (port == Uri.InvalidPort)
00408 {
00409 getHost();
00410 if (port == Uri.InvalidPort)
00411
00412 port = getBridge().getServer().getPort();
00413 }
00414 return port;
00415 }
00416
00417
00418
00419
00420
00421
00422
00423
00424
00425 char[] getHost()
00426 {
00427
00428 if (host.length)
00429 return host;
00430
00431
00432 Uri uri = getRequestUri ();
00433
00434 host = uri.getHost ();
00435 port = uri.getPort ();
00436 if (host.length)
00437 return host;
00438
00439
00440 host = Text.trim (getHeader().get(HttpHeader.Host));
00441 port = Uri.InvalidPort;
00442
00443 if (host.length)
00444 {
00445 int colon = Text.indexOf (host, ':');
00446 if (colon >= 0)
00447 {
00448 if (colon < host.length)
00449 try {
00450 port = Text.atoi (host[colon+1..host.length]);
00451 } catch (Exception e){}
00452 host = host[0..colon];
00453 }
00454 return host;
00455 }
00456
00457
00458 host = getBridge().getServer().getHost();
00459 return host;
00460 }
00461 }
00462
00463
00464
00465
00466
00467
00468
00469
00470 private class StartLine : IWritable
00471 {
00472
00473
00474 version (UseTokenizer)
00475 {
00476 Buffer buf;
00477 BoundToken path,
00478 method,
00479 protocol;
00480
00481
00482
00483
00484
00485 this()
00486 {
00487 buf = new Buffer;
00488
00489
00490 path = new BoundToken (Tokenizers.space);
00491 method = new BoundToken (Tokenizers.space);
00492 protocol = new BoundToken (Tokenizers.space);
00493 }
00494
00495
00496
00497
00498
00499 void parse (char[] line)
00500 {
00501
00502 buf.setValidContent(line);
00503
00504
00505 if ((method.next(buf) && path.next(buf)) ^ protocol.next(buf))
00506 {}
00507 else
00508 throw new IOException ("Invalid HTTP start-line: '"~line~"'");
00509 }
00510
00511
00512
00513
00514
00515 char[] getMethod()
00516 {
00517 return method.toString();
00518 }
00519
00520
00521
00522
00523
00524 char[] getPath()
00525 {
00526 return path.toString();
00527 }
00528
00529
00530
00531
00532
00533 char[] getProtocol()
00534 {
00535 return protocol.toString();
00536 }
00537
00538
00539
00540
00541
00542 char[] toString()
00543 {
00544 return getMethod()~" "~getPath()~" "~getProtocol();
00545 }
00546
00547
00548
00549
00550
00551 void write (IWriter writer)
00552 {
00553 writer.put(toString()).cr();
00554 }
00555 }
00556 else
00557 {
00558 private import mango.http.utils.TokenStack;
00559
00560 Token[] tokens;
00561
00562
00563
00564
00565
00566
00567
00568 this()
00569 {
00570 tokens = new Token[0];
00571 TokenStack.resize (tokens, 3);
00572 }
00573
00574
00575
00576
00577
00578
00579
00580 void parse (char[] line)
00581 {
00582
00583 int index;
00584 int anchor = 0;
00585 int count = 0;
00586
00587 do {
00588 index = Text.indexOf (line, ' ', anchor);
00589 if (index == -1)
00590 index = line.length;
00591 tokens[count].set(line[anchor..index]);
00592 anchor = index + 1;
00593 } while (++count < 3 && anchor < line.length);
00594
00595 if (count != 3 || anchor < line.length)
00596 throw new IOException ("Invalid HTTP start-line: '"~line~"'");
00597 }
00598
00599
00600
00601
00602
00603
00604
00605 char[] getMethod()
00606 {
00607 return tokens[0].toString();
00608 }
00609
00610
00611
00612
00613
00614
00615
00616 char[] getPath()
00617 {
00618 return tokens[1].toString();
00619 }
00620
00621
00622
00623
00624
00625
00626
00627 char[] getProtocol()
00628 {
00629 return tokens[2].toString();
00630 }
00631
00632
00633
00634
00635
00636
00637
00638 char[] toString()
00639 {
00640 return getMethod()~" "~getPath()~" "~getProtocol();
00641 }
00642
00643
00644
00645
00646
00647
00648
00649 void write (IWriter writer)
00650 {
00651 writer.put (toString).cr();
00652 }
00653 }
00654 }
00655