Przeglądaj źródła

First Commit

master
Daniel Covington 3 dni temu
commit
2b49353836
37 zmienionych plików z 3361 dodań i 0 usunięć
  1. +129
    -0
      AGENTS.md
  2. +281
    -0
      CLAUDE.md
  3. +164
    -0
      Core/App.asp
  4. +43
    -0
      Core/Bootstrap.asp
  5. +7
    -0
      Core/ClockService.asp
  6. +48
    -0
      Core/Controllers/GreetController.asp
  7. +41
    -0
      Core/Controllers/HomeController.asp
  8. +17
    -0
      Core/Controllers/NotFoundController.asp
  9. +34
    -0
      Core/Dispatcher.asp
  10. +208
    -0
      Core/DynamicIncludes.asp
  11. +69
    -0
      Core/HttpRequest.asp
  12. +9
    -0
      Core/IncludeAll.asp
  13. +118
    -0
      Core/Router.asp
  14. +12
    -0
      Core/Views/Error.asp
  15. +14
    -0
      Core/Views/Greet.asp
  16. +20
    -0
      Core/Views/Home.asp
  17. +10
    -0
      Core/Views/NotFound.asp
  18. +3
    -0
      Core/Views/_Footer.asp
  19. +22
    -0
      Core/Views/_Header.asp
  20. +36
    -0
      Public/Default.asp
  21. +35
    -0
      Public/web.config
  22. +19
    -0
      RUNNING.md
  23. +981
    -0
      applicationhost.config
  24. +84
    -0
      build-iisexpress-config.ps1
  25. +25
    -0
      run-iisexpress.cmd
  26. +28
    -0
      run-iisexpress.ps1
  27. +10
    -0
      stop-iisexpress.cmd
  28. +2
    -0
      stop-iisexpress.ps1
  29. +153
    -0
      tests/AppTests.vbs
  30. +128
    -0
      tests/ControllerTests.vbs
  31. +85
    -0
      tests/DispatcherTests.vbs
  32. +76
    -0
      tests/HttpRequestTests.vbs
  33. +118
    -0
      tests/RouterTests.vbs
  34. +46
    -0
      tests/RunTests.vbs
  35. +278
    -0
      tests/TestSupport.vbs
  36. +3
    -0
      tests/run-tests.cmd
  37. +5
    -0
      tests/run-tests.ps1

+ 129
- 0
AGENTS.md Wyświetl plik

@@ -0,0 +1,129 @@
# AGENTS.md

## Purpose
This repository is maintained as if the agent were operating with the combined judgment of **150 senior engineers** specializing in:
- Classic ASP
- VBScript
- IIS hosting and deployment
- Object-oriented design patterns adapted for Classic ASP
- MVC-style separation of concerns for legacy web applications
- Refactoring and modernizing legacy ASP codebases safely

The agent should behave like a disciplined team of expert ASP Classic architects and implementers: careful, consistent, pragmatic, and deeply experienced with legacy Microsoft web stacks.

## Core Role
When working in this repository, act as a team of elite:
- Classic ASP developers
- VBScript developers
- IIS configuration and troubleshooting specialists
- Legacy application modernization engineers
- OOP and MVC design specialists for ASP Classic environments

The goal is to produce code and guidance that is robust, maintainable, production-safe, and realistic for Classic ASP applications running on IIS.

## Operating Principles
- Prefer **safe, minimal, targeted changes** over broad rewrites.
- Respect legacy behavior unless the task explicitly requires behavior changes.
- Preserve compatibility with Classic ASP and VBScript runtime constraints.
- Favor readability and maintainability over cleverness.
- Assume the code may run in older IIS-hosted environments unless the repository clearly states otherwise.
- Treat every change as production-sensitive.

## Technical Standards

### Classic ASP and VBScript
- Write valid, idiomatic Classic ASP and VBScript.
- Use `Option Explicit` in VBScript files wherever practical and consistent with the file.
- Prefer clear variable names; avoid one-letter names.
- Keep business logic out of presentation markup when possible.
- Reuse shared includes, helper modules, and utility functions rather than duplicating logic.
- Be careful with VBScript type coercion, `Null`, `Empty`, and string/number comparisons.
- Guard against common runtime issues such as:
- object not set
- type mismatch
- null propagation
- response already sent
- unclosed recordsets or connections

### OOP and MVC Style
Classic ASP does not provide native full OOP/MVC frameworks, so apply these patterns pragmatically.

- Separate responsibilities into clear layers when the project structure allows:
- presentation/view
- request handling/controller logic
- business/domain logic
- data access
- Encapsulate repeated logic in classes or include-based modules where appropriate.
- Avoid mixing SQL, HTML, and request-processing logic in one large file when making substantial updates.
- Prefer incremental refactoring toward modular design instead of risky rewrites.
- Maintain consistent naming for controllers, models, services, repositories, and helpers if those patterns exist.
- Try to make classes and objects as generic as possiable

### IIS and Hosting Awareness
- Assume deployment on IIS unless told otherwise.
- Be mindful of:
- application paths
- virtual directories
- session/application state
- request/response buffering
- COM component usage
- permissions for file access
- ADO connection lifecycles
- 32-bit vs 64-bit IIS compatibility when relevant
- When suggesting config-related fixes, prefer solutions realistic for Classic ASP on IIS.

## Database and ADO Guidance
- Use parameterized commands when the codebase pattern supports them.
- Close and release ADO objects properly.
- Minimize connection lifetime.
- Avoid inline SQL duplication where shared data-access helpers exist.
- Be conservative when changing SQL behavior in legacy applications.
- Watch for SQL injection, null handling, and recordset cursor/lock assumptions.

## Debugging and Reliability
- Diagnose root causes instead of masking errors.
- Preserve existing behavior where possible while improving reliability.
- Add defensive checks around request values, session state, objects, and database calls.
- When handling errors, keep the implementation consistent with the app�s current error strategy.
- Do not introduce modern dependencies that are unrealistic for a Classic ASP/IIS environment unless explicitly requested.

## Refactoring Expectations
- Refactor only to the degree necessary for the task.
- Keep file structure and include patterns stable unless there is a clear reason to change them.
- If improving architecture, do so in small, reversible steps.
- Avoid unnecessary framework-style abstractions that do not fit Classic ASP.

## Style Guidelines
- Match the repository�s existing formatting and naming conventions.
- Keep functions and procedures focused and cohesive.
- Prefer explicitness over hidden side effects.
- Use comments sparingly and only where they add real maintenance value.
- Do not remove legacy patterns unless they are part of the requested change or clearly harmful.

## What the Agent Should Optimize For
- Correctness in Classic ASP/VBScript execution
- IIS deployment realism
- Maintainability for legacy teams
- Clear separation of concerns
- Low-risk modernization
- Production-safe debugging and fixes

## What to Avoid
- Do not rewrite working legacy code just to make it look modern.
- Do not introduce unsupported language features or platform assumptions.
- Do not break existing includes, global state expectations, or IIS path assumptions without necessity.
- Do not over-engineer abstractions beyond what Classic ASP can support cleanly.

## Preferred Mindset
Think and act like a coordinated panel of 150 top-tier experts in:
- ASP Classic
- VBScript
- IIS
- ADO/database-backed web applications
- MVC-inspired architecture for legacy systems
- pragmatic object-oriented design in constrained environments

Every recommendation and code change should reflect senior-level judgment, legacy-platform realism, and respect for production stability.

- Before writing code tell the user what you plan to do and ask if there should be any changes


+ 281
- 0
CLAUDE.md Wyświetl plik

@@ -0,0 +1,281 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## What this is

A minimal Classic ASP / VBScript application hosted on IIS Express, structured as a front-controller
MVC-ish app. There is no build step, package manager, or test suite — this is raw `.asp` served
directly by IIS.

## Running the site

The IIS Express site config is generated, not hand-maintained. `applicationhost.config` at the repo
root is regenerated from your machine's installed IIS Express base config every time you launch —
do not hand-edit it; edit `build-iisexpress-config.ps1` instead if the site definition needs to change.

- Start: `./run-iisexpress.ps1` (or `run-iisexpress.cmd`)
- Start with IIS request tracing: `./run-iisexpress.ps1 -TraceErrors` (or `run-iisexpress.cmd trace`)
- Stop: `./stop-iisexpress.ps1` (kills any running `iisexpress` process)
- URL: `http://localhost:8080`

Requirements: IIS Express installed locally with the Classic ASP feature enabled. The site's physical
root is `Public/` (see `build-iisexpress-config.ps1`'s `-PhysicalPath`), not the repo root.

There's no build/lint step, but there is an object-level test suite (see **Tests** below) — run it
before and after any change to `Core/*.asp`. View rendering and end-to-end wiring are still verified
manually: run the site and hit it in a browser or `curl http://localhost:8080/...`.

## Tests

`tests/` holds a standalone VBScript test harness for the classes in `Core/` (not the `Public/`
views — see why below). Run it with:

- `tests/run-tests.ps1` (or `tests/run-tests.cmd`)
- Exits non-zero if any test fails, so it's safe to gate on.

**Why a separate harness instead of testing through IIS:** spinning up IIS Express per test would be
far too slow for a TDD loop, and `Core/App.asp`, `Router.asp`, `HttpRequest.asp`, `ClockService.asp`,
and the `Core/Controllers/*.asp` files are all pure `<% Class ... End Class %>` blocks with no HTML —
no reason they can't run in plain `cscript.exe`. `tests/RunTests.vbs` reads each of those files, strips
the `<%`/`%>` wrapper, and runs the remaining VBScript through `ExecuteGlobal` — this loads the *real*
class definitions (no separate copy to drift out of sync), just outside IIS. This only works because
those files never declare their own `Option Explicit` (same reason as the `#include` note above) and
contain nothing but class definitions — don't add top-level executable statements to them, or the
strip-and-execute loader breaks. `Core/IncludeAll.asp` and `Core/Bootstrap.asp` are deliberately *not*
in this list — they're front-controller wiring, not reusable classes, and `Bootstrap.asp` in particular
is exactly the executable code this loader can't handle. Those two are only exercised by the manual
live-IIS checks described above.

`tests/TestSupport.vbs` provides everything `cscript` doesn't have natively:
- `AssertEquals` / `AssertTrue` / `AssertFalse` / `AssertIsNothing` / `AssertNotNothing`, plus
`RunTest(name, procNameAsString)` which invokes the named zero-arg test Sub via `Execute
procName & "()"` (VBScript has no clean first-class function reference for this — `GetRef` exists
but is really meant for event binding, so a name string + `Execute` is the reliable option) and
records pass/fail.
- `FakeServer` / `FakeResponse` stand in for the ASP `Server`/`Response` intrinsics that don't exist
outside IIS — they're assigned to plain global variables literally named `Server` and `Response`
before any `Core/*.asp` file is loaded, so `Server.CreateObject(...)` / `Response.Status = ...` in
the real code resolve to the fakes without the production files knowing the difference.
- `FakeAspRequest` stands in for the real ASP `Request` object that `HttpRequest.Bind()` expects
(`SetServerVariable`/`SetQueryString`/`SetForm`/`SetCookie` to seed it per test).
- `FakeService` is just a neutral dummy object for tests that need "some object" to register.

Each `Core` class/concept gets its own suite (`RouterTests.vbs`, `AppTests.vbs`,
`HttpRequestTests.vbs`, `ControllerTests.vbs`, `DispatcherTests.vbs`) — a suite is just a `.vbs` file of `Sub Test_*()`
procedures followed by explicit `RunTest "description", "Test_ProcName"` calls at the bottom (no
auto-discovery/reflection — VBScript doesn't have a clean way to enumerate procedures at runtime).
`tests/RunTests.vbs` is the entry point that wires the framework, fakes, `Core/*.asp` classes, and
suites together in order and prints a summary.

**A real bug this harness caught immediately:** `IsObject(Nothing)` returns `True` in VBScript — it
only tells you the variable is *object-typed*, not that it holds an actual object. Every
`If Not IsObject(m_X) Then Err.Raise ...`-style "has this been set yet" guard in `App`, `HttpRequest`,
and `GreetController` was silently broken because of this — they'd never raise, even when nothing had
been bound/set. Manual IIS testing never caught it because `Default.asp` always binds/sets things
correctly before use. Fixed by checking `m_X Is Nothing` instead (or `(Not IsObject(x)) Or
(x Is Nothing)` when validating a caller-supplied argument that could be a non-object, not just
`Nothing`). If you add a similar "was this ever assigned" guard, use `Is Nothing`, not `IsObject`.

## Architecture

**`Core/` lives outside the web root on purpose.** The IIS physical root is `Public/`, and
`Core/*.asp` sits one level up at the repo root — so those class files can never be requested
directly over HTTP, only pulled in via server-side `#include`. This only works because
`build-iisexpress-config.ps1` sets `enableParentPaths="true"` for the site, which permits `..` in
`file=` include paths. Keep new core/business-logic classes in `Core/`, not `Public/`, and never add
`Option Explicit` to a file that gets `#include`d — `Default.asp` already declares it once at the top
of the merged script, and VBScript raises a syntax error on a second `Option Explicit` later in the
same compiled page.

**`Public/Default.asp` pulls in every `Core/*.asp` class via one `<!--#include file="../Core/
IncludeAll.asp" -->`**, rather than listing each file itself. `Core/IncludeAll.asp` is nothing but a
flat list of sibling `#include`s (`App.asp`, `ClockService.asp`, `HttpRequest.asp`, `Router.asp`,
`Dispatcher.asp`, `DynamicIncludes.asp`, `Controllers/*.asp`) — IIS's SSI preprocessor expands nested
`#include`s recursively, resolving each one relative to the file that contains it, so paths inside
`IncludeAll.asp` are written relative to `Core/` (no `../` needed) even though `Default.asp` itself
reaches `IncludeAll.asp` with one. Add a new `Core/*.asp` class's `#include` line here, not to
`Default.asp` — `IncludeAll.asp` only loads class *definitions*, nothing executable.

**`Core/` also holds front-controller plumbing that isn't a class**, for the same "keep it out of the
web root" reason as everything else in `Core/` — not everything there is a reusable service. Alongside
`IncludeAll.asp` (loads class definitions), `Core/Bootstrap.asp` is the actual construction/wiring step
that used to live directly in `Default.asp`: it sets `Response.Buffer`/`ContentType`, `Dim`s every
page-level variable, `New`s up `App`/`ClockService`/`HttpRequest`/`Router`/`Dispatcher`, registers
routes and the `clockService`, binds the request, and calls `Boot`. Both are included right after each
other from `Default.asp` (`IncludeAll.asp` first, since `Bootstrap.asp`'s `New App` etc. need the class
definitions already loaded). **`Bootstrap.asp` turns `On Error Resume Next` on and deliberately leaves
it on** — `Default.asp`'s `Match`/`Dispatch` calls run under that same error-handling mode before
`Default.asp` itself checks `Err.Number` and calls `On Error Goto 0`. This works because `#include`
(and this nested pair of them) all splice into one compiled script, same as everywhere else in this
file — but it means you can't understand `Default.asp`'s error handling by reading `Default.asp` alone.

**Front controller.** `Public/web.config` rewrites every request that isn't a real file or directory
to `Public/Default.asp` (preserving the query string). `Default.asp` is therefore the single entry
point for the whole app, and now holds only the per-request flow - class loading and object wiring
both live in the two `Core/` includes above:

```
Default.asp
<!--#include IncludeAll.asp --> ' loads every Core/*.asp class definition
<!--#include Bootstrap.asp --> ' New's + wires everything, AddRoute's, Binds, Boots,
' and leaves On Error Resume Next switched on
→ oRouter.Match(oApplication.Route) ' -> Dictionary{ControllerName, Params} or Nothing
→ oDispatcher.Dispatch app, routeMatch, controllerKey, oController ' ByRef outputs - see Dispatcher below
→ check Err.Number, then On Error Goto 0 ' closes out the Resume Next opened in Bootstrap.asp
→ viewName = "Error" (if Err.Number <> 0) or controllerKey (otherwise)
→ Require "../Core/Views/" & viewName & ".asp" ' runtime-picked view - see DynamicIncludes below
```

**Routing.** `Core/Router.asp` (`Router` class) holds registered routes and matches a request path
against them: `AddRoute(pattern, controllerName)` / `Match(route)`. Patterns are split on `/`; a
segment written `{name}` captures whatever's in that position into a `Params` dictionary, everything
else must match literally (case-insensitive). `Match` returns a `Dictionary` with `ControllerName`
and `Params`, or `Nothing` if no pattern fits — first registered match wins, so register more specific
patterns before catch-alls. `Router` deliberately knows nothing about concrete controller types (that's
why `RouterTests.vbs` can exercise it with made-up controller-name strings) — see `Dispatcher` below
for where matching a key turns into an actual running controller.

**Patterns support multiple `{param}` segments** (e.g. `/greet/{name}/{id}` captures both `name` and
`id`), and two patterns that only differ in segment count can safely map to the same controller — they
never both match the same request, since `Match` requires an exact segment-count match before it even
looks at individual segments (`/greet/{name}` and `/greet/{name}/{id}` for `Greet` is the live example:
`Bootstrap.asp` registers both, and `GreetController.HasId` is only `True` when the longer one
matched). **What `Router`/patterns do *not* give you**: a generic ASP.NET-MVC-style
`{controller}/{action}/{id}` catch-all that dispatches to *any* controller/action pair. VBScript can't
instantiate a script-defined `Class` from a string, so even a wildcard pattern still needs `Dispatcher`
to hard-map each real controller name to a `New XController` call — a catch-all route would change the
pattern, not remove that mapping. Query strings are unrelated to routing entirely: `App.ResolveRoute()`
strips everything after `?` before a route is ever matched, and a controller reads query args
separately via `app.HttpRequest.QueryString("key")`.

**Dispatching.** `Core/Dispatcher.asp` (`Dispatcher` class, single method `Dispatch(ByRef app, ByRef
routeMatch, ByRef controllerKey, ByRef controller)`) is the one place that turns a `Router.Match`
result into a running controller. It resolves `controllerKey` (`"NotFound"` if `routeMatch` is
`Nothing`, or if the matched key isn't one of its known `Case`s), constructs the matching controller,
and calls `Init`/`Execute` on it — reporting the resolved key and the controller instance back to the
caller via the `ByRef` parameters. **Important VBScript limitation:** script-defined `Class`es can't
be instantiated dynamically from a string (no `CreateObject("HomeController")`), so `Dispatcher` still
needs a static `Select Case controllerKey → New <X>Controller` internally — this limitation is
unavoidable regardless of which file holds it, but it's confined to `Dispatcher` alone now (this
limitation does NOT apply to picking the *view*, which is dynamic — see `DynamicIncludes` below).
Adding a route means: new controller in `Core/Controllers/`, new view in `Core/Views/` named to match
the controller key, one `AddRoute` call in `Default.asp`, one `Case` branch in `Dispatcher.Dispatch`.

**`App.Route` depends on `HTTP_X_ORIGINAL_URL`, not `PATH_INFO`.** Because `Public/web.config`
internally rewrites every request to `Default.asp`, `PATH_INFO` inside the page is always empty —
it reflects the rewritten target, never what was actually requested. IIS's URL Rewrite module stores
the real pre-rewrite path in the `HTTP_X_ORIGINAL_URL` server variable, so `App.ResolveRoute()` reads
that first and only falls back to `PATH_INFO` if it's absent (e.g. a direct hit on `Default.asp`
itself, bypassing the rewrite). If routes ever stop resolving correctly, check this first before
suspecting the `Router`.

**Views live in `Core/Views/` and are picked at runtime via `Core/DynamicIncludes.asp`, not a static
`#include` chain.** Classic ASP's `<!--#include-->` is resolved at parse time, so it normally can't
pick a file based on a runtime-computed route. `DynamicIncludes.asp` works around this: `Require(path)`
reads a file's raw text directly (`Server.MapPath` + `FileSystemObject`, bypassing the SSI
preprocessor entirely), compiles it into equivalent VBScript (HTML becomes `Response.Write` calls,
`<%= %>` becomes `Response.Write`, and any `<!--#include-->` *inside* that file becomes a nested
`Require(...)` call), and runs the result via `ExecuteGlobal` — so the path can be a runtime string.
`Default.asp` computes `viewName` from the `controllerKey` that `Dispatcher.Dispatch` resolved (or
`"Error"` on failure) and calls `Require "../Core/Views/" & viewName & ".asp"` once. Since this still
runs via `ExecuteGlobal` in the same script as `Default.asp`, the loaded view can reference
`Default.asp`'s single generic `oController` variable (whatever `Dispatcher.Dispatch` produced) and
`errorDescription` directly — no `Session`-stashing needed. Every view accesses its controller through
that same `oController` name, not a type-specific one (`Home.asp` uses `oController.App.Route` and
`oController.ServiceSummary`; `Greet.asp` uses `oController.Name`) — this is what lets `Default.asp`
stay generic instead of needing a `Select Case` to pick which named variable a view should see.
`Home.asp` renders the diagnostic/welcome page via `HomeController`; `Greet.asp` renders
`GreetController`'s `{name}` param; `NotFound.asp` renders when the router has no match (and
`NotFoundController.Execute` sets a real `404` `Response.Status`); `Error.asp` is the friendly fallback
shown when `Boot` or a controller raises. **Convention: a view's filename must match its controller
key exactly** (`Home` → `Home.asp`) since `Default.asp` derives one from the other - there's no
separate mapping table.

**`Core/DynamicIncludes.asp` internals and gotchas**, since three real bugs were found and fixed while
wiring this in (this file predates and wasn't written as part of this session's other work, so verify
any further changes to it live rather than trusting a read-through):
- `ExecuteFile` tracks `DynamicInclude_CurrentPath` so nested `#include`-turned-`Require` calls inside
a loaded file resolve relative to *that file's* directory. It originally (a) appended the *full*
path (directory included) on top of an already-updated `DynamicInclude_CurrentPath`, duplicating the
directory segment, and (b) restored `DynamicInclude_CurrentPath` to the hardcoded global default
after each call instead of the caller's own baseline — which broke a parent file that includes two
siblings in sequence (exactly what every view does: `_Header.asp` then `_Footer.asp`). Both are
fixed now: only the bare filename is appended to `DynamicInclude_CurrentPath`, and the caller's
baseline is explicitly saved and restored.
- `ReadFile` normalizes whatever line endings it finds in the source file to `vbNewLine`. This isn't
cosmetic: `ParseFile` splices literal HTML line breaks into generated code as `" & vbNewLine & "` by
searching for `vbNewLine` (CRLF) specifically — files saved with bare LF endings (as this repo's
files are) would leave a raw, unconverted newline embedded inside a generated `Response.Write "..."`
string literal, which fails to compile. This surfaced as a `200 OK` with a completely empty body and
no error message (a compile error in `ExecuteGlobal`'d code isn't caught by `Require`'s own
`On Error Resume Next`) — if that symptom shows up again, dump `ParseFile`'s raw output for the view
in question and read it before assuming the bug is elsewhere.
- Before calling `Require` for a *new* top-level render (not a nested one), reset
`DynamicInclude_CurrentPath = vbNullString` first, as `Default.asp` does. Its resting default
(`DI_CurrentFolder = "../"`) is baked in for a different calling convention than ours and would
double up the `../` prefix on top of a full relative path like `"../Core/Views/Home.asp"`.
- `Err.Raise 53, "DynamicIncludes.ReadFile", "..."` — the fix from before this file was wired in
(originally passed the message as `Source` instead of `Description`) — and the three debug
`Response.Write` leftovers are already removed; don't reintroduce either pattern.
- No caching: every `Require`/`Include` re-reads the file from disk and re-runs several regex passes
over it on every call. Fine at this app's scale; worth knowing if it ever needs to matter.

The shared page chrome (`<head>`/CSS/card wrapper) lives in `Core/Views/_Header.asp` and
`Core/Views/_Footer.asp` — every view sets `pageTitle`, `badgeClass` (`"ok"` or `"bad"`), and
`badgeText` right before `#include`-ing `_Header.asp`, then closes with `#include _Footer.asp`.
These three variables are `Dim`'d once in `Default.asp` (not in the view files) for the same reason
`Option Explicit` can only be declared once — `Home.asp` and `Error.asp` both get compiled into the
same merged script (only one branch executes, but both are always parsed), so a second `Dim` of the
same name in either view would be a redeclaration error. Add any new shared chrome variable to
`Default.asp`'s `Dim` block, not to a view file.

**`Core/App.asp` (`App` class)** is the composition root / mini service container — pure app/request
infrastructure, no view- or route-specific logic:
- Holds a `Scripting.Dictionary` (`m_Services`) as a service locator — `RegisterInstance` /
`Resolve` / `HasService` / `ServiceNames()`. `InjectInto` uses `CallByName` to push itself onto any
object exposing a `SetApp` method (best-effort — errors are swallowed via `On Error Resume Next`).
- Never touches the raw ASP `Request` object directly — it goes through the injected `HttpRequest`
wrapper (`SetRequest` / `EnsureRequest`), so route resolution and server variables are testable
independent of IIS.
- `ResolveRoute()` derives the route (see the `HTTP_X_ORIGINAL_URL` note above), normalizing
`/Default.asp` to `/`.

**`Core/Controllers/*.asp`** each follow the same shape: `Init(app, routeParams)` to receive the
`App` instance and the router's captured params, `Execute()` to run any per-request logic, an `App`
property for the view to reach request context through, plus whatever controller-specific data the
view needs (e.g. `HomeController.ServiceSummary` turns `App.ServiceNames()` into the HTML-encoded,
comma-joined text the old `App` class used to build itself — that formatting belongs to the
controller/view, not the DI container). This uniform `Init`/`Execute`/`App` shape is what lets
`Dispatcher.Dispatch` treat every controller identically.

**`Core/HttpRequest.asp` (`HttpRequest` class)** wraps the ASP intrinsic `Request` object
(`Bind`) and proxies `Item`, `QueryString`, `Form`, `ServerVariable`, `Cookie`, `ClientCertificate`,
`TotalBytes`, `BinaryRead`. Every accessor calls `EnsureBound` first and raises a custom error
(`vbObjectError + 110x`) if `Bind` was never called. Always `Bind` before use.

**`Core/ClockService.asp`** is a trivial example service (`GetNowText`) showing the
register/resolve pattern — not core infrastructure, just a template for adding new services.

## Conventions (from AGENTS.md)

This repo is meant to be treated as production-sensitive legacy code, not a scratch project. Key rules
that aren't obvious from the code alone:

- **Ask before writing code.** State the plan and check in before making changes.
- Prefer small, targeted diffs over rewrites; don't modernize working code just to make it look nicer.
- Avoid one-letter variable names. (`Option Explicit` belongs only in `Default.asp` — see the
architecture note above on why it can't also live in `#include`d files.)
- Keep business logic out of markup; route new logic through classes/services (extend the
`App` service-locator pattern) rather than duplicating it inline in `.asp` pages.
- New services follow the `ClockService` shape: a plain class, registered into `App` via
`RegisterInstance` in `Default.asp`, resolved via `App.Resolve("name")` where needed.
Objects that need the container can implement `SetApp` and be wired via `App.InjectInto`.
- Be deliberate about VBScript's `Null`/`Empty` coercion and type comparisons; guard against
"object not set", type mismatch, and unclosed ADO recordsets/connections if/when data access is added.
- No ORM/framework — if SQL or ADO is introduced, use parameterized commands and minimize
connection lifetime.
- New logic in `Core/` gets a test in `tests/` (see **Tests** above) — write the failing test first
where practical, then make it pass. Keep `Core/*.asp` files pure class definitions (no top-level
executable code, no `Option Explicit`) so the test harness's strip-and-`ExecuteGlobal` loader keeps
working.

+ 164
- 0
Core/App.asp Wyświetl plik

@@ -0,0 +1,164 @@
<%
Class App
Private m_Route
Private m_Method
Private m_ServerName
Private m_ServerPort
Private m_PhysicalRoot
Private m_StartedAt
Private m_Services
Private m_Request

Private Sub Class_Initialize()
m_Route = "/"
m_Method = "GET"
m_ServerName = ""
m_ServerPort = ""
m_PhysicalRoot = ""
m_StartedAt = Now()
Set m_Services = Server.CreateObject("Scripting.Dictionary")
m_Services.CompareMode = 1
Set m_Request = Nothing
End Sub

Private Sub Class_Terminate()
Set m_Request = Nothing
Set m_Services = Nothing
End Sub

Public Sub SetRequest(ByRef requestObject)
' IsObject(Nothing) is True in VBScript, so it can't detect "no object"
' on its own - it must be paired with an explicit "Is Nothing" check.
If (Not IsObject(requestObject)) Or (requestObject Is Nothing) Then
Err.Raise vbObjectError + 1003, "App.SetRequest", "requestObject must be an object."
End If

Set m_Request = requestObject
End Sub

Public Sub Boot()
EnsureRequest
m_Method = m_Request.ServerVariable("REQUEST_METHOD")
m_ServerName = m_Request.ServerVariable("SERVER_NAME")
m_ServerPort = m_Request.ServerVariable("SERVER_PORT")
m_PhysicalRoot = Server.MapPath("/")
m_Route = ResolveRoute()
End Sub

Public Sub RegisterInstance(ByVal serviceName, ByRef serviceInstance)
If Len(Trim(CStr(serviceName))) = 0 Then
Err.Raise vbObjectError + 1000, "App.RegisterInstance", "serviceName is required."
End If

If m_Services.Exists(CStr(serviceName)) Then
m_Services.Remove CStr(serviceName)
End If

Set m_Services.Item(CStr(serviceName)) = serviceInstance
End Sub

Public Function Resolve(ByVal serviceName)
If Not m_Services.Exists(CStr(serviceName)) Then
Err.Raise vbObjectError + 1001, "App.Resolve", "Service not registered: " & CStr(serviceName)
End If

Set Resolve = m_Services.Item(CStr(serviceName))
End Function

Public Function HasService(ByVal serviceName)
HasService = m_Services.Exists(CStr(serviceName))
End Function

Public Sub InjectInto(ByRef targetObject)
If (Not IsObject(targetObject)) Or (targetObject Is Nothing) Then
Err.Raise vbObjectError + 1002, "App.InjectInto", "targetObject must be an object."
End If

On Error Resume Next
CallByName targetObject, "SetApp", VbMethod, Me
On Error GoTo 0
End Sub

Public Property Get Route()
Route = m_Route
End Property

Public Property Get RequestMethod()
RequestMethod = m_Method
End Property

Public Property Get ServerName()
ServerName = m_ServerName
End Property

Public Property Get ServerPort()
ServerPort = m_ServerPort
End Property

Public Property Get PhysicalRoot()
PhysicalRoot = m_PhysicalRoot
End Property

Public Property Get StartedAt()
StartedAt = m_StartedAt
End Property

Public Property Get HttpRequest()
EnsureRequest
Set HttpRequest = m_Request
End Property

Public Function ServiceNames()
ServiceNames = m_Services.Keys
End Function

Private Function ResolveRoute()
Dim originalUrl
Dim pathInfo
Dim queryIndex

EnsureRequest

' Public/web.config rewrites every request to Default.asp, so PATH_INFO
' inside this page is always empty/"/Default.asp" - it never reflects what
' was actually requested. IIS's URL Rewrite module stores the real request
' path in HTTP_X_ORIGINAL_URL before rewriting, so prefer that when present.
originalUrl = Trim(CStr(m_Request.ServerVariable("HTTP_X_ORIGINAL_URL")))

If Len(originalUrl) > 0 Then
queryIndex = InStr(originalUrl, "?")
If queryIndex > 0 Then
originalUrl = Left(originalUrl, queryIndex - 1)
End If

If LCase(originalUrl) = "/default.asp" Then
ResolveRoute = "/"
Else
ResolveRoute = originalUrl
End If

Exit Function
End If

pathInfo = Trim(CStr(m_Request.ServerVariable("PATH_INFO")))

If Len(pathInfo) = 0 Then
ResolveRoute = "/"
Exit Function
End If

If LCase(pathInfo) = "/default.asp" Then
ResolveRoute = "/"
Exit Function
End If

ResolveRoute = pathInfo
End Function

Private Sub EnsureRequest()
If m_Request Is Nothing Then
Err.Raise vbObjectError + 1004, "App.EnsureRequest", "No request object has been injected into the app."
End If
End Sub
End Class
%>

+ 43
- 0
Core/Bootstrap.asp Wyświetl plik

@@ -0,0 +1,43 @@
<%
Response.Buffer = True
Response.ContentType = "text/html"

Dim oApplication
Dim oClockService
Dim oRequest
Dim oRouter
Dim oDispatcher
Dim routeMatch
Dim controllerKey
Dim oController
Dim requestFailed
Dim errorDescription
Dim pageTitle
Dim badgeClass
Dim badgeText
Dim viewName

requestFailed = False
errorDescription = ""
controllerKey = ""

Set oApplication = New App
Set oClockService = New ClockService
Set oRequest = New HttpRequest
Set oRouter = New Router
Set oDispatcher = New Dispatcher

oRouter.AddRoute "/", "Home"
oRouter.AddRoute "/greet/{name}", "Greet"
oRouter.AddRoute "/greet/{name}/{id}", "Greet"

' Left switched on deliberately - Default.asp's Match/Dispatch call runs
' under this same On Error Resume Next before checking Err.Number itself.
On Error Resume Next

Call oRequest.Bind(Request)

oApplication.RegisterInstance "clockService", oClockService
oApplication.SetRequest oRequest
oApplication.Boot
%>

+ 7
- 0
Core/ClockService.asp Wyświetl plik

@@ -0,0 +1,7 @@
<%
Class ClockService
Public Function GetNowText()
GetNowText = CStr(Now())
End Function
End Class
%>

+ 48
- 0
Core/Controllers/GreetController.asp Wyświetl plik

@@ -0,0 +1,48 @@
<%
Class GreetController
Private m_App
Private m_Name
Private m_Id

Public Sub Init(ByRef app, ByRef routeParams)
Set m_App = app

' IsObject(Nothing) is True in VBScript, so it can't detect "no object"
' on its own - it must be paired with an explicit "Is Nothing" check.
If Not (routeParams Is Nothing) Then
If routeParams.Exists("name") Then
m_Name = Trim(CStr(routeParams.Item("name")))
End If
If routeParams.Exists("id") Then
m_Id = Trim(CStr(routeParams.Item("id")))
End If
End If

If Len(m_Name) = 0 Then
m_Name = "there"
End If
End Sub

Public Sub Execute()
' Nothing further to prepare - the name/id are already resolved in Init.
End Sub

Public Property Get App()
Set App = m_App
End Property

Public Property Get Name()
Name = m_Name
End Property

Public Property Get Id()
Id = m_Id
End Property

' True only when the route matched /greet/{name}/{id} - the shorter
' /greet/{name} pattern never captures an "id".
Public Property Get HasId()
HasId = (Len(m_Id) > 0)
End Property
End Class
%>

+ 41
- 0
Core/Controllers/HomeController.asp Wyświetl plik

@@ -0,0 +1,41 @@
<%
Class HomeController
Private m_App

Public Sub Init(ByRef app, ByRef routeParams)
Set m_App = app
End Sub

Public Sub Execute()
' Nothing to prepare beyond what App already exposes.
End Sub

Public Property Get App()
Set App = m_App
End Property

Public Property Get ServiceSummary()
Dim serviceNames
Dim output
Dim index

serviceNames = m_App.ServiceNames()

If UBound(serviceNames) < 0 Then
ServiceSummary = "None"
Exit Property
End If

output = ""

For index = 0 To UBound(serviceNames)
If Len(output) > 0 Then
output = output & ", "
End If
output = output & Server.HTMLEncode(CStr(serviceNames(index)))
Next

ServiceSummary = output
End Property
End Class
%>

+ 17
- 0
Core/Controllers/NotFoundController.asp Wyświetl plik

@@ -0,0 +1,17 @@
<%
Class NotFoundController
Private m_App

Public Sub Init(ByRef app, ByRef routeParams)
Set m_App = app
End Sub

Public Sub Execute()
Response.Status = "404 Not Found"
End Sub

Public Property Get App()
Set App = m_App
End Property
End Class
%>

+ 34
- 0
Core/Dispatcher.asp Wyświetl plik

@@ -0,0 +1,34 @@
<%
Class Dispatcher
' Given a Router.Match(...) result (or Nothing), resolves the controller
' key - falling back to "NotFound" when nothing matched, or when the
' matched key doesn't map to a known controller - then constructs, Inits,
' and Executes the corresponding controller. controllerKey and controller
' are ByRef outputs: Select Case controllerKey after calling this to pick
' the matching view (its filename must equal the resolved key).
Public Sub Dispatch(ByRef app, ByRef routeMatch, ByRef controllerKey, ByRef controller)
Dim routeParams

If routeMatch Is Nothing Then
controllerKey = "NotFound"
Set routeParams = Nothing
Else
controllerKey = routeMatch.Item("ControllerName")
Set routeParams = routeMatch.Item("Params")
End If

Select Case controllerKey
Case "Home"
Set controller = New HomeController
Case "Greet"
Set controller = New GreetController
Case Else
controllerKey = "NotFound"
Set controller = New NotFoundController
End Select

controller.Init app, routeParams
controller.Execute
End Sub
End Class
%>

+ 208
- 0
Core/DynamicIncludes.asp Wyświetl plik

@@ -0,0 +1,208 @@
<%
' Self-explained Constant
Const DI_CurrentFolder = "../"

' ASP block tags
Private DI_CloseTag : DI_CloseTag = Chr(37)&Chr(62)
Private DI_OpenTag : DI_OpenTag = Chr(60)&Chr(37)
Private DI_OpenClose : DI_OpenClose = DI_OpenTag & DI_CloseTag
Private DI_OpenBreakClose : DI_OpenBreakClose = DI_OpenTag & (vbNewLine & DI_CloseTag)
Private DI_CloseOpen : DI_CloseOpen = DI_CloseTag & DI_OpenTag
Private DI_CloseBreakOpen : DI_CloseBreakOpen = DI_CloseTag & (vbNewLine & DI_OpenTag)

' ASP write block tags
Private DI_WriteTag : DI_WriteTag = DI_OpenTag & "="
Private DI_ResponseWrite : DI_ResponseWrite = DI_OpenTag & " Response.Write "
Private DI_Require : DI_Require = DI_OpenTag & (" Require($1) " & DI_CloseTag)

' Public variables for file inclusion
Public DynamicInclude_PreviousPath : DynamicInclude_PreviousPath = vbNullString
Public DynamicInclude_CurrentPath : DynamicInclude_CurrentPath = DI_CurrentFolder
Public DynamicInclude_TrimHtml : DynamicInclude_TrimHtml = false
Public DynamicInclude_TrimNewlines : DynamicInclude_TrimNewlines = false

' Executes an imported file in the global namespace
Sub ExecuteFile(ByVal File)
Const DI_Bar = "/"
Const DI_ReverseBar = "\"

Dim BarIndex, Parsed, Path, FileName, CallerPath

Path = Replace(File, DI_ReverseBar, DI_Bar)
BarIndex = InStrRev(Path, DI_Bar)
FileName = IIf(IsNull(BarIndex) Or BarIndex = 0, Path, Mid(Path, BarIndex + 1))
Path = IIf(IsNull(BarIndex) Or BarIndex = 0, vbNullString, Mid(Path, 1, BarIndex))

' Update paths for recursive imports, remembering the caller's own
' baseline so it can be restored (not clobbered to the global default)
' once this file - and anything it recursively includes - has run.
CallerPath = DynamicInclude_CurrentPath
DynamicInclude_PreviousPath = DynamicInclude_CurrentPath
DynamicInclude_CurrentPath = DynamicInclude_CurrentPath & Path

' Parse and execute the file - only the bare filename is appended here,
' since the directory portion is already folded into DynamicInclude_CurrentPath.
Parsed = ParseFile(DynamicInclude_CurrentPath & FileName)
ExecuteGlobal Trim(Parsed)

' Restore the caller's baseline so sibling #include's in the same parent
' file (e.g. a header then a footer) keep resolving correctly.
DynamicInclude_CurrentPath = CallerPath
DynamicInclude_PreviousPath = vbNullString
End Sub

' Reads and parses an ASP file
Function ParseFile(ByRef File)
Const DI_LineJoin = """ & vbNewLine & """
Const DI_Space = " "
Const DI_Quote = """"
Const DI_DoubleQuote = """"""

Dim LineArray(4), KeepLeft, KeepRight, Match, Rows, PlainRow, Regex

' Read the file content
ParseFile = ReadFile(File)

' Replace tags and write commands
ParseFile = Replace(ParseFile, DI_CloseOpen, DI_CloseBreakOpen)
ParseFile = Replace(ParseFile, DI_OpenClose, DI_OpenBreakClose)
ParseFile = Replace(ParseFile, DI_WriteTag, DI_ResponseWrite)

' Convert ASP imports to Require calls
Set Regex = New RegExp
With Regex
.Global = True
.MultiLine = True
.IgnoreCase = True
.Pattern = "<!--\s*#include file=(""[^""]+"")\s*-->"
End With
ParseFile = Regex.Replace(ParseFile, DI_Require)

' Ensure proper ASP tags are present
If Left(ParseFile, 2) <> DI_OpenTag Then
ParseFile = (DI_OpenClose & vbNewLine) & ParseFile
End If
If Right(ParseFile, 2) <> DI_CloseTag Then
ParseFile = ParseFile & (vbNewLine & DI_OpenClose)
End If

' Trim the first open tag and last close tag
ParseFile = Mid(ParseFile, 3, Len(ParseFile) - 4)
ParseFile = Replace(ParseFile, "%", "&percnt;")
ParseFile = Replace(ParseFile, "<&percnt;", DI_OpenTag)
ParseFile = Replace(ParseFile, "&percnt;>", DI_CloseTag)

' Trim HTML if required
If DynamicInclude_TrimHtml Then
Regex.Pattern = "([^%]>)\s+(<[^%]\/?)"
ParseFile = Regex.Replace(ParseFile, "$1$2")
Regex.Pattern = "([^%](?!pre)>)\s*([^\s]+)\s*(<[^%](?!pre))"
ParseFile = Regex.Replace(ParseFile, "$1$2$3")
End If

' Insert plain texts in Response.write commands
Regex.Pattern = (DI_CloseTag & "([^%])+") & DI_OpenTag
Set Rows = Regex.Execute(ParseFile)
LineArray(0) = vbNewLine
LineArray(1) = "Response.write """
LineArray(3) = DI_Quote
LineArray(4) = vbNewLine

For Each Match In Rows
PlainRow = Mid(Match.Value, 3, Len(Match.Value) - 4)
If (Left(PlainRow, 2) = vbNewLine) Then
PlainRow = Right(PlainRow, Len(PlainRow) - 2)
End If
PlainRow = Replace(PlainRow, DI_Quote, DI_DoubleQuote)
PlainRow = Replace(PlainRow, vbNewLine, DI_LineJoin)
If (Right(PlainRow, Len(DI_LineJoin)) = DI_LineJoin) Then
PlainRow = Left(PlainRow, Len(PlainRow) - Len(DI_LineJoin))
End If
If DynamicInclude_TrimHtml Then
KeepLeft = (Left(PlainRow, 1) = DI_Space)
KeepRight = (Right(PlainRow, 1) = DI_Space)
PlainRow = Trim(PlainRow)
If KeepLeft Then
PlainRow = DI_Space & PlainRow
End If
If KeepRight Then
PlainRow = PlainRow & DI_Space
End If
End If
LineArray(2) = PlainRow
ParseFile = Replace(ParseFile, Match.Value, Join(LineArray, vbNullString))
Next

' Clean up
Set Rows = Nothing
Set Match = Nothing
Erase LineArray

' Remove empty Response.write commands and duplicate new lines
ParseFile = Replace(ParseFile, "Response.write """"" & vbNewLine, vbNullString)
If DynamicInclude_TrimNewlines Then
Regex.Pattern = "(?:\s+\n)+"
ParseFile = Regex.Replace(ParseFile, vbNewLine)
End If
ParseFile = Replace(ParseFile, "&percnt;", "%")

Set Regex = Nothing
End Function

' Reads a file as plain text
Function ReadFile(ByRef File)
Dim System : Set System = Server.CreateObject("Scripting.FileSystemObject")
Dim Path, FileData

If File = vbNullString Then
File = DI_CurrentFolder
End If

Path = Server.MapPath(File)

If System.FileExists(Path) Then
Set FileData = System.OpenTextFile(Path, 1)
ReadFile = FileData.ReadAll
Set FileData = Nothing

' Normalize line endings to vbNewLine regardless of how the file was
' saved. ParseFile's plain-text-to-Response.write splicing searches
' for vbNewLine specifically - a lone LF left unconverted embeds a
' raw newline inside a generated string literal, breaking it.
ReadFile = Replace(ReadFile, Chr(13) & Chr(10), Chr(10))
ReadFile = Replace(ReadFile, Chr(10), vbNewLine)
Else
Err.Raise 53, "DynamicIncludes.ReadFile", "File '" & File & "' was not found."
End If

Set System = Nothing
End Function

' Tries to include a file silently
Sub Include(ByVal File)
On Error Resume Next
ExecuteFile File
On Error GoTo 0
End Sub

' Tries to include a file and stops execution on error
Sub Require(ByVal File)
On Error Resume Next
ExecuteFile File
If Err.Number > 0 Then
Response.Write "---FATAL ERROR: while trying to execute <b>" & File & "</b>---<br>" & _
"---Error description: " & Err.Description & "<br>" & _
"---Error Source: " & Err.Source & "<hr>" & _
"---Error Line: " & Err.Line & "<hr>"
Response.End
End If
On Error GoTo 0
End Sub
Function IIf(expr, truePart, falsePart)
If CBool(expr) Then
IIf = truePart
Else
IIf = falsePart
End If
End Function
%>

+ 69
- 0
Core/HttpRequest.asp Wyświetl plik

@@ -0,0 +1,69 @@
<%
Class HttpRequest
Private m_Request

Private Sub Class_Initialize()
Set m_Request = Nothing
End Sub

Public Sub Bind(ByRef aspRequest)
' IsObject(Nothing) is True in VBScript, so it can't detect "no object"
' on its own - it must be paired with an explicit "Is Nothing" check.
If (Not IsObject(aspRequest)) Or (aspRequest Is Nothing) Then
Err.Raise vbObjectError + 1100, "HttpRequest.Bind", "aspRequest must be an object."
End If

Set m_Request = aspRequest
End Sub

Public Property Get IsBound()
IsBound = Not (m_Request Is Nothing)
End Property

Public Function Item(ByVal key)
EnsureBound "Item"
Item = m_Request(CStr(key))
End Function

Public Function QueryString(ByVal key)
EnsureBound "QueryString"
QueryString = m_Request.QueryString(CStr(key))
End Function

Public Function Form(ByVal key)
EnsureBound "Form"
Form = m_Request.Form(CStr(key))
End Function

Public Function ServerVariable(ByVal key)
EnsureBound "ServerVariable"
ServerVariable = m_Request.ServerVariables(CStr(key))
End Function

Public Function Cookie(ByVal key)
EnsureBound "Cookie"
Cookie = m_Request.Cookies(CStr(key))
End Function

Public Function ClientCertificate(ByVal key)
EnsureBound "ClientCertificate"
ClientCertificate = m_Request.ClientCertificate(CStr(key))
End Function

Public Property Get TotalBytes()
EnsureBound "TotalBytes"
TotalBytes = m_Request.TotalBytes
End Property

Public Function BinaryRead(ByVal count)
EnsureBound "BinaryRead"
BinaryRead = m_Request.BinaryRead(CLng(count))
End Function

Private Sub EnsureBound(ByVal memberName)
If m_Request Is Nothing Then
Err.Raise vbObjectError + 1101, "HttpRequest." & CStr(memberName), "ASP Request object has not been bound."
End If
End Sub
End Class
%>

+ 9
- 0
Core/IncludeAll.asp Wyświetl plik

@@ -0,0 +1,9 @@
<!--#include file="App.asp" -->
<!--#include file="ClockService.asp" -->
<!--#include file="HttpRequest.asp" -->
<!--#include file="Router.asp" -->
<!--#include file="Dispatcher.asp" -->
<!--#include file="DynamicIncludes.asp" -->
<!--#include file="Controllers/HomeController.asp" -->
<!--#include file="Controllers/GreetController.asp" -->
<!--#include file="Controllers/NotFoundController.asp" -->

+ 118
- 0
Core/Router.asp Wyświetl plik

@@ -0,0 +1,118 @@
<%
Class Router
Private m_Routes

Private Sub Class_Initialize()
Set m_Routes = Server.CreateObject("Scripting.Dictionary")
End Sub

Private Sub Class_Terminate()
If IsObject(m_Routes) Then
Set m_Routes = Nothing
End If
End Sub

Public Sub AddRoute(ByVal pattern, ByVal controllerName)
Dim routeInfo
Dim normalizedPattern

If Len(Trim(CStr(pattern))) = 0 Then
Err.Raise vbObjectError + 1200, "Router.AddRoute", "pattern is required."
End If

If Len(Trim(CStr(controllerName))) = 0 Then
Err.Raise vbObjectError + 1201, "Router.AddRoute", "controllerName is required."
End If

normalizedPattern = NormalizePath(CStr(pattern))

Set routeInfo = Server.CreateObject("Scripting.Dictionary")
routeInfo.Add "Segments", SplitPath(normalizedPattern)
routeInfo.Add "ControllerName", CStr(controllerName)

m_Routes.Add m_Routes.Count, routeInfo
End Sub

' Returns a Dictionary with "ControllerName" and "Params" (a Dictionary of
' captured {name}-style segments), or Nothing if no registered pattern matches.
Public Function Match(ByVal route)
Dim routeSegments
Dim routeKey
Dim routeInfo
Dim patternSegments
Dim params
Dim isMatch
Dim segmentIndex
Dim result

routeSegments = SplitPath(NormalizePath(CStr(route)))

For Each routeKey In m_Routes.Keys
Set routeInfo = m_Routes.Item(routeKey)
patternSegments = routeInfo.Item("Segments")

If UBound(patternSegments) = UBound(routeSegments) Then
Set params = Server.CreateObject("Scripting.Dictionary")
isMatch = True

For segmentIndex = 0 To UBound(patternSegments)
If IsParamSegment(patternSegments(segmentIndex)) Then
params.Add ParamName(patternSegments(segmentIndex)), routeSegments(segmentIndex)
ElseIf LCase(patternSegments(segmentIndex)) <> LCase(routeSegments(segmentIndex)) Then
isMatch = False
Exit For
End If
Next

If isMatch Then
Set result = Server.CreateObject("Scripting.Dictionary")
result.Add "ControllerName", routeInfo.Item("ControllerName")
result.Add "Params", params
Set Match = result
Exit Function
End If
End If
Next

Set Match = Nothing
End Function

Private Function IsParamSegment(ByVal segment)
IsParamSegment = (Left(segment, 1) = "{" And Right(segment, 1) = "}")
End Function

Private Function ParamName(ByVal segment)
ParamName = Mid(segment, 2, Len(segment) - 2)
End Function

Private Function NormalizePath(ByVal path)
Dim result
result = Trim(CStr(path))

If Len(result) = 0 Then
result = "/"
End If

If Left(result, 1) <> "/" Then
result = "/" & result
End If

If Len(result) > 1 And Right(result, 1) = "/" Then
result = Left(result, Len(result) - 1)
End If

NormalizePath = result
End Function

Private Function SplitPath(ByVal path)
Dim trimmedPath
trimmedPath = path

If Left(trimmedPath, 1) = "/" Then
trimmedPath = Mid(trimmedPath, 2)
End If

SplitPath = Split(trimmedPath, "/")
End Function
End Class
%>

+ 12
- 0
Core/Views/Error.asp Wyświetl plik

@@ -0,0 +1,12 @@
<%
pageTitle = "Classic ASP App - Error"
badgeClass = "bad"
badgeText = "Something went wrong"
%>
<!--#include file="_Header.asp" -->
<h1>We hit a problem handling your request</h1>
<p>Please try again. If this keeps happening, contact the site administrator.</p>
<% If Len(errorDescription) > 0 Then %>
<p><code><%= Server.HTMLEncode(errorDescription) %></code></p>
<% End If %>
<!--#include file="_Footer.asp" -->

+ 14
- 0
Core/Views/Greet.asp Wyświetl plik

@@ -0,0 +1,14 @@
<%
pageTitle = "Classic ASP App - Greet"
badgeClass = "ok"
badgeText = "Route matched with a parameter"
%>
<!--#include file="_Header.asp" -->
<h1>Hello, <%= Server.HTMLEncode(oController.Name) %>!</h1>
<% If oController.HasId Then %>
<p>Visit reference: <code><%= Server.HTMLEncode(oController.Id) %></code></p>
<% End If %>
<p>This page is rendered by <code>GreetController</code>, matched from the pattern
<code>/greet/{name}</code> or <code>/greet/{name}/{id}</code>.</p>
<p><a href="/">Back home</a></p>
<!--#include file="_Footer.asp" -->

+ 20
- 0
Core/Views/Home.asp Wyświetl plik

@@ -0,0 +1,20 @@
<%
pageTitle = "Classic ASP App"
badgeClass = "ok"
badgeText = "App class bootstrapped"
%>
<!--#include file="_Header.asp" -->
<h1>Welcome to your ASP Classic application</h1>
<p>The request is now being handled by <code>Core/Controllers/HomeController.asp</code>.</p>
<table>
<tr><th>Resolved Route</th><td><%= Server.HTMLEncode(oController.App.Route) %></td></tr>
<tr><th>Request Method</th><td><%= Server.HTMLEncode(oController.App.RequestMethod) %></td></tr>
<tr><th>Server Name</th><td><%= Server.HTMLEncode(oController.App.ServerName) %></td></tr>
<tr><th>Server Port</th><td><%= Server.HTMLEncode(oController.App.ServerPort) %></td></tr>
<tr><th>Physical Root</th><td><%= Server.HTMLEncode(oController.App.PhysicalRoot) %></td></tr>
<tr><th>Started At</th><td><%= Server.HTMLEncode(CStr(oController.App.StartedAt)) %></td></tr>
<tr><th>Registered Services</th><td><%= oController.ServiceSummary %></td></tr>
</table>
<p>Try a pattern route: <a href="/greet/World">/greet/World</a>
or <a href="/greet/World/42">/greet/World/42</a></p>
<!--#include file="_Footer.asp" -->

+ 10
- 0
Core/Views/NotFound.asp Wyświetl plik

@@ -0,0 +1,10 @@
<%
pageTitle = "Classic ASP App - Not Found"
badgeClass = "bad"
badgeText = "404 Not Found"
%>
<!--#include file="_Header.asp" -->
<h1>We couldn't find that page</h1>
<p>No route matches <code><%= Server.HTMLEncode(oController.App.Route) %></code>.</p>
<p><a href="/">Back home</a></p>
<!--#include file="_Footer.asp" -->

+ 3
- 0
Core/Views/_Footer.asp Wyświetl plik

@@ -0,0 +1,3 @@
</div>
</body>
</html>

+ 22
- 0
Core/Views/_Header.asp Wyświetl plik

@@ -0,0 +1,22 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title><%= Server.HTMLEncode(pageTitle) %></title>
<style>
body{font-family:Arial,Helvetica,sans-serif;background:#f4f6f8;color:#1f2933;margin:0;padding:40px;}
.card{max-width:900px;margin:0 auto;background:#fff;border:1px solid #d9e2ec;border-radius:12px;padding:32px;box-shadow:0 10px 30px rgba(15,23,42,.08);}
h1{margin-top:0;color:#102a43;}
p{line-height:1.6;}
.badge{display:inline-block;padding:8px 12px;border-radius:999px;font-weight:bold;margin-bottom:20px;}
.badge.ok{background:#e3fcec;color:#03543f;border:1px solid #84e1bc;}
.badge.bad{background:#fde8e8;color:#9b1c1c;border:1px solid #f8b4b4;}
table{width:100%;border-collapse:collapse;margin-top:20px;}
th,td{text-align:left;padding:12px;border-bottom:1px solid #e5e7eb;vertical-align:top;}
th{width:220px;color:#486581;}
code{background:#f0f4f8;padding:2px 6px;border-radius:4px;}
</style>
</head>
<body>
<div class="card">
<div class="badge <%= badgeClass %>"><%= Server.HTMLEncode(badgeText) %></div>

+ 36
- 0
Public/Default.asp Wyświetl plik

@@ -0,0 +1,36 @@
<% Option Explicit %>
<!--#include file="../Core/IncludeAll.asp" -->
<!--#include file="../Core/Bootstrap.asp" -->
<%
Set routeMatch = oRouter.Match(oApplication.Route)
oDispatcher.Dispatch oApplication, routeMatch, controllerKey, oController

If Err.Number <> 0 Then
requestFailed = True
errorDescription = Err.Description
Err.Clear
End If

On Error Goto 0
%>
<%
' The matched controller key doubles as its view's filename (Home/Greet/
' NotFound), so the view is picked at runtime via DynamicIncludes.asp's
' Require instead of a static per-view #include chain - see CLAUDE.md.
If requestFailed Then
viewName = "Error"
Else
viewName = controllerKey
End If

DynamicInclude_CurrentPath = vbNullString
Require "../Core/Views/" & viewName & ".asp"
%>
<%
Set oDispatcher = Nothing
Set oRouter = Nothing
Set oController = Nothing
Set oRequest = Nothing
Set oClockService = Nothing
Set oApplication = Nothing
%>

+ 35
- 0
Public/web.config Wyświetl plik

@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<defaultDocument enabled="true">
<files>
<clear />
<add value="Default.asp" />
<add value="index.asp" />
<add value="index.html" />
</files>
</defaultDocument>

<directoryBrowse enabled="false" />

<httpErrors errorMode="DetailedLocalOnly" />

<rewrite>
<rules>
<rule name="Route All Requests To Default.asp" stopProcessing="true">
<match url=".*" />
<conditions logicalGrouping="MatchAll">
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
<add input="{URL}" pattern="^/Default\.asp$" negate="true" />
</conditions>
<action type="Rewrite" url="Default.asp" appendQueryString="true" />
</rule>
</rules>
</rewrite>

<staticContent>
<clientCache cacheControlMode="UseMaxAge" cacheControlMaxAge="7.00:00:00" />
</staticContent>
</system.webServer>
</configuration>

+ 19
- 0
RUNNING.md Wyświetl plik

@@ -0,0 +1,19 @@
# Run Helpers

## IIS Express
Use either launcher from the repository root:

- PowerShell: `./run-iisexpress.ps1`
- PowerShell with IIS tracing: `./run-iisexpress.ps1 -TraceErrors`
- Command Prompt: `run-iisexpress.cmd`
- Command Prompt with IIS tracing: `run-iisexpress.cmd trace`

The launcher first rebuilds `applicationhost.config` from your installed IIS Express base config, then starts `AspClassicPublicSite`.

## URL
Open: `http://localhost:8080`

## Requirements
- IIS Express installed
- Site content in `Public`
- Classic ASP feature enabled for IIS Express / Windows IIS components

+ 981
- 0
applicationhost.config Wyświetl plik

@@ -0,0 +1,981 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--

IIS configuration sections.

For schema documentation, see
%IIS_BIN%\config\schema\IIS_schema.xml.
Please make a backup of this file before making any changes to it.

NOTE: The following environment variables are available to be used
within this file and are understood by the IIS Express.

%IIS_USER_HOME% - The IIS Express home directory for the user
%IIS_SITES_HOME% - The default home directory for sites
%IIS_BIN% - The location of the IIS Express binaries
%SYSTEMDRIVE% - The drive letter of %IIS_BIN%

-->
<configuration>
<!--

The <configSections> section controls the registration of sections.
Section is the basic unit of deployment, locking, searching and
containment for configuration settings.
Every section belongs to one section group.
A section group is a container of logically-related sections.
Sections cannot be nested.
Section groups may be nested.
<section
name="" [Required, Collection Key] [XML name of the section]
allowDefinition="Everywhere" [MachineOnly|MachineToApplication|AppHostOnly|Everywhere] [Level where it can be set]
overrideModeDefault="Allow" [Allow|Deny] [Default delegation mode]
allowLocation="true" [true|false] [Allowed in location tags]
/>
The recommended way to unlock sections is by using a location tag:
<location path="Default Web Site" overrideMode="Allow">
<system.webServer>
<asp />
</system.webServer>
</location>

-->
<configSections>
<sectionGroup name="system.applicationHost">
<section name="applicationPools" allowDefinition="AppHostOnly" overrideModeDefault="Deny" />
<section name="configHistory" allowDefinition="AppHostOnly" overrideModeDefault="Deny" />
<section name="customMetadata" allowDefinition="AppHostOnly" overrideModeDefault="Deny" />
<section name="listenerAdapters" allowDefinition="AppHostOnly" overrideModeDefault="Deny" />
<section name="log" allowDefinition="AppHostOnly" overrideModeDefault="Deny" />
<section name="serviceAutoStartProviders" allowDefinition="AppHostOnly" overrideModeDefault="Deny" />
<section name="sites" allowDefinition="AppHostOnly" overrideModeDefault="Deny" />
<section name="webLimits" allowDefinition="AppHostOnly" overrideModeDefault="Deny" />
</sectionGroup>
<sectionGroup name="system.webServer">
<section name="asp" overrideModeDefault="Deny" />
<section name="caching" overrideModeDefault="Allow" />
<section name="cgi" overrideModeDefault="Deny" />
<section name="defaultDocument" overrideModeDefault="Allow" />
<section name="directoryBrowse" overrideModeDefault="Allow" />
<section name="fastCgi" allowDefinition="AppHostOnly" overrideModeDefault="Deny" />
<section name="globalModules" allowDefinition="AppHostOnly" overrideModeDefault="Deny" />
<section name="handlers" overrideModeDefault="Deny" />
<section name="httpCompression" overrideModeDefault="Allow" allowDefinition="Everywhere" />
<section name="httpErrors" overrideModeDefault="Allow" />
<section name="httpLogging" overrideModeDefault="Deny" />
<section name="httpProtocol" overrideModeDefault="Allow" />
<section name="httpRedirect" overrideModeDefault="Allow" />
<section name="httpTracing" overrideModeDefault="Deny" />
<section name="isapiFilters" allowDefinition="MachineToApplication" overrideModeDefault="Deny" />
<section name="modules" allowDefinition="MachineToApplication" overrideModeDefault="Deny" />
<section name="applicationInitialization" allowDefinition="MachineToApplication" overrideModeDefault="Allow" />
<section name="odbcLogging" overrideModeDefault="Deny" />
<sectionGroup name="security">
<section name="access" overrideModeDefault="Deny" />
<section name="applicationDependencies" overrideModeDefault="Deny" />
<sectionGroup name="authentication">
<section name="anonymousAuthentication" overrideModeDefault="Deny" />
<section name="basicAuthentication" overrideModeDefault="Deny" />
<section name="clientCertificateMappingAuthentication" overrideModeDefault="Deny" />
<section name="digestAuthentication" overrideModeDefault="Deny" />
<section name="iisClientCertificateMappingAuthentication" overrideModeDefault="Deny" />
<section name="windowsAuthentication" overrideModeDefault="Deny" />
</sectionGroup>
<section name="authorization" overrideModeDefault="Allow" />
<section name="ipSecurity" overrideModeDefault="Deny" />
<section name="dynamicIpSecurity" overrideModeDefault="Deny" />
<section name="isapiCgiRestriction" allowDefinition="AppHostOnly" overrideModeDefault="Deny" />
<section name="requestFiltering" overrideModeDefault="Allow" />
</sectionGroup>
<section name="serverRuntime" overrideModeDefault="Deny" />
<section name="serverSideInclude" overrideModeDefault="Deny" />
<section name="staticContent" overrideModeDefault="Allow" />
<sectionGroup name="tracing">
<section name="traceFailedRequests" overrideModeDefault="Allow" />
<section name="traceProviderDefinitions" overrideModeDefault="Deny" />
</sectionGroup>
<section name="urlCompression" overrideModeDefault="Allow" />
<section name="validation" overrideModeDefault="Allow" />
<sectionGroup name="webdav">
<section name="globalSettings" overrideModeDefault="Deny" />
<section name="authoring" overrideModeDefault="Deny" />
<section name="authoringRules" overrideModeDefault="Deny" />
</sectionGroup>
<sectionGroup name="rewrite">
<section name="allowedServerVariables" overrideModeDefault="Deny" />
<section name="rules" overrideModeDefault="Allow" />
<section name="outboundRules" overrideModeDefault="Allow" />
<section name="globalRules" overrideModeDefault="Deny" allowDefinition="AppHostOnly" />
<section name="providers" overrideModeDefault="Allow" />
<section name="rewriteMaps" overrideModeDefault="Allow" />
</sectionGroup>
<section name="webSocket" overrideModeDefault="Deny" />
<section name="aspNetCore" overrideModeDefault="Allow" />
</sectionGroup>
</configSections>
<configProtectedData>
<providers>
<add name="IISWASOnlyRsaProvider" type="" description="Uses RsaCryptoServiceProvider to encrypt and decrypt" keyContainerName="iisWasKey" cspProviderName="" useMachineContainer="true" useOAEP="false" />
<add name="AesProvider" type="Microsoft.ApplicationHost.AesProtectedConfigurationProvider" description="Uses an AES session key to encrypt and decrypt" keyContainerName="iisConfigurationKey" cspProviderName="" useOAEP="false" useMachineContainer="true" sessionKey="AQIAAA5mAAAApAAA/HKxkz6alrlAPez0IUgujj/6k3WxCDriHp6jvpv3yEZmo7h6SMzGLxo4mTrIQVHSkB7tmElHKfUFTzE2BWF7nFWHY6Z6qmGBauFzwJMwESjril7Gjz69RBFH259HQ6aRDq9Xfx7U7H4HtdmnKNqGjgl/hwPQBGeIlWiDh+sYv3vKB0QU971tjX6H2B+9armlnC8UOuA6JYMDMI/VLLL16sng0fWAy5JYe0YVABVjiAWDW264RZW9Tr1Oax4qHZKg+SdjULxeOc2YmpX+d0yeITo1HkPF1hN1gHpIPIUDo05ilHUNfR3OkjVCIQK4cFKCq1s8NH+y+13MxUC4Fn1AlQ==" />
<add name="IISWASOnlyAesProvider" type="Microsoft.ApplicationHost.AesProtectedConfigurationProvider" description="Uses an AES session key to encrypt and decrypt" keyContainerName="iisWasKey" cspProviderName="" useOAEP="false" useMachineContainer="true" sessionKey="AQIAAA5mAAAApAAALmU8lTC+v2qtfQiiiquvvLpUQqKLEXs+jSKoWCM/uPhyB++k4dwug19mGidNK5FYiWK2KYE1yhjVJcbp12E98Q0R2nT7eBiCMY2JairxQ591rqABK7keGaIjwH7PwGzSpILl3RJ4YFvJ/7ZXEJxeDZIjW8ZxWVXx+/VyHs9U3WguLEkgMUX3jrxJi8LouxaIVPJAv/YQ1ZCWs8zImitxX/C/7o7yaIxznfsN5nGQzQfpUDPeby99aw2zPVTtZI2LaWIBON8guABvZ6JtJVDWmfdK6sodbnwdZkr6/Z2rfvamT1dC1SpQrGG7ulR/f9/GXvCaW10ZVKxekBF/CYlNMg==" />
</providers>
</configProtectedData>
<system.applicationHost>
<applicationPools>
<add name="Clr4IntegratedAppPool" managedRuntimeVersion="v4.0" managedPipelineMode="Integrated" CLRConfigFile="%IIS_BIN%\config\templates\PersonalWebServer\aspnet.config" autoStart="true" />
<add name="Clr4ClassicAppPool" managedRuntimeVersion="v4.0" managedPipelineMode="Classic" CLRConfigFile="%IIS_BIN%\config\templates\PersonalWebServer\aspnet.config" autoStart="true" />
<add name="Clr2IntegratedAppPool" managedRuntimeVersion="v2.0" managedPipelineMode="Integrated" CLRConfigFile="%IIS_BIN%\config\templates\PersonalWebServer\aspnet.config" autoStart="true" />
<add name="Clr2ClassicAppPool" managedRuntimeVersion="v2.0" managedPipelineMode="Classic" CLRConfigFile="%IIS_BIN%\config\templates\PersonalWebServer\aspnet.config" autoStart="true" />
<add name="UnmanagedClassicAppPool" managedRuntimeVersion="" managedPipelineMode="Classic" autoStart="true" />
<add name="IISExpressAppPool" managedRuntimeVersion="v4.0" managedPipelineMode="Integrated" CLRConfigFile="%IIS_BIN%\config\templates\PersonalWebServer\aspnet.config" autoStart="true" />
<applicationPoolDefaults managedRuntimeVersion="v4.0">
<processModel loadUserProfile="true" setProfileEnvironment="false" />
</applicationPoolDefaults>
</applicationPools>
<!--

The <listenerAdapters> section defines the protocols with which the
Windows Process Activation Service (WAS) binds.

-->
<listenerAdapters>
<add name="http" />
</listenerAdapters>
<sites>
<site name="Development Web Site" id="1" serverAutoStart="true">
<application path="/">
<virtualDirectory path="/" physicalPath="%IIS_BIN%\AppServer\empty_wwwroot" />
</application>
<bindings>
<binding protocol="http" bindingInformation=":8080:localhost" />
</bindings>
</site>
<siteDefaults>
<!-- To enable logging, please change the below attribute "enabled" to "true" -->
<logFile logFormat="W3C" directory="%AppData%\Microsoft\IISExpressLogs" enabled="false" />
<traceFailedRequestsLogging directory="%AppData%\Microsoft" enabled="false" maxLogFileSizeKB="1024" />
</siteDefaults>
<applicationDefaults applicationPool="IISExpressAppPool" />
<virtualDirectoryDefaults allowSubDirConfig="true" />
<site name="AspClassicPublicSite" id="2" serverAutoStart="true">
<application path="/" applicationPool="Clr4IntegratedAppPool">
<virtualDirectory path="/" physicalPath="H:\AI - ASP - Classic\Public" />
</application>
<bindings>
<binding protocol="http" bindingInformation="*:8080:localhost" />
</bindings>
</site>
</sites>
<webLimits />
</system.applicationHost>
<system.webServer>
<serverRuntime />
<asp scriptErrorSentToBrowser="true">
<cache diskTemplateCacheDirectory="%TEMP%\iisexpress\ASP Compiled Templates" />
<limits />
</asp>
<caching enabled="true" enableKernelCache="true">
</caching>
<cgi />
<defaultDocument enabled="true">
<files>
<add value="Default.htm" />
<add value="Default.asp" />
<add value="index.htm" />
<add value="index.html" />
<add value="iisstart.htm" />
<add value="default.aspx" />
</files>
</defaultDocument>
<directoryBrowse enabled="false" />
<fastCgi />
<!--

The <globalModules> section defines all native-code modules.
To enable a module, specify it in the <modules> section.

-->
<globalModules>
<add name="HttpLoggingModule" image="%IIS_BIN%\loghttp.dll" />
<add name="UriCacheModule" image="%IIS_BIN%\cachuri.dll" />
<add name="TokenCacheModule" image="%IIS_BIN%\cachtokn.dll" />
<add name="DynamicCompressionModule" image="%IIS_BIN%\compdyn.dll" />
<add name="StaticCompressionModule" image="%IIS_BIN%\compstat.dll" />
<add name="DefaultDocumentModule" image="%IIS_BIN%\defdoc.dll" />
<add name="DirectoryListingModule" image="%IIS_BIN%\dirlist.dll" />
<add name="ProtocolSupportModule" image="%IIS_BIN%\protsup.dll" />
<add name="HttpRedirectionModule" image="%IIS_BIN%\redirect.dll" />
<add name="ServerSideIncludeModule" image="%IIS_BIN%\iis_ssi.dll" />
<add name="StaticFileModule" image="%IIS_BIN%\static.dll" />
<add name="AnonymousAuthenticationModule" image="%IIS_BIN%\authanon.dll" />
<add name="CertificateMappingAuthenticationModule" image="%IIS_BIN%\authcert.dll" />
<add name="UrlAuthorizationModule" image="%IIS_BIN%\urlauthz.dll" />
<add name="BasicAuthenticationModule" image="%IIS_BIN%\authbas.dll" />
<add name="WindowsAuthenticationModule" image="%IIS_BIN%\authsspi.dll" />
<add name="IISCertificateMappingAuthenticationModule" image="%IIS_BIN%\authmap.dll" />
<add name="IpRestrictionModule" image="%IIS_BIN%\iprestr.dll" />
<add name="DynamicIpRestrictionModule" image="%IIS_BIN%\diprestr.dll" />
<add name="RequestFilteringModule" image="%IIS_BIN%\modrqflt.dll" />
<add name="CustomLoggingModule" image="%IIS_BIN%\logcust.dll" />
<add name="CustomErrorModule" image="%IIS_BIN%\custerr.dll" />
<add name="FailedRequestsTracingModule" image="%IIS_BIN%\iisfreb.dll" />
<add name="RequestMonitorModule" image="%IIS_BIN%\iisreqs.dll" />
<add name="IsapiModule" image="%IIS_BIN%\isapi.dll" />
<add name="IsapiFilterModule" image="%IIS_BIN%\filter.dll" />
<add name="CgiModule" image="%IIS_BIN%\cgi.dll" />
<add name="FastCgiModule" image="%IIS_BIN%\iisfcgi.dll" />
<!-- <add name="WebDAVModule" image="%IIS_BIN%\webdav.dll" /> -->
<add name="RewriteModule" image="%IIS_BIN%\rewrite.dll" />
<add name="ConfigurationValidationModule" image="%IIS_BIN%\validcfg.dll" />
<add name="WebSocketModule" image="%IIS_BIN%\iiswsock.dll" />
<add name="WebMatrixSupportModule" image="%IIS_BIN%\webmatrixsup.dll" />
<add name="ManagedEngine" image="%windir%\Microsoft.NET\Framework\v2.0.50727\webengine.dll" preCondition="integratedMode,runtimeVersionv2.0,bitness32" />
<add name="ManagedEngine64" image="%windir%\Microsoft.NET\Framework64\v2.0.50727\webengine.dll" preCondition="integratedMode,runtimeVersionv2.0,bitness64" />
<add name="ManagedEngineV4.0_32bit" image="%windir%\Microsoft.NET\Framework\v4.0.30319\webengine4.dll" preCondition="integratedMode,runtimeVersionv4.0,bitness32" />
<add name="ManagedEngineV4.0_64bit" image="%windir%\Microsoft.NET\Framework64\v4.0.30319\webengine4.dll" preCondition="integratedMode,runtimeVersionv4.0,bitness64" />
<add name="ApplicationInitializationModule" image="%IIS_BIN%\warmup.dll" />
<add name="AspNetCoreModule" image="%IIS_BIN%\aspnetcore.dll" />
<add name="AspNetCoreModuleV2" image="%IIS_BIN%\Asp.Net Core Module\V2\aspnetcorev2.dll" />
</globalModules>
<httpCompression directory="%TEMP%">
<scheme name="gzip" dll="%IIS_BIN%\gzip.dll" />
<dynamicTypes>
<add mimeType="text/*" enabled="true" />
<add mimeType="message/*" enabled="true" />
<add mimeType="application/x-javascript" enabled="true" />
<add mimeType="application/javascript" enabled="true" />
<add mimeType="*/*" enabled="false" />
</dynamicTypes>
<staticTypes>
<add mimeType="text/*" enabled="true" />
<add mimeType="message/*" enabled="true" />
<add mimeType="application/javascript" enabled="true" />
<add mimeType="application/atom+xml" enabled="true" />
<add mimeType="application/xaml+xml" enabled="true" />
<add mimeType="image/svg+xml" enabled="true" />
<add mimeType="*/*" enabled="false" />
</staticTypes>
</httpCompression>
<httpErrors lockAttributes="allowAbsolutePathsWhenDelegated,defaultPath">
<error statusCode="401" prefixLanguageFilePath="%IIS_BIN%\custerr" path="401.htm" />
<error statusCode="403" prefixLanguageFilePath="%IIS_BIN%\custerr" path="403.htm" />
<error statusCode="404" prefixLanguageFilePath="%IIS_BIN%\custerr" path="404.htm" />
<error statusCode="405" prefixLanguageFilePath="%IIS_BIN%\custerr" path="405.htm" />
<error statusCode="406" prefixLanguageFilePath="%IIS_BIN%\custerr" path="406.htm" />
<error statusCode="412" prefixLanguageFilePath="%IIS_BIN%\custerr" path="412.htm" />
<error statusCode="500" prefixLanguageFilePath="%IIS_BIN%\custerr" path="500.htm" />
<error statusCode="501" prefixLanguageFilePath="%IIS_BIN%\custerr" path="501.htm" />
<error statusCode="502" prefixLanguageFilePath="%IIS_BIN%\custerr" path="502.htm" />
</httpErrors>
<httpLogging dontLog="false" />
<httpProtocol>
<customHeaders>
<clear />
<add name="X-Powered-By" value="ASP.NET" />
</customHeaders>
<redirectHeaders>
<clear />
</redirectHeaders>
</httpProtocol>
<httpRedirect enabled="false" />
<httpTracing />
<isapiFilters>
<filter name="ASP.Net_2.0.50727-64" path="%windir%\Microsoft.NET\Framework64\v2.0.50727\aspnet_filter.dll" enableCache="true" preCondition="bitness64,runtimeVersionv2.0" />
<filter name="ASP.Net_2.0.50727.0" path="%windir%\Microsoft.NET\Framework\v2.0.50727\aspnet_filter.dll" enableCache="true" preCondition="bitness32,runtimeVersionv2.0" />
<filter name="ASP.Net_2.0_for_v1.1" path="%windir%\Microsoft.NET\Framework\v2.0.50727\aspnet_filter.dll" enableCache="true" preCondition="runtimeVersionv1.1" />
<filter name="ASP.Net_4.0_32bit" path="%windir%\Microsoft.NET\Framework\v4.0.30319\aspnet_filter.dll" enableCache="true" preCondition="bitness32,runtimeVersionv4.0" />
<filter name="ASP.Net_4.0_64bit" path="%windir%\Microsoft.NET\Framework64\v4.0.30319\aspnet_filter.dll" enableCache="true" preCondition="bitness64,runtimeVersionv4.0" />
</isapiFilters>
<odbcLogging />
<security>
<access sslFlags="None" />
<applicationDependencies>
<application name="Active Server Pages" groupId="ASP" />
</applicationDependencies>
<authentication>
<anonymousAuthentication enabled="true" userName="" />
<basicAuthentication enabled="false" />
<clientCertificateMappingAuthentication enabled="false" />
<digestAuthentication enabled="false" />
<iisClientCertificateMappingAuthentication enabled="false">
</iisClientCertificateMappingAuthentication>
<windowsAuthentication enabled="false">
<providers>
<add value="Negotiate" />
<add value="NTLM" />
</providers>
</windowsAuthentication>
</authentication>
<authorization>
<add accessType="Allow" users="*" />
</authorization>
<ipSecurity allowUnlisted="true" />
<isapiCgiRestriction notListedIsapisAllowed="true" notListedCgisAllowed="true">
<add path="%windir%\Microsoft.NET\Framework64\v4.0.30319\webengine4.dll" allowed="true" groupId="ASP.NET_v4.0" description="ASP.NET_v4.0" />
<add path="%windir%\Microsoft.NET\Framework\v4.0.30319\webengine4.dll" allowed="true" groupId="ASP.NET_v4.0" description="ASP.NET_v4.0" />
<add path="%windir%\Microsoft.NET\Framework64\v2.0.50727\aspnet_isapi.dll" allowed="true" groupId="ASP.NET v2.0.50727" description="ASP.NET v2.0.50727" />
<add path="%windir%\Microsoft.NET\Framework\v2.0.50727\aspnet_isapi.dll" allowed="true" groupId="ASP.NET v2.0.50727" description="ASP.NET v2.0.50727" />
</isapiCgiRestriction>
<requestFiltering>
<fileExtensions allowUnlisted="true" applyToWebDAV="true">
<add fileExtension=".asa" allowed="false" />
<add fileExtension=".asax" allowed="false" />
<add fileExtension=".ascx" allowed="false" />
<add fileExtension=".master" allowed="false" />
<add fileExtension=".skin" allowed="false" />
<add fileExtension=".browser" allowed="false" />
<add fileExtension=".sitemap" allowed="false" />
<add fileExtension=".config" allowed="false" />
<add fileExtension=".cs" allowed="false" />
<add fileExtension=".csproj" allowed="false" />
<add fileExtension=".vb" allowed="false" />
<add fileExtension=".vbproj" allowed="false" />
<add fileExtension=".webinfo" allowed="false" />
<add fileExtension=".licx" allowed="false" />
<add fileExtension=".resx" allowed="false" />
<add fileExtension=".resources" allowed="false" />
<add fileExtension=".mdb" allowed="false" />
<add fileExtension=".vjsproj" allowed="false" />
<add fileExtension=".java" allowed="false" />
<add fileExtension=".jsl" allowed="false" />
<add fileExtension=".ldb" allowed="false" />
<add fileExtension=".dsdgm" allowed="false" />
<add fileExtension=".ssdgm" allowed="false" />
<add fileExtension=".lsad" allowed="false" />
<add fileExtension=".ssmap" allowed="false" />
<add fileExtension=".cd" allowed="false" />
<add fileExtension=".dsprototype" allowed="false" />
<add fileExtension=".lsaprototype" allowed="false" />
<add fileExtension=".sdm" allowed="false" />
<add fileExtension=".sdmDocument" allowed="false" />
<add fileExtension=".mdf" allowed="false" />
<add fileExtension=".ldf" allowed="false" />
<add fileExtension=".ad" allowed="false" />
<add fileExtension=".dd" allowed="false" />
<add fileExtension=".ldd" allowed="false" />
<add fileExtension=".sd" allowed="false" />
<add fileExtension=".adprototype" allowed="false" />
<add fileExtension=".lddprototype" allowed="false" />
<add fileExtension=".exclude" allowed="false" />
<add fileExtension=".refresh" allowed="false" />
<add fileExtension=".compiled" allowed="false" />
<add fileExtension=".msgx" allowed="false" />
<add fileExtension=".vsdisco" allowed="false" />
<add fileExtension=".rules" allowed="false" />
</fileExtensions>
<verbs allowUnlisted="true" applyToWebDAV="true" />
<hiddenSegments applyToWebDAV="true">
<add segment="web.config" />
<add segment="bin" />
<add segment="App_code" />
<add segment="App_GlobalResources" />
<add segment="App_LocalResources" />
<add segment="App_WebReferences" />
<add segment="App_Data" />
<add segment="App_Browsers" />
</hiddenSegments>
</requestFiltering>
</security>
<serverSideInclude ssiExecDisable="false" />
<staticContent lockAttributes="isDocFooterFileName">
<mimeMap fileExtension=".323" mimeType="text/h323" />
<mimeMap fileExtension=".3g2" mimeType="video/3gpp2" />
<mimeMap fileExtension=".3gp2" mimeType="video/3gpp2" />
<mimeMap fileExtension=".3gp" mimeType="video/3gpp" />
<mimeMap fileExtension=".3gpp" mimeType="video/3gpp" />
<mimeMap fileExtension=".aac" mimeType="audio/aac" />
<mimeMap fileExtension=".aaf" mimeType="application/octet-stream" />
<mimeMap fileExtension=".aca" mimeType="application/octet-stream" />
<mimeMap fileExtension=".accdb" mimeType="application/msaccess" />
<mimeMap fileExtension=".accde" mimeType="application/msaccess" />
<mimeMap fileExtension=".accdt" mimeType="application/msaccess" />
<mimeMap fileExtension=".acx" mimeType="application/internet-property-stream" />
<mimeMap fileExtension=".adt" mimeType="audio/vnd.dlna.adts" />
<mimeMap fileExtension=".adts" mimeType="audio/vnd.dlna.adts" />
<mimeMap fileExtension=".afm" mimeType="application/octet-stream" />
<mimeMap fileExtension=".ai" mimeType="application/postscript" />
<mimeMap fileExtension=".aif" mimeType="audio/x-aiff" />
<mimeMap fileExtension=".aifc" mimeType="audio/aiff" />
<mimeMap fileExtension=".aiff" mimeType="audio/aiff" />
<mimeMap fileExtension=".appcache" mimeType="text/cache-manifest" />
<mimeMap fileExtension=".application" mimeType="application/x-ms-application" />
<mimeMap fileExtension=".art" mimeType="image/x-jg" />
<mimeMap fileExtension=".asd" mimeType="application/octet-stream" />
<mimeMap fileExtension=".asf" mimeType="video/x-ms-asf" />
<mimeMap fileExtension=".asi" mimeType="application/octet-stream" />
<mimeMap fileExtension=".asm" mimeType="text/plain" />
<mimeMap fileExtension=".asr" mimeType="video/x-ms-asf" />
<mimeMap fileExtension=".asx" mimeType="video/x-ms-asf" />
<mimeMap fileExtension=".atom" mimeType="application/atom+xml" />
<mimeMap fileExtension=".au" mimeType="audio/basic" />
<mimeMap fileExtension=".avi" mimeType="video/avi" />
<mimeMap fileExtension=".axs" mimeType="application/olescript" />
<mimeMap fileExtension=".bas" mimeType="text/plain" />
<mimeMap fileExtension=".bcpio" mimeType="application/x-bcpio" />
<mimeMap fileExtension=".bin" mimeType="application/octet-stream" />
<mimeMap fileExtension=".bmp" mimeType="image/bmp" />
<mimeMap fileExtension=".c" mimeType="text/plain" />
<mimeMap fileExtension=".cab" mimeType="application/vnd.ms-cab-compressed" />
<mimeMap fileExtension=".calx" mimeType="application/vnd.ms-office.calx" />
<mimeMap fileExtension=".cat" mimeType="application/vnd.ms-pki.seccat" />
<mimeMap fileExtension=".cdf" mimeType="application/x-cdf" />
<mimeMap fileExtension=".chm" mimeType="application/octet-stream" />
<mimeMap fileExtension=".class" mimeType="application/x-java-applet" />
<mimeMap fileExtension=".clp" mimeType="application/x-msclip" />
<mimeMap fileExtension=".cmx" mimeType="image/x-cmx" />
<mimeMap fileExtension=".cnf" mimeType="text/plain" />
<mimeMap fileExtension=".cod" mimeType="image/cis-cod" />
<mimeMap fileExtension=".cpio" mimeType="application/x-cpio" />
<mimeMap fileExtension=".cpp" mimeType="text/plain" />
<mimeMap fileExtension=".crd" mimeType="application/x-mscardfile" />
<mimeMap fileExtension=".crl" mimeType="application/pkix-crl" />
<mimeMap fileExtension=".crt" mimeType="application/x-x509-ca-cert" />
<mimeMap fileExtension=".csh" mimeType="application/x-csh" />
<mimeMap fileExtension=".css" mimeType="text/css" />
<mimeMap fileExtension=".csv" mimeType="application/octet-stream" />
<mimeMap fileExtension=".cur" mimeType="application/octet-stream" />
<mimeMap fileExtension=".dcr" mimeType="application/x-director" />
<mimeMap fileExtension=".deploy" mimeType="application/octet-stream" />
<mimeMap fileExtension=".der" mimeType="application/x-x509-ca-cert" />
<mimeMap fileExtension=".dib" mimeType="image/bmp" />
<mimeMap fileExtension=".dir" mimeType="application/x-director" />
<mimeMap fileExtension=".disco" mimeType="text/xml" />
<mimeMap fileExtension=".dll" mimeType="application/x-msdownload" />
<mimeMap fileExtension=".dll.config" mimeType="text/xml" />
<mimeMap fileExtension=".dlm" mimeType="text/dlm" />
<mimeMap fileExtension=".doc" mimeType="application/msword" />
<mimeMap fileExtension=".docm" mimeType="application/vnd.ms-word.document.macroEnabled.12" />
<mimeMap fileExtension=".docx" mimeType="application/vnd.openxmlformats-officedocument.wordprocessingml.document" />
<mimeMap fileExtension=".dot" mimeType="application/msword" />
<mimeMap fileExtension=".dotm" mimeType="application/vnd.ms-word.template.macroEnabled.12" />
<mimeMap fileExtension=".dotx" mimeType="application/vnd.openxmlformats-officedocument.wordprocessingml.template" />
<mimeMap fileExtension=".dsp" mimeType="application/octet-stream" />
<mimeMap fileExtension=".dtd" mimeType="text/xml" />
<mimeMap fileExtension=".dvi" mimeType="application/x-dvi" />
<mimeMap fileExtension=".dvr-ms" mimeType="video/x-ms-dvr" />
<mimeMap fileExtension=".dwf" mimeType="drawing/x-dwf" />
<mimeMap fileExtension=".dwp" mimeType="application/octet-stream" />
<mimeMap fileExtension=".dxr" mimeType="application/x-director" />
<mimeMap fileExtension=".eml" mimeType="message/rfc822" />
<mimeMap fileExtension=".emz" mimeType="application/octet-stream" />
<mimeMap fileExtension=".eot" mimeType="application/vnd.ms-fontobject" />
<mimeMap fileExtension=".eps" mimeType="application/postscript" />
<mimeMap fileExtension=".esd" mimeType="application/vnd.ms-cab-compressed" />
<mimeMap fileExtension=".etx" mimeType="text/x-setext" />
<mimeMap fileExtension=".evy" mimeType="application/envoy" />
<mimeMap fileExtension=".exe" mimeType="application/octet-stream" />
<mimeMap fileExtension=".exe.config" mimeType="text/xml" />
<mimeMap fileExtension=".fdf" mimeType="application/vnd.fdf" />
<mimeMap fileExtension=".fif" mimeType="application/fractals" />
<mimeMap fileExtension=".fla" mimeType="application/octet-stream" />
<mimeMap fileExtension=".flr" mimeType="x-world/x-vrml" />
<mimeMap fileExtension=".flv" mimeType="video/x-flv" />
<mimeMap fileExtension=".gif" mimeType="image/gif" />
<mimeMap fileExtension=".glb" mimeType="model/gltf-binary" />
<mimeMap fileExtension=".gtar" mimeType="application/x-gtar" />
<mimeMap fileExtension=".gz" mimeType="application/x-gzip" />
<mimeMap fileExtension=".h" mimeType="text/plain" />
<mimeMap fileExtension=".hdf" mimeType="application/x-hdf" />
<mimeMap fileExtension=".hdml" mimeType="text/x-hdml" />
<mimeMap fileExtension=".hhc" mimeType="application/x-oleobject" />
<mimeMap fileExtension=".hhk" mimeType="application/octet-stream" />
<mimeMap fileExtension=".hhp" mimeType="application/octet-stream" />
<mimeMap fileExtension=".hlp" mimeType="application/winhlp" />
<mimeMap fileExtension=".hqx" mimeType="application/mac-binhex40" />
<mimeMap fileExtension=".hta" mimeType="application/hta" />
<mimeMap fileExtension=".htc" mimeType="text/x-component" />
<mimeMap fileExtension=".htm" mimeType="text/html" />
<mimeMap fileExtension=".html" mimeType="text/html" />
<mimeMap fileExtension=".htt" mimeType="text/webviewhtml" />
<mimeMap fileExtension=".hxt" mimeType="text/html" />
<mimeMap fileExtension=".ico" mimeType="image/x-icon" />
<mimeMap fileExtension=".ics" mimeType="text/calendar" />
<mimeMap fileExtension=".ief" mimeType="image/ief" />
<mimeMap fileExtension=".iii" mimeType="application/x-iphone" />
<mimeMap fileExtension=".inf" mimeType="application/octet-stream" />
<mimeMap fileExtension=".ins" mimeType="application/x-internet-signup" />
<mimeMap fileExtension=".isp" mimeType="application/x-internet-signup" />
<mimeMap fileExtension=".IVF" mimeType="video/x-ivf" />
<mimeMap fileExtension=".jar" mimeType="application/java-archive" />
<mimeMap fileExtension=".java" mimeType="application/octet-stream" />
<mimeMap fileExtension=".jck" mimeType="application/liquidmotion" />
<mimeMap fileExtension=".jcz" mimeType="application/liquidmotion" />
<mimeMap fileExtension=".jfif" mimeType="image/pjpeg" />
<mimeMap fileExtension=".jpb" mimeType="application/octet-stream" />
<mimeMap fileExtension=".jpe" mimeType="image/jpeg" />
<mimeMap fileExtension=".jpeg" mimeType="image/jpeg" />
<mimeMap fileExtension=".jpg" mimeType="image/jpeg" />
<mimeMap fileExtension=".js" mimeType="application/javascript" />
<mimeMap fileExtension=".json" mimeType="application/json" />
<mimeMap fileExtension=".jsonld" mimeType="application/ld+json" />
<mimeMap fileExtension=".jsx" mimeType="text/jscript" />
<mimeMap fileExtension=".latex" mimeType="application/x-latex" />
<mimeMap fileExtension=".less" mimeType="text/css" />
<mimeMap fileExtension=".lit" mimeType="application/x-ms-reader" />
<mimeMap fileExtension=".lpk" mimeType="application/octet-stream" />
<mimeMap fileExtension=".lsf" mimeType="video/x-la-asf" />
<mimeMap fileExtension=".lsx" mimeType="video/x-la-asf" />
<mimeMap fileExtension=".lzh" mimeType="application/octet-stream" />
<mimeMap fileExtension=".m13" mimeType="application/x-msmediaview" />
<mimeMap fileExtension=".m14" mimeType="application/x-msmediaview" />
<mimeMap fileExtension=".m1v" mimeType="video/mpeg" />
<mimeMap fileExtension=".m2ts" mimeType="video/vnd.dlna.mpeg-tts" />
<mimeMap fileExtension=".m3u" mimeType="audio/x-mpegurl" />
<mimeMap fileExtension=".m4a" mimeType="audio/mp4" />
<mimeMap fileExtension=".m4v" mimeType="video/mp4" />
<mimeMap fileExtension=".man" mimeType="application/x-troff-man" />
<mimeMap fileExtension=".manifest" mimeType="application/x-ms-manifest" />
<mimeMap fileExtension=".map" mimeType="text/plain" />
<mimeMap fileExtension=".mdb" mimeType="application/x-msaccess" />
<mimeMap fileExtension=".mdp" mimeType="application/octet-stream" />
<mimeMap fileExtension=".me" mimeType="application/x-troff-me" />
<mimeMap fileExtension=".mht" mimeType="message/rfc822" />
<mimeMap fileExtension=".mhtml" mimeType="message/rfc822" />
<mimeMap fileExtension=".mid" mimeType="audio/mid" />
<mimeMap fileExtension=".midi" mimeType="audio/mid" />
<mimeMap fileExtension=".mix" mimeType="application/octet-stream" />
<mimeMap fileExtension=".mmf" mimeType="application/x-smaf" />
<mimeMap fileExtension=".mno" mimeType="text/xml" />
<mimeMap fileExtension=".mny" mimeType="application/x-msmoney" />
<mimeMap fileExtension=".mov" mimeType="video/quicktime" />
<mimeMap fileExtension=".movie" mimeType="video/x-sgi-movie" />
<mimeMap fileExtension=".mp2" mimeType="video/mpeg" />
<mimeMap fileExtension=".mp3" mimeType="audio/mpeg" />
<mimeMap fileExtension=".mp4" mimeType="video/mp4" />
<mimeMap fileExtension=".mp4v" mimeType="video/mp4" />
<mimeMap fileExtension=".mpa" mimeType="video/mpeg" />
<mimeMap fileExtension=".mpe" mimeType="video/mpeg" />
<mimeMap fileExtension=".mpeg" mimeType="video/mpeg" />
<mimeMap fileExtension=".mpg" mimeType="video/mpeg" />
<mimeMap fileExtension=".mpp" mimeType="application/vnd.ms-project" />
<mimeMap fileExtension=".mpv2" mimeType="video/mpeg" />
<mimeMap fileExtension=".ms" mimeType="application/x-troff-ms" />
<mimeMap fileExtension=".msi" mimeType="application/octet-stream" />
<mimeMap fileExtension=".mso" mimeType="application/octet-stream" />
<mimeMap fileExtension=".mvb" mimeType="application/x-msmediaview" />
<mimeMap fileExtension=".mvc" mimeType="application/x-miva-compiled" />
<mimeMap fileExtension=".nc" mimeType="application/x-netcdf" />
<mimeMap fileExtension=".nsc" mimeType="video/x-ms-asf" />
<mimeMap fileExtension=".nws" mimeType="message/rfc822" />
<mimeMap fileExtension=".ocx" mimeType="application/octet-stream" />
<mimeMap fileExtension=".oda" mimeType="application/oda" />
<mimeMap fileExtension=".odc" mimeType="text/x-ms-odc" />
<mimeMap fileExtension=".ods" mimeType="application/oleobject" />
<mimeMap fileExtension=".oga" mimeType="audio/ogg" />
<mimeMap fileExtension=".ogg" mimeType="video/ogg" />
<mimeMap fileExtension=".ogv" mimeType="video/ogg" />
<mimeMap fileExtension=".one" mimeType="application/onenote" />
<mimeMap fileExtension=".onea" mimeType="application/onenote" />
<mimeMap fileExtension=".onetoc" mimeType="application/onenote" />
<mimeMap fileExtension=".onetoc2" mimeType="application/onenote" />
<mimeMap fileExtension=".onetmp" mimeType="application/onenote" />
<mimeMap fileExtension=".onepkg" mimeType="application/onenote" />
<mimeMap fileExtension=".osdx" mimeType="application/opensearchdescription+xml" />
<mimeMap fileExtension=".otf" mimeType="font/otf" />
<mimeMap fileExtension=".p10" mimeType="application/pkcs10" />
<mimeMap fileExtension=".p12" mimeType="application/x-pkcs12" />
<mimeMap fileExtension=".p7b" mimeType="application/x-pkcs7-certificates" />
<mimeMap fileExtension=".p7c" mimeType="application/pkcs7-mime" />
<mimeMap fileExtension=".p7m" mimeType="application/pkcs7-mime" />
<mimeMap fileExtension=".p7r" mimeType="application/x-pkcs7-certreqresp" />
<mimeMap fileExtension=".p7s" mimeType="application/pkcs7-signature" />
<mimeMap fileExtension=".pbm" mimeType="image/x-portable-bitmap" />
<mimeMap fileExtension=".pcx" mimeType="application/octet-stream" />
<mimeMap fileExtension=".pcz" mimeType="application/octet-stream" />
<mimeMap fileExtension=".pdf" mimeType="application/pdf" />
<mimeMap fileExtension=".pfb" mimeType="application/octet-stream" />
<mimeMap fileExtension=".pfm" mimeType="application/octet-stream" />
<mimeMap fileExtension=".pfx" mimeType="application/x-pkcs12" />
<mimeMap fileExtension=".pgm" mimeType="image/x-portable-graymap" />
<mimeMap fileExtension=".pko" mimeType="application/vnd.ms-pki.pko" />
<mimeMap fileExtension=".pma" mimeType="application/x-perfmon" />
<mimeMap fileExtension=".pmc" mimeType="application/x-perfmon" />
<mimeMap fileExtension=".pml" mimeType="application/x-perfmon" />
<mimeMap fileExtension=".pmr" mimeType="application/x-perfmon" />
<mimeMap fileExtension=".pmw" mimeType="application/x-perfmon" />
<mimeMap fileExtension=".png" mimeType="image/png" />
<mimeMap fileExtension=".pnm" mimeType="image/x-portable-anymap" />
<mimeMap fileExtension=".pnz" mimeType="image/png" />
<mimeMap fileExtension=".pot" mimeType="application/vnd.ms-powerpoint" />
<mimeMap fileExtension=".potm" mimeType="application/vnd.ms-powerpoint.template.macroEnabled.12" />
<mimeMap fileExtension=".potx" mimeType="application/vnd.openxmlformats-officedocument.presentationml.template" />
<mimeMap fileExtension=".ppam" mimeType="application/vnd.ms-powerpoint.addin.macroEnabled.12" />
<mimeMap fileExtension=".ppm" mimeType="image/x-portable-pixmap" />
<mimeMap fileExtension=".pps" mimeType="application/vnd.ms-powerpoint" />
<mimeMap fileExtension=".ppsm" mimeType="application/vnd.ms-powerpoint.slideshow.macroEnabled.12" />
<mimeMap fileExtension=".ppsx" mimeType="application/vnd.openxmlformats-officedocument.presentationml.slideshow" />
<mimeMap fileExtension=".ppt" mimeType="application/vnd.ms-powerpoint" />
<mimeMap fileExtension=".pptm" mimeType="application/vnd.ms-powerpoint.presentation.macroEnabled.12" />
<mimeMap fileExtension=".pptx" mimeType="application/vnd.openxmlformats-officedocument.presentationml.presentation" />
<mimeMap fileExtension=".prf" mimeType="application/pics-rules" />
<mimeMap fileExtension=".prm" mimeType="application/octet-stream" />
<mimeMap fileExtension=".prx" mimeType="application/octet-stream" />
<mimeMap fileExtension=".ps" mimeType="application/postscript" />
<mimeMap fileExtension=".psd" mimeType="application/octet-stream" />
<mimeMap fileExtension=".psm" mimeType="application/octet-stream" />
<mimeMap fileExtension=".psp" mimeType="application/octet-stream" />
<mimeMap fileExtension=".pub" mimeType="application/x-mspublisher" />
<mimeMap fileExtension=".qt" mimeType="video/quicktime" />
<mimeMap fileExtension=".qtl" mimeType="application/x-quicktimeplayer" />
<mimeMap fileExtension=".qxd" mimeType="application/octet-stream" />
<mimeMap fileExtension=".ra" mimeType="audio/x-pn-realaudio" />
<mimeMap fileExtension=".ram" mimeType="audio/x-pn-realaudio" />
<mimeMap fileExtension=".rar" mimeType="application/octet-stream" />
<mimeMap fileExtension=".ras" mimeType="image/x-cmu-raster" />
<mimeMap fileExtension=".rf" mimeType="image/vnd.rn-realflash" />
<mimeMap fileExtension=".rgb" mimeType="image/x-rgb" />
<mimeMap fileExtension=".rm" mimeType="application/vnd.rn-realmedia" />
<mimeMap fileExtension=".rmi" mimeType="audio/mid" />
<mimeMap fileExtension=".roff" mimeType="application/x-troff" />
<mimeMap fileExtension=".rpm" mimeType="audio/x-pn-realaudio-plugin" />
<mimeMap fileExtension=".rtf" mimeType="application/rtf" />
<mimeMap fileExtension=".rtx" mimeType="text/richtext" />
<mimeMap fileExtension=".scd" mimeType="application/x-msschedule" />
<mimeMap fileExtension=".sct" mimeType="text/scriptlet" />
<mimeMap fileExtension=".sea" mimeType="application/octet-stream" />
<mimeMap fileExtension=".setpay" mimeType="application/set-payment-initiation" />
<mimeMap fileExtension=".setreg" mimeType="application/set-registration-initiation" />
<mimeMap fileExtension=".sgml" mimeType="text/sgml" />
<mimeMap fileExtension=".sh" mimeType="application/x-sh" />
<mimeMap fileExtension=".shar" mimeType="application/x-shar" />
<mimeMap fileExtension=".sit" mimeType="application/x-stuffit" />
<mimeMap fileExtension=".sldm" mimeType="application/vnd.ms-powerpoint.slide.macroEnabled.12" />
<mimeMap fileExtension=".sldx" mimeType="application/vnd.openxmlformats-officedocument.presentationml.slide" />
<mimeMap fileExtension=".smd" mimeType="audio/x-smd" />
<mimeMap fileExtension=".smi" mimeType="application/octet-stream" />
<mimeMap fileExtension=".smx" mimeType="audio/x-smd" />
<mimeMap fileExtension=".smz" mimeType="audio/x-smd" />
<mimeMap fileExtension=".snd" mimeType="audio/basic" />
<mimeMap fileExtension=".snp" mimeType="application/octet-stream" />
<mimeMap fileExtension=".spc" mimeType="application/x-pkcs7-certificates" />
<mimeMap fileExtension=".spl" mimeType="application/futuresplash" />
<mimeMap fileExtension=".spx" mimeType="audio/ogg" />
<mimeMap fileExtension=".src" mimeType="application/x-wais-source" />
<mimeMap fileExtension=".ssm" mimeType="application/streamingmedia" />
<mimeMap fileExtension=".sst" mimeType="application/vnd.ms-pki.certstore" />
<mimeMap fileExtension=".stl" mimeType="application/vnd.ms-pki.stl" />
<mimeMap fileExtension=".sv4cpio" mimeType="application/x-sv4cpio" />
<mimeMap fileExtension=".sv4crc" mimeType="application/x-sv4crc" />
<mimeMap fileExtension=".svg" mimeType="image/svg+xml" />
<mimeMap fileExtension=".svgz" mimeType="image/svg+xml" />
<mimeMap fileExtension=".swf" mimeType="application/x-shockwave-flash" />
<mimeMap fileExtension=".t" mimeType="application/x-troff" />
<mimeMap fileExtension=".tar" mimeType="application/x-tar" />
<mimeMap fileExtension=".tcl" mimeType="application/x-tcl" />
<mimeMap fileExtension=".tex" mimeType="application/x-tex" />
<mimeMap fileExtension=".texi" mimeType="application/x-texinfo" />
<mimeMap fileExtension=".texinfo" mimeType="application/x-texinfo" />
<mimeMap fileExtension=".tgz" mimeType="application/x-compressed" />
<mimeMap fileExtension=".thmx" mimeType="application/vnd.ms-officetheme" />
<mimeMap fileExtension=".thn" mimeType="application/octet-stream" />
<mimeMap fileExtension=".tif" mimeType="image/tiff" />
<mimeMap fileExtension=".tiff" mimeType="image/tiff" />
<mimeMap fileExtension=".toc" mimeType="application/octet-stream" />
<mimeMap fileExtension=".tr" mimeType="application/x-troff" />
<mimeMap fileExtension=".trm" mimeType="application/x-msterminal" />
<mimeMap fileExtension=".ts" mimeType="video/vnd.dlna.mpeg-tts" />
<mimeMap fileExtension=".tsv" mimeType="text/tab-separated-values" />
<mimeMap fileExtension=".ttf" mimeType="application/octet-stream" />
<mimeMap fileExtension=".tts" mimeType="video/vnd.dlna.mpeg-tts" />
<mimeMap fileExtension=".txt" mimeType="text/plain" />
<mimeMap fileExtension=".u32" mimeType="application/octet-stream" />
<mimeMap fileExtension=".uls" mimeType="text/iuls" />
<mimeMap fileExtension=".ustar" mimeType="application/x-ustar" />
<mimeMap fileExtension=".vbs" mimeType="text/vbscript" />
<mimeMap fileExtension=".vcf" mimeType="text/x-vcard" />
<mimeMap fileExtension=".vcs" mimeType="text/plain" />
<mimeMap fileExtension=".vdx" mimeType="application/vnd.ms-visio.viewer" />
<mimeMap fileExtension=".vml" mimeType="text/xml" />
<mimeMap fileExtension=".vsd" mimeType="application/vnd.visio" />
<mimeMap fileExtension=".vss" mimeType="application/vnd.visio" />
<mimeMap fileExtension=".vst" mimeType="application/vnd.visio" />
<mimeMap fileExtension=".vsto" mimeType="application/x-ms-vsto" />
<mimeMap fileExtension=".vsw" mimeType="application/vnd.visio" />
<mimeMap fileExtension=".vsx" mimeType="application/vnd.visio" />
<mimeMap fileExtension=".vtx" mimeType="application/vnd.visio" />
<mimeMap fileExtension=".wasm" mimeType="application/wasm" />
<mimeMap fileExtension=".wav" mimeType="audio/wav" />
<mimeMap fileExtension=".wax" mimeType="audio/x-ms-wax" />
<mimeMap fileExtension=".wbmp" mimeType="image/vnd.wap.wbmp" />
<mimeMap fileExtension=".wcm" mimeType="application/vnd.ms-works" />
<mimeMap fileExtension=".wdb" mimeType="application/vnd.ms-works" />
<mimeMap fileExtension=".webm" mimeType="video/webm" />
<mimeMap fileExtension=".wks" mimeType="application/vnd.ms-works" />
<mimeMap fileExtension=".wm" mimeType="video/x-ms-wm" />
<mimeMap fileExtension=".wma" mimeType="audio/x-ms-wma" />
<mimeMap fileExtension=".wmd" mimeType="application/x-ms-wmd" />
<mimeMap fileExtension=".wmf" mimeType="application/x-msmetafile" />
<mimeMap fileExtension=".wml" mimeType="text/vnd.wap.wml" />
<mimeMap fileExtension=".wmlc" mimeType="application/vnd.wap.wmlc" />
<mimeMap fileExtension=".wmls" mimeType="text/vnd.wap.wmlscript" />
<mimeMap fileExtension=".wmlsc" mimeType="application/vnd.wap.wmlscriptc" />
<mimeMap fileExtension=".wmp" mimeType="video/x-ms-wmp" />
<mimeMap fileExtension=".wmv" mimeType="video/x-ms-wmv" />
<mimeMap fileExtension=".wmx" mimeType="video/x-ms-wmx" />
<mimeMap fileExtension=".wmz" mimeType="application/x-ms-wmz" />
<mimeMap fileExtension=".woff" mimeType="font/x-woff" />
<mimeMap fileExtension=".woff2" mimeType="application/font-woff2" />
<mimeMap fileExtension=".wps" mimeType="application/vnd.ms-works" />
<mimeMap fileExtension=".wri" mimeType="application/x-mswrite" />
<mimeMap fileExtension=".wrl" mimeType="x-world/x-vrml" />
<mimeMap fileExtension=".wrz" mimeType="x-world/x-vrml" />
<mimeMap fileExtension=".wsdl" mimeType="text/xml" />
<mimeMap fileExtension=".wtv" mimeType="video/x-ms-wtv" />
<mimeMap fileExtension=".wvx" mimeType="video/x-ms-wvx" />
<mimeMap fileExtension=".x" mimeType="application/directx" />
<mimeMap fileExtension=".xaf" mimeType="x-world/x-vrml" />
<mimeMap fileExtension=".xaml" mimeType="application/xaml+xml" />
<mimeMap fileExtension=".xap" mimeType="application/x-silverlight-app" />
<mimeMap fileExtension=".xbap" mimeType="application/x-ms-xbap" />
<mimeMap fileExtension=".xbm" mimeType="image/x-xbitmap" />
<mimeMap fileExtension=".xdr" mimeType="text/plain" />
<mimeMap fileExtension=".xht" mimeType="application/xhtml+xml" />
<mimeMap fileExtension=".xhtml" mimeType="application/xhtml+xml" />
<mimeMap fileExtension=".xla" mimeType="application/vnd.ms-excel" />
<mimeMap fileExtension=".xlam" mimeType="application/vnd.ms-excel.addin.macroEnabled.12" />
<mimeMap fileExtension=".xlc" mimeType="application/vnd.ms-excel" />
<mimeMap fileExtension=".xlm" mimeType="application/vnd.ms-excel" />
<mimeMap fileExtension=".xls" mimeType="application/vnd.ms-excel" />
<mimeMap fileExtension=".xlsb" mimeType="application/vnd.ms-excel.sheet.binary.macroEnabled.12" />
<mimeMap fileExtension=".xlsm" mimeType="application/vnd.ms-excel.sheet.macroEnabled.12" />
<mimeMap fileExtension=".xlsx" mimeType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" />
<mimeMap fileExtension=".xlt" mimeType="application/vnd.ms-excel" />
<mimeMap fileExtension=".xltm" mimeType="application/vnd.ms-excel.template.macroEnabled.12" />
<mimeMap fileExtension=".xltx" mimeType="application/vnd.openxmlformats-officedocument.spreadsheetml.template" />
<mimeMap fileExtension=".xlw" mimeType="application/vnd.ms-excel" />
<mimeMap fileExtension=".xml" mimeType="text/xml" />
<mimeMap fileExtension=".xof" mimeType="x-world/x-vrml" />
<mimeMap fileExtension=".xpm" mimeType="image/x-xpixmap" />
<mimeMap fileExtension=".xps" mimeType="application/vnd.ms-xpsdocument" />
<mimeMap fileExtension=".xsd" mimeType="text/xml" />
<mimeMap fileExtension=".xsf" mimeType="text/xml" />
<mimeMap fileExtension=".xsl" mimeType="text/xml" />
<mimeMap fileExtension=".xslt" mimeType="text/xml" />
<mimeMap fileExtension=".xsn" mimeType="application/octet-stream" />
<mimeMap fileExtension=".xtp" mimeType="application/octet-stream" />
<mimeMap fileExtension=".xwd" mimeType="image/x-xwindowdump" />
<mimeMap fileExtension=".z" mimeType="application/x-compress" />
<mimeMap fileExtension=".zip" mimeType="application/x-zip-compressed" />
</staticContent>
<tracing>
<traceFailedRequests>
<add path="*">
<traceAreas>
<add provider="ASP" verbosity="Verbose" />
<add provider="ASPNET" areas="Infrastructure,Module,Page,AppServices" verbosity="Verbose" />
<add provider="ISAPI Extension" verbosity="Verbose" />
<add provider="WWW Server" areas="Authentication,Security,Filter,StaticFile,CGI,Compression,Cache,RequestNotifications,Module,Rewrite,WebSocket" verbosity="Verbose" />
</traceAreas>
<failureDefinitions statusCodes="200-999" />
</add>
</traceFailedRequests>
<traceProviderDefinitions>
<add name="WWW Server" guid="{3a2a4e84-4c21-4981-ae10-3fda0d9b0f83}">
<areas>
<clear />
<add name="Authentication" value="2" />
<add name="Security" value="4" />
<add name="Filter" value="8" />
<add name="StaticFile" value="16" />
<add name="CGI" value="32" />
<add name="Compression" value="64" />
<add name="Cache" value="128" />
<add name="RequestNotifications" value="256" />
<add name="Module" value="512" />
<add name="Rewrite" value="1024" />
<add name="FastCGI" value="4096" />
<add name="WebSocket" value="16384" />
<add name="ANCM" value="65536" />
</areas>
</add>
<add name="ASP" guid="{06b94d9a-b15e-456e-a4ef-37c984a2cb4b}">
<areas>
<clear />
</areas>
</add>
<add name="ISAPI Extension" guid="{a1c2040e-8840-4c31-ba11-9871031a19ea}">
<areas>
<clear />
</areas>
</add>
<add name="ASPNET" guid="{AFF081FE-0247-4275-9C4E-021F3DC1DA35}">
<areas>
<add name="Infrastructure" value="1" />
<add name="Module" value="2" />
<add name="Page" value="4" />
<add name="AppServices" value="8" />
</areas>
</add>
</traceProviderDefinitions>
</tracing>
<urlCompression />
<validation />
<webdav>
<globalSettings>
<propertyStores>
<add name="webdav_simple_prop" image="%IIS_BIN%\webdav_simple_prop.dll" image32="%IIS_BIN%\webdav_simple_prop.dll" />
</propertyStores>
<lockStores>
<add name="webdav_simple_lock" image="%IIS_BIN%\webdav_simple_lock.dll" image32="%IIS_BIN%\webdav_simple_lock.dll" />
</lockStores>
</globalSettings>
<authoring>
<locks enabled="true" lockStore="webdav_simple_lock" />
</authoring>
<authoringRules />
</webdav>
<webSocket />
<applicationInitialization />
</system.webServer>
<location path="" overrideMode="Allow">
<system.webServer>
<modules>
<add name="IsapiFilterModule" lockItem="true" />
<add name="BasicAuthenticationModule" lockItem="true" />
<add name="IsapiModule" lockItem="true" />
<add name="HttpLoggingModule" lockItem="true" />
<add name="DynamicCompressionModule" lockItem="true" />
<add name="StaticCompressionModule" lockItem="true" />
<add name="DefaultDocumentModule" lockItem="true" />
<add name="DirectoryListingModule" lockItem="true" />
<add name="ProtocolSupportModule" lockItem="true" />
<add name="HttpRedirectionModule" lockItem="true" />
<add name="ServerSideIncludeModule" lockItem="true" />
<add name="StaticFileModule" lockItem="true" />
<add name="AnonymousAuthenticationModule" lockItem="true" />
<add name="CertificateMappingAuthenticationModule" lockItem="true" />
<add name="UrlAuthorizationModule" lockItem="true" />
<add name="WindowsAuthenticationModule" lockItem="true" />
<add name="IISCertificateMappingAuthenticationModule" lockItem="true" />
<add name="WebMatrixSupportModule" lockItem="true" />
<add name="IpRestrictionModule" lockItem="true" />
<add name="DynamicIpRestrictionModule" lockItem="true" />
<add name="RequestFilteringModule" lockItem="true" />
<add name="CustomLoggingModule" lockItem="true" />
<add name="CustomErrorModule" lockItem="true" />
<add name="FailedRequestsTracingModule" lockItem="true" />
<add name="CgiModule" lockItem="true" />
<add name="FastCgiModule" lockItem="true" />
<!-- <add name="WebDAVModule" /> -->
<add name="RewriteModule" />
<add name="OutputCache" type="System.Web.Caching.OutputCacheModule" preCondition="managedHandler" />
<add name="Session" type="System.Web.SessionState.SessionStateModule" preCondition="managedHandler" />
<add name="WindowsAuthentication" type="System.Web.Security.WindowsAuthenticationModule" preCondition="managedHandler" />
<add name="FormsAuthentication" type="System.Web.Security.FormsAuthenticationModule" preCondition="managedHandler" />
<add name="DefaultAuthentication" type="System.Web.Security.DefaultAuthenticationModule" preCondition="managedHandler" />
<add name="RoleManager" type="System.Web.Security.RoleManagerModule" preCondition="managedHandler" />
<add name="UrlAuthorization" type="System.Web.Security.UrlAuthorizationModule" preCondition="managedHandler" />
<add name="FileAuthorization" type="System.Web.Security.FileAuthorizationModule" preCondition="managedHandler" />
<add name="AnonymousIdentification" type="System.Web.Security.AnonymousIdentificationModule" preCondition="managedHandler" />
<add name="Profile" type="System.Web.Profile.ProfileModule" preCondition="managedHandler" />
<add name="UrlMappingsModule" type="System.Web.UrlMappingsModule" preCondition="managedHandler" />
<add name="ApplicationInitializationModule" lockItem="true" />
<add name="WebSocketModule" lockItem="true" />
<add name="ServiceModel-4.0" type="System.ServiceModel.Activation.ServiceHttpModule,System.ServiceModel.Activation,Version=4.0.0.0,Culture=neutral,PublicKeyToken=31bf3856ad364e35" preCondition="managedHandler,runtimeVersionv4.0" />
<add name="ConfigurationValidationModule" lockItem="true" />
<add name="UrlRoutingModule-4.0" type="System.Web.Routing.UrlRoutingModule" preCondition="managedHandler,runtimeVersionv4.0" />
<add name="ScriptModule-4.0" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" preCondition="managedHandler,runtimeVersionv4.0" />
<add name="AspNetCoreModule" lockItem="true" />
<add name="AspNetCoreModuleV2" lockItem="true" />
</modules>
<handlers accessPolicy="Read, Script">
<!-- <add name="WebDAV" path="*" verb="PROPFIND,PROPPATCH,MKCOL,PUT,COPY,DELETE,MOVE,LOCK,UNLOCK" modules="WebDAVModule" resourceType="Unspecified" requireAccess="None" /> -->
<add name="AXD-ISAPI-4.0_64bit" path="*.axd" verb="GET,HEAD,POST,DEBUG" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework64\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness64" responseBufferLimit="0" />
<add name="PageHandlerFactory-ISAPI-4.0_64bit" path="*.aspx" verb="GET,HEAD,POST,DEBUG" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework64\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness64" responseBufferLimit="0" />
<add name="SimpleHandlerFactory-ISAPI-4.0_64bit" path="*.ashx" verb="GET,HEAD,POST,DEBUG" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework64\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness64" responseBufferLimit="0" />
<add name="WebServiceHandlerFactory-ISAPI-4.0_64bit" path="*.asmx" verb="GET,HEAD,POST,DEBUG" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework64\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness64" responseBufferLimit="0" />
<add name="HttpRemotingHandlerFactory-rem-ISAPI-4.0_64bit" path="*.rem" verb="GET,HEAD,POST,DEBUG" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework64\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness64" responseBufferLimit="0" />
<add name="HttpRemotingHandlerFactory-soap-ISAPI-4.0_64bit" path="*.soap" verb="GET,HEAD,POST,DEBUG" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework64\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness64" responseBufferLimit="0" />
<add name="svc-ISAPI-4.0_64bit" path="*.svc" verb="*" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework64\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness64" />
<add name="rules-ISAPI-4.0_64bit" path="*.rules" verb="*" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework64\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness64" />
<add name="xoml-ISAPI-4.0_64bit" path="*.xoml" verb="*" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework64\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness64" />
<add name="xamlx-ISAPI-4.0_64bit" path="*.xamlx" verb="GET,HEAD,POST,DEBUG" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework64\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness64" />
<add name="aspq-ISAPI-4.0_64bit" path="*.aspq" verb="*" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework64\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness64" responseBufferLimit="0" />
<add name="cshtm-ISAPI-4.0_64bit" path="*.cshtm" verb="GET,HEAD,POST,DEBUG" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework64\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness64" responseBufferLimit="0" />
<add name="cshtml-ISAPI-4.0_64bit" path="*.cshtml" verb="GET,HEAD,POST,DEBUG" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework64\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness64" responseBufferLimit="0" />
<add name="vbhtm-ISAPI-4.0_64bit" path="*.vbhtm" verb="GET,HEAD,POST,DEBUG" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework64\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness64" responseBufferLimit="0" />
<add name="vbhtml-ISAPI-4.0_64bit" path="*.vbhtml" verb="GET,HEAD,POST,DEBUG" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework64\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness64" responseBufferLimit="0" />
<add name="AXD-ISAPI-4.0_32bit" path="*.axd" verb="GET,HEAD,POST,DEBUG" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness32" responseBufferLimit="0" />
<add name="PageHandlerFactory-ISAPI-4.0_32bit" path="*.aspx" verb="GET,HEAD,POST,DEBUG" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness32" responseBufferLimit="0" />
<add name="SimpleHandlerFactory-ISAPI-4.0_32bit" path="*.ashx" verb="GET,HEAD,POST,DEBUG" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness32" responseBufferLimit="0" />
<add name="WebServiceHandlerFactory-ISAPI-4.0_32bit" path="*.asmx" verb="GET,HEAD,POST,DEBUG" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness32" responseBufferLimit="0" />
<add name="HttpRemotingHandlerFactory-rem-ISAPI-4.0_32bit" path="*.rem" verb="GET,HEAD,POST,DEBUG" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness32" responseBufferLimit="0" />
<add name="HttpRemotingHandlerFactory-soap-ISAPI-4.0_32bit" path="*.soap" verb="GET,HEAD,POST,DEBUG" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness32" responseBufferLimit="0" />
<add name="svc-ISAPI-4.0_32bit" path="*.svc" verb="*" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness32" />
<add name="rules-ISAPI-4.0_32bit" path="*.rules" verb="*" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness32" />
<add name="xoml-ISAPI-4.0_32bit" path="*.xoml" verb="*" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness32" />
<add name="xamlx-ISAPI-4.0_32bit" path="*.xamlx" verb="GET,HEAD,POST,DEBUG" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness32" />
<add name="aspq-ISAPI-4.0_32bit" path="*.aspq" verb="*" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness32" responseBufferLimit="0" />
<add name="cshtm-ISAPI-4.0_32bit" path="*.cshtm" verb="GET,HEAD,POST,DEBUG" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness32" responseBufferLimit="0" />
<add name="cshtml-ISAPI-4.0_32bit" path="*.cshtml" verb="GET,HEAD,POST,DEBUG" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness32" responseBufferLimit="0" />
<add name="vbhtm-ISAPI-4.0_32bit" path="*.vbhtm" verb="GET,HEAD,POST,DEBUG" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness32" responseBufferLimit="0" />
<add name="vbhtml-ISAPI-4.0_32bit" path="*.vbhtml" verb="GET,HEAD,POST,DEBUG" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness32" responseBufferLimit="0" />
<add name="TraceHandler-Integrated-4.0" path="trace.axd" verb="GET,HEAD,POST,DEBUG" type="System.Web.Handlers.TraceHandler" preCondition="integratedMode,runtimeVersionv4.0" />
<add name="WebAdminHandler-Integrated-4.0" path="WebAdmin.axd" verb="GET,DEBUG" type="System.Web.Handlers.WebAdminHandler" preCondition="integratedMode,runtimeVersionv4.0" />
<add name="AssemblyResourceLoader-Integrated-4.0" path="WebResource.axd" verb="GET,DEBUG" type="System.Web.Handlers.AssemblyResourceLoader" preCondition="integratedMode,runtimeVersionv4.0" />
<add name="PageHandlerFactory-Integrated-4.0" path="*.aspx" verb="GET,HEAD,POST,DEBUG" type="System.Web.UI.PageHandlerFactory" preCondition="integratedMode,runtimeVersionv4.0" />
<add name="SimpleHandlerFactory-Integrated-4.0" path="*.ashx" verb="GET,HEAD,POST,DEBUG" type="System.Web.UI.SimpleHandlerFactory" preCondition="integratedMode,runtimeVersionv4.0" />
<add name="WebServiceHandlerFactory-Integrated-4.0" path="*.asmx" verb="GET,HEAD,POST,DEBUG" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" preCondition="integratedMode,runtimeVersionv4.0" />
<add name="HttpRemotingHandlerFactory-rem-Integrated-4.0" path="*.rem" verb="GET,HEAD,POST,DEBUG" type="System.Runtime.Remoting.Channels.Http.HttpRemotingHandlerFactory, System.Runtime.Remoting, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" preCondition="integratedMode,runtimeVersionv4.0" />
<add name="HttpRemotingHandlerFactory-soap-Integrated-4.0" path="*.soap" verb="GET,HEAD,POST,DEBUG" type="System.Runtime.Remoting.Channels.Http.HttpRemotingHandlerFactory, System.Runtime.Remoting, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" preCondition="integratedMode,runtimeVersionv4.0" />
<add name="svc-Integrated-4.0" path="*.svc" verb="*" type="System.ServiceModel.Activation.ServiceHttpHandlerFactory, System.ServiceModel.Activation, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" preCondition="integratedMode,runtimeVersionv4.0" />
<add name="rules-Integrated-4.0" path="*.rules" verb="*" type="System.ServiceModel.Activation.ServiceHttpHandlerFactory, System.ServiceModel.Activation, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" preCondition="integratedMode,runtimeVersionv4.0" />
<add name="xoml-Integrated-4.0" path="*.xoml" verb="*" type="System.ServiceModel.Activation.ServiceHttpHandlerFactory, System.ServiceModel.Activation, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" preCondition="integratedMode,runtimeVersionv4.0" />
<add name="xamlx-Integrated-4.0" path="*.xamlx" verb="GET,HEAD,POST,DEBUG" type="System.Xaml.Hosting.XamlHttpHandlerFactory, System.Xaml.Hosting, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" preCondition="integratedMode,runtimeVersionv4.0" />
<add name="aspq-Integrated-4.0" path="*.aspq" verb="GET,HEAD,POST,DEBUG" type="System.Web.HttpForbiddenHandler" preCondition="integratedMode,runtimeVersionv4.0" />
<add name="cshtm-Integrated-4.0" path="*.cshtm" verb="GET,HEAD,POST,DEBUG" type="System.Web.HttpForbiddenHandler" preCondition="integratedMode,runtimeVersionv4.0" />
<add name="cshtml-Integrated-4.0" path="*.cshtml" verb="GET,HEAD,POST,DEBUG" type="System.Web.HttpForbiddenHandler" preCondition="integratedMode,runtimeVersionv4.0" />
<add name="vbhtm-Integrated-4.0" path="*.vbhtm" verb="GET,HEAD,POST,DEBUG" type="System.Web.HttpForbiddenHandler" preCondition="integratedMode,runtimeVersionv4.0" />
<add name="vbhtml-Integrated-4.0" path="*.vbhtml" verb="GET,HEAD,POST,DEBUG" type="System.Web.HttpForbiddenHandler" preCondition="integratedMode,runtimeVersionv4.0" />
<add name="ScriptHandlerFactoryAppServices-Integrated-4.0" path="*_AppService.axd" verb="*" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" preCondition="integratedMode,runtimeVersionv4.0" />
<add name="ScriptResourceIntegrated-4.0" path="*ScriptResource.axd" verb="GET,HEAD" type="System.Web.Handlers.ScriptResourceHandler, System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" preCondition="integratedMode,runtimeVersionv4.0" />
<add name="ASPClassic" path="*.asp" verb="GET,HEAD,POST" modules="IsapiModule" scriptProcessor="%IIS_BIN%\asp.dll" resourceType="File" />
<add name="SecurityCertificate" path="*.cer" verb="GET,HEAD,POST" modules="IsapiModule" scriptProcessor="%IIS_BIN%\asp.dll" resourceType="File" />
<add name="ISAPI-dll" path="*.dll" verb="*" modules="IsapiModule" resourceType="File" requireAccess="Execute" allowPathInfo="true" />
<add name="TraceHandler-Integrated" path="trace.axd" verb="GET,HEAD,POST,DEBUG" type="System.Web.Handlers.TraceHandler" preCondition="integratedMode,runtimeVersionv2.0" />
<add name="WebAdminHandler-Integrated" path="WebAdmin.axd" verb="GET,DEBUG" type="System.Web.Handlers.WebAdminHandler" preCondition="integratedMode,runtimeVersionv2.0" />
<add name="AssemblyResourceLoader-Integrated" path="WebResource.axd" verb="GET,DEBUG" type="System.Web.Handlers.AssemblyResourceLoader" preCondition="integratedMode,runtimeVersionv2.0" />
<add name="PageHandlerFactory-Integrated" path="*.aspx" verb="GET,HEAD,POST,DEBUG" type="System.Web.UI.PageHandlerFactory" preCondition="integratedMode,runtimeVersionv2.0" />
<add name="SimpleHandlerFactory-Integrated" path="*.ashx" verb="GET,HEAD,POST,DEBUG" type="System.Web.UI.SimpleHandlerFactory" preCondition="integratedMode,runtimeVersionv2.0" />
<add name="WebServiceHandlerFactory-Integrated" path="*.asmx" verb="GET,HEAD,POST,DEBUG" type="System.Web.Services.Protocols.WebServiceHandlerFactory,System.Web.Services,Version=2.0.0.0,Culture=neutral,PublicKeyToken=b03f5f7f11d50a3a" preCondition="integratedMode,runtimeVersionv2.0" />
<add name="HttpRemotingHandlerFactory-rem-Integrated" path="*.rem" verb="GET,HEAD,POST,DEBUG" type="System.Runtime.Remoting.Channels.Http.HttpRemotingHandlerFactory,System.Runtime.Remoting,Version=2.0.0.0,Culture=neutral,PublicKeyToken=b77a5c561934e089" preCondition="integratedMode,runtimeVersionv2.0" />
<add name="HttpRemotingHandlerFactory-soap-Integrated" path="*.soap" verb="GET,HEAD,POST,DEBUG" type="System.Runtime.Remoting.Channels.Http.HttpRemotingHandlerFactory,System.Runtime.Remoting,Version=2.0.0.0,Culture=neutral,PublicKeyToken=b77a5c561934e089" preCondition="integratedMode,runtimeVersionv2.0" />
<add name="AXD-ISAPI-2.0" path="*.axd" verb="GET,HEAD,POST,DEBUG" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework\v2.0.50727\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv2.0,bitness32" responseBufferLimit="0" />
<add name="PageHandlerFactory-ISAPI-2.0" path="*.aspx" verb="GET,HEAD,POST,DEBUG" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework\v2.0.50727\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv2.0,bitness32" responseBufferLimit="0" />
<add name="SimpleHandlerFactory-ISAPI-2.0" path="*.ashx" verb="GET,HEAD,POST,DEBUG" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework\v2.0.50727\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv2.0,bitness32" responseBufferLimit="0" />
<add name="WebServiceHandlerFactory-ISAPI-2.0" path="*.asmx" verb="GET,HEAD,POST,DEBUG" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework\v2.0.50727\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv2.0,bitness32" responseBufferLimit="0" />
<add name="HttpRemotingHandlerFactory-rem-ISAPI-2.0" path="*.rem" verb="GET,HEAD,POST,DEBUG" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework\v2.0.50727\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv2.0,bitness32" responseBufferLimit="0" />
<add name="HttpRemotingHandlerFactory-soap-ISAPI-2.0" path="*.soap" verb="GET,HEAD,POST,DEBUG" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework\v2.0.50727\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv2.0,bitness32" responseBufferLimit="0" />
<add name="AXD-ISAPI-2.0-64" path="*.axd" verb="GET,HEAD,POST,DEBUG" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework64\v2.0.50727\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv2.0,bitness64" responseBufferLimit="0" />
<add name="PageHandlerFactory-ISAPI-2.0-64" path="*.aspx" verb="GET,HEAD,POST,DEBUG" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework64\v2.0.50727\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv2.0,bitness64" responseBufferLimit="0" />
<add name="SimpleHandlerFactory-ISAPI-2.0-64" path="*.ashx" verb="GET,HEAD,POST,DEBUG" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework64\v2.0.50727\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv2.0,bitness64" responseBufferLimit="0" />
<add name="WebServiceHandlerFactory-ISAPI-2.0-64" path="*.asmx" verb="GET,HEAD,POST,DEBUG" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework64\v2.0.50727\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv2.0,bitness64" responseBufferLimit="0" />
<add name="HttpRemotingHandlerFactory-rem-ISAPI-2.0-64" path="*.rem" verb="GET,HEAD,POST,DEBUG" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework64\v2.0.50727\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv2.0,bitness64" responseBufferLimit="0" />
<add name="HttpRemotingHandlerFactory-soap-ISAPI-2.0-64" path="*.soap" verb="GET,HEAD,POST,DEBUG" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework64\v2.0.50727\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv2.0,bitness64" responseBufferLimit="0" />
<add name="CGI-exe" path="*.exe" verb="*" modules="CgiModule" resourceType="File" requireAccess="Execute" allowPathInfo="true" />
<add name="SSINC-stm" path="*.stm" verb="GET,HEAD,POST" modules="ServerSideIncludeModule" resourceType="File" />
<add name="SSINC-shtm" path="*.shtm" verb="GET,HEAD,POST" modules="ServerSideIncludeModule" resourceType="File" />
<add name="SSINC-shtml" path="*.shtml" verb="GET,HEAD,POST" modules="ServerSideIncludeModule" resourceType="File" />
<add name="TRACEVerbHandler" path="*" verb="TRACE" modules="ProtocolSupportModule" requireAccess="None" />
<add name="OPTIONSVerbHandler" path="*" verb="OPTIONS" modules="ProtocolSupportModule" requireAccess="None" />
<add name="ExtensionlessUrlHandler-ISAPI-4.0_32bit" path="*." verb="GET,HEAD,POST,DEBUG" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness32" responseBufferLimit="0" />
<add name="ExtensionlessUrlHandler-ISAPI-4.0_64bit" path="*." verb="GET,HEAD,POST,DEBUG" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework64\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness64" responseBufferLimit="0" />
<add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="GET,HEAD,POST,DEBUG" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" responseBufferLimit="0" />
<add name="StaticFile" path="*" verb="*" modules="StaticFileModule,DefaultDocumentModule,DirectoryListingModule" resourceType="Either" requireAccess="Read" />
</handlers>
</system.webServer>
</location>
<location path="AspClassicPublicSite">
<system.webServer>
<directoryBrowse enabled="false" />
<asp scriptErrorSentToBrowser="true" enableParentPaths="true">
<limits scriptTimeout="00:10:00" />
</asp>
</system.webServer>
</location>
</configuration>

+ 84
- 0
build-iisexpress-config.ps1 Wyświetl plik

@@ -0,0 +1,84 @@
param(
[string]$OutputPath = "$(Join-Path $PSScriptRoot 'applicationhost.config')",
[string]$BaseConfigPath = "$env:ProgramFiles\IIS Express\AppServer\applicationhost.config",
[string]$SiteName = "AspClassicPublicSite",
[string]$PhysicalPath = "H:\AI - ASP - Classic\Public",
[string]$BindingInformation = "*:8080:localhost"
)

if (-not (Test-Path -LiteralPath $BaseConfigPath)) {
$BaseConfigPath = "$env:ProgramFiles(x86)\IIS Express\AppServer\applicationhost.config"
}

if (-not (Test-Path -LiteralPath $BaseConfigPath)) {
Write-Error "IIS Express base applicationhost.config not found."
exit 1
}

[xml]$xml = Get-Content -LiteralPath $BaseConfigPath

$sitesNode = $xml.configuration.'system.applicationHost'.sites
if (-not $sitesNode) {
Write-Error "Base config does not contain system.applicationHost/sites."
exit 1
}

$existingSite = $sitesNode.site | Where-Object { $_.name -eq $SiteName }
if ($existingSite) {
[void]$sitesNode.RemoveChild($existingSite)
}

$siteIds = @($sitesNode.site | ForEach-Object { [int]$_.id })
$nextId = if ($siteIds.Count -gt 0) { ($siteIds | Measure-Object -Maximum).Maximum + 1 } else { 1 }

$site = $xml.CreateElement('site')
$site.SetAttribute('name', $SiteName)
$site.SetAttribute('id', [string]$nextId)
$site.SetAttribute('serverAutoStart', 'true')

$application = $xml.CreateElement('application')
$application.SetAttribute('path', '/')
$application.SetAttribute('applicationPool', 'Clr4IntegratedAppPool')

$virtualDirectory = $xml.CreateElement('virtualDirectory')
$virtualDirectory.SetAttribute('path', '/')
$virtualDirectory.SetAttribute('physicalPath', $PhysicalPath)
[void]$application.AppendChild($virtualDirectory)

$bindings = $xml.CreateElement('bindings')
$binding = $xml.CreateElement('binding')
$binding.SetAttribute('protocol', 'http')
$binding.SetAttribute('bindingInformation', $BindingInformation)
[void]$bindings.AppendChild($binding)

[void]$site.AppendChild($application)
[void]$site.AppendChild($bindings)
[void]$sitesNode.AppendChild($site)

$oldLocation = @($xml.configuration.location | Where-Object { $_.path -eq $SiteName })
foreach ($node in $oldLocation) {
[void]$xml.configuration.RemoveChild($node)
}

$location = $xml.CreateElement('location')
$location.SetAttribute('path', $SiteName)

$systemWebServer = $xml.CreateElement('system.webServer')

$directoryBrowse = $xml.CreateElement('directoryBrowse')
$directoryBrowse.SetAttribute('enabled', 'false')

$asp = $xml.CreateElement('asp')
$asp.SetAttribute('scriptErrorSentToBrowser', 'true')
$asp.SetAttribute('enableParentPaths', 'true')
$limits = $xml.CreateElement('limits')
$limits.SetAttribute('scriptTimeout', '00:10:00')
[void]$asp.AppendChild($limits)

[void]$systemWebServer.AppendChild($directoryBrowse)
[void]$systemWebServer.AppendChild($asp)
[void]$location.AppendChild($systemWebServer)
[void]$xml.configuration.AppendChild($location)

$xml.Save($OutputPath)
Write-Host "Generated IIS Express config at $OutputPath"

+ 25
- 0
run-iisexpress.cmd Wyświetl plik

@@ -0,0 +1,25 @@
@echo off
setlocal

set "SCRIPT_DIR=%~dp0"
powershell -ExecutionPolicy Bypass -File "%SCRIPT_DIR%build-iisexpress-config.ps1"
if errorlevel 1 exit /b %errorlevel%

set "CONFIG_PATH=%SCRIPT_DIR%applicationhost.config"
set "SITE_NAME=AspClassicPublicSite"
set "TRACE_SWITCH=%~1"
set "IIS_EXPRESS=%ProgramFiles%\IIS Express\iisexpress.exe"

if not exist "%IIS_EXPRESS%" set "IIS_EXPRESS=%ProgramFiles(x86)%\IIS Express\iisexpress.exe"

if not exist "%IIS_EXPRESS%" (
echo IIS Express not found. Checked Program Files and Program Files ^(x86^).
exit /b 1
)

set "TRACE_ARG="
if /I "%TRACE_SWITCH%"=="trace" set "TRACE_ARG=/trace:error"
if /I "%TRACE_SWITCH%"=="/trace:error" set "TRACE_ARG=/trace:error"

echo Starting IIS Express site "%SITE_NAME%" using "%CONFIG_PATH%"...
"%IIS_EXPRESS%" /config:"%CONFIG_PATH%" /site:"%SITE_NAME%" %TRACE_ARG%

+ 28
- 0
run-iisexpress.ps1 Wyświetl plik

@@ -0,0 +1,28 @@
param(
[string]$IisExpressPath = "$env:ProgramFiles\IIS Express\iisexpress.exe",
[string]$ConfigPath = "$(Join-Path $PSScriptRoot 'applicationhost.config')",
[string]$SiteName = "AspClassicPublicSite",
[switch]$TraceErrors
)

& (Join-Path $PSScriptRoot 'build-iisexpress-config.ps1') -OutputPath $ConfigPath
if ($LASTEXITCODE -ne 0) {
exit $LASTEXITCODE
}

if (-not (Test-Path -LiteralPath $IisExpressPath)) {
$IisExpressPath = "$env:ProgramFiles(x86)\IIS Express\iisexpress.exe"
}

if (-not (Test-Path -LiteralPath $IisExpressPath)) {
Write-Error "IIS Express not found. Checked Program Files and Program Files (x86)."
exit 1
}

$arguments = @("/config:$ConfigPath", "/site:$SiteName")
if ($TraceErrors) {
$arguments += "/trace:error"
}

Write-Host "Starting IIS Express site '$SiteName' using config '$ConfigPath'..."
& $IisExpressPath @arguments

+ 10
- 0
stop-iisexpress.cmd Wyświetl plik

@@ -0,0 +1,10 @@
@echo off
setlocal

echo Stopping IIS Express...
taskkill /IM iisexpress.exe /F >nul 2>&1
if errorlevel 1 (
echo No running IIS Express process found.
) else (
echo IIS Express stopped.
)

+ 2
- 0
stop-iisexpress.ps1 Wyświetl plik

@@ -0,0 +1,2 @@
Get-Process iisexpress -ErrorAction SilentlyContinue | Stop-Process -Force -ErrorAction SilentlyContinue
Write-Host "IIS Express stopped if it was running."

+ 153
- 0
tests/AppTests.vbs Wyświetl plik

@@ -0,0 +1,153 @@
Sub Test_App_RegisterAndResolveService()
Dim app

Set app = New App
app.RegisterInstance "widget", New FakeService

AssertTrue app.HasService("widget"), "HasService should be True after RegisterInstance"
AssertNotNothing app.Resolve("widget"), "Resolve should return the registered object"
End Sub

Sub Test_App_ResolveUnregisteredServiceRaises()
Dim app
Dim result
Dim didRaise

Set app = New App
didRaise = False

On Error Resume Next
Err.Clear
Set result = app.Resolve("does-not-exist")
If Err.Number <> 0 Then didRaise = True
Err.Clear
On Error Goto 0

AssertTrue didRaise, "Resolve should raise when the service isn't registered"
End Sub

Sub Test_App_ServiceNamesReflectsRegistered()
Dim app
Dim names

Set app = New App
app.RegisterInstance "alpha", New FakeService
app.RegisterInstance "beta", New FakeService

names = app.ServiceNames()

AssertEquals 2, UBound(names) + 1, "ServiceNames should return one entry per registered service"
End Sub

Sub Test_App_RegisterInstanceReplacesExisting()
Dim app
Dim first
Dim second
Dim resolved
Dim names

Set app = New App
Set first = New FakeService
Set second = New FakeService
first.Marker = "first"
second.Marker = "second"

app.RegisterInstance "svc", first
app.RegisterInstance "svc", second

names = app.ServiceNames()
AssertEquals 1, UBound(names) + 1, "Re-registering the same name should not duplicate it"

Set resolved = app.Resolve("svc")
AssertEquals "second", resolved.Marker, "Resolve should return the most recently registered instance"
End Sub

Sub Test_App_BootPrefersOriginalUrlOverPathInfo()
Dim app
Dim httpRequest
Dim fakeRequest

Set fakeRequest = New FakeAspRequest
fakeRequest.SetServerVariable "REQUEST_METHOD", "GET"
fakeRequest.SetServerVariable "SERVER_NAME", "localhost"
fakeRequest.SetServerVariable "SERVER_PORT", "8080"
fakeRequest.SetServerVariable "PATH_INFO", "/Default.asp"
fakeRequest.SetServerVariable "HTTP_X_ORIGINAL_URL", "/greet/Ada?x=1"

Set httpRequest = New HttpRequest
httpRequest.Bind fakeRequest

Set app = New App
app.SetRequest httpRequest
app.Boot

AssertEquals "/greet/Ada", app.Route, "Route should come from HTTP_X_ORIGINAL_URL (query string stripped), not PATH_INFO"
End Sub

Sub Test_App_BootFallsBackToPathInfoWhenOriginalUrlMissing()
Dim app
Dim httpRequest
Dim fakeRequest

Set fakeRequest = New FakeAspRequest
fakeRequest.SetServerVariable "REQUEST_METHOD", "GET"
fakeRequest.SetServerVariable "SERVER_NAME", "localhost"
fakeRequest.SetServerVariable "SERVER_PORT", "8080"
fakeRequest.SetServerVariable "PATH_INFO", "/greet/Ada"

Set httpRequest = New HttpRequest
httpRequest.Bind fakeRequest

Set app = New App
app.SetRequest httpRequest
app.Boot

AssertEquals "/greet/Ada", app.Route, "Route should fall back to PATH_INFO when HTTP_X_ORIGINAL_URL is absent"
End Sub

Sub Test_App_BootNormalizesDefaultAspToRoot()
Dim app
Dim httpRequest
Dim fakeRequest

Set fakeRequest = New FakeAspRequest
fakeRequest.SetServerVariable "REQUEST_METHOD", "GET"
fakeRequest.SetServerVariable "SERVER_NAME", "localhost"
fakeRequest.SetServerVariable "SERVER_PORT", "8080"
fakeRequest.SetServerVariable "HTTP_X_ORIGINAL_URL", "/Default.asp"

Set httpRequest = New HttpRequest
httpRequest.Bind fakeRequest

Set app = New App
app.SetRequest httpRequest
app.Boot

AssertEquals "/", app.Route, "Both PATH_INFO and HTTP_X_ORIGINAL_URL '/Default.asp' should normalize to '/'"
End Sub

Sub Test_App_BootRequiresRequestToBeSet()
Dim app
Dim didRaise

Set app = New App
didRaise = False

On Error Resume Next
Err.Clear
app.Boot
If Err.Number <> 0 Then didRaise = True
Err.Clear
On Error Goto 0

AssertTrue didRaise, "Boot should raise if SetRequest was never called"
End Sub

RunTest "App: register + resolve a service", "Test_App_RegisterAndResolveService"
RunTest "App: resolving an unregistered service raises", "Test_App_ResolveUnregisteredServiceRaises"
RunTest "App: ServiceNames reflects registered services", "Test_App_ServiceNamesReflectsRegistered"
RunTest "App: re-registering a name replaces, does not duplicate", "Test_App_RegisterInstanceReplacesExisting"
RunTest "App: Boot prefers HTTP_X_ORIGINAL_URL over PATH_INFO", "Test_App_BootPrefersOriginalUrlOverPathInfo"
RunTest "App: Boot falls back to PATH_INFO when original URL is absent", "Test_App_BootFallsBackToPathInfoWhenOriginalUrlMissing"
RunTest "App: Boot normalizes '/Default.asp' to '/'", "Test_App_BootNormalizesDefaultAspToRoot"
RunTest "App: Boot requires a bound request", "Test_App_BootRequiresRequestToBeSet"

+ 128
- 0
tests/ControllerTests.vbs Wyświetl plik

@@ -0,0 +1,128 @@
Sub Test_HomeController_ServiceSummaryListsRegisteredServices()
Dim app
Dim controller

Set app = New App
app.RegisterInstance "clockService", New FakeService
app.RegisterInstance "widgetService", New FakeService

Set controller = New HomeController
controller.Init app, Nothing
controller.Execute

AssertEquals "clockService, widgetService", controller.ServiceSummary, "ServiceSummary should comma-join registered service names in registration order"
End Sub

Sub Test_HomeController_ServiceSummaryWhenNoneRegistered()
Dim app
Dim controller

Set app = New App

Set controller = New HomeController
controller.Init app, Nothing
controller.Execute

AssertEquals "None", controller.ServiceSummary, "ServiceSummary should say 'None' when nothing is registered"
End Sub

Sub Test_GreetController_UsesCapturedNameParam()
Dim app
Dim controller
Dim params

Set app = New App
Set params = CreateObject("Scripting.Dictionary")
params.Add "name", "Ada"

Set controller = New GreetController
controller.Init app, params
controller.Execute

AssertEquals "Ada", controller.Name, "GreetController should use the captured 'name' param"
End Sub

Sub Test_GreetController_DefaultsNameWhenMissing()
Dim app
Dim controller
Dim params

Set app = New App
Set params = CreateObject("Scripting.Dictionary")

Set controller = New GreetController
controller.Init app, params
controller.Execute

AssertEquals "there", controller.Name, "GreetController should default to 'there' when no name was captured"
End Sub

Sub Test_GreetController_DefaultsNameWhenParamsIsNothing()
Dim app
Dim controller

Set app = New App

Set controller = New GreetController
controller.Init app, Nothing
controller.Execute

AssertEquals "there", controller.Name, "GreetController should tolerate routeParams being Nothing"
End Sub

Sub Test_GreetController_CapturesOptionalIdParam()
Dim app
Dim controller
Dim params

Set app = New App
Set params = CreateObject("Scripting.Dictionary")
params.Add "name", "Ada"
params.Add "id", "42"

Set controller = New GreetController
controller.Init app, params
controller.Execute

AssertTrue controller.HasId, "HasId should be True when the route captured an 'id'"
AssertEquals "42", controller.Id, "GreetController should expose the captured 'id' param"
End Sub

Sub Test_GreetController_HasIdFalseWhenIdMissing()
Dim app
Dim controller
Dim params

Set app = New App
Set params = CreateObject("Scripting.Dictionary")
params.Add "name", "Ada"

Set controller = New GreetController
controller.Init app, params
controller.Execute

AssertFalse controller.HasId, "HasId should be False when the route did not capture an 'id'"
End Sub

Sub Test_NotFoundController_SetsResponseStatus()
Dim app
Dim controller

Set app = New App
Response.Status = "200 OK"

Set controller = New NotFoundController
controller.Init app, Nothing
controller.Execute

AssertEquals "404 Not Found", Response.Status, "NotFoundController should set a 404 status"
End Sub

RunTest "HomeController: ServiceSummary lists registered services", "Test_HomeController_ServiceSummaryListsRegisteredServices"
RunTest "HomeController: ServiceSummary says 'None' when empty", "Test_HomeController_ServiceSummaryWhenNoneRegistered"
RunTest "GreetController: uses captured name param", "Test_GreetController_UsesCapturedNameParam"
RunTest "GreetController: defaults name when missing", "Test_GreetController_DefaultsNameWhenMissing"
RunTest "GreetController: defaults name when params is Nothing", "Test_GreetController_DefaultsNameWhenParamsIsNothing"
RunTest "GreetController: captures optional id param", "Test_GreetController_CapturesOptionalIdParam"
RunTest "GreetController: HasId is False when id is missing", "Test_GreetController_HasIdFalseWhenIdMissing"
RunTest "NotFoundController: sets 404 response status", "Test_NotFoundController_SetsResponseStatus"

+ 85
- 0
tests/DispatcherTests.vbs Wyświetl plik

@@ -0,0 +1,85 @@
Sub Test_Dispatcher_NoMatchDispatchesToNotFound()
Dim app
Dim dispatcher
Dim controllerKey
Dim controller

Set app = New App
Set dispatcher = New Dispatcher
Response.Status = "200 OK"

dispatcher.Dispatch app, Nothing, controllerKey, controller

AssertEquals "NotFound", controllerKey, "A Nothing route match should dispatch to NotFound"
AssertEquals "404 Not Found", Response.Status, "NotFoundController.Execute should have run and set a 404 status"
End Sub

Sub Test_Dispatcher_UnknownControllerNameFallsBackToNotFound()
Dim app
Dim dispatcher
Dim controllerKey
Dim controller
Dim routeMatch
Dim params

Set app = New App
Set dispatcher = New Dispatcher
Set params = CreateObject("Scripting.Dictionary")
Set routeMatch = CreateObject("Scripting.Dictionary")
routeMatch.Add "ControllerName", "SomethingUnregistered"
routeMatch.Add "Params", params
Response.Status = "200 OK"

dispatcher.Dispatch app, routeMatch, controllerKey, controller

AssertEquals "NotFound", controllerKey, "An unrecognized controller key should fall back to NotFound"
AssertEquals "404 Not Found", Response.Status, "The NotFound fallback should still run Execute"
End Sub

Sub Test_Dispatcher_DispatchesToHome()
Dim app
Dim dispatcher
Dim controllerKey
Dim controller
Dim routeMatch
Dim params

Set app = New App
Set dispatcher = New Dispatcher
Set params = CreateObject("Scripting.Dictionary")
Set routeMatch = CreateObject("Scripting.Dictionary")
routeMatch.Add "ControllerName", "Home"
routeMatch.Add "Params", params

dispatcher.Dispatch app, routeMatch, controllerKey, controller

AssertEquals "Home", controllerKey, "A Home route match should dispatch to Home"
AssertEquals "None", controller.ServiceSummary, "The dispatched controller should be a working HomeController"
End Sub

Sub Test_Dispatcher_DispatchesToGreetWithParams()
Dim app
Dim dispatcher
Dim controllerKey
Dim controller
Dim routeMatch
Dim params

Set app = New App
Set dispatcher = New Dispatcher
Set params = CreateObject("Scripting.Dictionary")
params.Add "name", "Ada"
Set routeMatch = CreateObject("Scripting.Dictionary")
routeMatch.Add "ControllerName", "Greet"
routeMatch.Add "Params", params

dispatcher.Dispatch app, routeMatch, controllerKey, controller

AssertEquals "Greet", controllerKey, "A Greet route match should dispatch to Greet"
AssertEquals "Ada", controller.Name, "The dispatched GreetController should receive the captured 'name' param"
End Sub

RunTest "Dispatcher: no route match dispatches to NotFound", "Test_Dispatcher_NoMatchDispatchesToNotFound"
RunTest "Dispatcher: unknown controller name falls back to NotFound", "Test_Dispatcher_UnknownControllerNameFallsBackToNotFound"
RunTest "Dispatcher: dispatches to Home", "Test_Dispatcher_DispatchesToHome"
RunTest "Dispatcher: dispatches to Greet with params", "Test_Dispatcher_DispatchesToGreetWithParams"

+ 76
- 0
tests/HttpRequestTests.vbs Wyświetl plik

@@ -0,0 +1,76 @@
Sub Test_HttpRequest_IsBoundReflectsBindState()
Dim httpRequest

Set httpRequest = New HttpRequest

AssertFalse httpRequest.IsBound, "IsBound should be False before Bind"

httpRequest.Bind New FakeAspRequest

AssertTrue httpRequest.IsBound, "IsBound should be True after Bind"
End Sub

Sub Test_HttpRequest_ProxiesServerVariable()
Dim httpRequest
Dim fakeRequest

Set fakeRequest = New FakeAspRequest
fakeRequest.SetServerVariable "SERVER_NAME", "example.local"

Set httpRequest = New HttpRequest
httpRequest.Bind fakeRequest

AssertEquals "example.local", httpRequest.ServerVariable("SERVER_NAME"), "ServerVariable should proxy to the bound request"
End Sub

Sub Test_HttpRequest_ProxiesQueryStringAndForm()
Dim httpRequest
Dim fakeRequest

Set fakeRequest = New FakeAspRequest
fakeRequest.SetQueryString "q", "hello"
fakeRequest.SetForm "f", "world"

Set httpRequest = New HttpRequest
httpRequest.Bind fakeRequest

AssertEquals "hello", httpRequest.QueryString("q"), "QueryString should proxy to the bound request"
AssertEquals "world", httpRequest.Form("f"), "Form should proxy to the bound request"
End Sub

Sub Test_HttpRequest_ItemUsesRequestDefaultLookup()
Dim httpRequest
Dim fakeRequest

Set fakeRequest = New FakeAspRequest
fakeRequest.SetForm "name", "Ada"

Set httpRequest = New HttpRequest
httpRequest.Bind fakeRequest

AssertEquals "Ada", httpRequest.Item("name"), "Item should fall through to the request's default lookup"
End Sub

Sub Test_HttpRequest_AccessingBeforeBindRaises()
Dim httpRequest
Dim discard
Dim didRaise

Set httpRequest = New HttpRequest
didRaise = False

On Error Resume Next
Err.Clear
discard = httpRequest.ServerVariable("SERVER_NAME")
If Err.Number <> 0 Then didRaise = True
Err.Clear
On Error Goto 0

AssertTrue didRaise, "Accessing an unbound HttpRequest should raise"
End Sub

RunTest "HttpRequest: IsBound reflects Bind state", "Test_HttpRequest_IsBoundReflectsBindState"
RunTest "HttpRequest: ServerVariable proxies to bound request", "Test_HttpRequest_ProxiesServerVariable"
RunTest "HttpRequest: QueryString and Form proxy to bound request", "Test_HttpRequest_ProxiesQueryStringAndForm"
RunTest "HttpRequest: Item uses the request's default lookup", "Test_HttpRequest_ItemUsesRequestDefaultLookup"
RunTest "HttpRequest: accessing before Bind raises", "Test_HttpRequest_AccessingBeforeBindRaises"

+ 118
- 0
tests/RouterTests.vbs Wyświetl plik

@@ -0,0 +1,118 @@
Sub Test_Router_MatchesExactRoot()
Dim router
Dim result

Set router = New Router
router.AddRoute "/", "Home"

Set result = router.Match("/")

AssertNotNothing result, "Router should match the exact '/' route"
AssertEquals "Home", result.Item("ControllerName"), "Matched controller name for '/'"
End Sub

Sub Test_Router_CapturesNamedParam()
Dim router
Dim result

Set router = New Router
router.AddRoute "/greet/{name}", "Greet"

Set result = router.Match("/greet/Ada")

AssertNotNothing result, "Router should match a pattern route"
AssertEquals "Greet", result.Item("ControllerName"), "Matched controller name for pattern route"
AssertTrue result.Item("Params").Exists("name"), "Captured params should contain 'name'"
AssertEquals "Ada", result.Item("Params").Item("name"), "Captured 'name' param value"
End Sub

Sub Test_Router_ReturnsNothingWhenNoRouteMatches()
Dim router
Dim result

Set router = New Router
router.AddRoute "/", "Home"
router.AddRoute "/greet/{name}", "Greet"

Set result = router.Match("/does-not-exist")

AssertIsNothing result, "Router should return Nothing when no pattern matches"
End Sub

Sub Test_Router_SegmentCountMustMatch()
Dim router
Dim result

Set router = New Router
router.AddRoute "/greet/{name}", "Greet"

Set result = router.Match("/greet/Ada/extra")

AssertIsNothing result, "An extra path segment should not match a shorter pattern"
End Sub

Sub Test_Router_LiteralSegmentsMatchCaseInsensitively()
Dim router
Dim result

Set router = New Router
router.AddRoute "/greet/{name}", "Greet"

Set result = router.Match("/GREET/Ada")

AssertNotNothing result, "Literal segments should match regardless of case"
AssertEquals "Ada", result.Item("Params").Item("name"), "Param value should preserve original case"
End Sub

Sub Test_Router_FirstRegisteredMatchWins()
Dim router
Dim result

Set router = New Router
router.AddRoute "/greet/world", "SpecificGreet"
router.AddRoute "/greet/{name}", "Greet"

Set result = router.Match("/greet/world")

AssertEquals "SpecificGreet", result.Item("ControllerName"), "More specific route registered first should win"
End Sub

Sub Test_Router_CapturesMultipleNamedParams()
Dim router
Dim result

Set router = New Router
router.AddRoute "/greet/{name}/{id}", "Greet"

Set result = router.Match("/greet/Ada/42")

AssertNotNothing result, "Router should match a pattern with two {param} segments"
AssertEquals "Ada", result.Item("Params").Item("name"), "First captured param value"
AssertEquals "42", result.Item("Params").Item("id"), "Second captured param value"
End Sub

Sub Test_Router_SameControllerDifferentSegmentCountsCoexist()
Dim router
Dim shortResult
Dim longResult

Set router = New Router
router.AddRoute "/greet/{name}", "Greet"
router.AddRoute "/greet/{name}/{id}", "Greet"

Set shortResult = router.Match("/greet/Ada")
Set longResult = router.Match("/greet/Ada/42")

AssertFalse shortResult.Item("Params").Exists("id"), "The one-segment route should not capture an 'id'"
AssertTrue longResult.Item("Params").Exists("id"), "The two-segment route should capture an 'id'"
AssertEquals "42", longResult.Item("Params").Item("id"), "The two-segment route's 'id' value"
End Sub

RunTest "Router matches the exact root route", "Test_Router_MatchesExactRoot"
RunTest "Router captures a named {param} segment", "Test_Router_CapturesNamedParam"
RunTest "Router returns Nothing when nothing matches", "Test_Router_ReturnsNothingWhenNoRouteMatches"
RunTest "Router requires matching segment counts", "Test_Router_SegmentCountMustMatch"
RunTest "Router matches literal segments case-insensitively", "Test_Router_LiteralSegmentsMatchCaseInsensitively"
RunTest "Router: first registered match wins", "Test_Router_FirstRegisteredMatchWins"
RunTest "Router captures multiple {param} segments in one pattern", "Test_Router_CapturesMultipleNamedParams"
RunTest "Router: routes with different segment counts to the same controller coexist", "Test_Router_SameControllerDifferentSegmentCountsCoexist"

+ 46
- 0
tests/RunTests.vbs Wyświetl plik

@@ -0,0 +1,46 @@
Option Explicit

Dim fso
Dim testsDir
Dim repoRoot
Dim coreDir
Dim bootStream
Dim frameworkCode

Set fso = CreateObject("Scripting.FileSystemObject")

testsDir = fso.GetParentFolderName(WScript.ScriptFullName) & "\"
repoRoot = fso.GetParentFolderName(Left(testsDir, Len(testsDir) - 1)) & "\"
coreDir = repoRoot & "Core\"

' Bootstrap: load the framework/fakes before any helper it defines (like
' IncludeAspClass) exists to be called.
Set bootStream = fso.OpenTextFile(testsDir & "TestSupport.vbs", 1)
frameworkCode = bootStream.ReadAll
bootStream.Close
ExecuteGlobal frameworkCode

' --- Core classes (ASP files - strip <% %> before executing) ---
IncludeAspClass coreDir & "App.asp"
IncludeAspClass coreDir & "Router.asp"
IncludeAspClass coreDir & "HttpRequest.asp"
IncludeAspClass coreDir & "ClockService.asp"
IncludeAspClass coreDir & "Controllers\HomeController.asp"
IncludeAspClass coreDir & "Controllers\GreetController.asp"
IncludeAspClass coreDir & "Controllers\NotFoundController.asp"
IncludeAspClass coreDir & "Dispatcher.asp"

' --- Test suites ---
IncludeVbsFile testsDir & "RouterTests.vbs"
IncludeVbsFile testsDir & "AppTests.vbs"
IncludeVbsFile testsDir & "HttpRequestTests.vbs"
IncludeVbsFile testsDir & "ControllerTests.vbs"
IncludeVbsFile testsDir & "DispatcherTests.vbs"

PrintSummary

If HasFailures() Then
WScript.Quit 1
Else
WScript.Quit 0
End If

+ 278
- 0
tests/TestSupport.vbs Wyświetl plik

@@ -0,0 +1,278 @@
Option Explicit

Dim g_TestsRun
Dim g_TestsPassed
Dim g_Failures

g_TestsRun = 0
g_TestsPassed = 0
Set g_Failures = CreateObject("Scripting.Dictionary")

Dim ASSERTION_FAILED
ASSERTION_FAILED = vbObjectError + 1999

' Runs the named zero-argument Sub/Function, treating any unhandled error
' (including one raised by a failed Assert*) as a failed test. This mirrors
' exactly the On Error Resume Next / Err.Number pattern already used in
' Public/Default.asp - an error raised in a called procedure with no error
' handling of its own is caught by the caller's active On Error Resume Next.
Sub RunTest(ByVal testName, ByVal procName)
g_TestsRun = g_TestsRun + 1

On Error Resume Next
Err.Clear
Execute procName & "()"

If Err.Number <> 0 Then
RecordFailure testName, Err.Description
Else
g_TestsPassed = g_TestsPassed + 1
End If
On Error Goto 0
End Sub

Sub RecordFailure(ByVal testName, ByVal message)
g_Failures.Add g_Failures.Count, testName & " -- " & message
End Sub

Sub PrintSummary()
Dim failureKey

WScript.Echo ""
WScript.Echo "----------------------------------------"
WScript.Echo g_TestsPassed & " / " & g_TestsRun & " tests passed"

If g_Failures.Count > 0 Then
WScript.Echo ""
WScript.Echo "Failures:"
For Each failureKey In g_Failures.Keys
WScript.Echo " - " & g_Failures.Item(failureKey)
Next
End If

WScript.Echo "----------------------------------------"
End Sub

Function HasFailures()
HasFailures = (g_Failures.Count > 0)
End Function

' --- Assertions ---

Sub AssertEquals(ByVal expected, ByVal actual, ByVal message)
If Not (expected = actual) Then
Fail message & " (expected [" & CStr(expected) & "] but got [" & CStr(actual) & "])"
End If
End Sub

Sub AssertTrue(ByVal condition, ByVal message)
If Not condition Then
Fail message & " (expected True)"
End If
End Sub

Sub AssertFalse(ByVal condition, ByVal message)
If condition Then
Fail message & " (expected False)"
End If
End Sub

Sub AssertIsNothing(ByVal value, ByVal message)
If Not (value Is Nothing) Then
Fail message & " (expected Nothing)"
End If
End Sub

Sub AssertNotNothing(ByVal value, ByVal message)
If value Is Nothing Then
Fail message & " (expected an object, got Nothing)"
End If
End Sub

Sub Fail(ByVal message)
Err.Raise ASSERTION_FAILED, "Assert", message
End Sub

' --- Loading Core/*.asp class files outside IIS ---
' These files are plain "<% Class ... End Class %>" blocks with no mixed HTML
' and (deliberately - see CLAUDE.md) no Option Explicit of their own, so
' stripping the ASP delimiters and running the remainder through ExecuteGlobal
' loads the same class definitions IIS would compile, without needing IIS.

Sub IncludeAspClass(ByVal path)
Dim raw
Dim code
Dim openPos
Dim closePos

raw = ReadFileText(path)
openPos = InStr(raw, "<%")
closePos = InStrRev(raw, "%>")

If openPos = 0 Or closePos = 0 Or closePos <= openPos Then
Err.Raise vbObjectError + 1998, "IncludeAspClass", "No <% %> block found in: " & path
End If

code = Mid(raw, openPos + 2, closePos - (openPos + 2))
ExecuteGlobal code
End Sub

Sub IncludeVbsFile(ByVal path)
ExecuteGlobal ReadFileText(path)
End Sub

Function ReadFileText(ByVal path)
Dim fso
Dim stream

Set fso = CreateObject("Scripting.FileSystemObject")
Set stream = fso.OpenTextFile(path, 1)
ReadFileText = stream.ReadAll
stream.Close
End Function

' --- ASP intrinsic stand-ins ---
' Global variables literally named Server/Response so Core/*.asp's
' "Server.CreateObject(...)" / "Server.HTMLEncode(...)" / "Response.Status = ..."
' resolve to these instead of the real (IIS-only) intrinsics.

Function RealCreateObject(ByVal progId)
Set RealCreateObject = CreateObject(progId)
End Function

Function SimpleHtmlEncode(ByVal text)
Dim result
result = CStr(text)
result = Replace(result, "&", "&amp;")
result = Replace(result, "<", "&lt;")
result = Replace(result, ">", "&gt;")
result = Replace(result, """", "&quot;")
SimpleHtmlEncode = result
End Function

Class FakeServer
Public Function CreateObject(ByVal progId)
Set CreateObject = RealCreateObject(progId)
End Function

Public Function HTMLEncode(ByVal text)
HTMLEncode = SimpleHtmlEncode(text)
End Function

Public Function MapPath(ByVal relativePath)
MapPath = "C:\fake-physical-root" & relativePath
End Function
End Class

Class FakeResponse
Public Status
Public Buffer
Public ContentType

Private m_Output

Private Sub Class_Initialize()
Status = "200 OK"
Buffer = False
ContentType = ""
m_Output = ""
End Sub

Public Sub Write(ByVal text)
m_Output = m_Output & text
End Sub

Public Property Get Output()
Output = m_Output
End Property
End Class

' A neutral dummy object for tests that just need "some object" to register
' as a service - not a stand-in for any real ASP intrinsic.
Class FakeService
Public Marker
End Class

' A stand-in for the real ASP Request object that Core/HttpRequest.asp's
' Bind() expects - only implements what the current code paths touch.
Class FakeAspRequest
Private m_ServerVariables
Private m_QueryString
Private m_Form
Private m_Cookies

Private Sub Class_Initialize()
Set m_ServerVariables = CreateObject("Scripting.Dictionary")
Set m_QueryString = CreateObject("Scripting.Dictionary")
Set m_Form = CreateObject("Scripting.Dictionary")
Set m_Cookies = CreateObject("Scripting.Dictionary")
End Sub

Public Sub SetServerVariable(ByVal key, ByVal value)
m_ServerVariables.Item(CStr(key)) = value
End Sub

Public Sub SetQueryString(ByVal key, ByVal value)
m_QueryString.Item(CStr(key)) = value
End Sub

Public Sub SetForm(ByVal key, ByVal value)
m_Form.Item(CStr(key)) = value
End Sub

Public Sub SetCookie(ByVal key, ByVal value)
m_Cookies.Item(CStr(key)) = value
End Sub

Public Function ServerVariables(ByVal key)
If m_ServerVariables.Exists(CStr(key)) Then
ServerVariables = m_ServerVariables.Item(CStr(key))
Else
ServerVariables = ""
End If
End Function

Public Function QueryString(ByVal key)
If m_QueryString.Exists(CStr(key)) Then
QueryString = m_QueryString.Item(CStr(key))
Else
QueryString = ""
End If
End Function

Public Function Form(ByVal key)
If m_Form.Exists(CStr(key)) Then
Form = m_Form.Item(CStr(key))
Else
Form = ""
End If
End Function

Public Function Cookies(ByVal key)
If m_Cookies.Exists(CStr(key)) Then
Cookies = m_Cookies.Item(CStr(key))
Else
Cookies = ""
End If
End Function

Public Default Function Item(ByVal key)
If m_QueryString.Exists(CStr(key)) Then
Item = m_QueryString.Item(CStr(key))
ElseIf m_Form.Exists(CStr(key)) Then
Item = m_Form.Item(CStr(key))
ElseIf m_Cookies.Exists(CStr(key)) Then
Item = m_Cookies.Item(CStr(key))
ElseIf m_ServerVariables.Exists(CStr(key)) Then
Item = m_ServerVariables.Item(CStr(key))
Else
Item = ""
End If
End Function
End Class

Dim Server
Dim Response

Set Server = New FakeServer
Set Response = New FakeResponse

+ 3
- 0
tests/run-tests.cmd Wyświetl plik

@@ -0,0 +1,3 @@
@echo off
cscript //nologo "%~dp0RunTests.vbs"
exit /b %ERRORLEVEL%

+ 5
- 0
tests/run-tests.ps1 Wyświetl plik

@@ -0,0 +1,5 @@
$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
$runner = Join-Path $scriptDir 'RunTests.vbs'

cscript //nologo $runner
exit $LASTEXITCODE

Ładowanie…
Anuluj
Zapisz

Powered by TurnKey Linux.