国产av日韩一区二区三区精品,成人性爱视频在线观看,国产,欧美,日韩,一区,www.成色av久久成人,2222eeee成人天堂

首頁 php教程 PHP開發(fā) GIT 傳輸協(xié)議實現(xiàn)

GIT 傳輸協(xié)議實現(xiàn)

Nov 23, 2016 pm 02:18 PM

GIT 傳輸協(xié)議實現(xiàn)

在 GIT 的三種主流傳輸協(xié)議 HTTP SSH GIT 中,GIT 協(xié)議是最少被使用的協(xié)議(也就是 URL 以?git://?開始的協(xié)議)。 這是由于 git 協(xié)議的權限控制幾乎沒有,要么全部可讀,要么全部可寫,要么全部可讀寫。所以對于代碼托管平臺來說, git 協(xié)議的目的僅僅是為了支持 公開項目的只讀訪問。

在 git 的各種傳輸協(xié)議中,git 協(xié)議無疑是最高效的,HTTP 受限于 HTTP 的特性,傳輸過程需要構造 HTTP 請求和響應。 如果是 HTTPS 還涉及到加密解密。另外 HTTP 的超時設置,以及包體大小限制都會影響用戶體驗。

而 SSH 協(xié)議的性能問題主要集中在加密解密上。當然相對于用戶的信息安全來說,這些代價都是可以接受。

git 協(xié)議實際上相當于 SSH 無加密無驗證,也就無從談起權限控制,但實際上代碼托管平臺內(nèi)部的一些同步服務,如果使用 git 協(xié)議實現(xiàn),將會得到很大的性能提升。

傳輸協(xié)議規(guī)范

git 協(xié)議的技術文檔可以從 git 源碼目錄的?Documentation/technical?找到,即?Packfile transfer protocols?創(chuàng)建 TCP 連接后,git 客戶端率先發(fā)送請求體,請求格式基于 BNF 的描述如下:

git-proto-request = request-command SP pathname NUL [ host-parameter NUL ]
request-command   = "git-upload-pack" / "git-receive-pack" / "git-upload-archive"   ; case sensitive
pathname          = *( %x01-ff ) ; exclude NUL
host-parameter    = "host=" hostname [ ":" port ]

一個例子如下:

0033git-upload-pack /project.git\0host=myserver.com\0

在 git 的協(xié)議中,pkt-line 是非常有意思的設計,行前 4 個字節(jié)表示整個行長,長度包括其前 4 字節(jié), 但是有個特例,0000 其代表行長為 0,但其自身長度是 4。

下面是一個關于請求的結構體:

struct GitRequest{
    std::string command;
    std::string path;
    std::string host;
};

git 有自帶的 git-daemon 實現(xiàn),這個服務程序監(jiān)聽 9418 端口,在接收到客戶端的請求后,先要判斷 command 是 否是被允許的,git 協(xié)議中有 fetch 和 push 以及 archive 之類的操作,分別對應的服務器上的命令是 git-upload-pack git-receive-pack git-upload-archive。HTTP 只會支持前兩種,SSH 會支持三種,而 代碼托管平臺的 git 通常支持的 是 git-upload-pack git-upload-archive。

當不允許的命令被接入時需要發(fā)送錯誤信息給客戶端,這個信息在不同的 git-daemon 實現(xiàn)中也不一樣,大體 如下所示。

001bERR service not enabled

git-daemon 將對請求路徑進行轉換,以期得到在服務器上的絕對路徑,同時可以判斷路徑是否存在,不存在時 可以給客戶端發(fā)送 Repository Not Found。而 host 可能時域名也可能時 ip 地址,當然也可以包括端口。 服務器可以在這里做進一步的限制,出于安全考慮應當考慮到請求是可以被偽造的。

客戶端發(fā)送請求過去后,服務器將啟動相應的命令,將命令標準錯誤和標準輸出的內(nèi)容發(fā)送給客戶端,將客戶端 傳輸過來的數(shù)據(jù)寫入到命令的標準輸入中來。

在請求體中,命令為 git-upload-pack /project.git 在服務器上運行時,就會類似

git-upload-pack ${RepositoriesRoot}/project.git

出于限制連接的目的,一般還會添加 --timeout=60 這樣的參數(shù)。timeout 并不是整個操作過程的超時。

與 HTTP 不同的是,git 協(xié)議的命令中沒有參數(shù) --stateless-rpc 和 --advertise-refs ,在 HTTP 中,兩個參數(shù)都存在時, 只輸出存儲庫的引用列表與 capabilities,與之對于的是 GET /repository.git/info/refs?service=git-upload(receive)-pack , 當只有 --stateless-rpc 時,等待客戶端的數(shù)據(jù),然后解析發(fā)送數(shù)據(jù)給客戶端,,與之對應的是 POST /repository.git/git-upload(receive)-pack。

進程輸入輸出的讀寫

在 C 語言中,有 popen 函數(shù),可以創(chuàng)建一個進程,并將進程的標準輸出或標準輸入創(chuàng)建成一個文件指針,即 FILE*其他可以使用 C 函數(shù)的語言很多也提供了類似的實現(xiàn),比如 Ruby,基于 Ruby 的 git HTTP 服務器 grack 正是使用 的 popen,相比與其他語言改造的 popen,C 語言中 popen 存在了一些缺陷,比如無法同時讀寫,如果要輸出標準 錯誤,需要在命令參數(shù)中額外的將標準錯誤重定向到標準輸出。

在 musl libc 的中,popen 的實現(xiàn)如下:

FILE *popen(const char *cmd, const char *mode)
{
    int p[2], op, e;
    pid_t pid;
    FILE *f;
    posix_spawn_file_actions_t fa;

    if (*mode == 'r') {
        op = 0;
    } else if (*mode == 'w') {
        op = 1;
    } else {
        errno = EINVAL;
        return 0;
    }

    if (pipe2(p, O_CLOEXEC)) return NULL;
    f = fdopen(p[op], mode);
    if (!f) {
        __syscall(SYS_close, p[0]);
        __syscall(SYS_close, p[1]);
        return NULL;
    }
    FLOCK(f);

    /* If the child's end of the pipe happens to already be on the final
     * fd number to which it will be assigned (either 0 or 1), it must
     * be moved to a different fd. Otherwise, there is no safe way to
     * remove the close-on-exec flag in the child without also creating
     * a file descriptor leak race condition in the parent. */
    if (p[1-op] == 1-op) {
        int tmp = fcntl(1-op, F_DUPFD_CLOEXEC, 0);
        if (tmp < 0) {
            e = errno;
            goto fail;
        }
        __syscall(SYS_close, p[1-op]);
        p[1-op] = tmp;
    }

    e = ENOMEM;
    if (!posix_spawn_file_actions_init(&fa)) {
        if (!posix_spawn_file_actions_adddup2(&fa, p[1-op], 1-op)) {
            if (!(e = posix_spawn(&pid, "/bin/sh", &fa, 0,
                (char *[]){ "sh", "-c", (char *)cmd, 0 }, __environ))) {
                posix_spawn_file_actions_destroy(&fa);
                f->pipe_pid = pid;
                if (!strchr(mode, &#39;e&#39;))
                    fcntl(p[op], F_SETFD, 0);
                __syscall(SYS_close, p[1-op]);
                FUNLOCK(f);
                return f;
            }
        }
        posix_spawn_file_actions_destroy(&fa);
    }
fail:
    fclose(f);
    __syscall(SYS_close, p[1-op]);

    errno = e;
    return 0;
}

在 Windows Visual C++ 中,popen 源碼在?C:\Program Files (x86)\Windows Kits\10\Source\${SDKVersion}\ucrt\conio\popen.cpp?, 按照 MSDN 文檔說明,Windows 32 GUI 程序,即 subsystem 是 Windows 的程序,使用 popen 可能導致程序無限失去響應。

所以在筆者實現(xiàn) git-daemon 及其他 git 服務器時,都不會使用 popen 這個函數(shù)。

為了支持跨平臺和簡化編程,筆者在實現(xiàn) svn 代理服務器時就使用了 Boost Asio 庫,后來也用 Asio 實現(xiàn)過一個 git 遠程命令服務, 每一個客戶端與服務器連接后,服務器啟動程序,需要創(chuàng)建 3 條管道,分別是 子進程的標準輸入 輸出 錯誤,即 stdout stdin stderr, 然后注冊讀寫異步事件,將子進程的輸出與錯誤寫入到 socket 發(fā)送出去,讀取 socket 寫入到子進程的標準輸入中。

在 POSIX 系統(tǒng)中,boost 有一個文件描述符類?boost::asio::posix::stream_descriptor?這個類不能是常規(guī)文件,以前用 go 做 HTTP 前端 沒注意就 coredump 掉。

在 Windows 系統(tǒng)中,boost 有文件句柄類?boost::asio::windows::stream_handle?此處的文件應當支持隨機讀取,比如命名管道(當然 在 Windows 系統(tǒng)的,匿名管道實際上也是命名管道的一種特例實現(xiàn))。

以上兩種類都支持?async_read?async_write?,所以可以很方便的實現(xiàn)異步的讀取。

上面的做法,唯一的缺陷是性能并不是非常高,代碼邏輯也比較復雜,當然好處是,錯誤異??煽匾恍?。

在 Linux 網(wǎng)絡通信中,類似與 git 協(xié)議這樣讀取子進程輸入輸出的服務程序的傳統(tǒng)做法是,將 子進程的 IO 重定向到 socket, 值得注意的是 boost 中 socket 是異步非阻塞的,然而,git 命令的標準輸入標準錯誤標準輸出都是同步的,所以在 fork 子進程之 前,需要將 socket 設置為同步阻塞,當 fork 失敗時,要設置回來。

socket_.native_non_blocking(false);

另外,為了記錄子進程是否異常退出,需要注冊信號 SIGCHLD 并且使用 waitpid 函數(shù)去等待,boost 就有?boost::asio::signal_set::async_wait?當然,如果你開發(fā)這樣一個服務,會發(fā)現(xiàn),頻繁的啟動子進程,響應信號,管理連接,這些操作才是性能的短板。

一般而言,Windows 平臺的 IO 并不能重定向到 socket,實際上,你如果使用 IOCP 也可以達到相應的效率。還有,Windows 的 socket API WSASocket WSADuplicateSocket 復制句柄 DuplicateHandle ,這些可以好好利用。

其他

對于非代碼托管平臺的從業(yè)者來說,上面的相關內(nèi)容可能顯得無足輕重,不過,網(wǎng)絡編程都是殊途同歸,最后核心理念都是類似的。關于 git-daemon 如果筆者有時間會實現(xiàn)一個跨平臺的簡易版并開源。


本站聲明
本文內(nèi)容由網(wǎng)友自發(fā)貢獻,版權歸原作者所有,本站不承擔相應法律責任。如您發(fā)現(xiàn)有涉嫌抄襲侵權的內(nèi)容,請聯(lián)系admin@php.cn

熱AI工具

Undress AI Tool

Undress AI Tool

免費脫衣服圖片

Undresser.AI Undress

Undresser.AI Undress

人工智能驅動的應用程序,用于創(chuàng)建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用于從照片中去除衣服的在線人工智能工具。

Clothoff.io

Clothoff.io

AI脫衣機

Video Face Swap

Video Face Swap

使用我們完全免費的人工智能換臉工具輕松在任何視頻中換臉!

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的代碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

功能強大的PHP集成開發(fā)環(huán)境

Dreamweaver CS6

Dreamweaver CS6

視覺化網(wǎng)頁開發(fā)工具

SublimeText3 Mac版

SublimeText3 Mac版

神級代碼編輯軟件(SublimeText3)