1 ! Copyright (C) 2005, 2010 Slava Pestov.
2 ! See http://factorcode.org/license.txt for BSD license.
3 USING: 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.binary io.encodings.iana io.crlf
9 io.streams.duplex fry ascii urls urls.encoding present locals
10 http http.parsers http.client.post-data mime.types ;
13 ERROR: too-many-redirects ;
17 : write-request-line ( request -- request )
20 [ url>> relative-url present write bl ]
21 [ "HTTP/" write version>> write crlf ]
24 : url-host ( url -- string )
25 [ host>> ] [ port>> ] bi dup "http" protocol-port =
26 [ drop ] [ ":" swap number>string 3append ] if ;
28 : set-host-header ( request header -- request header )
29 over url>> url-host "host" pick set-at ;
31 : set-cookie-header ( header cookies -- header )
32 unparse-cookie "cookie" pick set-at ;
34 : write-request-header ( request -- request )
35 dup header>> >hashtable
36 over url>> host>> [ set-host-header ] when
37 over post-data>> [ set-post-data-headers ] when*
38 over cookies>> [ set-cookie-header ] unless-empty
41 : write-request ( request -- )
50 : read-response-line ( response -- response )
51 read-crlf parse-response-line first3
52 [ >>version ] [ >>code ] [ >>message ] tri* ;
54 : detect-encoding ( response -- encoding )
55 [ content-charset>> name>encoding ]
56 [ content-type>> mime-type-encoding ] bi
59 : read-response-header ( response -- response )
61 dup "set-cookie" header parse-set-cookie >>cookies
62 dup "content-type" header [
64 [ >>content-type ] [ >>content-charset ] bi*
65 dup detect-encoding >>content-encoding
68 : read-response ( -- response )
71 read-response-header ;
73 DEFER: (with-http-request)
77 : redirect-url ( request url -- request )
78 '[ _ >url derive-url ensure-port ] change-url ;
80 : redirect? ( response -- ? )
81 code>> 300 399 between? ;
83 :: do-redirect ( quot: ( chunk -- ) response -- response )
85 redirects get request get redirects>> < [
87 response "location" header redirect-url
88 response code>> 307 = [ "GET" >>method ] unless
89 quot (with-http-request)
90 ] [ too-many-redirects ] if ; inline recursive
92 : read-chunk-size ( -- n )
93 read-crlf ";" split1 drop [ blank? ] trim-tail
94 hex> [ "Bad chunk size" throw ] unless* ;
96 : read-chunked ( quot: ( chunk -- ) -- )
97 read-chunk-size dup zero?
99 read [ swap call ] [ drop ] 2bi
100 read-crlf B{ } assert= read-chunked
101 ] if ; inline recursive
103 : read-response-body ( quot response -- )
105 "transfer-encoding" header "chunked" =
106 [ read-chunked ] [ each-block ] if ; inline
108 : <request-socket> ( -- stream )
109 request get url>> url-addr ascii <client> drop
110 1 minutes over set-timeout ;
112 : (with-http-request) ( request quot: ( chunk -- ) -- response )
118 [ request get write-request ]
122 read-response dup redirect?
123 request get redirects>> 0 > and [ t ] [
125 [ read-response-body ]
132 [ do-redirect ] [ nip ] if
133 ] with-variable ; inline recursive
135 : <client-request> ( url method -- request )
138 swap >url ensure-port >>url ; inline
142 : success? ( code -- ? ) 200 299 between? ;
144 ERROR: download-failed response ;
146 : check-response ( response -- response )
147 dup code>> success? [ download-failed ] unless ;
149 : check-response-with-body ( response body -- response body )
150 [ >>body check-response ] keep ;
152 : with-http-request ( request quot -- response )
153 [ (with-http-request) ] with-destructors ; inline
155 : http-request ( request -- response data )
156 [ [ % ] with-http-request ] B{ } make
157 over content-encoding>> decode check-response-with-body ;
159 : <get-request> ( url -- request )
160 "GET" <client-request> ;
162 : http-get ( url -- response data )
163 <get-request> http-request ;
165 : with-http-get ( url quot -- response )
166 [ <get-request> ] dip with-http-request ; inline
168 : download-name ( url -- name )
169 present file-name "?" split1 drop "/" ?tail drop ;
171 : download-to ( url file -- )
172 binary [ [ write ] with-http-get check-response drop ] with-file-writer ;
174 : download ( url -- )
175 dup download-name download-to ;
177 : <post-request> ( post-data url -- request )
178 "POST" <client-request>
181 : http-post ( post-data url -- response data )
182 <post-request> http-request ;
184 : <put-request> ( post-data url -- request )
185 "PUT" <client-request>
188 : http-put ( post-data url -- response data )
189 <put-request> http-request ;
191 : <delete-request> ( url -- request )
192 "DELETE" <client-request> ;
194 : http-delete ( url -- response data )
195 <delete-request> http-request ;
197 USING: vocabs vocabs.loader ;
199 "debugger" "http.client.debugger" require-when