4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

この記事は、富士通クラウドテクノロジーズ Advent Calendar 2019の22日目の記事です。21日目は@kzmakeさんのGCRA(Generic cell rate algorithm)でAPIリクエストをレート制限する話でした。

はじめに

二フクラPaaSチームの@hunter9xです。今回はURI参照の一部に対する自分の理解をまとめてみようと思います。基本知識ですが、ネットで調べてみた際にあまり情報がなかったので書いてみようと思いました。

URIの基本

rfc3986#section-3ではURIは大体5つのパーツから構成されます。

     foo://example.com:8042/over/there?name=ferret#nose
     \_/   \______________/\_________/ \_________/ \__/
      |           |            |            |        |
   scheme     authority       path        query   fragment
      |   _____________________|__
     / \ /                        \
     urn:example:animal:ferret:nose

更に// から最初の/までのauthority部は下記のように構成されます。

  authority   = [ userinfo "@" ] host [ ":" port ]

様々なプロトコルやソフトウェアで上記で定義されたURI参照を用いてリソースのアクセスを実現しています。URI参照では、渡されたURIのすべてのパーツに沿って参照しなくても、現在の場所(base URI)からターゲットのリソース(target URI)にアクセスすることができます。例えば、https://pfs.nifcloud.com/service/dns.htmにアクセスした後、https://pfs.nifcloud.com/service/ess.htmが渡されたとすると、https://pfs.nifcloud.com/service/までが同じなのでess.htmに参照すれば良いです。他には # などを用いた相対パスの参照があります。効率化のため、多くのプロトコルやソフトウェアではこのように柔軟にURIをパースしています。

相対URIの参照

さて、5つのパーツが揃っていないURIが渡された時にuri parserはどう動くべきかみて行きます。
詳細なアルゴリズムはrfc3986#section-5.2で示されており、かつパターンが非常に多いのでここでは、筆者自身が興味あるものだけを対象とします。

base uri target uri result
http://localhost:8080/hoge/ //localhost:8081 http://localhost:8081
http://localhost:8080/hoge/ /fuga http://localhost:8080/fuga
http://localhost:8080/hoge/ fuga http://localhost:8080/hoge/fuga
http://localhost:8080/hoge fuga http://localhost:8080/fuga

検証

検証内容は、HTTPクライアントがHTTP サーバーに base uri でアクセスする時に、HTTPサーバー側で targe uri にリダイレクトする状況を作り、HTTPクライアント側の動きを確認してみたいと思います。検証で使うものは下記の通りです。

項目 spec
HTTP サーバー python 3.7.4 http.server module
HTTP クライアント1 php 7.2.24 file_get_contents()
HTTP クライアント2 curl 7.58.0 (x86_64-pc-linux-gnu)

準備

  • 参照ディレクトリの構成
.
├── fuga
│   └── index.htm
└── hoge
    └── index.htm

HTTPサーバ側 (python)

import http.server
import socketserver

PORT = 8080
BIND = "127.0.0.1"
target_uri = ""

class Redirect(http.server.SimpleHTTPRequestHandler):
    def do_GET(self):
        self.send_response(301)
        self.send_header("Location", target_uri)
        self.end_headers()

with socketserver.TCPServer((BIND, PORT), Redirect) as httpd:
    print("serving at port", PORT)
    httpd.serve_forever()

すごく簡単なもので、無限にリダイレクトするようになっています。 target_uriを変更しながら4パターンをテストします。目的はクライアント側の動きをみるので、そちらでリダイレクト回数を制限し、サーバーの標準出力でHTTPヘッダーで結果を確認します。

HTTP クライアント 1 (php)

<?php
$url = ""; // base uri
$option = [
    "http" => [
        'method' => 'GET',
        'timeout' => 5,
        'follow_location' => 1,
        'max_redirects' => 2
    ]
];

file_get_contents($url, false, stream_context_create($option));

HTTP クライアント 2 (curl)

curl -v -L --max-redirs 2 {base uri}

結果

HTTP クライアントの base uri を変更し確認した結果になります。

sample num base uri target uri curl file_get_contents()
1 http://localhost:8080/hoge/ //localhost:8081 http://localhost:8081 http://localhost:8081
2 http://localhost:8080/hoge/ /fuga http://localhost:8080/fuga http://localhost:8080/fuga
3 http://localhost:8080/hoge/ fuga http://localhost:8080/hoge/fuga http://localhost:8080/hoge//fuga
4 http://localhost:8080/hoge fuga http://localhost:8080/fuga http://localhost:8080/fuga

4パターンの中で、3番目だけ file_get_contents() の動きが期待結果と違うことがわかります。一方 curl では期待どおりに動作していました。

終わりに

URI参照の基本について一部だけまとめました。実際2種類のHTTPクライアントの動作を確認し、参照アルゴリズムをみました。検証結果は正しいとは限らないのですが、URI参照の雰囲気を感じられたらなと思います。

明日は@imaisatoさんの災害は忘れたころにやって来るです。お楽しみに!

参考文献

4
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?