Upload main code

This commit is contained in:
Wessel Damian Tip
2019-01-16 17:06:03 +01:00
parent 81274e83c3
commit ad36e794a8
11 changed files with 301 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
node_modules/

49
README.md Normal file
View File

@@ -0,0 +1,49 @@
# ![Icon](media/kirb.gif "Dancing krib") 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

File diff suppressed because one or more lines are too long

47
extensions/static.js Normal file
View 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
View File

@@ -0,0 +1,4 @@
const kirbe = exports;
module.exports = require( './model/KirbeServer' );
kirbe.static = require( './extensions/static' );

BIN
media/kirb.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

16
model/KirbeRequest.js Normal file
View 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
View 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
View 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
View 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
View 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' );