やりたいこと
かっこよくターミナルからHTTPリクエストを送りたいと思いませんか?
TCPコネクションを nc
コマンドで確立し、その後TCP通信で GET
リクエストを送りましょう。
リクエストを送るホストは、ご存知の google.com
の ポート 80
とします。
その前にHTTPとは?
OSI参照モデルでいうアプリケーション層にあるHTTPというプロトコルは、Webページをブラウジングするためや、ログインIDやパスワードを送ることにも利用されるWWW(World Wide Web)を代表するプロトコルです。
Webサーバは、HTTPのフォーマットで書かれた HTTPリクエスト
をクライアント(ブラウザ)から受け取り、 ステータスコードとともに HTTPレスポンス
をクライアントに返します。
HTTPリクエストのメソッド
メソッド名 | 説明 |
---|---|
GET | URIを用いてサーバから情報を受け取るためのメソッド。サーバ側の情報を書き換えるといったことはしない。 |
HEAD | GETと基本的には同じだが、ステータスコードとヘッダーのみを返す点が異なる。 |
POST | クライアントからサーバになにかしらのデータを送る時に使われる。 |
PUT | クライアントが既に知っているURIにあるリソースを作ったり上書きしたりするために用いる。 |
DELETE | URIにあるリソースを削除するために用いる。 |
CONNECT | URIで指定されるサーバとのコネクションを確立する。 |
OPTIONS | 対象リソースとの通信のためのオプションを返す。 |
TRACE | 対象リソースへの経路に沿ったループバックテストを行う。 |
https://www.tutorialspoint.com/http/http_requests.htm のなかで紹介された表を訳しただけです。
POSTとPUTの違いは、https://www.keycdn.com/support/put-vs-post を参考にしました。
TCPコネクションを確立する
何はともあれ、TCPコネクションを確立しないとHTTP通信をすることはできません。
nc
コマンドでは、トランスポート層のプロトコルであるTCPとUDPを使ったコネクションを作ることができます。
なにもオプションを指定しない場合は、TCPコネクションを確立しようとします。
$ nc -v google.com 80
found 0 associations
found 1 connections:
1: flags=82<CONNECTED,PREFERRED>
outif en0
src 192.168.128.246 port 52041
dst 172.217.161.206 port 80
rank info not available
TCP aux info available
Connection to google.com port 80 [tcp/http] succeeded!
google.com
のWebサーバ(ポート80)とのTCPコネクションを結ぶことに成功しました。
このコネクションを通じて、対象とするホストにあるポートに対して、データの送受信をすることができます。
GETリクエスト
このTCPコネクションを用いて、GETリクエストでHTMLを取得してみましょう。
GET / HTTP/1.1
と入力した後、改行し、もう一度空行を送信します。
すると、HTTPリクエストが返ってきます。
HTTP/1.1 200 OK
Date: Thu, 23 May 2019 16:04:22 GMT
Expires: -1
Cache-Control: private, max-age=0
Content-Type: text/html; charset=ISO-8859-1
P3P: CP="This is not a P3P policy! See g.co/p3phelp for more info."
Server: gws
X-XSS-Protection: 0
X-Frame-Options: SAMEORIGIN
Set-Cookie: 1P_JAR=2019-05-23-16; expires=Sat, 22-Jun-2019 16:04:22 GMT; path=/; domain=.google.com
Set-Cookie: NID=184=rWXmA7VSwF4GNfvX7kNWjSzTx4sRBODwanmpsOznfM1qEnjS_taYCnk_mit7kYqE6WcNdgHXApOPuNnXEGwExSI0fsdiWV3e2dykYLNY2Kdmca4cwIhn1S7l0THIeiM9Pfh1quKMHM4H1GN6h6lN30InP85WKgpbuu8H2D-Osvw; expires=Fri, 22-Nov-2019 16:04:22 GMT; path=/; domain=.google.com; HttpOnly
Accept-Ranges: none
Vary: Accept-Encoding
Transfer-Encoding: chunked
52b6
<!doctype html><html itemscope="" itemtype="http://schema.org/WebPage" lang="ja">
... (数十行にも及ぶため、省略)
一番上の行にある200
というステータスコードによって、サーバから返されるレスポンスがどのようなものであるかがわかります。
ステータスコードの詳細は、https://developer.mozilla.org/ja/docs/Web/HTTP/Status でみることができます。
ざっくりいうと、200番台が成功、300番台がリダイレクトメッセージ、400番台がクライアントエラー、500番台がサーバエラーのように分類されています。
200
というステータスコードについて、上のURL内では次のように説明されています。
200 OK
リクエストが成功したことを示します。成功が意味することは、 HTTP メソッドにより異なります:
GET: リソースが取り出され、メッセージ本文で転送されます。
HEAD: エンティティヘッダーがメッセージ本文内にあります。
PUT 又は POST: アクションの結果を表すリソースがメッセージ本文で転送されます。
TRACE: サーバーが受理したリクエストメッセージがメッセージ本文に含まれています。
この場合、GETリクエストでしたので、指定したURIからリソースが取り出され、メッセージ本文がヘッダーの後に送られてきています。
ついでに、HEADメソッドでも同様に送ってみましょう。
HEAD / HTTP/1.1
HTTP/1.1 200 OK
Date: Thu, 23 May 2019 16:19:50 GMT
Expires: -1
Cache-Control: private, max-age=0
Content-Type: text/html; charset=ISO-8859-1
P3P: CP="This is not a P3P policy! See g.co/p3phelp for more info."
Server: gws
X-XSS-Protection: 0
X-Frame-Options: SAMEORIGIN
Set-Cookie: 1P_JAR=2019-05-23-16; expires=Sat, 22-Jun-2019 16:19:50 GMT; path=/; domain=.google.com
Set-Cookie: NID=184=pcNHM2fGSG7yQ8gkR1EgmWbAffLvLPXabxORlTrqWjHBablDRBEYc91PKbUwfnPS15_i7-Vt906vQkTIFpbvXUiJ8jfDaL4Pqsz4M_bz58uu6BCRNhs3Z_RqdYQyrC1z6Mxr9z-iANryEApMFiLYQHMyhPM5Vr-uOykygtTdJYg; expires=Fri, 22-Nov-2019 16:19:50 GMT; path=/; domain=.google.com; HttpOnly
Transfer-Encoding: chunked
Accept-Ranges: none
Vary: Accept-Encoding
リクエストヘッダーを変えてみる
ここまでは、1行のHTTPリクエストをWebサーバに送信してきましたが、HTTPリクエストにもヘッダーを追加することで、Webサーバに情報を与えてあげることができます。
指定できるヘッダーはたくさんあります(https://flaviocopes.com/http-request-headers/ )。
もちろん、GETだけでなく、POSTやPUTであっても同じようにヘッダーを用いることで通信の可能性を広げることができます。
Cookie
もこのリクエストヘッダに追加することができます。
例えば、User-Agent
を指定することで、特定のブラウザからアクセスしたという情報をWebサーバに伝えることができます。
GET / HTTP/1.1
User-Agent: Mozilla/5.0
ちょっとMozilla/5.0
からアクセスしたと嘘をついてみましょう。
HTTP/1.1 200 OK
Date: Thu, 23 May 2019 16:33:52 GMT
Expires: -1
Cache-Control: private, max-age=0
Content-Type: text/html; charset=UTF-8
P3P: CP="This is not a P3P policy! See g.co/p3phelp for more info."
Server: gws
X-XSS-Protection: 0
X-Frame-Options: SAMEORIGIN
Set-Cookie: 1P_JAR=2019-05-23-16; expires=Sat, 22-Jun-2019 16:33:52 GMT; path=/; domain=.google.com
Set-Cookie: NID=184=pKW_xUk70PPzuhvxzOxfHncQGORwfmqmE-IRr6VDf0j45_G58VZO9x3kpARSAvjzVtZGm8ID1AflpCX7_uwhjufqflgtdJOdKc5uJJj3yWj6ayGDPt_HjhOi8sxnDlLn_I8whAC29ySo1sqW7YvLGlFkkTNHq_HUbbRwahQrr5w; expires=Fri, 22-Nov-2019 16:33:52 GMT; path=/; domain=.google.com; HttpOnly
Accept-Ranges: none
Vary: Accept-Encoding
Transfer-Encoding: chunked
51ca
<!doctype html><html itemscope="" itemtype="http://schema.org/WebPage" lang="ja"><head><meta content="世界中のあらゆる情報を検索するためのツールを提供しています。さまざまな検索機能を活用して、お探しの情報を見つけてください。" name="description"><meta content="noodp" name="robots"><meta content="text/html; charset=UTF-8" http-equiv="Content-Type">
少し細かい話ですが、レスポンスヘッダのなかのcharset
がUTF-8
に変更されました。
そのおかげでメッセージ部分が日本語で表示されるようになっています。
まとめ
HTTPという超有名プロトコルも、元を正せばTCPコネクションでの特定フォーマットを用いたデータの送受信に過ぎないということがわかっていただけたでしょうか?
APIあたりの話も今後追加していこうと思います。