CGI 클래스
CGI 클래스
CGI(Common Gateway Interface)는 웹 서버에서 독립 프로그램으로 HTTP 요청을 전달하고, 그 출력을 웹 브라우저로 돌려보내는 간단한 프로토콜이에요. 기본적으로 CGI 프로그램은 요청의 파라미터를 환경 변수(GET) 또는 $stdin(POST)으로 받고, $stdout에 출력하는 모든 것을 클라이언트로 돌려보내요.
CGI 클래스는 HTTP 요청 파라미터를 가져오고, 쿠키를 관리하고, HTML 출력을 생성하는 기능을 제공해요. 세션 관리 기능은 CGI::Session 클래스가 담당해요.
출처: Ruby 3.3 API
본문
CGI는 큰 클래스로 여러 카테고리의 메서드를 제공하며, 상당수가 다른 모듈에서 믹스인된 것들이에요. 일부 문서는 이 클래스에, 일부는 CGI::QueryExtension과 CGI::HtmlExtension 모듈에 있어요. 쿠키 처리의 구체적인 정보는 CGI::Cookie를, 세션은 cgi/session.rb(CGI::Session)를 보세요.
쿼리용으로는 환경 변수, 파라미터, 쿠키, 멀티파트 요청 데이터에 접근하는 메서드를, 응답용으로는 출력을 쓰고 HTML을 생성하는 메서드를 제공해요.
쿼리 (Queries)
CGI 클래스는 CGI::QueryExtension 모듈에서 파라미터·쿠키 파싱 기능, 환경 변수 접근, 멀티파트 요청(업로드된 파일 포함) 파싱 지원을 동적으로 믹스인해요.
환경 변수 — 표준 CGI 환경 변수는 CGI 객체의 읽기 전용 속성으로 사용할 수 있어요. 그 목록은 다음과 같아요.
AUTH_TYPE HTTP_HOST REMOTE_IDENT
CONTENT_LENGTH HTTP_NEGOTIATE REMOTE_USER
CONTENT_TYPE HTTP_PRAGMA REQUEST_METHOD
GATEWAY_INTERFACE HTTP_REFERER SCRIPT_NAME
HTTP_ACCEPT HTTP_USER_AGENT SERVER_NAME
HTTP_ACCEPT_CHARSET PATH_INFO SERVER_PORT
HTTP_ACCEPT_ENCODING PATH_TRANSLATED SERVER_PROTOCOL
HTTP_ACCEPT_LANGUAGE QUERY_STRING SERVER_SOFTWARE
HTTP_CACHE_CONTROL REMOTE_ADDR
HTTP_FROM REMOTE_HOST
각 변수에는 같은 이름의 속성이 대응돼요. 전부 소문자이고 앞의 HTTP_는 빠져요. content_length와 server_port는 정수이고, 나머지는 문자열이에요.
파라미터 — params() 메서드는 요청의 모든 파라미터를 이름/값-목록 쌍의 해시로 반환해요. 여기서 값-목록은 하나 이상의 값을 가진 Array예요. CGI 객체 자체도 파라미터 이름에서 값으로 가는 해시처럼 동작하지만, 각 파라미터 이름에 대해 단일 값(문자열)만 반환해요.
예를 들어 요청에 "favourite_colours" 파라미터에 여러 값 "blue"와 "green"이 있다고 가정해볼게요.
cgi.params["favourite_colours"] # => ["blue", "green"]
cgi["favourite_colours"] # => "blue"
파라미터가 없으면 전자는 빈 배열을, 후자는 빈 문자열을 반환해요. 파라미터 존재 여부를 테스트하는 가장 간단한 방법은 has_key? 메서드예요.
쿠키 — HTTP 쿠키는 요청에서 자동으로 파싱돼요. cookies() 접근자로 접근할 수 있고, 쿠키 이름에서 CGI::Cookie 객체로 가는 해시를 반환해요.
멀티파트 요청 — 요청 메서드가 POST이고 콘텐츠 타입이 multipart/form-data라면 업로드된 파일을 포함할 수 있어요. 이 파일들은 QueryExtension 모듈이 요청의 파라미터에 저장해요. 파라미터 이름은 평소처럼 파일 입력 필드의 name 속성이에요. 하지만 값은 문자열이 아니라 IO 객체예요. 작은 파일은 IOString, 큰 파일은 Tempfile이에요. 이 객체에는 추가 싱글턴 메서드가 있어요.
local_path: 로컬 파일시스템에 있는 업로드된 파일의 경로.original_filename: 클라이언트 컴퓨터에서의 파일 이름.content_type: 파일의 콘텐츠 타입.
응답 (Responses)
CGI 클래스는 HTTP 클라이언트로 헤더와 콘텐츠 출력을 보내는 메서드를 제공하고, CGI::HtmlExtension과 CGI::TagMaker 모듈의 프로그램적 HTML 생성 메서드를 믹스인해요. HTML 생성에 사용할 정확한 HTML 버전은 객체 생성 시점에 지정돼요.
출력 작성 — HTTP 클라이언트로 출력을 보내는 가장 간단한 방법은 out() 메서드를 쓰는 거예요. HTTP 헤더를 해시 파라미터로 받고, 블록으로 본문 콘텐츠를 받아요. 헤더는 http_header() 메서드로 문자열로 생성할 수 있어요. 출력 스트림에는 print() 메서드로 직접 쓸 수 있어요.
HTML 생성 — 각 HTML 요소마다 그 요소를 문자열로 생성하는 메서드가 대응돼요. 메서드 이름은 요소 이름과 같고 전부 소문자예요. 요소의 속성은 해시로, 본문은 문자열로 평가되는 무인자 블록으로 전달돼요. HTML 생성 모듈은 항상 비어 있는 요소가 무엇인지 알고 있어서 전달된 본문을 조용히 버려요. 또 어느 요소가 짝을 이루는 닫는 태그를 필요로 하는지도 알아요. 다만 어느 속성이 어느 요소에 적법한지는 모르는 상태예요.
CGI::HtmlExtension 모듈에서 믹스인된 추가 HTML 생성 메서드도 있어요. 여기에는 여러 종류의 폼 입력을 위한 개별 메서드와, 공통으로 특정 속성을 취하는 요소를 위한 메서드가 포함돼요. 이 경우 속성은 해시 대신 인자로 직접 지정할 수 있어요.
유틸리티 HTML 이스케이프 함수 — cgi/util.rb에 유틸리티 도구가 정의돼 있어요. include하면 유틸리티 메서드를 함수처럼 쓸 수 있어요.
사용 예시
폼 값 가져오기
require "cgi"
cgi = CGI.new
value = cgi['field_name'] # <== value string for 'field_name'
# if not 'field_name' included, then return "".
fields = cgi.keys # <== array of field names
# returns true if form has 'field_name'
cgi.has_key?('field_name')
cgi.has_key?('field_name')
cgi.include?('field_name')
주의! 옛 cgi.rb(Ruby 1.6 포함)에서는 cgi['field_name']이 Array를 반환했어요.
폼 값을 해시로 가져오기
require "cgi"
cgi = CGI.new
params = cgi.params
cgi.params는 해시예요.
cgi.params['new_field_name'] = ["value"] # add new param
cgi.params['field_name'] = ["new_value"] # change value
cgi.params.delete('field_name') # delete param
cgi.params.clear # delete all params
폼 값을 파일에 저장하기 / 복원하기
require "pstore"
db = PStore.new("query.db")
db.transaction do
db["params"] = cgi.params
end
require "pstore"
db = PStore.new("query.db")
db.transaction do
cgi.params = db["params"]
end
멀티파트 폼 값 가져오기
require "cgi"
cgi = CGI.new
value = cgi['field_name'] # <== value string for 'field_name'
value.read # <== body of value
value.local_path # <== path to local file of value
value.original_filename # <== original filename of value
value.content_type # <== content_type of value
value는 StringIO 또는 Tempfile 클래스 메서드를 가져요.
쿠키 값 가져오기
require "cgi"
cgi = CGI.new
values = cgi.cookies['name'] # <== array of 'name'
# if not 'name' included, then return [].
names = cgi.cookies.keys # <== array of cookie names
cgi.cookies는 해시예요.
쿠키 객체 가져오기
require "cgi"
cgi = CGI.new
for name, cookie in cgi.cookies
cookie.expires = Time.now + 30
end
cgi.out("cookie" => cgi.cookies) {"string"}
cgi.cookies # { "name1" => cookie1, "name2" => cookie2, ... }
require "cgi"
cgi = CGI.new
cgi.cookies['name'].expires = Time.now + 30
cgi.out("cookie" => cgi.cookies['name']) {"string"}
HTTP 헤더와 HTML 문자열을 $DEFAULT_OUTPUT($>)으로 출력하기
require "cgi"
cgi = CGI.new("html4") # add HTML generation methods
cgi.out do
cgi.html do
cgi.head do
cgi.title { "TITLE" }
end +
cgi.body do
cgi.form("ACTION" => "uri") do
cgi.p do
cgi.textarea("get_text") +
cgi.br +
cgi.submit
end
end +
cgi.pre do
CGI.escapeHTML(
"params: #{cgi.params.inspect}\n" +
"cookies: #{cgi.cookies.inspect}\n" +
ENV.collect do |key, value|
"#{key} --> #{value}\n"
end.join("")
)
end
end
end
end
# add HTML generation methods
CGI.new("html3") # html3.2
CGI.new("html4") # html4.01 (Strict)
CGI.new("html4Tr") # html4.01 Transitional
CGI.new("html4Fr") # html4.01 Frameset
CGI.new("html5") # html5
일부 유틸리티 메서드
require 'cgi/util'
CGI.escapeHTML('Usage: foo "bar" <baz>')
일부 함수처럼 쓰는 유틸리티 메서드
require 'cgi/util'
include CGI::Util
escapeHTML('Usage: foo "bar" <baz>')
h('Usage: foo "bar" <baz>') # alias
Constants
CR: 캐리지 리턴 문자열.EOL: 표준 인터넷 줄바꿈 시퀀스.HTTP_STATUS: HTTP 상태 코드.LF: 라인피드 문자열.MAX_MULTIPART_LENGTH: 멀티파트일 때 최대 요청 파라미터 수.NEEDS_BINMODE: 바이너리 vs 텍스트 중 어떤 처리가 필요한지.PATH_SEPARATOR: 환경별 경로 구분자.
Attributes
accept_charset[R]: 이CGI인스턴스의 허용 문자 집합을 반환해요.
Public Class Methods
accept_charset()
모든 새 CGI 인스턴스의 허용 문자 집합을 반환해요.
# File lib/cgi/core.rb, line 759
def self.accept_charset
@@accept_charset
end
accept_charset=(accept_charset)
모든 새 CGI 인스턴스의 허용 문자 집합을 설정해요.
# File lib/cgi/core.rb, line 764
def self.accept_charset=(accept_charset)
@@accept_charset=accept_charset
end
new(tag_maker) { block }
new(options_hash = {}) { block }
새 CGI 인스턴스를 만들어요. tag_maker 형태는 { :tag_maker => tag_maker } 값으로 options_hash 형태를 쓰는 것과 같아요. options_hash 형태는 수신할 문자 집합도 지정할 수 있으므로 권장돼요.
options_hash는 세 가지 옵션을 인식해요.
-
:accept_charset: 수신한 쿼리 문자열의 인코딩을 지정해요. 생략하면@@accept_charset이 사용돼요. 인코딩이 유효하지 않으면CGI::InvalidEncoding이 발생해요. 예를 들어@@accept_charset이"UTF-8"이라고 가정해볼게요.지정하지 않았을 때:
cgi=CGI.new # @accept_charset # => "UTF-8""EUC-JP"로 지정했을 때:cgi=CGI.new(:accept_charset => "EUC-JP") # => "EUC-JP" -
:tag_maker: 사용할 HTML 생성 메서드 버전을 지정하는 문자열이에요. 지정하지 않으면 HTML 생성 메서드가 로드되지 않아요. 지원되는 값은"html3"(HTML 3.x),"html4"(HTML 4.0),"html4Tr"(HTML 4.0 Transitional),"html4Fr"(HTML 4.0 Frameset),"html5"(HTML 5)예요. -
:max_multipart_length: 멀티파트 데이터의 최대 길이를 지정해요. 정수 스칼라 또는 lambda일 수 있는데, lambda는 요청이 파싱될 때 평가돼요. 이렇게 하면 멀티파트 데이터를 받을지 결정할 때 더 복잡한 로직(예: 등록 사용자의 업로드 허용량 조회)을 설정할 수 있어요. 기본값은128 * 1024 * 1024바이트예요.cgi=CGI.new(:max_multipart_length => 268435456) # simple scalar cgi=CGI.new(:max_multipart_length => -> {check_filesystem}) # lambda
블록이 주어지면 유효하지 않은 인코딩을 만났을 때 호출돼요. 예를 들어:
encoding_errors={}
cgi=CGI.new(:accept_charset=>"EUC-JP") do |name,value|
encoding_errors[name] = value
end
마지막으로, CGI 객체가 표준 CGI 호출 환경에서 생성되지 않았다면(즉 환경에서 REQUEST_METHOD를 찾을 수 없다면) "오프라인" 모드로 실행돼요. 이 모드에서는 명령줄 또는(그것도 없으면) 표준 입력에서 파라미터를 읽어요. 그 외에는 쿠키와 다른 파라미터들이 REQUEST_METHOD에 따라 달라지는 표준 CGI 위치에서 자동으로 파싱돼요.
# File lib/cgi/core.rb, line 850
def initialize(options = {}, &block) # :yields: name, value
@accept_charset_error_block = block_given? ? block : nil
@options={
:accept_charset=>@@accept_charset,
:max_multipart_length=>@@max_multipart_length
}
case options
when Hash
@options.merge!(options)
when String
@options[:tag_maker]=options
end
@accept_charset=@options[:accept_charset]
@max_multipart_length=@options[:max_multipart_length]
if defined?(MOD_RUBY) && !ENV.key?("GATEWAY_INTERFACE")
Apache.request.setup_cgi_env
end
extend QueryExtension
@multipart = false
initialize_query() # set @params, @cookies
@output_cookies = nil
@output_hidden = nil
case @options[:tag_maker]
when "html3"
require_relative 'html'
extend Html3
extend HtmlExtension
when "html4"
require_relative 'html'
extend Html4
extend HtmlExtension
when "html4Tr"
require_relative 'html'
extend Html4Tr
extend HtmlExtension
when "html4Fr"
require_relative 'html'
extend Html4Tr
extend Html4Fr
extend HtmlExtension
when "html5"
require_relative 'html'
extend Html5
extend HtmlExtension
end
end
parse(query)
HTTP 쿼리 문자열을 key=>value 쌍의 해시로 파싱해요.
params = CGI.parse("query_string")
# {"name1" => ["value1", "value2", ...],
# "name2" => ["value1", "value2", ...], ... }
# File lib/cgi/core.rb, line 393
def self.parse(query)
params = {}
query.split(/[&;]/).each do |pairs|
key, value = pairs.split('=',2).collect{|v| CGI.unescape(v) }
next unless key
params[key] ||= []
params[key].push(value) if value
end
params.default=[].freeze
params
end
Public Instance Methods
header
http_header와 같은 메서드예요 (별칭).
http_header
HTTP 헤더 문자열을 반환하는 메서드예요. (CGI::HtmlExtension/CGI에서 제공되며, 다양한 옵션을 받아 응답 헤더를 만듭니다.)
out(content_type_string='text/html')
out(headers_hash)
HTTP 헤더와 본문을 $DEFAULT_OUTPUT($>)으로 출력해요.
문자열이 전달되면 콘텐츠 타입으로 간주돼요. 해시는 http_header에서 쓰는 것과 유사한 헤더 해시예요. 블록은 필수이며 응답의 본문으로 평가돼야 해요.
Content-Length는 콘텐츠 블록이 반환한 문자열 크기에서 자동 계산돼요.
ENV['REQUEST_METHOD'] == "HEAD"면 헤더만 출력돼요(콘텐츠 블록은 여전히 필요하지만 무시돼요).
문자 집합이 "iso-2022-jp", "euc-jp", "shift_jis" 중 하나면 콘텐츠가 그 문자 집합으로 변환되고 언어가 "ja"로 설정돼요.
예시:
cgi = CGI.new
cgi.out{ "string" }
# Content-Type: text/html
# Content-Length: 6
#
# string
cgi.out("text/plain") { "string" }
# Content-Type: text/plain
# Content-Length: 6
#
# string
cgi.out("nph" => true,
"status" => "OK", # == "200 OK"
"server" => ENV['SERVER_SOFTWARE'],
"connection" => "close",
"type" => "text/html",
"charset" => "iso-2022-jp",
# Content-Type: text/html; charset=iso-2022-jp
"language" => "ja",
"expires" => Time.now + (3600 * 24 * 30),
"cookie" => [cookie1, cookie2],
"my_header1" => "my_value",
"my_header2" => "my_value") { "string" }
# HTTP/1.1 200 OK
# Date: Sun, 15 May 2011 17:35:54 GMT
# Server: Apache 2.2.0
# Connection: close
# Content-Type: text/html; charset=iso-2022-jp
# Content-Length: 6
# Content-Language: ja
# Expires: Tue, 14 Jun 2011 17:35:54 GMT
# Set-Cookie: foo
# Set-Cookie: bar
# my_header1: my_value
# my_header2: my_value
#
# string
# File lib/cgi/core.rb, line 367
def out(options = "text/html") # :yield:
options = { "type" => options } if options.kind_of?(String)
content = yield
options["length"] = content.bytesize.to_s
output = stdoutput
output.binmode if defined? output.binmode
output.print http_header(options)
output.print content unless "HEAD" == env_table['REQUEST_METHOD']
end
print(*options)
인자 또는 인자 목록을 기본 출력 스트림으로 출력해요.
cgi = CGI.new
cgi.print # default: cgi.print == $DEFAULT_OUTPUT.print
# File lib/cgi/core.rb, line 383
def print(*options)
stdoutput.print(*options)
end
Private Instance Methods
_no_crlf_check(str)
HTTP 상태 또는 헤더 필드가 CR과 LF를 포함하지 않는지 검사해요.
# File lib/cgi/core.rb, line 191
def _no_crlf_check(str)
if str
str = str.to_s
raise "A HTTP status or header field must not include CR and LF" if str =~ /[\r\n]/
str
else
nil
end
end
env_table()
ENV의 동의어예요.
# File lib/cgi/core.rb, line 59
def env_table
ENV
end
stdinput()
$stdin의 동의어예요.
# File lib/cgi/core.rb, line 64
def stdinput
$stdin
end
stdoutput()
$stdout의 동의어예요.
# File lib/cgi/core.rb, line 69
def stdoutput
$stdout
end
더 알아보기
CGI::QueryExtension— 파라미터·쿠키·멀티파트 파싱 모듈.CGI::HtmlExtension— HTML 생성 메서드 모듈.CGI::Cookie— 쿠키 처리.CGI::Session—cgi/session.rb의 세션 관리.