diff --git a/doc/babel.config.js b/doc/babel.config.js
index e00595d..14e5baf 100644
--- a/doc/babel.config.js
+++ b/doc/babel.config.js
@@ -1,3 +1,3 @@
module.exports = {
- presets: [require.resolve('@docusaurus/core/lib/babel/preset')],
-};
+ presets: [require.resolve('@docusaurus/core/lib/babel/preset')]
+}
diff --git a/doc/docusaurus.config.js b/doc/docusaurus.config.js
index f46f7d9..48336c0 100644
--- a/doc/docusaurus.config.js
+++ b/doc/docusaurus.config.js
@@ -1,5 +1,5 @@
// @ts-check
-import { themes as prismThemes } from 'prism-react-renderer';
+import { themes as prismThemes } from 'prism-react-renderer'
/** @type {import('@docusaurus/types').Config} */
const config = {
@@ -18,7 +18,7 @@ const config = {
i18n: {
defaultLocale: 'en',
- locales: ['en'],
+ locales: ['en']
},
presets: [
@@ -28,24 +28,24 @@ const config = {
({
docs: {
sidebarPath: require.resolve('./sidebars.js'),
- editUrl: 'https://github.com/IntegerAlex/hasty-server/tree/master/doc',
+ editUrl: 'https://github.com/IntegerAlex/hasty-server/tree/master/doc'
},
blog: {
showReadingTime: true,
feedOptions: {
type: ['rss', 'atom'],
- xslt: true,
+ xslt: true
},
editUrl: 'https://github.com/IntegerAlex/hasty-server.git',
onInlineTags: 'warn',
onInlineAuthors: 'warn',
- onUntruncatedBlogPosts: 'warn',
+ onUntruncatedBlogPosts: 'warn'
},
theme: {
- customCss: require.resolve('./src/css/custom.css'),
- },
- }),
- ],
+ customCss: require.resolve('./src/css/custom.css')
+ }
+ })
+ ]
],
themeConfig:
@@ -54,34 +54,34 @@ const config = {
colorMode: {
defaultMode: 'dark',
disableSwitch: false,
- respectPrefersColorScheme: true,
+ respectPrefersColorScheme: true
},
announcementBar: {
id: 'welcome',
content: 'π Welcome to Hasty Server Docs! Explore and build amazing web servers.',
backgroundColor: '#4f46e5',
textColor: '#ffffff',
- isCloseable: true,
+ isCloseable: true
},
navbar: {
title: 'Hasty Server',
logo: {
alt: 'Hasty Server Logo',
- src: 'img/logo.svg',
+ src: 'img/logo.svg'
},
items: [
{
type: 'docSidebar',
sidebarId: 'docsSidebar',
position: 'left',
- label: 'Docs',
+ label: 'Docs'
},
{
href: 'https://github.com/IntegerAlex/hasty-server',
label: 'GitHub',
- position: 'right',
- },
- ],
+ position: 'right'
+ }
+ ]
},
footer: {
style: 'dark',
@@ -91,56 +91,56 @@ const config = {
items: [
{
label: 'Docs',
- to: '/docs/intro',
- },
- ],
+ to: '/docs/intro'
+ }
+ ]
},
{
title: 'Community',
items: [
{
label: 'GitHub',
- href: 'https://github.com/IntegerAlex/hasty-server.git',
- },
- ],
+ href: 'https://github.com/IntegerAlex/hasty-server.git'
+ }
+ ]
},
{
title: 'More',
items: [
{
label: 'GitHub',
- href: 'https://github.com/IntegerAlex/hasty-server',
- },
- ],
- },
+ href: 'https://github.com/IntegerAlex/hasty-server'
+ }
+ ]
+ }
],
- copyright: `Copyright Β© ${new Date().getFullYear()} Hasty Server.`,
+ copyright: `Copyright Β© ${new Date().getFullYear()} Hasty Server.`
},
prism: {
theme: {
...prismThemes.github,
plain: {
...prismThemes.github.plain,
- backgroundColor: '#f8fafc',
- },
+ backgroundColor: '#f8fafc'
+ }
},
darkTheme: {
...prismThemes.dracula,
plain: {
...prismThemes.dracula.plain,
- backgroundColor: '#1e293b',
- },
+ backgroundColor: '#1e293b'
+ }
},
- additionalLanguages: ['http', 'bash', 'json'],
+ additionalLanguages: ['http', 'bash', 'json']
},
docs: {
sidebar: {
hideable: true,
- autoCollapseCategories: true,
- },
+ autoCollapseCategories: true
+ }
},
- image: 'img/social-card.png',
- }),
-};
+ image: 'img/social-card.png'
+ })
+}
-export default config;
+export default config
diff --git a/doc/sidebars.js b/doc/sidebars.js
index 3710118..711aa66 100644
--- a/doc/sidebars.js
+++ b/doc/sidebars.js
@@ -1,6 +1,6 @@
/**
* Sidebar configuration for Hasty Server documentation
- *
+ *
* This file defines the navigation structure for the documentation site.
* The sidebar is organized into logical sections for easy navigation.
*/
@@ -18,10 +18,10 @@ const sidebars = {
collapsible: true,
collapsed: false,
items: [
- 'getting-started/quick-start',
- ],
+ 'getting-started/quick-start'
+ ]
},
-
+
// Guides section
{
type: 'category',
@@ -33,13 +33,13 @@ const sidebars = {
'guides/static-files',
'guides/error-handling',
- 'guides/limitations',
- ],
+ 'guides/limitations'
+ ]
},
-
+
// Additional standalone pages
- 'index',
- ],
-};
+ 'index'
+ ]
+}
-export default sidebars;
+export default sidebars
diff --git a/doc/src/components/HomepageFeatures/index.js b/doc/src/components/HomepageFeatures/index.js
index f7440b3..447f280 100644
--- a/doc/src/components/HomepageFeatures/index.js
+++ b/doc/src/components/HomepageFeatures/index.js
@@ -1,64 +1,64 @@
-import clsx from 'clsx';
-import Heading from '@theme/Heading';
-import styles from './styles.module.css';
+import clsx from 'clsx'
+import Heading from '@theme/Heading'
+import styles from './styles.module.css'
const FeatureList = [
{
title: 'High Performance',
description: 'Hasty Server is designed for speed. Itβs built using low-level networking APIs, ensuring minimal overhead and high concurrency. This makes it capable of handling thousands of requests per second with ease, perfect for high-traffic applications.',
- Svg: require('@site/static/img/undraw_docusaurus_mountain.svg').default,
+ Svg: require('@site/static/img/undraw_docusaurus_mountain.svg').default
},
{
title: 'Developer Friendly',
description: 'It offers a simple and intuitive API, inspired by Express.js. Whether youβre a beginner or an experienced developer, the clear structure and easy-to-use syntax allow you to build applications faster and with fewer bugs.',
- Svg: require('@site/static/img/undraw_docusaurus_mountain.svg').default,
+ Svg: require('@site/static/img/undraw_docusaurus_mountain.svg').default
},
{
title: 'Built-in Security',
description: 'Security is a top priority. Hasty Server includes built-in protections against common vulnerabilities such as XSS (Cross-Site Scripting), CSRF (Cross-Site Request Forgery), and SQL injection, helping you create secure applications without extra effort.',
- Svg: require('@site/static/img/undraw_docusaurus_mountain.svg').default,
+ Svg: require('@site/static/img/undraw_docusaurus_mountain.svg').default
},
{
title: 'Extensible Architecture',
description: 'With its middleware-based architecture, Hasty Server allows you to add custom plugins and middlewares. You can extend its functionality by adding your own handlers or integrating third-party libraries with ease.',
- Svg: require('@site/static/img/undraw_docusaurus_mountain.svg').default,
+ Svg: require('@site/static/img/undraw_docusaurus_mountain.svg').default
},
{
title: 'Lightweight & Minimal',
description: 'Despite its powerful features, Hasty Server remains lightweight and efficient. It minimizes resource consumption, making it ideal for resource-constrained environments such as microservices or serverless applications.',
- Svg: require('@site/static/img/undraw_docusaurus_mountain.svg').default,
+ Svg: require('@site/static/img/undraw_docusaurus_mountain.svg').default
},
{
title: 'Flexible Routing',
description: 'Hasty Server provides a flexible routing system, allowing you to define routes for different HTTP methods (GET, POST, PUT, DELETE, etc.). You can also create dynamic routes with parameters for even more customization.',
- Svg: require('@site/static/img/undraw_docusaurus_mountain.svg').default,
- },
-];
+ Svg: require('@site/static/img/undraw_docusaurus_mountain.svg').default
+ }
+]
-function Feature({Svg, title, description}) {
+function Feature ({ Svg, title, description }) {
return (
-
-
+
+
-
- );
+ )
}
-export default function HomepageFeatures() {
+export default function HomepageFeatures () {
return (
-
-
+
+
{FeatureList.map((props, idx) => (
))}
- );
+ )
}
diff --git a/src/lib/cors.js b/src/lib/cors.js
index 363cc1a..92f9c83 100644
--- a/src/lib/cors.js
+++ b/src/lib/cors.js
@@ -16,7 +16,7 @@ const DEFAULT_CORS_HEADERS = {
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
'Access-Control-Max-Age': '86400', // 24 hours
'Access-Control-Allow-Credentials': 'true'
-};
+}
/**
* Applies CORS headers to a response
@@ -25,17 +25,17 @@ const DEFAULT_CORS_HEADERS = {
* @param {Object} [customHeaders] - Custom CORS headers to merge with defaults
* @returns {void}
*/
-function applyCorsHeaders(response, enabled = true, customHeaders = {}) {
- if (!enabled) return;
+function applyCorsHeaders (response, enabled = true, customHeaders = {}) {
+ if (!enabled) return
- const headers = { ...DEFAULT_CORS_HEADERS, ...customHeaders };
+ const headers = { ...DEFAULT_CORS_HEADERS, ...customHeaders }
Object.entries(headers).forEach(([key, value]) => {
// Only set if explicitly provided in customHeaders or if not already set
if (customHeaders[key] || !response.headers[key]) {
- response.setHeader(key, value);
+ response.setHeader(key, value)
}
- });
+ })
}
/**
@@ -45,9 +45,9 @@ function applyCorsHeaders(response, enabled = true, customHeaders = {}) {
* @param {boolean} [enabled=true] - Whether CORS is enabled
* @returns {boolean} - True if this was a preflight request that was handled
*/
-function handlePreflight(request, response, enabled = true) {
+function handlePreflight (request, response, enabled = true) {
if (!enabled || request.method !== 'OPTIONS') {
- return false;
+ return false
}
applyCorsHeaders(response, true, {
@@ -55,14 +55,14 @@ function handlePreflight(request, response, enabled = true) {
DEFAULT_CORS_HEADERS['Access-Control-Allow-Methods'],
'Access-Control-Allow-Headers': request.headers['access-control-request-headers'] ||
DEFAULT_CORS_HEADERS['Access-Control-Allow-Headers']
- });
+ })
- response.status(204).send('');
- return true;
+ response.status(204).send('')
+ return true
}
module.exports = {
applyCorsHeaders,
handlePreflight,
DEFAULT_CORS_HEADERS
-};
+}
diff --git a/src/lib/httpParser.js b/src/lib/httpParser.js
index 4348221..f213335 100644
--- a/src/lib/httpParser.js
+++ b/src/lib/httpParser.js
@@ -23,7 +23,7 @@ const { findFirstBrac, HTTPbody, JSONbodyParser, queryParser } = require('./util
* @returns {Promise
} Parsed HTTP request object
* @throws {Error} If the request is malformed
*/
-async function httpParser(request, connection = {}) {
+async function httpParser (request, connection = {}) {
try {
/** @type {ParsedRequest} */
const req = {
@@ -34,21 +34,21 @@ async function httpParser(request, connection = {}) {
query: {},
ip: '127.0.0.1',
body: undefined
- };
+ }
// Convert buffer to string if necessary and handle empty requests
- const requestString = (request && request.toString) ? request.toString() : '';
+ const requestString = (request && request.toString) ? request.toString() : ''
// Set client IP address with fallback
if (connection && connection.remoteAddress) {
- req.ip = connection.remoteAddress;
+ req.ip = connection.remoteAddress
}
// Step 1: Split the request into headers and body by finding "\r\n\r\n"
// Split into headers and body parts
- const headerBodySplit = requestString.split('\r\n\r\n');
+ const headerBodySplit = requestString.split('\r\n\r\n')
if (headerBodySplit.length === 0 || !headerBodySplit[0]) {
- throw new Error('Invalid HTTP request: Missing headers');
+ throw new Error('Invalid HTTP request: Missing headers')
}
const headersPart = headerBodySplit[0] // First part is the headers
@@ -56,120 +56,120 @@ async function httpParser(request, connection = {}) {
// Step 2: Extract the headers (the first line is the request line, e.g., "POST /path HTTP/1.1")
// Split headers into lines, handling both CRLF and LF line endings
- const headers = headersPart.split(/\r?\n/).filter(line => line.trim());
+ const headers = headersPart.split(/\r?\n/).filter(line => line.trim())
// Parse the request line (first line of the headers)
- const requestLine = (headers[0] || '').split(/\s+/);
+ const requestLine = (headers[0] || '').split(/\s+/)
if (requestLine.length < 3 || !requestLine[0] || !requestLine[1] || !requestLine[2]) {
- throw new Error('Invalid request line format');
+ throw new Error('Invalid request line format')
}
// Parse method, path, and version with validation
- const method = requestLine[0].toUpperCase();
- const path = requestLine[1] || '/';
- const version = requestLine[2];
+ const method = requestLine[0].toUpperCase()
+ const path = requestLine[1] || '/'
+ const version = requestLine[2]
// Validate HTTP method
- const validMethods = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS'];
+ const validMethods = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS']
if (!validMethods.includes(method)) {
- throw new Error(`Unsupported HTTP method: ${method}`);
+ throw new Error(`Unsupported HTTP method: ${method}`)
}
// Validate HTTP version
if (!/^HTTP\/\d+\.\d+$/.test(version)) {
- throw new Error(`Invalid HTTP version: ${version}`);
+ throw new Error(`Invalid HTTP version: ${version}`)
}
- req.method = method;
- req.path = path;
- req.version = version;
+ req.method = method
+ req.path = path
+ req.version = version
// Parse headers with validation
for (let i = 1; i < headers.length; i++) {
- const line = (headers[i] || '').trim();
- if (!line) continue;
+ const line = (headers[i] || '').trim()
+ if (!line) continue
- const colonIndex = line.indexOf(':');
- if (colonIndex <= 0) continue; // Skip malformed headers
+ const colonIndex = line.indexOf(':')
+ if (colonIndex <= 0) continue // Skip malformed headers
- const key = line.slice(0, colonIndex).trim().toLowerCase();
- const value = line.slice(colonIndex + 1).trim();
+ const key = line.slice(0, colonIndex).trim().toLowerCase()
+ const value = line.slice(colonIndex + 1).trim()
// Handle duplicate headers by appending with comma (per HTTP spec)
if (req.headers[key]) {
if (Array.isArray(req.headers[key])) {
- req.headers[key].push(value);
+ req.headers[key].push(value)
} else {
- req.headers[key] = [req.headers[key], value];
+ req.headers[key] = [req.headers[key], value]
}
} else {
- req.headers[key] = value;
+ req.headers[key] = value
}
}
// Parse query string and clean path
try {
- const originalPath = req.path || '/';
+ const originalPath = req.path || '/'
// Parse query parameters safely
- const queryStart = originalPath.indexOf('?');
+ const queryStart = originalPath.indexOf('?')
if (queryStart !== -1) {
- req.query = queryParser(originalPath);
+ req.query = queryParser(originalPath)
// Clean path after extracting query
- req.path = decodeURIComponent(originalPath.slice(0, queryStart)) || '/';
+ req.path = decodeURIComponent(originalPath.slice(0, queryStart)) || '/'
} else {
- req.query = {};
- req.path = decodeURIComponent(originalPath) || '/';
+ req.query = {}
+ req.path = decodeURIComponent(originalPath) || '/'
}
} catch (error) {
- console.warn('Error parsing query string:', error);
- req.query = {};
- req.path = '/';
+ console.warn('Error parsing query string:', error)
+ req.query = {}
+ req.path = '/'
}
// Parse request body based on method and content type
try {
if (['POST', 'PUT', 'PATCH'].includes(req.method) && bodyPart) {
- const contentType = (req.headers['content-type'] || '').toLowerCase();
+ const contentType = (req.headers['content-type'] || '').toLowerCase()
if (contentType.includes('application/json')) {
try {
- req.body = JSONbodyParser(bodyPart);
+ req.body = JSONbodyParser(bodyPart)
} catch (error) {
- console.warn('Error parsing JSON body:', error);
- req.body = {};
+ console.warn('Error parsing JSON body:', error)
+ req.body = {}
}
} else if (contentType.includes('application/x-www-form-urlencoded')) {
try {
- req.body = queryParser('?' + bodyPart);
+ req.body = queryParser('?' + bodyPart)
} catch (error) {
- console.warn('Error parsing form data:', error);
- req.body = {};
+ console.warn('Error parsing form data:', error)
+ req.body = {}
}
} else {
// For other content types, keep as raw string
- req.body = bodyPart;
+ req.body = bodyPart
}
} else if (req.method === 'OPTIONS') {
// Handle OPTIONS preflight request
- req.body = {};
+ req.body = {}
req.cors = {
- origin: req.headers['origin'],
+ origin: req.headers.origin,
method: req.headers['access-control-request-method'] || '*',
headers: req.headers['access-control-request-headers'] || ''
- };
+ }
} else {
- req.body = undefined;
+ req.body = undefined
}
} catch (error) {
- console.warn('Error processing request body:', error);
- req.body = {};
+ console.warn('Error processing request body:', error)
+ req.body = {}
}
- return req;
+ return req
} catch (error) {
- console.error('Error parsing HTTP request:', error);
- throw error;
+ console.error('Error parsing HTTP request:', error)
+ throw error
}
}
diff --git a/src/lib/parser.js b/src/lib/parser.js
index 93feb2c..82b6a64 100644
--- a/src/lib/parser.js
+++ b/src/lib/parser.js
@@ -18,15 +18,15 @@ const net = require('net')
*
* @param {string} req - The string to search within.
* @param {string} target - The character to find in the string.
- * @returns {number} The index of the first occurrence of the target character,
+ * @returns {number} The index of the first occurrence of the target character,
* or -1 if the target character is not found.
- *
- * @example
- *
+ *
+ * @example
+ *
* const myString = "Hello, world!";
* const targetChar = "o";
* const index = findFirstBrac(myString, targetChar);
- *
+ *
* if (index !== -1) {
* console.log(`The first occurrence of '${targetChar}' is at index ${index}.`);
* } else {
@@ -48,7 +48,7 @@ function findFirstBrac (req, target) {
*
* @param {string} request - The HTTP request string to parse.
* @returns {Promise