]> gitweb.factorcode.org Git - factor.git/blob - basis/http/client/client.factor
Merge branch 'master' into experimental
[factor.git] / basis / http / client / client.factor
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 ;
11 IN: http.client
12
13 ERROR: too-many-redirects ;
14
15 CONSTANT: max-redirects 10
16
17 <PRIVATE
18
19 : write-request-line ( request -- request )
20     dup
21     [ method>> write bl ]
22     [ url>> relative-url present write bl ]
23     [ "HTTP/" write version>> write crlf ]
24     tri ;
25
26 : url-host ( url -- string )
27     [ host>> ] [ port>> ] bi dup "http" protocol-port =
28     [ drop ] [ ":" swap number>string 3append ] if ;
29
30 : set-host-header ( request header -- request header )
31     over url>> url-host "host" pick set-at ;
32
33 : set-cookie-header ( header cookies -- header )
34     unparse-cookie "cookie" pick set-at ;
35
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
41     write-header ;
42
43 : write-request ( request -- )
44     unparse-post-data
45     write-request-line
46     write-request-header
47     binary encode-output
48     write-post-data
49     flush
50     drop ;
51
52 : read-response-line ( response -- response )
53     read-crlf parse-response-line first3
54     [ >>version ] [ >>code ] [ >>message ] tri* ;
55
56 : read-response-header ( response -- response )
57     read-header >>header
58     dup "set-cookie" header parse-set-cookie >>cookies
59     dup "content-type" header [
60         parse-content-type
61         [ >>content-type ]
62         [ >>content-charset ] bi*
63     ] when* ;
64
65 : read-response ( -- response )
66     <response>
67     read-response-line
68     read-response-header ;
69
70 DEFER: (with-http-request)
71
72 SYMBOL: redirects
73
74 : redirect-url ( request url -- request )
75     '[ _ >url derive-url ensure-port ] change-url ;
76
77 : redirect? ( response -- ? )
78     code>> 300 399 between? ;
79
80 : do-redirect ( quot: ( chunk -- ) response -- response )
81     redirects inc
82     redirects get max-redirects < [
83         request get clone
84         swap "location" header redirect-url
85         "GET" >>method swap with-http-request
86     ] [ too-many-redirects ] if ; inline recursive
87
88 : read-chunk-size ( -- n )
89     read-crlf ";" split1 drop [ blank? ] trim-right
90     hex> [ "Bad chunk size" throw ] unless* ;
91
92 : read-chunked ( quot: ( chunk -- ) -- )
93     read-chunk-size dup zero?
94     [ 2drop ] [
95         read [ swap call ] [ drop ] 2bi
96         read-crlf B{ } assert= read-chunked
97     ] if ; inline recursive
98
99 : read-response-body ( quot response -- )
100     binary decode-input
101     "transfer-encoding" header "chunked" =
102     [ read-chunked ] [ each-block ] if ; inline
103
104 : <request-socket> ( -- stream )
105     request get url>> url-addr ascii <client> drop
106     1 minutes over set-timeout ;
107
108 PRIVATE>
109
110 : with-http-request ( request quot: ( chunk -- ) -- response )
111     swap
112     request [
113         <request-socket> [
114             [
115                 out>>
116                 [ request get write-request ]
117                 with-output-stream*
118             ] [
119                 in>> [
120                     read-response dup redirect? [ t ] [
121                         [ nip response set ]
122                         [ read-response-body ]
123                         [ ]
124                         2tri f
125                     ] if
126                 ] with-input-stream*
127             ] bi
128         ] with-disposal
129         [ do-redirect ] [ nip ] if
130     ] with-variable ; inline recursive
131
132 : <client-request> ( url method -- request )
133     <request>
134         swap >>method
135         swap >url ensure-port >>url ; inline
136
137 PRIVATE>
138
139 : success? ( code -- ? ) 200 299 between? ;
140
141 ERROR: download-failed response data ;
142
143 M: download-failed error.
144     "HTTP request failed:" print nl
145     [ response>> . ] [ data>> . ] bi ;
146
147 : check-response* ( response data -- response data )
148     over code>> success? [ download-failed ] unless ;
149
150 : with-http-request ( request quot -- response )
151     [ (with-http-request) check-response ] with-destructors ; inline
152
153 : http-request ( request -- response data )
154     [ [ % ] with-http-request ] B{ } make
155     over content-charset>> decode check-response* ;
156
157 : <client-request> ( url -- request )
158     <request> swap >url ensure-port >>url ;
159
160 : <client-data-request> ( data url -- request )
161     <client-request> swap >>post-data ;
162
163 : <get-request> ( url -- request )
164     "GET" <client-request> ;
165
166 : http-get ( url -- response data )
167     <get-request> http-request ;
168
169 : with-http-get ( url quot -- response )
170     [ <get-request> ] dip with-http-request check-response ; inline
171
172 : <delete-request> ( url -- request )
173     <client-request> "DELETE" >>method ;
174
175 : http-delete ( url -- response data )
176     <delete-request> http-request ;
177
178 : <trace-request> ( url -- request )
179     <client-request> "TRACE" >>method ;
180
181 : http-trace ( url -- response data )
182     <trace-request> http-request ;
183
184 : download-name ( url -- name )
185     present file-name "?" split1 drop "/" ?tail drop ;
186
187 : download-to ( url file -- )
188     binary [ [ write ] with-http-get drop ] with-file-writer ;
189
190 : download ( url -- )
191     dup download-name download-to ;
192
193 : <post-request> ( post-data url -- request )
194     "POST" <client-request>
195         swap >>post-data ;
196
197 : http-post ( post-data url -- response data )
198     <post-request> http-request ;
199
200 : <put-request> ( post-data url -- request )
201     "PUT" <client-request>
202         swap >>post-data ;
203
204 : http-put ( post-data url -- response data )
205     <put-request> http-request ;
206
207 USING: vocabs vocabs.loader ;
208
209 "debugger" vocab [ "http.client.debugger" require ] when