SaveFileHandler Documentation

Overview

The SaveFileHandler class is an HTTP handler that enables authenticated users to save or update file contents in their website's file structure. It provides a secure API endpoint for writing text-based file content, typically used by the web-based code editor to persist changes made to HTML, CSS, JavaScript, PHP, and other text files.


Purpose

This handler is part of the multi-tenant hosting service's file management system, allowing users to edit and save files through the web interface. It handles both creating new files and updating existing ones, making it a critical component of the online editing workflow.


Core Functionality

This handler performs the following key operations:


1. Request Validation


2. Authentication & Authorization


3. Content Normalization


4. Path Construction

The handler builds the target file path based on the authenticated user:

Where:


5. File Writing


6. Response Messages

Returns different responses based on the operation result:


Request Format

Method: POST

Content-Type: application/json

Body:

{
  "path": "relative/directory/path/",
  "filename": "file.ext",
  "content": "The complete file content as a string"
}

The path value should be relative to the user's static directory and may be empty for files in the root. The filename is the name of the file to save. The content is the complete text content to write to the file.


Response Format

Success Response:

Error Response:


Response Codes


Security Features


Error Handling


Logging

The handler provides console logging for debugging:


Use Cases

This handler is typically invoked when users:


Dependencies

This handler relies on:


Behavior Notes


Line Ending Normalization

The handler automatically converts Windows-style line endings (CRLF: \r\n) to Unix-style line endings (LF: \n). This ensures:


Performance Considerations


Potential Improvements

flowchart TD A(["Start handle"]) --> B{"Request method
== POST?"} B -->|No| C["Return silently
(No response sent)"] B -->|Yes| D["Read request body
with UTF-8 encoding"] C --> Z(["End"]) D --> E["Get sessionId via
getJavaSessionId(exchange)"] E --> F["Get username from
SessionManager.getUsername(sessionId)"] F --> G{"Username
is null?"} G -->|Yes| H["Log: 'Rejected: session not valid'
Send 403 Forbidden
Close response body"] G -->|No| I["Parse JSON body
using parseJsonToMap"] H --> Z I --> J["Extract content from map
Normalize line endings:
content = map.get('content').replace('\\r\\n', '\\n')"] J --> K["Build base userPath:
USER_DIR + user + /static/"] K --> L{"Username ==
ADMIN_HOST?"} L -->|Yes| M["Override userPath:
ADMIN_DIR + /static/"] L -->|No| N["Keep user base path"] M --> O N --> O O{"map.get(path) not
empty and not null?"} O -->|Yes| P["Append path to userPath"] O -->|No| Q["Keep userPath as is"] P --> R["Append filename:
userPath += map.get('filename')"] Q --> R R --> S["Call writeFile(userPath, content)"] S --> T{"writeFile returned
true (success)?"} T -->|Yes| U["Set msg = 'Success'
Convert to bytes"] T -->|No| V["Send 403 Forbidden
Close response body"] V --> Z U --> W["Set Content-Type header:
text/plain; charset=UTF-8"] W --> X["Send 200 response
with response.length"] X --> Y1["Write response bytes
to OutputStream"] Y1 --> Y2["Close OutputStream"] Y2 --> Z