1+ #include < webframe.hpp>
2+
3+ namespace webframe
4+ {
5+ uri::uri (const std::string &str)
6+ {
7+ parse (str);
8+ }
9+
10+ std::string uri::get_scheme () const
11+ {
12+ return _scheme;
13+ }
14+
15+ std::string uri::get_host () const
16+ {
17+ return _host;
18+ }
19+
20+ int uri::get_port () const
21+ {
22+ return _port;
23+ }
24+
25+ std::string uri::get_path () const
26+ {
27+ return _path;
28+ }
29+
30+ bool uri::get_query (const std::string &key, std::string &value) const
31+ {
32+ auto it = _query.find (key);
33+ bool found (false );
34+ if (it != _query.end ())
35+ {
36+ value = it->second ;
37+ found = true ;
38+ }
39+ return found;
40+ }
41+
42+ std::string uri::get_fragment () const
43+ {
44+ return _fragment;
45+ }
46+
47+ bool uri::find_keyword (const std::string &input_url, size_t &st, size_t &before, const std::string &delim, std::string &result)
48+ {
49+ size_t temp_st = st;
50+
51+ st = input_url.find (delim, before);
52+ if (st == std::string::npos)
53+ {
54+ st = temp_st;
55+ return false ;
56+ }
57+
58+ result = input_url.substr (before, st - before);
59+ before = st + delim.length ();
60+
61+ if (result.empty ())
62+ return false ;
63+
64+ return true ;
65+ }
66+
67+ bool uri::split_query (const std::string &str, const std::string &delim, std::string &key, std::string &value)
68+ {
69+ size_t st = str.find (delim);
70+
71+ if (st == std::string::npos)
72+ {
73+ key = str;
74+ value = " " ;
75+ return false ;
76+ }
77+
78+ key = str.substr (0 , st);
79+ value = str.substr (st + delim.length ());
80+
81+ return true ;
82+ }
83+
84+ void uri::parse (const std::string &str)
85+ {
86+ size_t st = 0 ;
87+ size_t before = 0 ;
88+
89+ // scheme 파싱 (예: "http", "https")
90+ // has_authority: authority(host/userinfo/port) 파싱 여부를 결정하는 플래그
91+ // - "://" 있음 → scheme 있는 절대 URL
92+ // - "//"로 시작 → scheme-relative URL
93+ // - 그 외(상대 경로 등) → authority 없음, path 파싱만 수행
94+ bool has_authority = uri::find_keyword (str, st, before, " ://" , _scheme);
95+ if (!has_authority && str.size () >= 2 && str[0 ] == ' /' && str[1 ] == ' /' )
96+ {
97+ has_authority = true ;
98+ before = 2 ;
99+ }
100+
101+ if (has_authority)
102+ {
103+ // userinfo 파싱 — "@" 앞의 "user:pass" 부분 분리
104+ // authority 영역(다음 "/" 또는 "?" 또는 "#"까지)에서만 "@"를 탐색
105+ size_t authority_end = str.find_first_of (" /?#" , before);
106+ size_t at_pos = str.find (' @' , before);
107+ if (at_pos != std::string::npos &&
108+ (authority_end == std::string::npos || at_pos < authority_end))
109+ {
110+ _userinfo = str.substr (before, at_pos - before);
111+ before = at_pos + 1 ;
112+ }
113+
114+ // host 파싱 — "/" 또는 "?" 또는 "#"가 나오기 전까지가 host
115+ // path가 없는 URL(예: http://example.com)도 처리
116+ size_t host_end = str.find_first_of (" /?#" , before);
117+ if (host_end == std::string::npos)
118+ {
119+ _host = str.substr (before);
120+ before = str.length ();
121+ }
122+ else
123+ {
124+ _host = str.substr (before, host_end - before);
125+ before = host_end;
126+ }
127+
128+ _port = 8080 ; // default port
129+
130+ // host에서 port 분리 (IPv6 bracketed notation 지원)
131+ if (!_host.empty () && _host.front () == ' [' )
132+ {
133+ // IPv6: [2001:db8::1] 또는 [2001:db8::1]:8080
134+ size_t bracket_close = _host.find (' ]' );
135+ if (bracket_close != std::string::npos)
136+ {
137+ // bracket 뒤에 ":port"가 있는지 확인
138+ if (bracket_close + 1 < _host.length () && _host[bracket_close + 1 ] == ' :' )
139+ {
140+ _port = std::atoi (_host.substr (bracket_close + 2 ).c_str ());
141+ }
142+ // bracket 안의 주소만 추출 ([ ] 제거)
143+ _host = _host.substr (1 , bracket_close - 1 );
144+ }
145+ }
146+ else
147+ {
148+ size_t colon_pos = _host.find (' :' );
149+ if (colon_pos != std::string::npos)
150+ {
151+ _port = std::atoi (_host.substr (colon_pos + 1 ).c_str ());
152+ _host = _host.substr (0 , colon_pos);
153+ }
154+ }
155+ }
156+
157+ // fragment(#) 분리 — 이후 path/query 파싱 범위를 제한
158+ size_t frag_pos = str.find (' #' , before);
159+ size_t effective_end = (frag_pos != std::string::npos) ? frag_pos : str.length ();
160+
161+ if (frag_pos != std::string::npos && frag_pos + 1 < str.length ())
162+ {
163+ _fragment = str.substr (frag_pos + 1 );
164+ }
165+
166+ // path 파싱 — "?" 또는 "#" 이전까지의 "/" 구분 세그먼트
167+ size_t query_pos = str.find (' ?' , before);
168+ if (query_pos != std::string::npos && query_pos >= effective_end)
169+ query_pos = std::string::npos; // "#" 뒤의 "?"는 무시
170+
171+ size_t path_end = effective_end;
172+ if (query_pos != std::string::npos)
173+ path_end = query_pos;
174+
175+ _path = str.substr (before, path_end - before);
176+
177+ // query string 파싱 — "#" 이전까지만
178+ if (query_pos != std::string::npos && query_pos + 1 < effective_end)
179+ {
180+ const std::string query_string = str.substr (query_pos + 1 , effective_end - query_pos - 1 );
181+
182+ size_t q_before = 0 ;
183+ while (q_before < query_string.length ())
184+ {
185+ size_t amp_pos = query_string.find (' &' , q_before);
186+ std::string pair;
187+
188+ if (amp_pos == std::string::npos)
189+ {
190+ pair = query_string.substr (q_before);
191+ q_before = query_string.length ();
192+ }
193+ else
194+ {
195+ pair = query_string.substr (q_before, amp_pos - q_before);
196+ q_before = amp_pos + 1 ;
197+ }
198+
199+ if (!pair.empty ())
200+ {
201+ std::string key, value;
202+ uri::split_query (pair, " =" , key, value);
203+ if (!key.empty ())
204+ _query.insert (std::unordered_map<std::string, std::string>::value_type (key, value));
205+ }
206+ }
207+ }
208+ }
209+
210+ }
0 commit comments