mirror of
https://github.com/Wessel/kirbe.git
synced 2026-06-08 14:10:13 +02:00
Upload main code
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
node_modules/
|
||||
49
README.md
Normal file
49
README.md
Normal file
@@ -0,0 +1,49 @@
|
||||
#  Kirbe
|
||||
|
||||
> A lightweight and fast Node.js HTTP server library
|
||||
|
||||
> [GitHub](https://www.github.com/PassTheWessel/Kirbe) **|** [NPM](https://www.npmjs.com/package/kirbe)
|
||||
|
||||
## Installing
|
||||
```sh
|
||||
$ yarn add kirbe # Install w/ Yarn (the superior package manager)
|
||||
$ npm i kirbe # Install w/ NPM
|
||||
```
|
||||
|
||||
## Usage
|
||||
#### Start a HTTP(s) server on port 8080 and add some routes
|
||||
```js
|
||||
const kirbe = require( 'kirbe' ); // Define kirbe
|
||||
const app = new kirbe(); // Make your kirbe client
|
||||
|
||||
app.route( '/bear', 'GET' ( req, res ) => res.status( 200 ).body({ 'bear': 'cop' }) );
|
||||
app.route( ( req, res ) => res.status( 404 ).body( 'Error: Content not found!' ).end() );
|
||||
app.get( '/kirb', ( req, res ) => {
|
||||
res.writeHead( 201, { 'test': 'hi' });
|
||||
res.end({ 'key': 'hi' });
|
||||
});
|
||||
|
||||
// HTTP
|
||||
app.listen( 8080, () => console.log( 'Listening on port 8080!' ) );
|
||||
// HTTPS
|
||||
const https = require( 'https' ); // This should be at the top of your code
|
||||
https.createServer( app.externalHandler ).listen( 8080 );
|
||||
```
|
||||
|
||||
## Default extensions ( [/extensions](extensions) )
|
||||
#### Static
|
||||
> Host static files on your website
|
||||
|
||||
##### Usage
|
||||
```js
|
||||
const path = require( 'path' ); // Define path
|
||||
const kirbe = require( 'kirbe' ); // Define kirbe
|
||||
const app = new kirbe(); // Make your kirbe client
|
||||
|
||||
app.use( kirbe.static( path.join( __dirname, 'static' ) ) );
|
||||
```
|
||||
|
||||
### Why use kirbe?
|
||||
Kirbe is a lightweight and fast HTTP server library, especially comparing to express which is around 1mb. If you want any featuers that aren't inside of Kirbe yet, you can open an issue or pull request.
|
||||
|
||||
You can join [https://discord.gg/SV7DAE9](https://discord.gg/SV7DAE9) if you need any support using kirbe!
|
||||
1
extensions/mimes.json
Normal file
1
extensions/mimes.json
Normal file
File diff suppressed because one or more lines are too long
47
extensions/static.js
Normal file
47
extensions/static.js
Normal file
@@ -0,0 +1,47 @@
|
||||
const fs = require( 'fs' );
|
||||
const url = require( 'url' );
|
||||
const { join, extname } = require( 'path' );
|
||||
|
||||
const mimes = JSON.parse( fs.readFileSync( join( __dirname, 'mimes.json' ) ) );
|
||||
|
||||
module.exports = ( baseDir, indexFile ) => {
|
||||
if (!baseDir) throw new Error('The argument "baseDir" is required for the extension "kirbe:static"');
|
||||
|
||||
indexFile = typeof indexFile === 'string' ? indexFile : 'index.html'
|
||||
return( req, res, next ) => {
|
||||
if( req.method !== 'GET' ) { next(); return; }
|
||||
|
||||
let requestedPath = req.parsedUrl.pathname.replace( /\/.\.\//g, '' );
|
||||
let requestedExt = extname( requestedPath );
|
||||
|
||||
const filePath = join( baseDir, requestedPath );
|
||||
|
||||
fs.stat( filePath, ( err, stats ) => {
|
||||
if( err ) { next(); return; }
|
||||
|
||||
if( stats.isFile() ) {
|
||||
stats.mtime.setMilliseconds( 0 );
|
||||
if ( stats.mtime <= new Date( req.headers[ 'if-modified-since' ] ) ) res.status( 304 ).end();
|
||||
else fs.createReadStream( filePath ).pipe( res.status( 200 ).coreRes );
|
||||
} else {
|
||||
if ( req.parsedUrl.pathname.charAt( req.parsedUrl.pathname.length -1 ) !== '/' ) {
|
||||
res.status(302).header({ 'Location': `${req.parsedUrl.pathname}/` }).end();
|
||||
return;
|
||||
}
|
||||
|
||||
requestedPath = join( filePath, indexFile );
|
||||
requestedExt = extname( requestedPath );
|
||||
|
||||
fs.readFile( requestedPath, ( err, data ) => {
|
||||
if (err) next();
|
||||
else {
|
||||
res.body( data ).status(200).header({
|
||||
'Content-Type': ( mimes.hasOwnProperty( requestedExt ) ? mimes[ requestedExt ] : 'application/octet-stream' ),
|
||||
'Last-Modified': stats.mtime.toString()
|
||||
}).end();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
};
|
||||
4
index.js
Normal file
4
index.js
Normal file
@@ -0,0 +1,4 @@
|
||||
const kirbe = exports;
|
||||
|
||||
module.exports = require( './model/KirbeServer' );
|
||||
kirbe.static = require( './extensions/static' );
|
||||
BIN
media/kirb.gif
Normal file
BIN
media/kirb.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 62 KiB |
16
model/KirbeRequest.js
Normal file
16
model/KirbeRequest.js
Normal file
@@ -0,0 +1,16 @@
|
||||
const { parse } = require( 'url' );
|
||||
|
||||
module.exports = class KirbeRequest {
|
||||
constructor( req, body ) {
|
||||
this.url = req.url;
|
||||
this.req = req;
|
||||
this.body = body;
|
||||
this.from = req.connection.remoteAddress;
|
||||
this.method = req.method;
|
||||
this.headers = req.headers;
|
||||
this.parsedUrl = parse( this.url, true );
|
||||
}
|
||||
|
||||
json() { return JSON.parse( this.body ); }
|
||||
query ( name ) { return this.parsedUrl.query[ name ]; }
|
||||
};
|
||||
50
model/KirbeResponse.js
Normal file
50
model/KirbeResponse.js
Normal file
@@ -0,0 +1,50 @@
|
||||
module.exports = class KirbeResponse {
|
||||
constructor( res ) {
|
||||
this.coreRes = res;
|
||||
this.headers = {};
|
||||
|
||||
this.statusCode = 200;
|
||||
this.statusMessage = null;
|
||||
|
||||
this.data = Buffer.alloc( 0 );
|
||||
}
|
||||
|
||||
body( body ) {
|
||||
if ( typeof body === 'object' && !Buffer.isBuffer( body ) ) {
|
||||
if ( !this.headers[ 'content-type' ] ) this.headers[ 'content-type' ] = 'application/json';
|
||||
|
||||
this.data = JSON.stringify( body );
|
||||
} else this.data = body;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
header( a, b ) {
|
||||
if ( typeof a === 'object' ) Object.keys( a ).forEach( ( v ) => this.headers[ v.toLowerCase() ] = a[ v ] );
|
||||
else this.headers[ a.toLowerCase() ] = b;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
status( code, message ) {
|
||||
this.statusCode = code;
|
||||
this.statusMessage = message;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
writeHead( status, headers ) {
|
||||
if ( status ) this.status( status );
|
||||
if ( headers ) this.header( headers );
|
||||
}
|
||||
|
||||
end( data ) {
|
||||
if ( data ) this.body( data );
|
||||
|
||||
if (this.statusMessage) this.coreRes.writeHead( this.statusCode, this.statusMessage, this.headers );
|
||||
else this.coreRes.writeHead( this.statusCode, this.headers );
|
||||
this.coreRes.end( this.data );
|
||||
|
||||
return this;
|
||||
}
|
||||
};
|
||||
71
model/KirbeServer.js
Normal file
71
model/KirbeServer.js
Normal file
@@ -0,0 +1,71 @@
|
||||
const { join } = require( 'path' );
|
||||
const { createServer } = require( 'http' );
|
||||
|
||||
const KirbeRequest = require( join( __dirname, 'KirbeRequest.js' ) );
|
||||
const KirbeResponse = require( join( __dirname, 'KirbeResponse.js' ) );
|
||||
|
||||
const methods = [ 'GET', 'HEAD', 'POST', 'PUT', 'DELETE', 'CONNECT', 'OPTIONS', 'TRACE', 'PATCH' ];
|
||||
const isUrl = ( c ) => (typeof c === 'string' && !methods.includes( c ) ) || c instanceof RegExp;
|
||||
|
||||
module.exports = class KirbeServer {
|
||||
constructor() {
|
||||
this.internalServer = createServer( ( req, res ) => this.handler.apply( this, [ req, res ] ) );
|
||||
this.externalHandler = ( req, res ) => this.handler( req, res );
|
||||
|
||||
this.routes = [];
|
||||
this.extensions = [];
|
||||
|
||||
methods.forEach( ( v ) => this[ v.toLowerCase() ] = ( a, b ) => this.route( v, a, b ) );
|
||||
}
|
||||
|
||||
use( extension ) { this.extensions.push( extension ); }
|
||||
|
||||
route( a, b, c ) {
|
||||
this.routes.push({
|
||||
'target': {
|
||||
'path' : isUrl( a ) ? a : ( isUrl( b ) ? b : null ),
|
||||
'method': methods.includes( a ) ? a : null
|
||||
},
|
||||
'handler': c || b || a
|
||||
});
|
||||
}
|
||||
|
||||
handler( req, res ) {
|
||||
let body = Buffer.alloc( 0 );
|
||||
|
||||
req.on( 'data', ( c ) => body = Buffer.concat([ body, c ]) );
|
||||
req.on( 'end', () => {
|
||||
const request = new KirbeRequest( req, body );
|
||||
const response = new KirbeResponse( res );
|
||||
|
||||
const start = () => {
|
||||
for( let i = 0; i < this.routes.length; i++ ) {
|
||||
const current = this.routes[ i ];
|
||||
|
||||
if ( current.target.method && request.method !== current.target.method ) continue;
|
||||
if ( current.target.path ) {
|
||||
if ( current.target.path instanceof RegExp && !request.parsedUrl.pathname.match( current.target.path ) ) continue;
|
||||
else if ( current.target.path !== request.parsedUrl.pathname ) continue;
|
||||
}
|
||||
|
||||
current.handler( request, response );
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
let currentExt = 0;
|
||||
const nextExt = () => {
|
||||
if ( this.extensions.length >= currentExt + 1 ) {
|
||||
currentExt++;
|
||||
this.extensions[ currentExt -1 ]( request, response, nextExt );
|
||||
} else start();
|
||||
};
|
||||
|
||||
nextExt();
|
||||
});
|
||||
}
|
||||
|
||||
listen( a, b, c ) {
|
||||
this.internalServer.listen( typeof a === 'number' ? a : 80, typeof b === 'string' ? b : null, typeof b === 'function' ? b : null );
|
||||
}
|
||||
};
|
||||
34
package.json
Normal file
34
package.json
Normal file
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"name": "kirbe",
|
||||
"version": "0.0.1",
|
||||
"description": "👻 A powerful and lightweight Node.js HTTP server library",
|
||||
"main": "index.js",
|
||||
"author": "Wessel \"wesselgame\" T <discord@go2it.eu>",
|
||||
"license": "MIT",
|
||||
"private": false,
|
||||
"files": [
|
||||
"index.js",
|
||||
"model",
|
||||
"LICENSE"
|
||||
],
|
||||
"bugs": {
|
||||
"url": "https://www.github.com/PassTheWessel/kirbe/issues"
|
||||
},
|
||||
"homepage": "https://www.github.com/PassTheWessel/kirbe#readme",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://www.github.com/PassTheWessel/kirbe"
|
||||
},
|
||||
"keywords": [
|
||||
"framework",
|
||||
"fast",
|
||||
"web",
|
||||
"rest",
|
||||
"restful",
|
||||
"router",
|
||||
"app",
|
||||
"api",
|
||||
"lightweight"
|
||||
],
|
||||
"devDependencies": { "wumpfetch": "^0.0.4" }
|
||||
}
|
||||
28
test.js
Normal file
28
test.js
Normal file
@@ -0,0 +1,28 @@
|
||||
const w = require( 'wumpfetch' );
|
||||
const Kirbe = require( './index' );
|
||||
|
||||
const app = new Kirbe();
|
||||
|
||||
app.use( ( req, res, next ) => req.url === '/testExtension' ? res.status( 200 ).end() : next() );
|
||||
|
||||
app.post( '/parse', ( req, res ) => res.body({ 'sent': req.json() }).end() );
|
||||
app.route( 'GET', '/statusMsg', ( req, res ) => res.status( 200, 'kirbe won' ).end() );
|
||||
app.route( 'POST', ( req, res ) => res.body( 'Gotta catch em all!' ).end() );
|
||||
app.route( '/compatibility', ( req, res ) => {
|
||||
res.writeHead( 201, { 'test': 'hi' });
|
||||
res.end({ 'key': 'hi' });
|
||||
});
|
||||
app.route( ( req, res ) => res.status( 404 ).body( 'Error: Content not found!' ).end() );
|
||||
|
||||
;( async() => {
|
||||
const res = [];
|
||||
res.push( await w( 'http://127.0.0.1:4040/bear' ).send() );
|
||||
res.push( await w( 'http://127.0.0.1:4040/statusMsg' ).send() );
|
||||
res.push( await w( 'http://127.0.0.1:4040/compatibility' ).send() );
|
||||
res.push( await w( 'http://127.0.0.1:4040/testExtension' ).send() );
|
||||
res.push( await w( 'http://127.0.0.1:4040/parse', 'POST' ).body({ 'hello': 123 }).send() );
|
||||
|
||||
res.forEach( ( v, i ) => console.log( `${i}: ${v.statusCode}` ) );
|
||||
})();
|
||||
|
||||
app.listen( 4040, '127.0.0.1' );
|
||||
Reference in New Issue
Block a user