Created
March 18, 2026 03:52
-
-
Save truongluu/3f4612ca41aaa7ab80e2574a973bbf03 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import net from 'net'; | |
| import { BSON } from 'bson'; | |
| // Tạo kết nối TCP tới MongoDB server chạy ở localhost:27019. | |
| const socket = net.connect(27019, 'localhost', () => { | |
| // Callback này chạy khi TCP connect thành công. | |
| // Bên trong, ta tự đóng gói gói tin theo MongoDB Wire Protocol (OP_MSG). | |
| // Lệnh find tương đương: db.users.find({ age: 18 }) trên database "mysql". | |
| // BSON.serialize biến object JS thành Buffer BSON để gửi qua mạng. | |
| const doc = BSON.serialize({ find: 'users', filter: { age: 18 }, '$db': 'mysql' }) | |
| // Header OP_MSG có tổng 20 bytes: 16 bytes chuẩn + 4 bytes flagBits. | |
| const header = Buffer.alloc(20) | |
| // messageLength = header(20) + kind byte(1) + độ dài BSON body. | |
| // Byte này nằm ở offset 0 trong header. | |
| header.writeInt32LE(20 + 1 + doc.length, 0) // messageLength includes section kind byte | |
| // requestID: id request hiện tại để match response nếu cần. | |
| header.writeInt32LE(1, 4) // requestID | |
| // responseTo của request luôn là 0. | |
| header.writeInt32LE(0, 8) // responseTo | |
| // opCode 2013 = OP_MSG (kiểu message hiện đại của MongoDB). | |
| header.writeInt32LE(2013, 12) // opCode OP_MSG | |
| // flagBits cho OP_MSG, 0 nghĩa không bật cờ đặc biệt nào. | |
| header.writeUInt32LE(0, 16) // flagBits | |
| // Section kind byte: 0 nghĩa "single BSON document section". | |
| const kindByte = Buffer.from([0]) // kind = 0 (body section) | |
| // Gửi gói tin hoàn chỉnh: header + kind byte + BSON command document. | |
| socket.write(Buffer.concat([header, kindByte, doc])) | |
| }) | |
| // Lắng nghe dữ liệu phản hồi từ server. | |
| socket.on('data', (chunk) => { | |
| // Trong TypeScript, chunk có thể là string hoặc Buffer. | |
| // Chuẩn hóa về Buffer để dùng các hàm readInt32LE/readUInt8 an toàn. | |
| const data = typeof chunk === 'string' ? Buffer.from(chunk) : chunk | |
| // In raw data dạng hex để debug giao thức nhị phân. | |
| console.log('Raw bytes:', data.toString('hex')) | |
| // In dạng utf-8 chỉ để tham khảo; dữ liệu nhị phân thường sẽ khó đọc. | |
| console.log('Raw bytes:', data.toString('utf-8')) | |
| // Cần ít nhất 16 bytes để đọc được header cơ bản của MongoDB message. | |
| if (data.length < 16) { | |
| // Nếu chưa đủ header thì chờ gói tiếp theo. | |
| console.log('Incomplete MongoDB header, waiting for more data') | |
| return | |
| } | |
| // Parse OP_MSG response: 16-byte header + 4-byte flagBits + sections | |
| // Đọc tổng độ dài message từ 4 bytes đầu. | |
| const messageLength = data.readInt32LE(0) | |
| // responseTo giúp biết response này trả cho request nào. | |
| const responseTo = data.readInt32LE(8) | |
| // opCode để xác định kiểu message nhận về. | |
| const opCode = data.readInt32LE(12) | |
| // Nếu data hiện tại chưa đủ messageLength thì chưa parse được đầy đủ. | |
| if (data.length < messageLength) { | |
| console.log(`Incomplete MongoDB message: got ${data.length}, expected ${messageLength}`) | |
| return | |
| } | |
| // Chỉ xử lý OP_MSG; kiểu khác thì bỏ qua để tránh parse sai format. | |
| if (opCode !== 2013) { | |
| console.log('Unexpected opCode:', opCode) | |
| return | |
| } | |
| // 4 bytes sau header cơ bản là flagBits của OP_MSG. | |
| const flags = data.readUInt32LE(16) | |
| // Bắt đầu đọc section từ offset 20 (16 header + 4 flagBits). | |
| let offset = 20 | |
| // Mảng chứa các document đã deserialize từ response. | |
| const docs: unknown[] = [] | |
| // Duyệt từng section cho đến hết message. | |
| while (offset < messageLength) { | |
| // Mỗi section bắt đầu bằng 1 byte kind. | |
| const kind = data.readUInt8(offset) | |
| // Sau khi đọc kind thì nhích con trỏ 1 byte. | |
| offset += 1 | |
| // Kind 0: section chứa đúng 1 BSON document. | |
| if (kind === 0) { | |
| // 4 bytes đầu của BSON luôn là kích thước chính document đó. | |
| const bsonSize = data.readInt32LE(offset) | |
| // Tính vị trí kết thúc document BSON. | |
| const bsonEnd = offset + bsonSize | |
| // Chặn lỗi nếu buffer thực tế ngắn hơn BSON khai báo. | |
| if (bsonEnd > data.length) { | |
| throw new Error(`Truncated BSON document: end=${bsonEnd}, bufferLength=${data.length}`) | |
| } | |
| // Cắt đúng bytes của BSON document. | |
| const bsonBuffer = data.slice(offset, bsonEnd) | |
| // Deserialize BSON thành object JS và đưa vào danh sách kết quả. | |
| docs.push(BSON.deserialize(bsonBuffer)) | |
| // Dời offset tới ngay sau document vừa đọc. | |
| offset = bsonEnd | |
| // Xử lý section tiếp theo. | |
| continue | |
| } | |
| // Kind 1: document sequence (nhiều BSON docs trong cùng section). | |
| if (kind === 1) { | |
| // Section kind 1: document sequence = int32 size + cstring id + docs | |
| // sectionSize là kích thước phần còn lại của section (không gồm byte kind). | |
| const sectionSize = data.readInt32LE(offset) | |
| // sectionEnd là vị trí kết thúc section này. | |
| const sectionEnd = offset + sectionSize | |
| // Skip cstring identifier | |
| // Bỏ qua section size (4 bytes) để tới chuỗi cstring identifier. | |
| offset += 4 | |
| // Bỏ qua cstring (kết thúc bằng byte 0). | |
| while (offset < sectionEnd && data[offset] !== 0) { | |
| offset += 1 | |
| } | |
| // Bỏ byte 0 kết thúc cstring. | |
| offset += 1 | |
| // Sau cstring là chuỗi các BSON docs liên tiếp. | |
| while (offset < sectionEnd) { | |
| // Đọc kích thước BSON kế tiếp. | |
| const bsonSize = data.readInt32LE(offset) | |
| // Tính vị trí kết thúc BSON đó. | |
| const bsonEnd = offset + bsonSize | |
| // Chặn lỗi nếu BSON vượt quá buffer đang có. | |
| if (bsonEnd > data.length) { | |
| throw new Error(`Truncated BSON in section 1: end=${bsonEnd}, bufferLength=${data.length}`) | |
| } | |
| // Cắt và deserialize từng BSON document trong sequence. | |
| const bsonBuffer = data.slice(offset, bsonEnd) | |
| docs.push(BSON.deserialize(bsonBuffer)) | |
| // Dịch con trỏ sang BSON kế tiếp. | |
| offset = bsonEnd | |
| } | |
| // Xử lý section tiếp theo (nếu còn). | |
| continue | |
| } | |
| // Nếu gặp kind lạ thì báo lỗi để debug protocol. | |
| throw new Error(`Unknown OP_MSG section kind: ${kind}`) | |
| } | |
| // In thông tin metadata của response đã parse. | |
| console.log('Parsed response:', { messageLength, responseTo, opCode, flags }) | |
| // In các BSON documents đã chuyển thành object JS. | |
| console.log('Parsed BSON documents:', docs) | |
| }) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment