1 ! Copyright (C) 2005, 2009 Slava Pestov.
2 ! See http://factorcode.org/license.txt for BSD license.
3 USING: accessors assocs kernel math math.parser namespaces make
4 sequences strings splitting calendar continuations accessors vectors
5 math.order hashtables byte-arrays destructors
6 io io.sockets io.streams.string io.files io.timeouts
7 io.pathnames io.encodings io.encodings.string io.encodings.ascii
8 io.encodings.utf8 io.encodings.8-bit io.encodings.binary io.crlf
9 io.streams.duplex fry ascii urls urls.encoding present
10 http http.parsers http.client.post-data ;
13 ERROR: too-many-redirects ;
15 CONSTANT: max-redirects 10
19 : write-request-line ( request -- request )
22 [ url>> relative-url present write bl ]
23 [ "HTTP/" write version>> write crlf ]
26 : url-host ( url -- string )
27 [ host>> ] [ port>> ] bi dup "http" protocol-port =
28 [ drop ] [ ":" swap number>string 3append ] if ;
30 : set-host-header ( request header -- request header )
31 over url>> url-host "host" pick set-at ;
33 : set-cookie-header ( header cookies -- header )
34 unparse-cookie "cookie" pick set-at ;
36 : write-request-header ( request -- request )
37 dup header>> >hashtable
38 over url>> host>> [ set-host-header ] when
39 over post-data>> [ set-post-data-headers ] when*
40 over cookies>> [ set-cookie-header ] unless-empty
43 : write-request ( request -- )
52 : read-response-line ( response -- response )
53 read-crlf parse-response-line first3
54 [ >>version ] [ >>code ] [ >>message ] tri* ;
56 : read-response-header ( response -- response )
58 dup "set-cookie" header parse-set-cookie >>cookies
59 dup "content-type" header [
62 [ >>content-charset ] bi*
65 : read-response ( -- response )
68 read-response-header ;
70 DEFER: (with-http-request)
74 : redirect-url ( request url -- request )
75 '[ _ >url derive-url ensure-port ] change-url ;
77 : redirect? ( response -- ? )
78 code>> 300 399 between? ;
80 : do-redirect ( quot: ( chunk -- ) response -- response )
82 redirects get max-redirects < [
84 swap "location" header redirect-url
85 "GET" >>method swap (with-http-request)
86 ] [ too-many-redirects ] if ; inline recursive
88 : read-chunk-size ( -- n )
89 read-crlf ";" split1 drop [ blank? ] trim-tail
90 hex> [ "Bad chunk size" throw ] unless* ;
92 : read-chunked ( quot: ( chunk -- ) -- )
93 read-chunk-size dup zero?
95 read [ swap call ] [ drop ] 2bi
96 read-crlf B{ } assert= read-chunked
97 ] if ; inline recursive
99 : read-response-body ( quot response -- )
101 "transfer-encoding" header "chunked" =
102 [ read-chunked ] [ each-block ] if ; inline
104 : <request-socket> ( -- stream )
105 request get url>> url-addr ascii <client> drop
106 1 minutes over set-timeout ;
108 : (with-http-request) ( request quot: ( chunk -- ) -- response )
114 [ request get write-request ]
118 read-response dup redirect? [ t ] [
120 [ read-response-body ]
127 [ do-redirect ] [ nip ] if
128 ] with-variable ; inline recursive
130 : <client-request> ( url method -- request )
133 swap >url ensure-port >>url ; inline
137 : success? ( code -- ? ) 200 299 between? ;
139 ERROR: download-failed response ;
141 : check-response ( response -- response )
142 dup code>> success? [ download-failed ] unless ;
144 : with-http-request ( request quot -- response )
145 [ (with-http-request) check-response ] with-destructors ; inline
147 : http-request ( request -- response data )
148 [ [ % ] with-http-request ] B{ } make
149 over content-charset>> decode ;
151 : <get-request> ( url -- request )
152 "GET" <client-request> ;
154 : http-get ( url -- response data )
155 <get-request> http-request ;
157 : with-http-get ( url quot -- response )
158 [ <get-request> ] dip with-http-request ; inline
160 : download-name ( url -- name )
161 present file-name "?" split1 drop "/" ?tail drop ;
163 : download-to ( url file -- )
164 binary [ [ write ] with-http-get drop ] with-file-writer ;
166 : download ( url -- )
167 dup download-name download-to ;
169 : <post-request> ( post-data url -- request )
170 "POST" <client-request>
173 : http-post ( post-data url -- response data )
174 <post-request> http-request ;
176 : <put-request> ( post-data url -- request )
177 "PUT" <client-request>
180 : http-put ( post-data url -- response data )
181 <put-request> http-request ;
183 USING: vocabs vocabs.loader ;
185 "debugger" vocab [ "http.client.debugger" require ] when