URI 모듈

URI 모듈

URIUniform Resource Identifier(RFC 2396)를 다루는 클래스들을 제공하는 모듈이에요. URL을 문자열로 파싱하고, 구성 요소(스킴·호스트·경로·쿼리 등)를 나누고, 다시 조립하는 일을 표준화된 방식으로 해 주는 라이브러리예요.

출처: Ruby 4.0 API — URI

특징

  • URI를 다루는 일관된 방식을 제공해요.
  • 커스텀 URI 스킴을 도입하기 쉬워요.
  • 대체 URI::Parser(또는 다른 패턴·정규식)를 쓸 수 있는 유연성이 있어요.

기본 예제

require 'uri'

uri = URI("http://foo.com/posts?id=30&limit=5#time=1305298413")
#=> #<URI::HTTP http://foo.com/posts?id=30&limit=5#time=1305298413>

uri.scheme    #=> "http"
uri.host      #=> "foo.com"
uri.path      #=> "/posts"
uri.query     #=> "id=30&limit=5"
uri.fragment  #=> "time=1305298413"

uri.to_s      #=> "http://foo.com/posts?id=30&limit=5#time=1305298413"

URI(...)로 문자열을 감싸면 자동으로 파싱되어, scheme·host·path·query·fragment 같은 접근자로 각 부분을 꺼낼 수 있어요.

커스텀 URI 추가

module URI
  class RSYNC < Generic
    DEFAULT_PORT = 873
  end
  register_scheme 'RSYNC', RSYNC
end
#=> URI::RSYNC

URI.scheme_list
#=> {"FILE"=>URI::File, "FTP"=>URI::FTP, "HTTP"=>URI::HTTP,
#    "HTTPS"=>URI::HTTPS, "LDAP"=>URI::LDAP, "LDAPS"=>URI::LDAPS,
#    "MAILTO"=>URI::MailTo, "RSYNC"=>URI::RSYNC}

uri = URI("rsync://rsync.foo.com")
#=> #<URI::RSYNC rsync://rsync.foo.com>

Generic을 상속받은 새 클래스를 만들고 register_scheme으로 등록하면, 그 스킴 문자열을 파싱할 때 커스텀 클래스가 사용돼요.

관련 RFC

  • RFC822, RFC1738, RFC2255, RFC2368, RFC2373, RFC2396, RFC2732, RFC3986

클래스 트리

URI::Generic(uri/generic.rb) — URI::File(uri/file.rb), URI::FTP(uri/ftp.rb), URI::HTTP(uri/http.rb), URI::HTTPS(uri/https.rb), URI::LDAP(uri/ldap.rb), URI::LDAPS(uri/ldaps.rb), URI::MailTo(uri/mailto.rb)

  • URI::Parser — uri/common.rb
  • URI::REGEXP — uri/common.rb, URI::REGEXP::PATTERN — uri/common.rb
  • URI::Util — uri/common.rb
  • URI::Error — uri/common.rb, URI::InvalidURIError, URI::InvalidComponentError, URI::BadURIError

저작권 정보

  • 저자: Akira Yamada [email protected]
  • 문서화: Akira Yamada, Dmitry V. Sabanin, Vincent Batts
  • 라이선스: Copyright © 2001 akira yamada. Ruby와 같은 조건으로 재배포·수정 가능해요.

상수 (Constants)

  • DEFAULT_PARSER — 기본 파서 인스턴스
  • RFC2396_PARSER — RFC 2396용 기본 파서 인스턴스
  • RFC3986_PARSER — RFC 3986용 기본 파서 인스턴스

Public Class Methods

decode_uri_component(str, enc=Encoding::UTF_8)

URI.decode_www_form_component과 비슷하지만, '+'를 보존해요. 즉 URL 인코딩된 구성 요소를 디코드하되, +를 공백으로 바꾸지 않아요.

decode_www_form(str, enc=Encoding::UTF_8, separator: '&', use__charset_: false, isindex: false)

주어진 ASCII 문자열 str로부터 이름/값 쌍을 뽑아내요. Content-Type'application/x-www-form-urlencoded'인 HTTP 폼 데이터를 디코드할 때 써요.

반환값은 2-원소 서브배열들의 배열이에요. 각 서브배열은 이름/값 쌍(둘 다 문자열)이에요. 각 반환 문자열은 인코딩 enc를 가지며, String#scrub으로 잘못된 문자는 제거돼요.

URI.decode_www_form('foo=0&bar=1&baz')
# => [["foo", "0"], ["bar", "1"], ["baz", ""]]

URI.decode_www_form_component과 유사한 변환이 적용돼요.

URI.decode_www_form('f%23o=%2F&b-r=%24&b+z=%40')
# => [["f#o", "/"], ["b-r", "$"], ["b z", "@"]]

연속된 구분자도 처리돼요.

URI.decode_www_form('foo=0&&bar=1&&baz=2')
# => [["foo", "0"], ["", ""], ["bar", "1"], ["", ""], ["baz", "2"]]

다른 구분자를 지정할 수도 있어요.

URI.decode_www_form('foo=0--bar=1--baz', separator: '--')
# => [["foo", "0"], ["bar", "1"], ["baz", ""]]

decode_www_form_component(str, enc=Encoding::UTF_8)

주어진 URL-인코딩 문자열 str을 디코드한 문자열을 돌려줘요.

입력은 먼저 Encoding::ASCII-8BIT로 인코딩되고(String#b), 그다음 디코드된 뒤 마지막에 지정한 인코딩 enc로 강제 변환돼요.

반환 문자열의 규칙은 다음과 같아요.

  • 보존: '*', '.', '-', '_' 문자와 'a'..'z', 'A'..'Z', '0'..'9' 범위의 문자. 예: URI.decode_www_form_component('*.-_azAZ09') # => "*.-_azAZ09"
  • 변환: '+'' '(공백)으로, 각 "퍼센트 표기"를 ASCII 문자로. 예: URI.decode_www_form_component('Here+are+some+punctuation+characters%3A+%2C%3B%3F%3A') # => "Here are some punctuation characters: ,;?:"

관련: URI.decode_uri_component('+' 보존).

encode_uri_component(str, enc=nil)

URI.encode_www_form_component과 비슷하지만, ' '(공백)을 '+' 대신 '%20'으로 인코딩해요.

encode_www_form(enum, enc=nil)

주어진 Enumerable enum으로부터 URL-인코딩된 문자열을 만들어요. Content-Type'application/x-www-form-urlencoded'인 HTTP 요청의 폼 데이터로 쓰기에 적합해요.

반환 문자열은 enum의 원소들을 각각 하나 이상의 URL-인코딩 문자열로 변환하고, 모두 '&'로 연결한 형태예요.

URI.encode_www_form([['foo', 0], ['bar', 1], ['baz', 2]])
# => "foo=0&bar=1&baz=2"
URI.encode_www_form({foo: 0, bar: 1, baz: 2})
# => "foo=0&bar=1&baz=2"

내부적으로 URI.encode_www_form_component을 사용해 특정 문자들을 변환해요.

URI.encode_www_form('f#o': '/', 'b-r': '$', 'b z': '@')
# => "f%23o=%2F&b-r=%24&b+z=%40"

enum이 배열류일 때 각 원소를 필드로 변환하는 규칙은 다음과 같아요.

  • 원소가 2개 이상의 배열이면 처음 두 원소로 필드를 만들고 나머지는 무시돼요: name=value 형태. 예: URI.encode_www_form([%w[foo bar], %w[baz bat bah]]) # => "foo=bar&baz=bat"
  • 원소가 1개 원소 배열이면 ele[0]으로 필드를 만들어요. 예: URI.encode_www_form([['foo'], [:bar], [0]]) # => "foo&bar&0"
  • 그 외에는 ele 자체로 필드를 만들어요. 예: URI.encode_www_form(['foo', :bar, 0]) # => "foo&bar&0"

배열류 enum의 원소들은 섞여 있을 수 있어요.

URI.encode_www_form([['foo', 0], ['bar', 1, 2], ['baz'], :bat])
# => "foo=0&bar=1&baz&bat"

enum이 해시류일 때 각 key/value 쌍은 하나 이상의 필드로 변환돼요.

  • value가 배열로 변환 가능하면, value의 각 원소가 key와 짝을 이뤄 필드를 만들어요. 예: URI.encode_www_form({foo: [:bar, 1], baz: [:bat, :bam, 2]}) # => "foo=bar&foo=1&baz=bat&baz=bam&baz=2"
  • 그 외에는 keyvalue가 짝을 이뤄 필드를 만들어요. 예: URI.encode_www_form({foo: 0, bar: 1, baz: 2}) # => "foo=0&bar=1&baz=2"

해시류 enum의 원소들도 섞여 있을 수 있어요.

URI.encode_www_form({foo: [0, 1], bar: 2})
# => "foo=0&foo=1&bar=2"

encode_www_form_component(str, enc=nil)

주어진 문자열 str로부터 URL-인코딩된 문자열을 만들어요.

반환 문자열의 규칙은 다음과 같아요.

  • 보존: '*', '.', '-', '_' 문자와 'a'..'z', 'A'..'Z', '0'..'9' 범위 문자. 예: URI.encode_www_form_component('*.-_azAZ09') # => "*.-_azAZ09"
  • 변환: ' '(공백)을 '+'로, 그 외 문자를 "퍼센트 표기"로. 문자 c의 퍼센트 표기는 '%%%X' % c.ord. 예: URI.encode_www_form_component('Here are some punctuation characters: ,;?:') # => "Here+are+some+punctuation+characters%3A+%2C%3B%3F%3A"

인코딩 규칙은 다음과 같아요.

  • str의 인코딩이 Encoding::ASCII_8BIT이면 인자 enc는 무시돼요.
  • 그 외에는 str을 먼저 Encoding::UTF_8로 변환한 뒤(적절한 문자 치환 포함) 인코딩 enc로 변환해요.

어느 경우든 반환 문자열은 Encoding::US_ASCII로 강제 인코딩돼요.

관련: URI.encode_uri_component(' ''%20'으로 인코딩).

for(scheme, *arguments, default: Generic)

주어진 scheme, arguments, default로부터 새 객체를 만들어요.

  • 새 객체는 URI.scheme_list[scheme.upcase]의 인스턴스예요.
  • schemearguments로 클래스 초기화자를 호출해 객체를 초기화해요. URI::Generic.new 참고.
values = ['john.doe', 'www.example.com', '123', nil, '/forum/questions/', nil, 'tag=networking&order=newest', 'top']
URI.for('https', *values)
# => #<URI::HTTPS https://[email protected]:123/forum/questions/?tag=networking&order=newest#top>
URI.for('foo', *values, default: URI::HTTP)
# => #<URI::HTTP foo://[email protected]:123/forum/questions/?tag=networking&order=newest#top>

join(*str)

주어진 URI 문자열 str들을 RFC 2396에 따라 병합해요. str의 각 문자열은 병합 전에 RFC 3986 URI로 변환돼요.

URI.join("http://example.com/","main.rbx")
# => #<URI::HTTP http://example.com/main.rbx>

URI.join('http://example.com', 'foo')
# => #<URI::HTTP http://example.com/foo>

URI.join('http://example.com', '/foo', '/bar')
# => #<URI::HTTP http://example.com/bar>

URI.join('http://example.com', '/foo', 'bar')
# => #<URI::HTTP http://example.com/bar>

URI.join('http://example.com', '/foo/', 'bar')
# => #<URI::HTTP http://example.com/foo/bar>

/로 시작하는 문자는 루트 기준, 그렇지 않으면 현재 경로에 이어붙이는 일반적인 URL 병합 규칙이 적용돼요.

open(name, *rest, &block)

URI를 포함한 여러 자원을 열 수 있게 해주는 메서드예요.

  • 첫 인자가 open 메서드에 응답하면, 나머지 인자로 그 객체의 open을 호출해요.
  • 첫 인자가 (protocol)://로 시작하는 문자열이면 URI.parse로 파싱하고, 파싱된 객체가 open에 응답하면 그걸 호출해요.
  • 그 외에는 Kernel#open을 호출해요.

OpenURI::OpenRead#openURI::HTTP#open, URI::HTTPS#open, URI::FTP#open, Kernel#open을 제공해요. http://, https://, ftp://로 시작하는 URI(또는 문자열)를 받을 수 있고, 그 경우 열린 파일 객체는 OpenURI::Meta로 확장돼요.

parse(uri)

주어진 문자열 uri로부터 새 URI 객체를 만들어 돌려줘요.

URI.parse('https://[email protected]:123/forum/questions/?tag=networking&order=newest#top')
# => #<URI::HTTPS https://[email protected]:123/forum/questions/?tag=networking&order=newest#top>
URI.parse('http://[email protected]:123/forum/questions/?tag=networking&order=newest#top')
# => #<URI::HTTP http://[email protected]:123/forum/questions/?tag=networking&order=newest#top>

uri에 잘못된 URI 문자가 포함될 수 있다면, 먼저 URI::RFC2396_PARSER.escape로 문자열을 이스케이프하는 걸 권장해요.

parser=(parser = RFC3986_PARSER)

기본 파서 인스턴스를 설정해요. 파서를 교체하면 그 뒤의 파싱·조립 동작이 새 파서를 기준으로 동작해요.

register_scheme(scheme, klass)

주어진 scheme을 가진 URI를 파싱할 때 인스턴스화할 클래스로 klass를 등록해요.

URI.register_scheme('MS_SEARCH', URI::Generic) # => URI::Generic
URI.scheme_list['MS_SEARCH']                   # => URI::Generic

schemeString#upcase를 적용한 결과가 유효한 상수 이름이어야 한다는 점에 주의하세요.

scheme_list()

정의된 스킴들의 해시를 돌려줘요.

URI.scheme_list
# =>
{"MAILTO"=>URI::MailTo,
 "LDAPS"=>URI::LDAPS,
 "WS"=>URI::WS,
 "HTTP"=>URI::HTTP,
 "HTTPS"=>URI::HTTPS,
 "LDAP"=>URI::LDAP,
 "FILE"=>URI::File,
 "FTP"=>URI::FTP}

관련: URI.register_scheme.

split(uri)

문자열 uri로 이루어지는 URI의 각 부분을 나타내는 9-원소 배열을 돌려줘요. 각 배열 원소는 문자열 또는 nil이에요.

names = %w[scheme userinfo host port registry path opaque query fragment]
values = URI.split('https://[email protected]:123/forum/questions/?tag=networking&order=newest#top')
names.zip(values)
# =>
[["scheme", "https"],
 ["userinfo", "john.doe"],
 ["host", "www.example.com"],
 ["port", "123"],
 ["registry", nil],
 ["path", "/forum/questions/"],
 ["opaque", nil],
 ["query", "tag=networking&order=newest"],
 ["fragment", "top"]]

Private Class Methods

_decode_uri_component(regexp, str, enc)

주어진 URL-인코딩 문자열 str에서 regexp와 일치하는 문자를 디코드한 문자열을 돌려줘요. 실제 파싱의 공통 로직으로, 위 공개 메서드들이 내부적으로 사용해요.

_encode_uri_component(regexp, table, str, enc)

주어진 문자열 str에서 regexp와 일치하는 문자를 table에 따라 URI-인코딩한 문자열을 돌려줘요.