diff --git a/HTTP/README.md b/HTTP/README.md new file mode 100644 index 000000000..e69de29bb diff --git a/HTTP/server.ts b/HTTP/server.ts new file mode 100644 index 000000000..40074515c --- /dev/null +++ b/HTTP/server.ts @@ -0,0 +1,490 @@ +import * as net from "net"; + +// ===== Types ===== +type TCPConn = { + socket: net.Socket; +}; + +type DynBuf = { + data: Buffer; + length: number; +}; + +type HTTPReq = { + method: string; + uri: Buffer; + version: string; + headers: Buffer[]; +}; + +type HTTPRes = { + code: number; + headers: Buffer[]; + body: BodyReader; +}; + +type BodyReader = { + length: number; + read: () => Promise; +}; + +// ===== Constants ===== +const kMaxHeaderLen = 1024 * 8; // 8 KB + +// ===== Error Class ===== +class HTTPError extends Error { + code: number; + constructor(code: number, message: string) { + super(message); + this.code = code; + } +} + +// ===== Buffer Utilities ===== +function bufPush(buf: DynBuf, data: Buffer): void { + const newLen = buf.length + data.length; + if (buf.data.length < newLen) { + let cap = Math.max(buf.data.length, 32); + while (cap < newLen) { + cap *= 2; + } + const grown = Buffer.alloc(cap); + buf.data.copy(grown, 0, 0); + buf.data = grown; + } + data.copy(buf.data, buf.length, 0); + buf.length = newLen; +} + +function bufPop(buf: DynBuf, len: number): void { + buf.data.copyWithin(0, len, buf.length); + buf.length -= len; +} + +// ===== Socket Reading ===== +async function soRead(conn: TCPConn): Promise { + return new Promise((resolve, reject) => { + const onData = (chunk: Buffer) => { + cleanup(); + resolve(chunk); + }; + const onEnd = () => { + cleanup(); + resolve(Buffer.alloc(0)); + }; + const onError = (err: Error) => { + cleanup(); + reject(err); + }; + const cleanup = () => { + conn.socket.off("data", onData); + conn.socket.off("end", onEnd); + conn.socket.off("error", onError); + }; + conn.socket.once("data", onData); + conn.socket.once("end", onEnd); + conn.socket.once("error", onError); + }); +} + +// ===== HTTP Parsing ===== +function splitLines(data: Buffer): Buffer[] { + const lines: Buffer[] = []; + let start = 0; + + for (let i = 0; i < data.length - 1; i++) { + if (data[i] === 13 && data[i + 1] === 10) { + lines.push(data.subarray(start, i)); + start = i + 2; + i++; + } + } + + if (start < data.length) { + lines.push(data.subarray(start)); + } + + return lines; +} + +function parseRequestLine(line: Buffer): { + method: string; + uri: Buffer; + version: string; +} { + const parts: Buffer[] = []; + let start = 0; + + for (let i = 0; i < line.length; i++) { + if (line[i] === 32) { + if (i > start) { + parts.push(line.subarray(start, i)); + } + start = i + 1; + } + } + if (start < line.length) { + parts.push(line.subarray(start)); + } + + if (parts.length !== 3) { + throw new HTTPError(400, "bad request line"); + } + + const method = parts[0].toString("latin1"); + const uri = parts[1]; + const version = parts[2].toString("latin1"); + + const validMethods = [ + "GET", + "POST", + "PUT", + "DELETE", + "HEAD", + "OPTIONS", + "PATCH", + ]; + if (!validMethods.includes(method)) { + throw new HTTPError(405, "method not allowed"); + } + + if (version !== "HTTP/1.1" && version !== "HTTP/1.0") { + throw new HTTPError(505, "HTTP version not supported"); + } + + return { method, uri, version }; +} + +function validateHeader(header: Buffer): void { + const colonIdx = header.indexOf(58); + + if (colonIdx <= 0) { + throw new HTTPError(400, "bad header format"); + } + + const name = header.subarray(0, colonIdx); + + for (let i = 0; i < name.length; i++) { + const c = name[i]; + const isValid = + (c >= 65 && c <= 90) || + (c >= 97 && c <= 122) || + (c >= 48 && c <= 57) || + c === 45; + + if (!isValid) { + throw new HTTPError(400, "invalid header name"); + } + } + + if (name.length === 0) { + throw new HTTPError(400, "empty header name"); + } +} + +function parseHTTPReq(data: Buffer): HTTPReq { + const lines = splitLines(data); + + if (lines.length < 2) { + throw new HTTPError(400, "incomplete request"); + } + + const { method, uri, version } = parseRequestLine(lines[0]); + + const headers: Buffer[] = []; + for (let i = 1; i < lines.length; i++) { + const line = lines[i]; + + if (line.length === 0) { + break; + } + + validateHeader(line); + headers.push(line); + } + + return { method, uri, version, headers }; +} + +function cutMessage(buf: DynBuf): null | HTTPReq { + const idx = buf.data.subarray(0, buf.length).indexOf("\r\n\r\n"); + + if (idx < 0) { + if (buf.length >= kMaxHeaderLen) { + throw new HTTPError(413, "header is too large"); + } + return null; + } + + const msg = parseHTTPReq(buf.data.subarray(0, idx + 4)); + bufPop(buf, idx + 4); + return msg; +} + +// ===== Header Utilities ===== +function fieldGet(headers: Buffer[], key: string): Buffer | null { + const keyLower = key.toLowerCase(); + for (const header of headers) { + const colonIdx = header.indexOf(58); + if (colonIdx > 0) { + const name = header + .subarray(0, colonIdx) + .toString("latin1") + .toLowerCase() + .trim(); + if (name === keyLower) { + // Trim manually: skip leading/trailing whitespace + let start = colonIdx + 1; + let end = header.length; + while (start < end && (header[start] === 32 || header[start] === 9)) + start++; + while (end > start && (header[end - 1] === 32 || header[end - 1] === 9)) + end--; + return header.subarray(start, end); + } + } + } + return null; +} + +function parseDec(s: string): number { + return parseInt(s, 10); +} + +// ===== Body Reader ===== +function readerFromConnLength( + conn: TCPConn, + buf: DynBuf, + remain: number, +): BodyReader { + return { + length: remain, + read: async (): Promise => { + if (remain === 0) { + return Buffer.alloc(0); + } + if (buf.length === 0) { + const data = await soRead(conn); + bufPush(buf, data); + if (data.length === 0) { + throw new HTTPError(400, "Unexpected EOF from HTTP body"); + } + } + const consume = Math.min(buf.length, remain); + remain -= consume; + const body = Buffer.from(buf.data.subarray(0, consume)); + buf.data.copyWithin(0, consume, buf.length); + buf.length -= consume; + return body; + }, + }; +} + +function readerFromReq(conn: TCPConn, buf: DynBuf, req: HTTPReq): BodyReader { + let bodyLen = -1; + const contentLen = fieldGet(req.headers, "Content-Length"); + if (contentLen) { + bodyLen = parseDec(contentLen.toString("latin1")); + if (isNaN(bodyLen)) { + throw new HTTPError(400, "bad Content-Length"); + } + } + + const bodyAllowed = !(req.method === "GET" || req.method === "HEAD"); + const chunked = + fieldGet(req.headers, "Transfer-Encoding")?.equals( + Buffer.from("chunked"), + ) || false; + + if (!bodyAllowed && (bodyLen > 0 || chunked)) { + throw new HTTPError(400, "HTTP body not allowed"); + } + if (!bodyAllowed) { + bodyLen = 0; + } + + if (bodyLen >= 0) { + return readerFromConnLength(conn, buf, bodyLen); + } else if (chunked) { + throw new HTTPError(501, "TODO: chunked encoding"); + } else { + throw new HTTPError(501, "TODO: read until EOF"); + } +} + +// ===== Response Writing ===== +async function writeHTTPResp(conn: TCPConn, resp: HTTPRes): Promise { + if (resp.body.length < 0) { + throw new Error("chunked encoding is not supported"); + } + + const statusMessages: { [key: number]: string } = { + 200: "OK", + 400: "Bad Request", + 404: "Not Found", + 405: "Method Not Allowed", + 413: "Payload Too Large", + 500: "Internal Server Error", + 501: "Not Implemented", + 505: "HTTP Version Not Supported", + }; + + const statusText = statusMessages[resp.code] || "Unknown"; + const statusLine = `HTTP/1.1 ${resp.code} ${statusText}\r\n`; + + conn.socket.write(statusLine); + + for (const header of resp.headers) { + conn.socket.write(header); + conn.socket.write("\r\n"); + } + conn.socket.write("\r\n"); + + while (true) { + const chunk = await resp.body.read(); + if (chunk.length === 0) { + break; + } + conn.socket.write(chunk); + } +} + +// ===== Request Handler ===== +async function handleReq(req: HTTPReq, body: BodyReader): Promise { + const uri = req.uri.toString("latin1"); + console.log(`Request: ${req.method} ${uri}`); + + let bodyData = Buffer.alloc(0); + while (true) { + const chunk = await body.read(); + if (chunk.length === 0) break; + bodyData = Buffer.concat([bodyData, chunk]); + } + + if (uri === "/echo") { + return { + code: 200, + headers: [ + Buffer.from("Content-Type: text/plain"), + Buffer.from(`Content-Length: ${bodyData.length}`), + ], + body: { + length: bodyData.length, + read: async () => { + const result = bodyData; + bodyData = Buffer.alloc(0); + return result; + }, + }, + }; + } + + const responseText = `Hello from HTTP server!\nYou requested: ${uri}\n`; + const responseBuffer = Buffer.from(responseText); + + return { + code: 200, + headers: [ + Buffer.from("Content-Type: text/plain"), + Buffer.from(`Content-Length: ${responseBuffer.length}`), + ], + body: { + length: responseBuffer.length, + read: async () => { + const result = responseBuffer; + return result; + }, + }, + }; +} + +// ===== Server Client Handler ===== +async function serverClient(conn: TCPConn): Promise { + const buf: DynBuf = { data: Buffer.alloc(0), length: 0 }; + + try { + while (true) { + const msg: null | HTTPReq = cutMessage(buf); + if (!msg) { + const data = await soRead(conn); + bufPush(buf, data); + if (data.length === 0) { + console.log("Client disconnected"); + break; + } + continue; + } + + const reqBody: BodyReader = readerFromReq(conn, buf, msg); + const res: HTTPRes = await handleReq(msg, reqBody); + await writeHTTPResp(conn, res); + + if ( + fieldGet(msg.headers, "Connection") + ?.toString("latin1") + .toLowerCase() === "close" + ) { + break; + } + + const contentLen = fieldGet(msg.headers, "Content-Length"); + if (!contentLen && msg.method !== "GET" && msg.method !== "HEAD") { + break; + } + } + } catch (err) { + if (err instanceof HTTPError) { + console.error(`HTTP Error ${err.code}: ${err.message}`); + const errorMsg = Buffer.from(err.message); + const errorResp: HTTPRes = { + code: err.code, + headers: [ + Buffer.from("Content-Type: text/plain"), + Buffer.from(`Content-Length: ${errorMsg.length}`), + ], + body: { + length: errorMsg.length, + read: async () => errorMsg, + }, + }; + try { + await writeHTTPResp(conn, errorResp); + } catch (writeErr) { + console.error("Failed to write error response:", writeErr); + } + } else { + console.error("Unexpected error:", err); + } + } finally { + conn.socket.end(); + } +} + +// ===== Main Server ===== +function startServer(port: number): void { + const server = net.createServer((socket) => { + console.log("New client connected"); + const conn: TCPConn = { socket }; + serverClient(conn).catch((err) => { + console.error("Error handling client:", err); + }); + }); + + server.listen(port, () => { + console.log(`HTTP server listening on port ${port}`); + console.log(`Try: curl http://localhost:${port}/`); + console.log( + `Try: curl -X POST -d "test data" http://localhost:${port}/echo`, + ); + }); +} + +// Start the server +startServer(8080); + +function foo(blaz: { + oddly: "long" | "type"; + but: "hey" | "this" | "is" | number; +}) {} + +function new_fn(bar: number) {} diff --git a/Messenger_BOT/bot.js b/Messenger_BOT/bot.js new file mode 100644 index 000000000..90ed185ab --- /dev/null +++ b/Messenger_BOT/bot.js @@ -0,0 +1,110 @@ +const express = require("express"); +const bodyParser = require("body-parser"); +const request = require("request"); +const apiai = require("apiai"); + +const app = express(); + +// Environment variables +const PAGE_ACCESS_TOKEN = process.env.PAGE_ACCESS_TOKEN || "your_page_token"; +const CLIENT_ACCESS_TOKEN = + process.env.CLIENT_ACCESS_TOKEN || "your_apiai_token"; +const VERIFY_TOKEN = process.env.VERIFY_TOKEN || "tuxedo_cat"; + +// Initialize API.AI +const apiaiApp = apiai(CLIENT_ACCESS_TOKEN); + +// Middleware +app.use(bodyParser.json()); +app.use(bodyParser.urlencoded({ extended: true })); + +// Start server +const server = app.listen(process.env.PORT || 5000, () => { + console.log( + "Express server listening on port %d in %s mode", + server.address().port, + app.settings.env, + ); +}); + +// Facebook Webhook Verification +app.get("/webhook", (req, res) => { + // Fixed: hub.mode (was hub.mdoe) + if ( + req.query["hub.mode"] === "subscribe" && + req.query["hub.verify_token"] === VERIFY_TOKEN + ) { + console.log("Validating webhook"); + res.status(200).send(req.query["hub.challenge"]); + } else { + console.error("Failed validation. Make sure the validation tokens match."); + res.status(403).end(); + } +}); + +// Handle all messages +// Fixed: Added missing '/' in route +app.post("/webhook", (req, res) => { + console.log("Webhook received:", req.body); + + if (req.body.object === "page") { + req.body.entry.forEach((entry) => { + entry.messaging.forEach((event) => { + console.log("Event:", event); + + // Process message events + if (event.message && event.message.text) { + sendMessage(event); + } + }); + }); + } + + res.status(200).end(); +}); + +// Send message function +function sendMessage(event) { + let sender = event.sender.id; + let text = event.message.text; + + // Send text to API.AI + let apiai = apiaiApp.textRequest(text, { + sessionId: "tabby_cat", // use any arbitrary id + }); + + apiai.on("response", (response) => { + // Got a response from api.ai + let aiText = response.result.fulfillment.speech; + + // Send response back to Facebook Messenger + request( + { + // Fixed: facebook.com (was facebok.com) + url: "https://graph.facebook.com/v2.6/me/messages", + // Fixed: PAGE_ACCESS_TOKEN (was PAGE_ACCSESS_TOKEN) + qs: { access_token: PAGE_ACCESS_TOKEN }, + method: "POST", + json: { + recipient: { id: sender }, + message: { text: aiText }, // Fixed: use aiText not original text + }, + }, + function (error, response) { + if (error) { + console.log("Error sending message:", error); + } else if (response.body.error) { + console.log("Error:", response.body.error); + } else { + console.log("Message sent successfully!"); + } + }, + ); + }); + + apiai.on("error", (error) => { + console.log("API.AI Error:", error); + }); + + apiai.end(); +} diff --git a/virtual_dom_with_JS/dom.jsx b/virtual_dom_with_JS/dom.jsx new file mode 100644 index 000000000..9b854e98a --- /dev/null +++ b/virtual_dom_with_JS/dom.jsx @@ -0,0 +1,71 @@ +/**@jsx h */ +function h(type, props, ...children) { + return { type, props: props || {}, children }; +} +function setBoolean($target, name, value) { + if (value) { + $target.setAttribute(name, value); + $target[name] = true; + } else { + $target[name] = false; + } +} + +function isCustomProp(name) { + return false; +} +function setProp($target, name, value) { + if (isCustomProp(name)) { + return; + } else if (name === "className") { + $target.setAttribute("class", value); + } else if (typeof value === "boolean") { + setBoolean($target, name, value); + } else { + $target.setAttribute(name, value); + } +} + +function setProps($target, props) { + Object.keys(props).forEach((name) => { + setProp($target, name, props[name]); + }); +} + +function createElement(node) { + if (typeof node === "string") { + return document.createTextNode(node); + } + + const $el = document.createElement(node.type); + setProps($el, node.props); + node.children.map(createElement).forEach($el.appendChild.bind($el)); + return $el; +} + +const f = ( +
    +
  • item 1
  • +
  • + + +
  • +
+); + +const $root = document.getElementById("root"); +$root.appendChild(createElement(f)); + +function removeBooleanProp($target, name) { + $target.removeAttribute(name); + $target[name] = false; +} +function removeProp($target, name, value) { + if (isCustomProp(name)) { + return; + } else if (name === "className") { + $target.removeAttribute("class"); + } else if (typeof value === "boolean") { + removeBooleanProp($target, name); + } +} diff --git a/virtual_dom_with_JS/inde.html b/virtual_dom_with_JS/inde.html new file mode 100644 index 000000000..e2fcc54e1 --- /dev/null +++ b/virtual_dom_with_JS/inde.html @@ -0,0 +1 @@ +
diff --git a/virtual_dom_with_JS/index.css b/virtual_dom_with_JS/index.css new file mode 100644 index 000000000..5dae0046e --- /dev/null +++ b/virtual_dom_with_JS/index.css @@ -0,0 +1,4 @@ +.item { + background: yellow; + margin: 10px 0; +}