# http 架構
[](https://hackmd.io/HfXJwWd1TP2et8gHkc03bQ)
首先在開始撰寫之前,先來個好文推薦,我有很多觀念是透過這個網站理解的
[NotFalse 技術客](https://notfalse.net/http-series)
---
基本上說到架構就需要提到 [OSI模型](https://zh.wikipedia.org/wiki/OSI%E6%A8%A1%E5%9E%8B)
的七層網路架構,但是對於一二三層的部分我們都不介紹了,感謝前人的努力讓我們對網路的基本架構都不需要煩惱</br>
我們直接從第四層、傳輸層來對http的架構開始介紹,第四層包括 TCP、UDP、DCCP、SSL等等的 </br>
基本上以上所說的TCP協議等等的,在現代的OS系統皆有遵循以及提供system call提供調用</br>
目前來說http是建構在TCP之上的,所以希望讀者能夠有一些TCP的概念(三方交握,四方揮手)以及使用過Socket等等的
---
剛剛說過目前http是建構在tcp上
( 先不提gooogle想推的HTTP/3 [QUIC](https://zh.wikipedia.org/wiki/QUIC) )</br>
所以我們能夠透過撰寫 Socket 的 Tcp Server 來接 http request 觀察 http 的請求是什麼樣子 [參考文章](https://notfalse.net/39/http-message-format)
```
GET / HTTP/1.1
Host: 127.0.0.1:5678
Connection: keep-alive
sec-ch-ua: "Chromium";v="88", "Google Chrome";v="88", ";Not A Brand";v="99"
sec-ch-ua-mobile: ?0
DNT: 1
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.192 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-TW,zh;q=0.9,ja;q=0.8,en-US;q=0.7,en;q=0.6,zh-CN;q=0.5
```
我們會先得到這段訊息,這段訊息其實就是 http 的 起始行 (start-line ) 和 header </br>
> 起始行 (start-line) :</br>
> 就是第一行 `GET / HTTP/1.1`</br>
> 他會提供 request 的 method 以及 http 的版本,假如你有在網址加上一些request-target , query像是這樣`http://127.0.0.1:5678/?id=1`那起始行會像這樣`GET /?id=1 HTTP/1.1`</br>
接下來是 header 他存者很多 request 的相關資料,比如用的瀏覽器與版本、語言等等的,也可以在前端設定傳遞一些資訊給後端
接下來可能有人覺得奇怪 http 只有 header 嗎? 那 POST 怎麼辦怎麼沒有 Body,有的只是剛剛的 method 為 GET 假如我們使用 POST 得到的結果會像這樣
```
POST / HTTP/1.1
Host: 127.0.0.1:5678
Connection: keep-alive
Content-Length: 11
DNT: 1
sec-ch-ua-mobile: ?0
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.192 Safari/537.36
sec-ch-u
a: "Chromium";v="88", "Google Chrome";v="88", ";Not A Brand";v="99"
Content-Type: application/json
Accept: */*
Origin: chrome-extension://aejoelaoggembcahagimdiliamlcdmfm
Sec-Fetch-Site: none
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Accept-Encodin
g: gzip, deflate, br
Accept-Language: zh-TW,zh;q=0.9,ja;q=0.8,en-US;q=0.7,en;q=0.6,zh-CN;q=0.5
Hello Word!
```
大部分的地方都差不多,但是在Body上會有一行空白</br>
> 這個是 CRLF(carriage return followed by line feed) 簡單來說他不是一般的換行(`\n`),而是比較嚴謹的換行(`\r\n`),但好像現代語言用`\n`就可以了</br>
假如未來有需要撰寫時要記得在header後面會有一行換行
---
request 的部分講完了接下來換成講 response 基本上大架構是一致的,比較不同的是起始行會長這樣`HTTP/1.1 200 OK`</br>
那個 200 就是成功連線,但是200平常比較少見(因為成功通常不會特別跟你說成功),平常比較常見的通常是404(網頁不存在)或403等等的錯誤訊息,有興趣的可以去找找 [HTTP Status Code](https://zh.wikipedia.org/wiki/HTTP%E7%8A%B6%E6%80%81%E7%A0%81)</br>
簡單來說在處理 http server 時假如要給狀態碼就是在起始行做設定
---
> 假如有人要測試</br>
可以使用 : </br>
[傳request軟體](https://chrome.google.com/webstore/detail/talend-api-tester-free-ed/aejoelaoggembcahagimdiliamlcdmfm)
or [Postman](https://www.postman.com/downloads/)</br>
and 伺服器程式
``` c
#include<stdio.h>
#include<stdlib.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<string.h>
#include<arpa/inet.h>
#include<unistd.h>
int main()
{
struct sockaddr_in server,client;
int sock,readSize,addresssSize,i,csock;
char buf[256];
bzero(&server,sizeof(server));
server.sin_family = PF_INET;
server.sin_addr.s_addr = inet_addr("127.0.0.1");
server.sin_port = htons(5678);
sock = socket(PF_INET,SOCK_STREAM,0);
bind(sock,(struct sockaddr*)&server,sizeof(server));
listen(sock,5);
system("lsof -i:5678");
addresssSize = sizeof(client);
csock = accept(sock,(struct sockaddr*)&client,&addresssSize);
while(1)
{
readSize = recv(csock,buf,sizeof(buf),0);
buf[readSize] = '\0';
printf("%s \n",buf);
}
close(sock);
}
```