HTTP相關總結

HTTP/1.1

一、簡介

       HTTP協議:超文本傳輸協議(HyperText Transfer Protocol, HTTP),使用TCP作爲運輸層協議。HTTP協議的長連接和短連接,實質上是TCP協議的長連接和短連接。HTTP 是一個無狀態協議,但是web站點往往希望能夠識別用戶,因此有了cookie和session。

二、HTTP報文格式

 

   Client request:
     GET /hello.txt HTTP/1.1
     User-Agent: curl/7.16.3 libcurl/7.16.3 OpenSSL/0.9.7l zlib/1.2.3
     Host: www.example.com
     Accept-Language: en, mi


   Server response:
     HTTP/1.1 200 OK
     Date: Mon, 27 Jul 2009 12:28:53 GMT
     Server: Apache
     Last-Modified: Wed, 22 Jul 2009 19:15:56 GMT
     ETag: "34aa387-d-1568eb00"
     Accept-Ranges: bytes
     Content-Length: 51
     Vary: Accept-Encoding
     Content-Type: text/plain

     Hello World! My payload includes a trailing CRLF.

 

1、HTTP請求報文 

image.png

 

請求行:

     請求行由請求方法字段、URL字段和HTTP協議版本字段3個字段組成,它們用空格分隔。例如,GET /index.html HTTP/1.1。

HTTP協議的請求方法有GET、POST、HEAD、PUT、DELETE、TRACE等。

注意:HTTP協議本身並沒有限制URL的長度,實際中URL長度限制是實現http協議的客戶端(瀏覽器等)和服務器限制的。

 

首部(Header Fields)

請求頭部由關鍵字/值對組成,每行一對,關鍵字和值用英文冒號“:”分隔。請求頭部通知服務器有關於客戶端請求的信息,典型的首部頭(包括請求頭或者響應頭)有:

User-Agent:產生請求的瀏覽器類型。

Accept:客戶端可識別的內容類型列表。

Host:請求的主機名,允許多個域名同處一個IP地址,即虛擬主機。

Cookie:該請求域名下的cookie值。

Set-Cookie:_security_token=51568727949026296;Path=/;Domain=.dasouche-fin.net;Max-Age=600000。

Content-Type:請求的與實體對應的MIME信息,application/x-www-form-urlencoded、text/html;charset=UTF-8等。

Connection:keep-alive,持久連接,HTTP/1.0需要顯示聲明,HTTP/1.1默認開啓。關閉:Connection:close。

Referer:該首部包含了當前請求頁面的來源頁面的地址,即表示當前頁面是通過此來源頁面裏的鏈接進入的。服務端一般使用 Referer 首部識別訪問來源,可能會以此進行統計分析、日誌記錄以及緩存優化等。

Accept-Language:瀏覽器可接受的語言,比如Accept-Language:en,zh。

Accept-Encoding:客戶端能夠理解的內容編碼方式——通常是某種壓縮算法——進行通知,通過內容協商的方式,服務端會選擇一個客戶端提議的方式,使用並在響應報文首部Content-Encoding中通知客戶端該選擇。需要注意的是,服務器端並不強制要求一定使用何種壓縮模式。

注意:有字符集限制,中文漢字可能需要URLEncoder.encode、URLDecoder.decode

 

實體主體:(Message Body)

實體主體是可選的,如GET時,實體主體爲空;Post請求時使用。

 

2、HTTP響應報文 

image.png

與請求報文類似,只是請求行與狀態行的不同。在RFC中,請求行/狀態行稱爲起始行(Start Line,狀態值翻譯爲“短語”(reason-phrase)更好一些.

狀態碼:There are five values for the first digit:

   o  1xx (Informational): The request was received, continuing process

   o  2xx (Successful): The request was successfully received, understood, and accepted

   o  3xx (Redirection): Further action needs to be taken in order to complete the request

   o  4xx (Client Error): The request contains bad syntax or cannot be fulfilled

   o  5xx (Server Error): The server failed to fulfill an apparently valid request

      在網絡世界中,瀏覽器絕對不是唯一的 HTTP 客戶端工具,上面圖片中提到的 Postman等工具也是HTTP客戶端實現的一種,其實還有更多,只不過瀏覽器的使用最爲廣泛而已;明確不能將瀏覽器對HTTP協議的實現,當做了HTTP協議本身。

 

三、跨域問題

1、跨域問題的產生

        跨域問題的產生原因:瀏覽器的同源策略。爲什麼要這個策略?安全問題,缺少易受到跨站腳本攻擊(XSS)、跨站請求僞造(CSRF)等攻擊。

所謂同源是指:協議、域名、端口相同。

image.png

參考:

image.png

注意:如果一個主機IP上有多個域名應用,在跨域問題上,域僅僅是通過“URL的首部”來識別而不會根據域名對應的IP地址是否相同來判斷。

 

2、解決方案

1、原生js的JSONP

       由於同源策略,一般來說位於server1.example.com的網頁無法與 server2.example.com的服務器溝通,而HTML的 <script>元素是一個例外。利用 <script>元素的這個開放策略,網頁可以得到從其他來源動態產生的JSON數據,而這種使用模式就是所謂的 JSONP。

演示代碼:

服務端:

 

const url = require('url');

require('http').createServer((req, res) => {
    const data = {
        x: '隔壁超市薯片半價啦'
    };
    const callback = url.parse(req.url, true).query.callback
    res.writeHead(200);
    //回調原頁面上函數處理返回結果
    res.end(`${callback}(${JSON.stringify(data)})`);

}).listen(3000, 'localhost');
console.log('服務器已啓動...');

瀏覽器端:http://local.dasouche-fin.net:8086/index.jsp

 

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
    <title>$Title$</title>
  </head>
  <body>
  <script>
      function jsonpCallback(data){
          alert('跨域成功!'+data.x)
      }
  </script>
<script src="http://localhost:3000/?callback=jsonpCallback"></script>
</body>
</html>

缺點:

只能是get請求,因爲<script >是GET請求,不安全可能會遭受XSS攻擊。

 

2、jQuery的jsonp形式

jsonp技術跟ajax沒有任何關係,只不過jQuery將jsonp和ajax技術做了結合。

有很多種寫法,只列出一種:

 

$.ajax({
    url:'http://外域.com/xxx.php',
    dataType:"jsonp",
    jsonp: "callback",
    jsonpCallback:"ooo",
    success:function(data){
        console.log(data);
    }
});

 

 

3、跨域資源共享 CORS

       CORS(Cross-Origin Resource Sharing)是一種機制(W3C 標準),它使用額外的HTTP 頭來告訴瀏覽器  讓運行在一個 origin (domain) 上的Web應用被准許訪問來自不同源服務器上的指定的資源,需要瀏覽器和服務端都支持才行。

 

3.1 簡單請求

使用下列方法之一:

GET

HEAD

POST

HTTP的頭信息不超出以下幾種字段:

Accept

Accept-Language

Content-Language

Last-Event-ID

Content-Type:只限於三個值application/x-www-form-urlencoded、multipart/form-data、text/plain

Content-Type 的值僅限於下列三者之一:

text/plain

multipart/form-data

application/x-www-form-urlencoded

請求中的任意XMLHttpRequestUpload 對象均沒有註冊任何事件監聽器;XMLHttpRequestUpload 對象可以使用 XMLHttpRequest.upload 屬性訪問。

請求中沒有使用 ReadableStream 對象。

 

簡單請求流程:

image.png

分別查看請求報文和響應報文:

GET /resources/public-data/ HTTP/1.1
Host: bar.other
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b3pre) Gecko/20081130 Minefield/3.1b3pre
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Connection: keep-alive
Referer: http://foo.example/examples/access-control/simpleXSInvocation.html
Origin: http://foo.example


HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 00:23:53 GMT
Server: Apache/2.0.61 
Access-Control-Allow-Origin: *
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: application/xml

[XML Data]

 

 

3.2 非簡單請求

      非簡單請求是那種對服務器有特殊要求的請求,比如請求方法是PUT或DELETE,或者Content-Type字段的類型是application/json。

      1、非簡單請求的CORS請求,會在正式通信之前,增加一次HTTP查詢請求,稱爲"預檢"請求(preflight)。

      2、服務器收到"預檢"請求以後,檢查了Origin、Access-Control-Request-Method和Access-Control-Request-Headers字段以後,確認允許跨源請求,就可以做出迴應。

      3、如果瀏覽器否定了"預檢"請求,會返回一個正常的HTTP迴應,但是沒有任何CORS相關的頭信息字段。這時,瀏覽器就會認定,服務器不同意預檢請求,因此觸發一個錯誤,被XMLHttpRequest對象的onerror回調函數捕獲。控制檯會打印出跨域報錯信息。

      4、一旦服務器通過了"預檢"請求,以後每次瀏覽器正常的CORS請求,就都跟簡單請求一樣,會有一個Origin頭信息字段。服務器的迴應,也都會有一個Access-Control-Allow-Origin頭信息字段。

 

非簡單請求流程:

 

image.png

預檢請求的請求報文和響應報文:

OPTIONS /resources/post-here/ HTTP/1.1
Host: bar.other
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b3pre) Gecko/20081130 Minefield/3.1b3pre
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Connection: keep-alive
Origin: http://foo.example
Access-Control-Request-Method: POST
Access-Control-Request-Headers: X-PINGOTHER, Content-Type


HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:39 GMT
Server: Apache/2.0.61 (Unix)
Access-Control-Allow-Origin: http://foo.example
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: X-PINGOTHER, Content-Type
Access-Control-Max-Age: 86400
Vary: Accept-Encoding, Origin
Content-Encoding: gzip
Content-Length: 0
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Content-Type: text/plain

預檢請求完成之後,發送實際請求報文和響應報文:

POST /resources/post-here/ HTTP/1.1
Host: bar.other
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b3pre) Gecko/20081130 Minefield/3.1b3pre
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Connection: keep-alive
X-PINGOTHER: pingpong
Content-Type: text/xml; charset=UTF-8
Referer: http://foo.example/examples/preflightInvocation.html
Content-Length: 55
Origin: http://foo.example
Pragma: no-cache
Cache-Control: no-cache

<?xml version="1.0"?><person><name>Arun</name></person>



HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:40 GMT
Server: Apache/2.0.61 (Unix)
Access-Control-Allow-Origin: http://foo.example
Vary: Accept-Encoding, Origin
Content-Encoding: gzip
Content-Length: 235
Keep-Alive: timeout=2, max=99
Connection: Keep-Alive
Content-Type: text/plain

[Some GZIP'd payload]

 

3.3 CORS相關首部

Request headers:

  • Origin:

      指示了請求來自於哪個站點。該字段僅指示服務器名稱,並不包含任何路徑信息。

 

  • Access-Control-Request-Method:

     請求首部  Access-Control-Request-Method 出現於 preflight request (預檢請求)中,用於通知服務器在真正的請求中會採用哪種  HTTP 方法。因爲預檢請求所使用的方法總是 OPTIONS ,與實際請求所使用的方法不一樣,所以這個首部是必要的。

 

  • Access-Control-Request-Headers

      請求首部  Access-Control-Request-Headers 出現於 preflight request (預檢請求)中,用於通知服務器在真正的請求中會採用哪些請求首部。如果瀏覽器請求包括Access-Control-Request-Headers字段,則Access-Control-Allow-Headers字段是必需的。它也是一個逗號分隔的字符串,表明服務器支持的所有頭信息字段,不限於瀏覽器在"預檢"中請求的字段。

 

Response headers:

  • Access-Control-Allow-Origin

      響應頭指定了該響應的資源是否被允許與給定的origin共享。該字段是必須的。它的值要麼是請求時Origin字段的值,要麼是一個*,表示接受任意域名的請求。

 

  • Access-Control-Allow-Methods

      該字段必需,它的值是逗號分隔的一個字符串,表明服務器支持的所有跨域請求的方法。注意,返回的是所有支持的方法,而不單是瀏覽器請求的那個方法。這是爲了避免多次"預檢"請求。

 

  • Access-Control-Allow-Credentials

     CORS請求默認不發送Cookie和HTTP認證信息。如果要把Cookie發到服務器,一方面要服務器同意,指定Access-Control-Allow-Credentials字段。

 

  • Access-Control-Expose-Headers

     該字段可選。CORS請求時,XMLHttpRequest對象的getResponseHeader()方法只能拿到6個基本字段:Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma。如果想拿到其他字段,就必須在Access-Control-Expose-Headers裏面指定。

 

  • Access-Control-Max-Age

      該字段可選,用來指定本次預檢請求的有效期,單位爲秒。上面結果中,有效期是20天(1728000秒),即允許緩存該條迴應1728000秒(即20天),在此期間,不用發出另一條預檢請求。

  • Access-Control-Allow-Headers

       如果瀏覽器請求包括Access-Control-Request-Headers字段,則Access-Control-Allow-Headers字段是必需的。它也是一個逗號分隔的字符串,表明服務器支持的所有頭信息字段,不限於瀏覽器在"預檢"中請求的字段。

 

CORS與JSONP的使用目的相同,但是比JSONP更強大。

JSONP只支持GET請求,CORS支持所有類型的HTTP請求。JSONP的優勢在於支持老式瀏覽器,以及可以向不支持CORS的網站請求數據。

 

4、Nginx反向代理

正向代理 和反向代理 ? ?

......

 

 

四、相關文檔

HTTP/1.1 RFC文檔:https://tools.ietf.org/html/rfc7230#section-2.7.1

Cookie RFC文檔:https://tools.ietf.org/html/rfc6265#section-4.2

XSS攻擊介紹文檔:https://tech.meituan.com/2018/09/27/fe-security.html

CSRF介紹文檔:https://tech.meituan.com/2018/10/11/fe-security-csrf.html
CORS文檔:https://www.w3.org/TR/cors/
                    https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Access_control_CORS

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章