petcarescript
Version:
PetCareScript - A modern, expressive programming language designed for humans
274 lines (229 loc) • 9.16 kB
JavaScript
/**
* PetCareScript HTTP Library
* HTTP client and server functionality
*/
const http = require('http');
const https = require('https');
const url = require('url');
const querystring = require('querystring');
class HTTPClient {
constructor() {
this.defaultHeaders = {
'User-Agent': 'PetCareScript-HTTP/1.2.3'
};
this.timeout = 30000; // 30 seconds default timeout
}
async request(method, urlString, options = {}) {
return new Promise((resolve, reject) => {
const urlObj = url.parse(urlString);
const isHttps = urlObj.protocol === 'https:';
const client = isHttps ? https : http;
const requestOptions = {
hostname: urlObj.hostname,
port: urlObj.port || (isHttps ? 443 : 80),
path: urlObj.path,
method: method.toUpperCase(),
headers: { ...this.defaultHeaders, ...options.headers },
timeout: options.timeout || this.timeout
};
if (options.body && typeof options.body === 'object') {
const jsonData = JSON.stringify(options.body);
requestOptions.headers['Content-Type'] = 'application/json';
requestOptions.headers['Content-Length'] = Buffer.byteLength(jsonData);
}
const req = client.request(requestOptions, (res) => {
let data = '';
res.on('data', (chunk) => {
data += chunk;
});
res.on('end', () => {
let parsedData = data;
try {
if (res.headers['content-type'] &&
res.headers['content-type'].includes('application/json')) {
parsedData = JSON.parse(data);
}
} catch (e) {
// Keep as string if JSON parsing fails
}
resolve({
status: res.statusCode,
statusText: res.statusMessage,
headers: res.headers,
data: parsedData,
size: Buffer.byteLength(data),
url: urlString
});
});
});
req.on('error', (error) => {
reject(new Error(`HTTP Request failed: ${error.message}`));
});
req.on('timeout', () => {
req.abort();
reject(new Error(`HTTP Request timeout after ${requestOptions.timeout}ms`));
});
if (options.body) {
if (typeof options.body === 'object') {
req.write(JSON.stringify(options.body));
} else {
req.write(options.body);
}
}
req.end();
});
}
async get(url, options = {}) {
return this.request('GET', url, options);
}
async post(url, data, options = {}) {
return this.request('POST', url, { ...options, body: data });
}
async put(url, data, options = {}) {
return this.request('PUT', url, { ...options, body: data });
}
async patch(url, data, options = {}) {
return this.request('PATCH', url, { ...options, body: data });
}
async delete(url, options = {}) {
return this.request('DELETE', url, options);
}
// Método para fazer múltiplas requisições em paralelo
async parallel(requests) {
return Promise.all(requests.map(req => {
const { method, url, data, options } = req;
switch (method.toUpperCase()) {
case 'GET': return this.get(url, options);
case 'POST': return this.post(url, data, options);
case 'PUT': return this.put(url, data, options);
case 'PATCH': return this.patch(url, data, options);
case 'DELETE': return this.delete(url, options);
default: throw new Error(`Unsupported method: ${method}`);
}
}));
}
}
// Middleware melhorados
const Middleware = {
cors: (options = {}) => {
const {
origin = '*',
methods = 'GET,HEAD,PUT,PATCH,POST,DELETE',
allowedHeaders = 'Content-Type,Authorization',
credentials = false,
maxAge = 86400 // 24 hours
} = options;
return {
name: 'CORS',
type: 'middleware',
arity: () => 3,
call: (interpreter, args) => {
const [req, res, next] = args;
res.headers = res.headers || {};
res.headers['Access-Control-Allow-Origin'] = origin;
res.headers['Access-Control-Allow-Methods'] = methods;
res.headers['Access-Control-Allow-Headers'] = allowedHeaders;
res.headers['Access-Control-Max-Age'] = maxAge;
if (credentials) {
res.headers['Access-Control-Allow-Credentials'] = 'true';
}
if (req.method === 'OPTIONS') {
res.statusCode = 204;
return res.send('');
}
if (next) next();
}
};
},
json: (options = {}) => {
const { limit = '100kb', strict = true } = options;
return {
name: 'JSON',
type: 'middleware',
arity: () => 3,
call: (interpreter, args) => {
const [req, res, next] = args;
if (req.headers['content-type'] &&
req.headers['content-type'].includes('application/json')) {
// JSON parsing would be handled here
req.body = req.body || {};
}
if (next) next();
}
};
},
logger: (format = 'combined') => {
return {
name: 'Logger',
type: 'middleware',
arity: () => 3,
call: (interpreter, args) => {
const [req, res, next] = args;
const timestamp = new Date().toISOString();
switch (format) {
case 'tiny':
console.log(`${req.method} ${req.path} ${res.statusCode || 200}`);
break;
case 'combined':
default:
console.log(`[${timestamp}] ${req.method} ${req.path} - ${req.ip || '127.0.0.1'}`);
break;
}
if (next) next();
}
};
},
rateLimit: (options = {}) => {
const { windowMs = 15 * 60 * 1000, max = 100 } = options;
const requests = new Map();
return {
name: 'RateLimit',
type: 'middleware',
arity: () => 3,
call: (interpreter, args) => {
const [req, res, next] = args;
const ip = req.ip || '127.0.0.1';
const now = Date.now();
if (!requests.has(ip)) {
requests.set(ip, []);
}
const userRequests = requests.get(ip);
const recentRequests = userRequests.filter(time => now - time < windowMs);
if (recentRequests.length >= max) {
res.statusCode = 429;
return res.json({ error: 'Too many requests' });
}
recentRequests.push(now);
requests.set(ip, recentRequests);
if (next) next();
}
};
}
};
const HTTPLib = {
client: new HTTPClient(),
middleware: Middleware,
// Factory functions
createClient: (options = {}) => {
const client = new HTTPClient();
if (options.timeout) client.timeout = options.timeout;
if (options.headers) Object.assign(client.defaultHeaders, options.headers);
return client;
},
// Direct HTTP methods
get: (url, options) => new HTTPClient().get(url, options),
post: (url, data, options) => new HTTPClient().post(url, data, options),
put: (url, data, options) => new HTTPClient().put(url, data, options),
patch: (url, data, options) => new HTTPClient().patch(url, data, options),
delete: (url, options) => new HTTPClient().delete(url, options),
// Utility functions
parseURL: (urlString) => url.parse(urlString),
buildURL: (baseURL, params) => {
const urlObj = new URL(baseURL);
Object.entries(params).forEach(([key, value]) => {
urlObj.searchParams.set(key, value);
});
return urlObj.toString();
}
};
module.exports = HTTPLib;