HTTP Request in TCP

net

파이썬으로 개발된 OCR 서버와 TCP로 이미지 데이터를 http 형식으로 전송하는 방법을 알아내기 위해, Node.js 의 내장 모듈 net 을 사용했다.

Http Request

Host: localhost:3000
User-Agent: WithMe_TCP/1.0
Accept: */*
Content-Length: 1043
Content-Type: multipart/form-data; boundary=------------------------d599bfa08c4b3286

--------------------------d599bfa08c4b3286
Content-Disposition: form-data; name="file"; filename="icon.png"
Content-Type: image/png

[Binary Data]
--------------------------d599bfa08c4b3286--

Client Code

import net from 'net';
import fs from 'fs';

const options = {
    host: 'localhost',
    path: '/ocr',
    port: 3000,
    method: 'POST',
    image: '../ocr/testData/icon.png'
};

(async() => {
    const serverSocket = net.connect(options.port, options.host, async () => {
        serverSocket.on("data", (b) => {
            console.log(b.toString());
        });
        serverSocket.on('error', (e) => {console.log(e)})

        const Image = await ReadImage(options.image);
        const Boundary = '------------------------d599bfa08c4b3286';
        const Body = [
            `--${Boundary}`,
            `Content-Disposition: form-data; name="file"; filename="${options.image.split('/').pop()}"`,
            `Content-Type: image/${Image.extension}`,
            '',
            Image.data.toString('binary'),
            `--${Boundary}--`,
            ''
        ]
        const Header = [
            `${options.method} ${options.path} HTTP/1.1`,
            `Host: ${options.host}${(options.port!=80)?':'+options.port:''}`,
            'User-Agent: WithMe_TCP/1.0',
            'Accept: */*',
            `Content-Length: ${Buffer.from(Body.join('\r\n'), 'binary').byteLength}`,
            `Content-Type: multipart/form-data; boundary=${Boundary}`,
            '',
            ''
        ]

        let BinHeader = new Uint8Array(Buffer.from(Header.join('\r\n'), 'binary').byteLength);
        let BinBody = new Uint8Array(Buffer.from(Body.join('\r\n'), 'binary').byteLength);
        
        Buffer.from(Header.join('\r\n'), 'binary').forEach((v, idx) => {
            BinHeader.set([v], idx)
        })

        Buffer.from(Body.join('\r\n'), 'binary').forEach((v, idx) => {
            BinBody.set([v], idx);
        })


        console.log(BinHeader);
        console.log(BinBody);

        fs.writeFileSync('./http', Header.join('\r\n') + Body.join('\r\n'), 'binary');

        serverSocket.write(BinHeader);
        serverSocket.write(BinBody);
    });
})();

function ReadImage(location: string): Promise<{data: Buffer, extension: undefined | string}> {
    return new Promise(resolve => {
        const data = fs.readFileSync(location);
        console.log(data.slice(0, 8),data.length);

        const extension = location.split('.').pop();

        return resolve({data: data, extension: extension})
    })
}

설명

http는 Content-Type에 따라 Body의 구조가 다르다

MIME 에 따라 json, binary 등 많은 형식이 있다. 위 코드에서는 이미지 데이터를 전송하기 위해 multipart/form-data 를 사용했다.

boundary

multipart 라는 이름에서 보이듯이, Body가 여러개의 파트로 나누어져 있고 이를 분리하는 역할을 하는 것이 boundary 이다.

시작은 "--BoundaryString" 로 시작하며, 데이터의 맨 마지막에 "--BoundaryString--" 의 형식으로 끝이 난다.

curl

http request를 콘솔에서 보내주는 도구인데, --trace 옵션을 통하여 서버로 전송되는 모든 데이터를 하나하나 뜯어볼 수 있어 도움이 많이 되었다.

--trace file
--trace-ascii file
위 명령어를 통해 파일에 저장가능

BinaryData

Nodejs의 문제인지는 모르겠지만, socket.write의 인자는 string | Uint8Array 를 처리할 수 있다.
그런데 string을 넘겨주니 서버에서 받는 데이터에 변조가 일어나 정상적인 처리가 불가능했다.

파일 인코딩도 바꿔보며 테스트 한 결과 Binary 형식에서 문자열로 변경하는 중에 일어나는 문제로 보여 직접 Binary데이터를 Uint8Array 형태로 변환해 주었다.

좋은 웹페이지 즐겨찾기