1 ! Copyright (C) 2005, 2008 Slava Pestov.
2 ! See http://factorcode.org/license.txt for BSD license.
3 USING: accessors assocs kernel math math.parser namespaces
4 sequences io io.sockets io.streams.string io.files io.timeouts
5 strings splitting calendar continuations accessors vectors
6 math.order hashtables byte-arrays prettyprint
13 fry debugger summary ascii urls present
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 : write-request-header ( request -- request )
29 dup header>> >hashtable
30 over url>> host>> [ over url>> url-host "host" pick set-at ] when
32 [ raw>> length "content-length" pick set-at ]
33 [ content-type>> "content-type" pick set-at ]
36 over cookies>> f like [ unparse-cookie "cookie" pick set-at ] when*
39 GENERIC: >post-data ( object -- post-data )
41 M: post-data >post-data ;
43 M: string >post-data "application/octet-stream" <post-data> ;
45 M: byte-array >post-data "application/octet-stream" <post-data> ;
47 M: assoc >post-data assoc>query "application/x-www-form-urlencoded" <post-data> ;
51 : unparse-post-data ( request -- request )
52 [ >post-data ] change-post-data ;
54 : write-post-data ( request -- request )
55 dup method>> "POST" = [ dup post-data>> raw>> write ] when ;
57 : write-request ( request -- )
65 : read-response-line ( response -- response )
66 read-crlf parse-response-line first3
67 [ >>version ] [ >>code ] [ >>message ] tri* ;
69 : read-response-header ( response -- response )
71 dup "set-cookie" header parse-set-cookie >>cookies
72 dup "content-type" header [
75 [ >>content-charset ] bi*
78 : read-response ( -- response )
81 read-response-header ;
85 ERROR: too-many-redirects ;
87 M: too-many-redirects summary
89 [ "Redirection limit of " % max-redirects # " exceeded" % ] "" make ;
97 : redirect-url ( request url -- request )
98 '[ , >url derive-url ensure-port ] change-url ;
100 : do-redirect ( response data -- response data )
101 over code>> 300 399 between? [
104 redirects get max-redirects < [
106 swap "location" header redirect-url
107 "GET" >>method (http-request)
115 : read-chunk-size ( -- n )
116 read-crlf ";" split1 drop [ blank? ] trim-right
117 hex> [ "Bad chunk size" throw ] unless* ;
120 read-chunk-size dup zero?
121 [ drop ] [ read % read-crlf B{ } assert= read-chunks ] if ;
123 : read-response-body ( response -- response data )
124 dup "transfer-encoding" header "chunked" = [
126 [ read-chunks ] B{ } make
127 over content-charset>> decode
129 dup content-charset>> decode-input
130 input-stream get contents
133 : (http-request) ( request -- response data )
135 dup url>> url-addr ascii [
144 : success? ( code -- ? ) 200 = ;
146 ERROR: download-failed response body ;
148 M: download-failed error.
149 "HTTP download failed:" print nl
150 [ response>> . nl ] [ body>> write ] bi ;
152 : check-response ( response data -- response data )
153 over code>> success? [ download-failed ] unless ;
155 : http-request ( request -- response data )
156 (http-request) check-response ;
158 : <get-request> ( url -- request )
161 swap >url ensure-port >>url ;
163 : http-get ( url -- response data )
164 <get-request> http-request ;
166 : download-name ( url -- name )
167 present file-name "?" split1 drop "/" ?tail drop ;
169 : download-to ( url file -- )
170 #! Downloads the contents of a URL to a file.
172 [ content-charset>> ] [ '[ , write ] ] bi*
175 : download ( url -- )
176 dup download-name download-to ;
178 : <post-request> ( post-data url -- request )
181 swap >url ensure-port >>url
184 : http-post ( post-data url -- response data )
185 <post-request> http-request ;