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