-
Notifications
You must be signed in to change notification settings - Fork 18
/
BoilerplateHTTPServer.pas
2542 lines (2243 loc) · 87.8 KB
/
BoilerplateHTTPServer.pas
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/// HTML5 Boilerplate integration with Synopse mORMot Framework
// Licensed under The MIT License (MIT)
unit BoilerplateHTTPServer;
(*
This unit is a path of integration project between HTML5 Boilerplate and
Synopse mORMot Framework.
https://synopse.info
https://html5boilerplate.com
Boilerplate HTTP Server
(c) 2016-Present Yevgeny Iliyn
https://github.com/eugeneilyin/mORMotBP
Version 1.0
- First public release
Version 1.1
- minor "Cache-Control" parameters order changes
- added bpoDelegateIndexToInheritedDefault to delegate index.html to Default()
- added bpoDelegate404ToInherited_404 to delegate 404.html to _404()
Version 1.2
- fix "Accept-Encoding" parsing when gzip in the end of encodings list
- make Pre-Build events notice more visible in tests and demo
- minor code refactoring
Version 1.3
- fix bug in packassets tool when asset name started with '.'
Version 1.4
- make TAsset to be packed record for better x86/x64 platforms compatibility
Version 1.5
- fix EnableCacheByETag test scenarios
Version 1.6
- add custom options registration
- add Vary header into http response for compressible resources
Version 1.7
- add custom options registration for group of URLs
Version 1.8
- support redirections in /404 response
- changed HTML_* to HTTP_* constants following the mORMot refactoring
- support new HTTP context initialization spec
Version 1.8.1
- RegisterCustomOptions now supports URLs prefixes
Version 2.0
- All Delphi compilers support started from Delphi 6
(special BuildEvents IDE extenstion provided for old Delphi 6/7/2005/2006)
- Free Pascal support
(for Lazarus IDE pre-build.sh scipt provided to compress and embed static
assets over "Run / Build File" IDE option)
- Kylix 3 support (over CrossKilyx)
- Zopfli compression support for static assets
(save up to 5-15% of traffic and delivery time compared to max GZip Level)
- Brotli compression support for static assets as per RFC 7932
(save another 15%-25% of traffic and delivery time compared to Zopfli)
- All assets compressions (GZip/Zopfli, and Brotli) now precomputed and
embedded, so you save your CPU cycles by skipping any static assets
compression on production
- Add additional cache bursting strategy. See bpoEnableCacheBustingBeforeExt
- Following RFC 7946 the GeoJSON applications now
use application/geo+json MIME type
- MIME Type for RDF XML documents now application/rdf+xml
following as per RFC 3870
- Add support of .mjs files with EcmaScript modules
(or JavaScript modules) MIME types
- Add web assembly (.wasm) MIME type support
- Woff fonts (.woff) now have updated font/woff MIME type
- Woff version 2 fonts (.woff2) now have updated font/woff2 MIME type
- True Type collection .ttc fonts now have separate font/collection MIME type
- TTF fonts (.ttf) now have separate font/ttf MIME type
- OTF fonts (.otf) now have separate font/otf MIME type
- Add support for .ics (text/calendar), and .markdown, .md (text/markdown)
MIME types
- Upgrade the required 'charset=UTF-8' MIME type list
- Upgrade Content Sequrity Policy (CSP)
- New bpoEnableReferrerPolicy options
- The GZippedMimeTypes has been removed
(just pack your assets with updated assetslz tool)
- Deprecation of Iframes cookies support in Internet Explorer
- TAssets.SaveAssets remove regexp for assets matching
(this excludes dependency over SynTable.pas)
Version 2.1
- bpoVaryAcceptEncoding now supports content created by the inherited class
- bpoDeleteXPoweredBy was excluded from DEFAULT_BOILERPLATE_OPTIONS
Version 2.2
- Add TBoilerplateHTTPServer.ContentSecurityPolicyReportOnly property
Version 2.3
- Upgrade options to Apache Server Configs v4.0.0
- bpoDelegateUnauthorizedTo404 set content for HTTP 401 "Unauthorized"
response code equals to '/404'
- bpoDelegateNotAcceptableTo404 set content for HTTP 406 "Not Acceptable"
response code equals to '/404'
- bpoDelegateHidden block access to all hidden files and directories except
for the visible content from within the "/.well-known/" hidden directory
- bpoDisableTRACEMethod prevents TRACE requests being made via JavaScript
- TStrictSSL supports strictSSLIncludeSubDomainsPreload
- DNSPrefetchControl property to control DNS prefetching
Version 2.4
- Add TBoilerplateHTTPServer.OnGetAsset for external assets support
*)
interface
{$I Synopse.inc} // define HASINLINE CPU32 CPU64
uses
SysUtils,
SynCommons,
SynCrtSock,
mORMot,
mORMotHttpServer,
BoilerplateAssets;
type
/// Primary options for TBoilerplateHTTPServer class instance
TBoilerplateOption = (
/// Cross-origin requests
//
// Allow cross-origin requests.
//
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS
// https://enable-cors.org/
// https://www.w3.org/TR/cors/
//
// (!) Do not use this without understanding the consequences.
// This will permit access from any other website.
// Instead of using this file, consider using a specific rule such as
// allowing access based on (sub)domain: "subdomain.example.com"
bpoAllowCrossOrigin,
/// Cross-origin images
//
// Send the CORS header for images when browsers request it.
//
// https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_enabled_image
// https://blog.chromium.org/2011/07/using-cross-domain-images-in-webgl-and.html
//
// - Use TBoilerplateHTTPServer.FileTypesImage to specify file types
bpoAllowCrossOriginImages,
/// Cross-origin web fonts
//
// Allow cross-origin access to web fonts.
//
// https://developers.google.com/fonts/docs/troubleshooting
//
// - Use TBoilerplateHTTPServer.FileTypesFont to specify file types
bpoAllowCrossOriginFonts,
/// Cross-origin resource timing
//
// Allow cross-origin access to the timing information for all resources.
//
// If a resource isn't served with a `Timing-Allow-Origin` header that would
// allow its timing information to be shared with the document, some of the
// attributes of the `PerformanceResourceTiming` object will be set to zero.
//
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Timing-Allow-Origin
// https://www.w3.org/TR/resource-timing/
// https://www.stevesouders.com/blog/2014/08/21/resource-timing-practical-tips/
bpoAllowCrossOriginTiming,
/// Custom error messages/pages
// Customize what server returns to the client in case of an error.
// Set content for HTTP 400 "Bad Request" response code equals to '/404'
bpoDelegateBadRequestTo404,
// Set content for HTTP 401 "Unauthorized" response code equals to '/404'
bpoDelegateUnauthorizedTo404,
// Set content for HTTP 403 "Forbidden" response code equals to '/404'
bpoDelegateForbiddenTo404,
// Set content for HTTP 404 "Not Found" response code equals to '/404'
bpoDelegateNotFoundTo404,
// Set content for HTTP 405 "Not Allowed" response code equals to '/404'
bpoDelegateNotAllowedTo404,
// Set content for HTTP 406 "Not Acceptable" response code equals to '/404'
bpoDelegateNotAcceptableTo404,
/// Internet Explorer Document modes
//
// Force Internet Explorer 8/9/10 to render pages in the highest mode
// available in various cases when it may not.
//
// https://hsivonen.fi/doctype/#ie8
//
// (!) Starting with Internet Explorer 11, document modes are deprecated.
// If your business still relies on older web apps and services that were
// designed for older versions of Internet Explorer, you might want to
// consider enabling `Enterprise Mode` throughout your company.
//
// https://msdn.microsoft.com/en-us/library/ie/bg182625.aspx#docmode
// https://blogs.msdn.microsoft.com/ie/2014/04/02/stay-up-to-date-with-enterprise-mode-for-internet-explorer-11/
// https://msdn.microsoft.com/en-us/library/ff955275.aspx
bpoSetXUACompatible,
/// Media types
//
// Serve resources with the proper media types (f.k.a. MIME types).
//
// http://www.iana.org/assignments/media-types/
// https://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types
//
// - Use TBoilerplateOption.ForceMIMETypes to set MIME types
bpoForceMIMEType,
/// Character encodings
// Serve all resources labeled as `text/html` or `text/plain`
// with the media type `charset` parameter set to `UTF-8`.
bpoForceTextUTF8Charset,
/// Serve the following file types with the media type `charset` parameter
// set to `UTF-8`.
//
// - Use TBoilerplateHTTPServer.FileTypesRequiredCharSet to setup file types
bpoForceUTF8Charset,
/// Forcing `https://`
//
// Redirect from the `http://` to the `https://` version of the URL.
bpoForceHTTPS,
/// Forcing `https://`
//
// (1) If you're using cPanel AutoSSL or the Let's Encrypt webroot method it
// will fail to validate the certificate if validation requests are
// redirected to HTTPS. Turn on the condition(s) you need.
//
// https://www.iana.org/assignments/well-known-uris/well-known-uris.xhtml
// https://tools.ietf.org/html/draft-ietf-acme-acme-12
//
// /.well-known/acme-challenge/
// /.well-known/cpanel-dcv/[\w-]+$
// /.well-known/pki-validation/[A-F0-9]{32}\.txt(?:\ Comodo\ DCV)?$
//
// The next simplified patterns are used:
//
// /.well-known/acme-challenge/*
// /.well-known/cpanel-dcv/*
// /.well-known/pki-validation/*
bpoForceHTTPSExceptLetsEncrypt,
/// Protect website against clickjacking.
//
// The example below sends the `X-Frame-Options` response header with the
// value `DENY`, informing browsers not to display the content of the web
// page in any frame.
// This might not be the best setting for everyone. You should read about
// the other two possible values the `X-Frame-Options` header field can
// have: `SAMEORIGIN` and `ALLOW-FROM`.
// https://tools.ietf.org/html/rfc7034#section-2.1.
//
// Keep in mind that while you could send the `X-Frame-Options` header for
// all of your website's pages, this has the potential downside that it
// forbids even non-malicious framing of your content (e.g.: when users
// visit your website using a Google Image Search results page).
//
// Nonetheless, you should ensure that you send the `X-Frame-Options` header
// for all pages that allow a user to make a state-changing operation
// (e.g: pages that contain one-click purchase links, checkout or
// bank-transfer confirmation pages, pages that make permanent configuration
// changes, etc.).
//
// Sending the `X-Frame-Options` header can also protect your website
// against more than just clickjacking attacks.
// https://cure53.de/xfo-clickjacking.pdf.
//
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options
// https://tools.ietf.org/html/rfc7034
// https://blogs.msdn.microsoft.com/ieinternals/2010/03/30/combating-clickjacking-with-x-frame-options/
// https://www.owasp.org/index.php/Clickjacking
bpoSetXFrameOptions,
/// Block access to all hidden files and directories except for the
// visible content from within the `/.well-known/` hidden directory.
//
// These types of files usually contain user preferences or the preserved
// state of a utility, and can include rather private places like, for
// example, the `.git` or `.svn` directories.
//
// The `/.well-known/` directory represents the standard (RFC 5785) path
// prefix for "well-known locations" (e.g.: `/.well-known/manifest.json`,
// `/.well-known/keybase.txt`), and therefore, access to its visible content
// should not be blocked.
//
// https://www.mnot.net/blog/2010/04/07/well-known
// https://tools.ietf.org/html/rfc5785
bpoDelegateHidden,
/// Block access to files that can expose sensitive information.
//
// By default, block access to backup and source files that may be left by
// some text editors and can pose a security risk when anyone has access to
// them.
//
// https://feross.org/cmsploit/
//
// (!) Update TBoilerplateHTTPServer.FileTypesBlocked property to include
// any files that might end up on your production server and can expose
// sensitive information about your website. These files may include:
// configuration files, files that contain metadata about the project
// (e.g.: project dependencies, build scripts, etc.).
//
// - Use TBoilerplateHTTPServer.FileTypesBlocked to specify file types
// - This option also blocks any URL paths ended with '~' or '#'
bpoDelegateBlocked,
// Content Type Options
//
// Prevent some browsers from MIME-sniffing the response.
//
// This reduces exposure to drive-by download attacks and cross-origin data
// leaks, and should be left uncommented, especially if the server is
// serving user-uploaded content or content that could potentially be
// treated as executable by the browser.
//
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options
// https://blogs.msdn.microsoft.com/ie/2008/07/02/ie8-security-part-v-comprehensive-protection/
// https://mimesniff.spec.whatwg.org/
bpoPreventMIMESniffing,
// Cross-Site Scripting (XSS) Protection
//
// Protect website reflected Cross-Site Scripting (XSS) attacks.
//
// (1) Try to re-enable the cross-site scripting (XSS) filter built into
// most web browsers.
//
// The filter is usually enabled by default, but in some cases, it may
// be disabled by the user. However, in Internet Explorer, for example,
// it can be re-enabled just by sending the `X-XSS-Protection` header
// with the value of `1`.
//
// (2) Prevent web browsers from rendering the web page if a potential
// reflected (a.k.a non-persistent) XSS attack is detected by the
// filter.
//
// By default, if the filter is enabled and browsers detect a reflected
// XSS attack, they will attempt to block the attack by making the
// smallest possible modifications to the returned web page.
//
// Unfortunately, in some browsers (e.g.: Internet Explorer), this
// default behavior may allow the XSS filter to be exploited. Therefore,
// it's better to inform browsers to prevent the rendering of the page
// altogether, instead of attempting to modify it.
//
// https://hackademix.net/2009/11/21/ies-xss-filter-creates-xss-vulnerabilities
//
// (!) Do not rely on the XSS filter to prevent XSS attacks! Ensure that you
// are taking all possible measures to prevent XSS attacks, the most
// obvious being: validating and sanitizing your website's inputs.
//
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-XSS-Protection
// https://blogs.msdn.microsoft.com/ie/2008/07/02/ie8-security-part-iv-the-xss-filter/
// https://blogs.msdn.microsoft.com/ieinternals/2011/01/31/controlling-the-xss-filter/
// https://www.owasp.org/index.php/Cross-site_Scripting_%28XSS%29
//
// - Use TBoilerplateHTTPServer.FileTypesAsset to exclude some file types
bpoEnableXSSFilter,
/// Referrer Policy
//
// Set a strict Referrer Policy to mitigate information leakage.
//
// (1) The `Referrer-Policy` header is included in responses for resources
// that are able to request (or navigate to) other resources.
//
// This includes the commonly used resource types:
// HTML, CSS, XML/SVG, PDF documents, scripts and workers.
//
// To prevent referrer leakage entirely, specify the `no-referrer` value
// instead. Note that the effect could impact analytics metrics negatively.
//
// To check your Referrer Policy, you can use an online service, such as:
// https://securityheaders.com/
// https://observatory.mozilla.org/
//
// https://scotthelme.co.uk/a-new-security-header-referrer-policy/
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy
//
// - Use TBoilerplateHTTPServer.ReferrerPolicy property
// - Use TBoilerplateHTTPServer.ReferrerPolicyContentTypes property
bpoEnableReferrerPolicy,
/// Disable TRACE HTTP Method
//
// Prevent HTTP Server from responding to `TRACE` HTTP request.
//
// The TRACE method, while seemingly harmless, can be successfully leveraged
// in some scenarios to steal legitimate users' credentials.
//
// Modern browsers now prevent TRACE requests being made via JavaScript,
// however, other ways of sending TRACE requests with browsers have been
// discovered, such as using Java.
//
// https://tools.ietf.org/html/rfc7231#section-4.3.8
// https://www.owasp.org/index.php/Cross_Site_Tracing
// https://www.owasp.org/index.php/Test_HTTP_Methods_(OTG-CONFIG-006)
// https://httpd.apache.org/docs/current/mod/core.html#traceenable
bpoDisableTRACEMethod,
/// Server-side technology information
//
// Remove the `X-Powered-By` response header that:
//
// Better add Conditional Define: NOXPOWEREDNAME into the Project / Options
//
// * is set by some frameworks and server-side languages (e.g.: ASP.NET, PHP),
// and its value contains information about them (e.g.: their name, version
// number)
//
// * doesn't provide any value to users, contributes to header bloat, and in
// some cases, the information it provides can expose vulnerabilities
//
// (!) If you can, you should disable the `X-Powered-By` header from the
// language/framework level (e.g.: for PHP, you can do that by setting
// `expose_php = off` in `php.ini`).
//
// https://php.net/manual/en/ini.core.php#ini.expose-php
bpoDeleteXPoweredBy,
/// Force compression for mangled `Accept-Encoding` request headers
//
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Encoding
// https://calendar.perfplanet.com/2010/pushing-beyond-gzipping/
//
// - Use TBoilerplateHTTPServer.MangledEncodingHeaders
// - Use TBoilerplateHTTPServer.MangledEncodingHeaderValues
bpoFixMangledAcceptEncoding,
/// Map the following filename extensions to the specified encoding type in
// order to make HTTP Server serve the file types with the appropriate
// `Content-Encoding` response header (do note that this will NOT make
// HTTP Server compress them!).
//
// If these files types would be served without an appropriate
// `Content-Encoding` response header, client applications (e.g.: browsers)
// wouldn't know that they first need to uncompress the response, and thus,
// wouldn't be able to understand the content.
//
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Encoding
// https://httpd.apache.org/docs/current/mod/mod_mime.html#addencoding
//
// - Use TBoilerplateHTTPServer.FileTypesForceGZipHeader to setup file types
bpoForceGZipHeader,
/// Allow static assets to be cached by proxy servers
bpoSetCachePublic,
/// Allow static assets to be cached only by browser, but not by intermediate proxy servers
bpoSetCachePrivate,
/// Content transformation |
//
// Prevent intermediate caches or proxies (such as those used by mobile
// network providers) and browsers data-saving features from modifying
// the website's content using the `cache-control: no-transform` directive.
//
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control
// https://tools.ietf.org/html/rfc7234#section-5.2.2.4
//
// (!) Carefully consider the impact on your visitors before disabling
// content transformation. These transformations are performed to
// improve the experience for data- and cost-constrained users
// (e.g. users on a 2G connection).
//
// You can test the effects of content transformation applied by
// Google's Lite Mode by visiting:
// https://googleweblight.com/i?u=https://www.example.com
//
// https://support.google.com/webmasters/answer/6211428
//
// https://developers.google.com/speed/pagespeed/module/configuration#notransform
bpoSetCacheNoTransform,
/// Allow static assets to be validated with server before return cached copy
bpoSetCacheNoCache,
/// Allow static assets not to be cached
bpoSetCacheNoStore,
/// Allow static assets to be cached strictly following the server rules
bpoSetCacheMustRevalidate,
/// Add 'max-age' value based on content-type/expires mapping
//
// Serve resources with a far-future expiration date.
//
// (!) If you don't control versioning with filename-based cache busting, you
// should consider lowering the cache times to something like one week.
//
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Expires
//
// - Use TBoilerplateHTTPServer.Expires options to control expirations
bpoSetCacheMaxAge,
// Use ETag / If-None-Match caching
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag
// https://developer.yahoo.com/performance/rules.html#etags
// https://tools.ietf.org/html/rfc7232#section-2.3
bpoEnableCacheByETag,
// Use Last-Modified/If-Modified-Since caching
// https://developer.yahoo.com/performance/rules.html#etags
// https://tools.ietf.org/html/rfc7232#section-2.3
bpoEnableCacheByLastModified,
/// Cache expiration
//
// Serve resources with a far-future expiration date.
//
// (!) If you don't control versioning with filename-based cache busting, you
// should consider lowering the cache times to something like one week.
//
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Expires
// https://httpd.apache.org/docs/current/mod/mod_expires.html
//
// - TBoilerplateHTTPServer.Expires
bpoSetExpires,
/// Enables filename-based cache busting
// Removes all query path of the URL `/style.css?v231` to `/style.css`
bpoEnableCacheBusting,
/// Filename-based cache busting
// Removes infix query path of the URL `/style.123456.css` to `/style.css`
//
// If you're not using a build process to manage your filename version revving,
// you might want to consider enabling the following directives.
//
// To understand why this is important and even a better solution than using
// something like `*.css?v231`, please see:
// https://www.stevesouders.com/blog/2008/08/23/revving-filenames-dont-use-querystring/
bpoEnableCacheBustingBeforeExt,
/// Remove 'Server-InternalState' HTTP header
bpoDeleteServerInternalState,
// Delete content generation for '' and '/' URLs to '/index.html'
bpoDelegateRootToIndex,
/// Instead of index.html rendering the inherited "/Default" URL will be called
// It allows to inject custom IMVCApplication.Default() interface method
bpoDelegateIndexToInheritedDefault,
/// Instead of 404.html rendering the inherited "/404" URL will be called
// It allows to inject custom IMVCApplication._404() interface method
bpoDelegate404ToInherited_404,
/// Add 'Vary: Accept-Encoding' header for assets with GZip/Brotli encoding
bpoVaryAcceptEncoding
);
TBoilerplateOptions = set of TBoilerplateOption;
/// Suppressing or forcing the `www.` at the beginning of URLs
//
// The same content should never be available under two different URLs,
// especially not with and without `www.` at the beginning.
// This can cause SEO problems (duplicate content), and therefore, you should
// choose one of the alternatives and redirect the other one.
//
// (!) NEVER USE BOTH WWW-RELATED RULES AT THE SAME TIME!
//
// (1) The rule assumes by default that both HTTP and HTTPS environments are
// available for redirection.
// If your SSL certificate could not handle one of the domains used during
// redirection, you should turn the condition on.
//
// - wwwOff:
// Do not suppress or force 'www.' at the beginning of URLs
//
// - wwwSuppress:
// Suppressing the `www.` at the beginning of URLs
// Redirects www.example.com --> example.com
//
// wwwForce:
// Forcing the `www.` at the beginning of URLs
// Redirects example.com --> www.example.com
// Be aware that wwwForce might not be a good idea if you use "real"
// subdomains for certain parts of your website.
TWWWRewrite = (wwwOff, wwwSuppress, wwwForce);
/// HTTP Strict Transport Security (HSTS)
//
// Force client-side TLS (Transport Layer Security) redirection.
//
// If a user types `example.com` in their browser, even if the server redirects
// them to the secure version of the website, that still leaves a window of
// opportunity (the initial HTTP connection) for an attacker to downgrade or
// redirect the request.
//
// The following header ensures that a browser only connects to your server
// via HTTPS, regardless of what the users type in the browser's address bar.
//
// (!) Be aware that Strict Transport Security is not revokable and you
// must ensure being able to serve the site over HTTPS for the duration
// you've specified in the `max-age` directive. When you don't have a
// valid TLS connection anymore (e.g. due to an expired TLS certificate)
// your visitors will see a nasty error message even when attempting to
// connect over HTTP.
//
// (1) Preloading Strict Transport Security.
// To submit your site for HSTS preloading, it is required that:
// * the `includeSubDomains` directive is specified
// * the `preload` directive is specified
// * the `max-age` is specified with a value of at least 31536000 seconds
// (1 year).
// https://hstspreload.org/#deployment-recommendations
//
// https://tools.ietf.org/html/rfc6797#section-6.1
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security
// https://www.html5rocks.com/en/tutorials/security/transport-layer-security/
// https://blogs.msdn.microsoft.com/ieinternals/2014/08/18/strict-transport-security/
// https://hstspreload.org/
//
// - strictSSLOff:
// Do not provide HSTS header
//
// - strictSSLOn:
// Add 'max-age=31536000' HSTS header value
//
// - strictSSLIncludeSubDomains:
// Add 'max-age=31536000; includeSubDomains' HSTS header value
//
// - strictSSLIncludeSubDomainsPreload:
// Add 'max-age=31536000; includeSubDomains; preload' HSTS header value
TStrictSSL = (strictSSLOff, strictSSLOn, strictSSLIncludeSubDomains,
strictSSLIncludeSubDomainsPreload);
/// DNS Prefetch control
//
// Control DNS prefetching, a feature by which browsers proactively perform
// domain name resolution on both links that the user may choose to follow
// as well as URLs for items referenced by the document, including images,
// CSS, JavaScript, and so forth.
//
// This prefetching is performed in the background, so that the DNS is likely
// to have been resolved by the time the referenced items are needed. This
// reduces latency when the user clicks a link.
//
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-DNS-Prefetch-Control
//
// dnsPrefetchNone - Do not add `X-DNS-Prefetch-Control` header
// dnsPrefetchOff - Turn off DNS Prefetch
// dnsPrefetchOn - Turn on DNS Prefetch (default)
TDNSPrefetchControl = (dnsPrefetchNone, dnsPrefetchOff, dnsPrefetchOn);
type
THTTPAssetType = (atContent, atFile, atMovedPermanentlyRedirect,
atFoundRedirect, atSeeOtherRedirect, atTemporaryRedirect,
atPermanentRedirect);
THTTPGetAssetEvent = function(const Path: RawUTF8;
var AssetType: THTTPAssetType; var Asset: TAsset): Boolean of object;
/// TBoilerplateHTTPServer
TBoilerplateHTTPServer = class(TSQLHttpServer)
protected
FAssets: TAssets;
FOptions: TBoilerplateOptions;
FContentSecurityPolicy: SockString;
FContentSecurityPolicyReportOnly: SockString;
FStrictSSL: TStrictSSL;
FReferrerPolicy: SockString;
FReferrerPolicyContentTypes: SockString;
FReferrerPolicyContentTypesUpArray: TSockStringDynArray;
FWWWRewrite: TWWWRewrite;
FDNSPrefetchControl: TDNSPrefetchControl;
FDNSPrefetchControlContentTypes: SockString;
FDNSPrefetchControlContentTypesUpArray: TSockStringDynArray;
FFileTypesImage: SockString;
FFileTypesImageUpArray: TSockStringDynArray;
FFileTypesFont: SockString;
FFileTypesFontUpArray: TSockStringDynArray;
FForceMIMETypesValues: TSynNameValue;
FFileTypesRequiredCharSet: SockString;
FFileTypesRequiredCharSetUpArray: TSockStringDynArray;
FFileTypesBlocked: SockString;
FFileTypesBlockedUpArray: TSockStringDynArray;
FMangledEncodingHeaders: SockString;
FMangledEncodingHeadersUpArray: TSockStringDynArray;
FMangledEncodingHeaderValues: SockString;
FMangledEncodingHeaderValuesUpArray: TSockStringDynArray;
FFileTypesForceGZipHeader: SockString;
FFileTypesForceGZipHeaderUpArray: TSockStringDynArray;
FExpires: SockString;
FExpiresDefault: PtrInt;
FExpiresValues: TSynNameValue;
FStaticRoot: TFileName;
FCustomOptions: TSynNameValue;
FCustomOptionPrefixes: TSynNameValue;
FOnGetAsset: THTTPGetAssetEvent;
/// Init assets and set properties default values
procedure Init; virtual;
/// Extract url path and file type
// - Ext
// Extracted lowercased file type (e.g. '.css', or '.svg')
// - EnableCacheBusting
// Removes '?' sign and all followed URL content. This is usefull for
// resources cache busting on client side for urls like '/bkg.jpg?v=1.2.3'
// See bpoEnableCacheBusting
// - EnableCacheBustingBeforeExt
// Use more effective cache bust strategy to route all requests such
// as `/style.12345.css` to `/style.css`.
// See bpoEnableCacheBustingBeforeExt
procedure SplitURL(const URL: SockString; var Path, ExtUp: SockString;
const EnableCacheBusting, EnableCacheBustingBeforeExt: Boolean);
{$IFDEF HASINLINE} inline; {$ENDIF}
/// Fills the RawUTF8 array with upper-cased, sorted, deduplicated values
// ready for binary search
procedure UpArrayFromCSV(const CSV: SockString;
var Values: TSockStringDynArray; const PrefixUp: SockString = '';
const PostfixUp: SockString = ''; const Sep: AnsiChar = ',');
{$IFDEF HASINLINE} inline; {$ENDIF}
/// Binary search of value in sorted, deduplicated, upper-cased array
function InArray(const UpValue: SockString;
const UpValues: TSockStringDynArray): Boolean;
{$IFDEF HASINLINE} inline; {$ENDIF}
/// Extracts HTTP Header value and returns the headers without header
function ExtractCustomHeader(const Headers, NameUp: SockString;
out Value: SockString): SockString;
{$IFDEF HASINLINE} inline; {$ENDIF}
/// Retrieves HTTP Header trimmed value
function GetCustomHeader(const Headers: SockString;
const NameUp: SockString): SockString;
{$IFDEF HASINLINE} inline; {$ENDIF}
/// Add HTTP header value to Context.OutHeaders
procedure AddCustomHeader(Context: THttpServerRequest;
const Name, Value: SockString);
{$IFDEF HASINLINE} inline; {$ENDIF}
/// Remove HTTP header value from Context.OutHeaders
function DeleteCustomHeader(Context: THttpServerRequest;
const NameUp: SockString): SockString;
{$IFDEF HASINLINE} inline; {$ENDIF}
/// Validate that HTTP client can accept GZip and Brotli content encodings
procedure GetAcceptedEncodings(Context: THttpServerRequest;
const FixMangled: Boolean; var GZipAccepted, BrotliAccepted: Boolean);
{$IFDEF HASINLINE} inline; {$ENDIF}
/// Check ETag of Last-Modified values
function WasModified(Context: THttpServerRequest; Asset: PAsset;
const Encoding: TAssetEncoding;
const CheckETag, CheckModified: Boolean): Boolean;
/// Converts "text/html=1m", "image/x-icon=1w", etc. expires to seconds
function ExpiresToSecs(const Value: RawUTF8): PtrInt;
{$IFNDEF VER180}{$IFDEF HASINLINE} inline; {$ENDIF}{$ENDIF}
/// Converts asset unix timestamp to text in HTTP-like format
// - i.e. "Tue, 15 Nov 1994 12:45:26 GMT" to be used as a value of
// "Date", "Expires" or "Last-Modified" HTTP headers
// - handle UTC/GMT time zone
function UnixTimeToHTTPDate(const Value: TUnixTime): RawUTF8;
/// Get number of seconds, when content will be expired
function GetExpires(const ContentTypeUp: SockString): PtrInt;
{$IFDEF HASINLINE} inline; {$ENDIF}
/// Removes charset from content type
function GetContentTypeUp(const Value: SockString): SockString;
{$IFDEF HASINLINE} inline; {$ENDIF}
/// Find custom options registered for specific URL by RegisterCustomOptions
function FindCustomOptions(const URLPath: RawUTF8;
const Default: TBoilerplateOptions): TBoilerplateOptions;
{$IFDEF HASINLINE} inline; {$ENDIF}
/// Checks that Path is not started from '.' and has no '/.' sequences,
// except '/.well-known/' sequence.
// - See bpoDelegateHidden option
function ContainsHiddenExceptWellKnown(const Path: SockString): Boolean;
{$IFDEF HASINLINE} inline; {$ENDIF}
/// Check that Path is not ended with '~' or '#' and upper-cased file
// extension is not in blocked list
// - See bpoDelegateBlocked option
// - Use TBoilerplateHTTPServer.FileTypesBlocked to specify file types
function IsBlockedPathOrExt(const Path, ExtUp: SockString): Boolean;
{$IFDEF HASINLINE} inline; {$ENDIF}
procedure InitForceMIMETypesValues;
{$IFDEF HASINLINE} inline; {$ENDIF}
procedure SetReferrerPolicyContentTypes(const Value: SockString);
{$IFDEF HASINLINE} inline; {$ENDIF}
procedure SetDNSPrefetchControlContentTypes(const Value: SockString);
{$IFDEF HASINLINE} inline; {$ENDIF}
procedure SetFileTypesImage(const Value: SockString);
{$IFDEF HASINLINE} inline; {$ENDIF}
procedure SetFileTypesFont(const Value: SockString);
{$IFDEF HASINLINE} inline; {$ENDIF}
procedure SetFileTypesRequiredCharSet(const Value: SockString);
{$IFDEF HASINLINE} inline; {$ENDIF}
procedure SetFileTypesBlocked(const Value: SockString);
{$IFDEF HASINLINE} inline; {$ENDIF}
procedure SetMangledEncodingHeaders(const Value: SockString);
{$IFDEF HASINLINE} inline; {$ENDIF}
procedure SetMangledEncodingHeaderValues(const Value: SockString);
{$IFDEF HASINLINE} inline; {$ENDIF}
procedure SetFileTypesForceGZipHeader(const Value: SockString);
{$IFDEF HASINLINE} inline; {$ENDIF}
procedure SetExpires(const Value: SockString);
{$IFDEF HASINLINE} inline; {$ENDIF}
protected
function Request(Context: THttpServerRequest): Cardinal; override;
public
/// Standart TSQLHttpServer constructor
constructor Create(const aPort: AnsiString;
const aServers: array of TSQLRestServer;
const aDomainName: AnsiString = '+';
aHttpServerKind: TSQLHttpServerOptions = HTTP_DEFAULT_MODE;
ServerThreadPoolCount: Integer = 32;
aHttpServerSecurity: TSQLHttpServerSecurity = secNone;
const aAdditionalURL: AnsiString = ''; const aQueueName: SynUnicode = '');
overload;
/// Standart TSQLHttpServer constructor
constructor Create(const aPort: AnsiString; aServer: TSQLRestServer;
const aDomainName: AnsiString = '+';
aHttpServerKind: TSQLHttpServerOptions = HTTP_DEFAULT_MODE;
aRestAccessRights: PSQLAccessRights = nil;
ServerThreadPoolCount: Integer = 32;
aHttpServerSecurity: TSQLHttpServerSecurity = secNone;
const aAdditionalURL: AnsiString = ''; const aQueueName: SynUnicode = '');
overload;
/// Standart TSQLHttpServer constructor
constructor Create(aServer: TSQLRestServer;
aDefinition: TSQLHttpServerDefinition); overload;
public
/// Load static assets from specific RT_RCDATA synzl-compressed resource
procedure LoadFromResource(const ResName: string);
{$IFDEF HASINLINE} inline; {$ENDIF}
/// Register custom Cache-Control options for specific URL
// For example if you want to cache most of *.html pages with standart
// Cache-Control options, but change this rule for
// default page or login page.
// For URL prefixes use asterisk char postfix, e.g. '/customer/*'
procedure RegisterCustomOptions(const URLPath: RawUTF8;
const CustomOptions: TBoilerplateOptions); overload;
{$IFDEF HASINLINE} inline; {$ENDIF}
/// Register custom Cache-Control options for specific URL's
// For example if you want cache most *.html pages with standart
// Cache-Control options, but change this rule for default page or api calls
// For URL prefixes use asterisk char postfix, e.g. '/customer/*'
procedure RegisterCustomOptions(const URLParts: TRawUTF8DynArray;
CustomOptions: TBoilerplateOptions); overload;
{$IFDEF HASINLINE} inline; {$ENDIF}
/// Removes custom options usage for specific URL
procedure UnregisterCustomOptions(const URLPath: RawUTF8); overload;
{$IFDEF HASINLINE} inline; {$ENDIF}
/// Removes custom options usage for specific URLs
procedure UnregisterCustomOptions(
const URLPaths: TRawUTF8DynArray); overload;
{$IFDEF HASINLINE} inline; {$ENDIF}
/// See TBoilerplateOption.bpoForceMIMEType
procedure SetForceMIMETypes(const ExtMIMETypePairs: TRawUTF8DynArray);
{$IFDEF HASINLINE} inline; {$ENDIF}
/// If this directory is not empty, all loaded static assets will be
// pre-saved as files into this directory.
//
// To minimize disk IO operations file modified timestamp and file size
// will be checked before saving.
//
// The STATICFILE_CONTENT_TYPE will be used to inform the lower level API
// to send the response as file content. All assets will be saved into
// three different sub-directories:
// - 'identity' for plain unmodified assets
// - 'gzip' for assets with gzip/zipfli compression
// - 'brotli' for assets with brotli compression
//
// See 'assetslz', 'resedit' tools and LoadFromResource method to load
// server static assets from the prepared and embedded synlz-packed
// RC_RTDATA resource.
property StaticRoot: TFileName read FStaticRoot write FStaticRoot;
/// See TBoilerplateOptions
property Options: TBoilerplateOptions read FOptions write FOptions;
/// See TStrictSSL
property StrictSSL: TStrictSSL read FStrictSSL write FStrictSSL;
/// See TWWWRewrite
property WWWRewrite: TWWWRewrite read FWWWRewrite write FWWWRewrite;
/// See TDNSPrefetchControl
property DNSPrefetchControl: TDNSPrefetchControl read FDNSPrefetchControl
write FDNSPrefetchControl;
/// Specified CSV list of content types used for DNS prefetch control
property DNSPrefetchControlContentTypes: SockString
read FDNSPrefetchControlContentTypes
write SetDNSPrefetchControlContentTypes;
/// Content Security Policy (CSP)
//
// Mitigate the risk of cross-site scripting and other content-injection
// attacks.
//
// This can be done by setting a `Content Security Policy` which whitelists
// trusted sources of content for your website.
//
// There is no policy that fits all websites, you will have to modify the
// `Content-Security-Policy` directives in the example depending on your
// needs.
//
// The example policy below aims to:
//
// (1) Restrict all fetches by default to the origin of the current website
// by setting the `default-src` directive to `'self'` - which acts as a
// fallback to all "Fetch directives"
// (https://developer.mozilla.org/en-US/docs/Glossary/Fetch_directive).
//
// This is convenient as you do not have to specify all Fetch
// directives that apply to your site, for example:
// `connect-src 'self'; font-src 'self'; script-src 'self'; style-src
// 'self'`, etc.
//
// This restriction also means that you must explicitly define from
// which site(s) your website is allowed to load resources from.
//
// (2) The `<base>` element is not allowed on the website. This is to
// prevent attackers from changing the locations of resources loaded
// from relative URLs.
//
// If you want to use the `<base>` element, then `base-uri 'self'` can
// be used instead.
//
// (3) Form submissions are only allowed from the current website by
// setting: `form-action 'self'`.
//
// (4) Prevents all websites (including your own) from embedding your
// webpages within e.g. the `<iframe>` or `<object>` element by
// setting: `frame-ancestors 'none'`.
//
// The `frame-ancestors` directive helps avoid "Clickjacking" attacks
// and is similar to the `X-Frame-Options` header.
//
// Browsers that support the CSP header will ignore `X-Frame-Options`
// if `frame-ancestors` is also specified.
//
// (5) Forces the browser to treat all the resources that are served over
// HTTP as if they were loaded securely over HTTPS by setting the
// `upgrade-insecure-requests` directive.
//
// Please note that `upgrade-insecure-requests` does not ensure HTTPS
// for the top-level navigation. If you want to force the website
// itself to be loaded over HTTPS you must include the