EzyHTTP Serves Static Files

Updated at 1783611087000
EzyHTTP can serve static files such as HTML, CSS, JavaScript, images, fonts, WebAssembly files, or other downloadable assets. The feature is disabled by default, so an application only exposes static resources when you explicitly enable it.
Static file serving in EzyHTTP is designed around two ideas:
  • only files discovered from configured resource locations are mapped to public URLs;
  • file content is streamed asynchronously so long downloads do not keep normal request handler threads busy.

Why Static Files Are Served Asynchronously

Serving a normal JSON response is usually quick: the controller returns an object, EzyHTTP serializes it, writes the response, and the request thread can handle the next request.
Static files are different. A client may download a large image, video, archive, or WebAssembly bundle. If every download keeps one request handling thread blocked until the whole file is written, a small number of slow clients can occupy the server's worker threads for a long time.
For example, imagine the server has 8 request handler threads and one client starts 8 large downloads at the same time. In a fully blocking model, those 8 threads can all be busy writing file data. Other clients may have to wait even if their requests are small.
EzyHTTP avoids that by returning an asynchronous response for static resources. The request is accepted, the servlet async context is used, and file streaming is delegated to a resource download manager. That manager keeps a queue of download entries and a dedicated worker pool. Each worker reads a chunk from an input stream, writes that chunk to the response output stream, and puts unfinished downloads back into the queue.
In simplified form, the download loop behaves like this:
take a download task from the queue
read a small chunk from the file input stream
write the chunk to the HTTP response output stream

if the file is finished:
    close the input stream
    complete the async response
else:
    put the task back into the queue
This keeps static resource transfer separate from normal controller execution. It also gives multiple downloads a chance to make progress instead of letting one large response monopolize a worker for the whole transfer.

Enable Static Resources

Static resources are disabled by default. To enable them in application.properties, add:
resources.enable=true
The same setting in application.yaml is:
resources:
  enable: true
When resources.enable is true and no custom location is configured, EzyHTTP looks for resources under:
static
In a Maven or Gradle project this usually means:
src/main/resources/static
For example:
src/main/resources/static/index.html
src/main/resources/static/css/style.css
src/main/resources/static/js/main.js
src/main/resources/static/images/logo.png
These files are exposed as:
/index.html
/css/style.css
/js/main.js
/images/logo.png
The configured resource location itself is not part of the public URL. It is only the root used to discover files.

Configure Resource Locations

Use resources.location when you only need one location:
resources.location=public
With this configuration:
src/main/resources/public/css/style.css
is served as:
/css/style.css
Use resources.locations when you want to scan multiple locations:
resources.locations=static,public,assets
In YAML:
resources:
  enable: true
  locations:
    - static
    - public
    - assets
If resources.locations is present, it takes priority over resources.location.
EzyHTTP can discover resources from normal folders during development and from classpath or JAR resources after the application is packaged. That means the same configuration works both when running from an IDE and when running from a built application artifact.

How URLs Are Created

For each discovered file, EzyHTTP removes the configured location prefix and uses the remaining path as the request URI.
Example with resources.location=static:
File path under the resource locationPublic URI
static/index.html/index.html
static/css/style.css/css/style.css
static/js/main.js/js/main.js
static/docs/getting-started.html/docs/getting-started.html
Only real files are registered. Directories are used for traversal, but they are not mapped as downloadable resources.
By default, EzyHTTP accepts file paths that look like normal static asset paths: letters, digits, underscores, hyphens, dots, slashes, and backslashes, ending with a file extension. This keeps the mapping focused on files such as .html, .css, .js, .png, .jpg, .svg, .wasm, .gz, and similar assets.

Filter Files With A Pattern

If you want to expose only a subset of files, configure resources.pattern with a regular expression.
Example: serve only CSS and JavaScript files:
resources.pattern=.*.(css|js)
Example: serve only files inside static/public:
resources.pattern=static/public/.*..+
The pattern is matched against the resource path discovered from the configured location. EzyHTTP also normalizes Windows-style separators to / when checking the pattern, so the same rule can work across operating systems.

Request Handling Flow

When static resources are enabled, EzyHTTP builds a GET handler for each discovered file during application startup.
The runtime flow is:
flowchart TD
    A[Application starts] --> B{resources.enable?}
    B -- false --> C[No static resource handlers]
    B -- true --> D[Resolve resource locations]
    D --> E[Scan folders, classpath resources, or JAR entries]
    E --> F[Create one GET mapping per file]
    F --> G[Client requests a static URI]
    G --> H[Open resource input stream]
    H --> I[Set content type and response headers]
    I --> J[Stream file through async download manager]
    J --> K[Complete async response]
If a mapped file cannot be opened at request time, EzyHTTP returns a not found response. If an unexpected streaming error happens after the response starts, the async response is completed and the status is set to an internal server error.

Content Type And Content Encoding

EzyHTTP chooses the response content type from the file extension.
Typical examples:
FileExpected response type
index.htmltext/html
style.csstext/css
main.jsJavaScript content type
image.pngPNG image content type
file.gzgzip content type
There is also special handling for assets whose extension carries both the real file type and the compression extension. For example:
resource.wasm.gz
is treated as a WebAssembly resource with gzip content encoding. This lets a client receive the correct actual content type while still knowing that the transferred bytes are gzip encoded.

Range Requests

EzyHTTP supports HTTP byte range downloads for static resources. If the request includes a Range header, the server reads only the requested byte range and responds with partial content.
Example request header:
Range: bytes=0-1023
The response includes headers such as:
Accept-Ranges: bytes
Content-Range: bytes 0-1023/FILE_SIZE
Content-Length: 1024
This is useful for large files, resumable downloads, media playback, and clients that only need part of a resource.
If the range has a start but no explicit end, EzyHTTP reads a bounded chunk from that start position instead of blindly streaming the rest of the file. The current maximum chunk length for this open-ended range path is 2 MB.
Example:
Range: bytes=1048576-
The server starts at byte 1048576 and returns at most one bounded chunk, adjusted if the file ends earlier.

Download Manager Settings

Static file downloads use the resource download manager. You can tune it with these properties:
PropertyDefaultMeaning
resources.download.capacity100000Maximum number of queued download tasks.
resources.download.thread_pool_sizeavailable processorsNumber of worker threads used for static download streaming.
resources.download.buffer_size1024Number of bytes each worker attempts to read and write per step.
Example:
resources.enable=true
resources.download.capacity=20000
resources.download.thread_pool_size=8
resources.download.buffer_size=4096
Larger buffers can reduce loop overhead for big files, while smaller buffers can make scheduling between many concurrent downloads more granular. The best value depends on your file sizes, network behavior, and server resources.

Async Timeout

Static resource handlers use servlet asynchronous processing. EzyHTTP also supports a general async timeout setting:
async.default_timeout=10000
In YAML:
async:
  default_timeout: 10000
This value is in milliseconds. A value of 0 means EzyHTTP does not configure a default timeout itself.

Security Notes

EzyHTTP does not expose arbitrary filesystem paths. It scans the configured resource locations at startup and registers handlers only for files that are discovered and accepted by the file path pattern.
That means a request cannot simply walk to any file outside the configured static locations. For example, if only static is configured, a request URI such as /../../secret.txt is not a registered resource mapping.
For production applications, it is still a good practice to:
  • put only public assets inside static resource folders;
  • use resources.pattern when only part of a folder should be public;
  • avoid mixing private files and public files in the same resource location;
  • serve very large or high-traffic assets from a CDN or object storage when that better fits your deployment.

Complete Example

Project structure:
src/main/resources/static/index.html
src/main/resources/static/css/style.css
src/main/resources/static/js/main.js
src/main/resources/static/wasm/app.wasm.gz
Configuration:
resources.enable=true
resources.location=static
resources.download.thread_pool_size=4
resources.download.buffer_size=4096
Generated GET mappings:
GET /index.html
GET /css/style.css
GET /js/main.js
GET /wasm/app.wasm.gz
When a browser requests /css/style.css, EzyHTTP opens the corresponding resource, sets the response content type from the file extension, streams the file through the async download manager, and completes the async response after the last bytes are written.

Summary

Static file serving in EzyHTTP is opt-in. Once enabled, the framework scans configured resource locations, turns discovered files into GET mappings, and streams file content asynchronously through a dedicated download manager.
This gives you a simple way to ship small and medium static assets with your application while keeping normal request handling threads available for controllers. For large public traffic, the same design can still be combined with external caching, CDN, or object storage depending on how your application is deployed.

Table Of Contents