Initial commit of OpenSPARC T2 architecture model.
[OpenSPARC-T2-SAM] / obp / obp / pkg / netinet / http.fth
CommitLineData
920dae64
AT
1\ ========== Copyright Header Begin ==========================================
2\
3\ Hypervisor Software File: http.fth
4\
5\ Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved.
6\
7\ - Do no alter or remove copyright notices
8\
9\ - Redistribution and use of this software in source and binary forms, with
10\ or without modification, are permitted provided that the following
11\ conditions are met:
12\
13\ - Redistribution of source code must retain the above copyright notice,
14\ this list of conditions and the following disclaimer.
15\
16\ - Redistribution in binary form must reproduce the above copyright notice,
17\ this list of conditions and the following disclaimer in the
18\ documentation and/or other materials provided with the distribution.
19\
20\ Neither the name of Sun Microsystems, Inc. or the names of contributors
21\ may be used to endorse or promote products derived from this software
22\ without specific prior written permission.
23\
24\ This software is provided "AS IS," without a warranty of any kind.
25\ ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
26\ INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
27\ PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN
28\ MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR
29\ ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
30\ DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN
31\ OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR
32\ FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE
33\ DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY,
34\ ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF
35\ SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
36\
37\ You acknowledge that this software is not designed, licensed or
38\ intended for use in the design, construction, operation or maintenance of
39\ any nuclear facility.
40\
41\ ========== Copyright Header End ============================================
42id: @(#)http.fth 1.1 04/09/07
43purpose: HTTP support
44copyright: Copyright 2004 Sun Microsystems, Inc. All Rights Reserved
45copyright: Use is subject to license terms.
46
47\ RFC 2616: Hypertext Transfer Protocol -- HTTP/1.1
48\ RFC 2046: Multipurpose Internet Mail Extensions: Media Types
49
50headerless
51
520 instance value http-sockid
53/insock instance buffer: http-srv-addr
54/ip-addr instance buffer: http-server-ip
55/ip-addr instance buffer: http-proxy-ip
560 instance value http-server-port
570 instance value http-proxy-port
58
59\ Register the HTTP server and port. Use port 80 (decimal) as the
60\ default port.
61: http-init-server ( server$ -- )
62 parse-hostport http-server-ip inet-aton drop ( port$ )
63 $dnumber if IPPORT_HTTP then to http-server-port ( )
64;
65
66\ Register the HTTP proxy server and port. Use port 8080 (decimal) as
67\ the default proxy port.
68: http-init-proxy ( proxy$ -- )
69 inaddr-any http-proxy-ip copy-ip-addr ( proxy$ )
70 dup if ( proxy$ )
71 2dup check-htproxy$-form ( proxy$ )
72 parse-hostport http-proxy-ip inet-aton drop ( port$ )
73 $dnumber if IPPORT_HTTP_ALT then to http-proxy-port ( )
74 else ( null$ )
75 2drop ( )
76 then ( )
77;
78
79\ Is a proxy server in use?
80: use-proxy? ( -- flag ) http-proxy-ip inaddr-any? 0= ;
81
82\ Issue an HTTP GET request. The absolute URI form must be used if
83\ the request is being made to a proxy. Since persistent connections
84\ are the default with HTTP/1.1, we use the "close" connection option
85\ to signal that a persistent connection is not required.
86
87: http-send-request ( url$ -- )
88 d# 512 dup alloc-mem swap >r >r ( url$ ) ( r: len adr )
89 r@ 0 ( url$ msg$ )
90 " GET " strcat ( url$ msg$ )
91 use-proxy? if ( url$ msg$ )
92 2over strcat ( url$ msg$ )
93 else ( url$ msg$ )
94 " /" strcat ( url$ msg$ )
95 2over parse-http-url 2drop strcat ( url$ msg$ )
96 then ( url$ msg$ )
97 " HTTP/1.1"r"n" strcat ( url$ msg$ )
98 " Host: " strcat ( url$ msg$ )
99 2swap parse-http-url 2swap 2drop strcat ( msg$ )
100 " "r"n" strcat ( msg$ )
101 " Connection: close"r"n" strcat ( msg$ )
102 " "r"n" strcat ( msg$ )
103 http-sockid -rot 0 sosend drop ( )
104 r> r> free-mem ( ) ( r: )
105;
106
107\ Incoming data is buffered before further processing since we may
108\ need to peek at the data stream to determine the course of action.
109
110struct
111 /n field >http-bufadr \ Receive buffer address
112 /l field >http-bufsize \ Buffer size
113 /l field >http-bufstart \ Offset to start of data
114 /l field >http-bufnbytes \ Unread bytes in buffer
115constant /http-inbuf
116
1170 instance value http-inbuf
118
119\ Allocate receive buffer resources
120: htbuf-alloc ( -- htbuf )
121 /http-inbuf alloc-mem >r
122 h# 1000 dup alloc-mem r@ >http-bufadr !
123 r@ >http-bufsize l!
124 0 r@ >http-bufstart l!
125 0 r@ >http-bufnbytes l!
126 r>
127;
128
129\ Get receive buffer address and size
130: htbuf>adr,size ( htbuf -- adr size )
131 dup >http-bufadr @ swap >http-bufsize l@
132;
133
134\ Get address and size of unread data in receive buffer
135: htbuf>data,len ( htbuf -- data len )
136 dup >http-bufadr @ over >http-bufstart l@ ca+ swap >http-bufnbytes l@
137;
138
139\ Check if the receive buffer is empty
140: htbuf-empty? ( htbuf -- empty? ) >http-bufnbytes l@ 0= ;
141
142\ Read data arriving on this socket.
143: http-read-bytes ( adr len -- actual ) http-sockid -rot 0 sorecv ;
144
145\ Read data into the buffer. Called only when the buffer is empty.
146: htbuf-fill ( htbuf -- n )
147 dup htbuf>adr,size http-read-bytes ( htbuf n )
148 tuck over >http-bufnbytes l! ( n htbuf )
149 0 swap >http-bufstart l! ( n )
150;
151
152\ Read data from receive buffer, reading more data into the buffer
153\ if it is empty.
154: htbuf-read ( htbuf adr len -- #read )
155 rot >r ( adr len ) ( r: htbuf )
156 r@ htbuf-empty? if ( adr len )
157 r@ htbuf-fill 0= if ( adr len )
158 2drop r> drop 0 exit ( 0 )
159 then ( adr len )
160 show-progress ( adr len )
161 then ( adr len )
162 r@ htbuf>data,len ( adr len data n )
163 2swap rot min dup >r move r> ( #read )
164 r@ >http-bufnbytes 2dup l@ swap - swap l! ( #read )
165 r> >http-bufstart 2dup l@ + swap l! ( #read )
166;
167
168\ Peek at next character in stream
169: htbuf-peekchar ( htbuf -- char true | false )
170 dup htbuf-empty? if
171 dup htbuf-fill 0= if drop false exit then
172 then
173 htbuf>data,len drop c@ true
174;
175
176\ Free receive buffer resources
177: htbuf-free ( htbuf -- )
178 dup htbuf>adr,size free-mem /http-inbuf free-mem
179;
180
181\ Each HTTP header line ends with a CRLF sequence which serves as the
182\ end-of-line marker. But, HTTP/1.1 header field values can be folded
183\ onto multiple lines if the continuation line begins with a space
184\ or horizontal tab.
185
186: is-space? ( char -- flag ) dup h# 20 = swap h# 09 = or ;
187
188: http-read-hdrline ( adr maxlen -- adr len )
189 over >r over ca+ swap ( end nxt ) ( r: adr )
190 begin ( end nxt )
191 2dup = if nip r> tuck - exit then ( end nxt )
192 http-inbuf over 1 htbuf-read 0= if ( end nxt )
193 nip r> tuck - exit ( adr len )
194 then ( end nxt )
195 dup c@ linefeed = if ( end nxt )
196 http-inbuf htbuf-peekchar if ( end nxt char )
197 is-space? 0= if ( end nxt )
198 nip r> tuck - exit ( adr len )
199 then ( end nxt )
200 then ( end nxt )
201 else ( end nxt )
202 dup c@ carret <> if ca1+ then ( end nxt' )
203 then
204 again
205;
206
207\ Return the next HTTP header line token. Words may be separated
208\ by linear white space, or one of "," ";" or "=". The field value
209\ may be quoted string.
210
211: htnextfield ( $ -- rem$ tok$ )
212 " "t"r"n,=;" ( $ delim$ )
213 2swap 2over string-skipchars dup if ( delim$ $' )
214 over c@ ascii " = if ( delim$ $' )
215 1 /string ascii " left-parse-string ( delim$ rem$ tok$ )
216 else ( delim$ $' )
217 2over strtok ( delim$ rem$ tok$ )
218 then ( delim$ rem$ tok$ )
219 else ( delim$ $' )
220 null$ ( delim$ rem$ null$ )
221 then ( delim$ rem$ tok$ )
222 2rot 2drop ( rem$ tok$ )
223;
224
225\ The HTTP response begins with a Status-Line and is followed by
226\ message headers and a message body.
227\
228\ We expect a 2xx status code in the response status line, and process
229\ the "Content-Length", "Content-Type" and "Transfer-Encoding" HTTP
230\ message header fields. The message body may be a multipart message
231\ and may have the "chunked" transfer encoding applied to it.
232
2330 instance value http-transfer-length \ Total message length
234false instance value http-is-multipart? \ Multipart message?
235d# 72 instance buffer: http-part-boundary \ Multipart message boundary
2360 instance value http-bodypart-length \ Current bodypart length
237false instance value http-is-chunked? \ Chunked transfer?
238
239\ Process HTTP status line in the response.
240: http-check-statusline ( $ -- )
241 " " strtok ( rem$ ver$ )
242 2dup " HTTP/1.1" $= >r 2dup " HTTP/1.0" $= r> or 0= if
243 ." HTTP: Bad Version: " type cr -1 throw
244 then 2drop ( rem$ )
245 " " strtok ( msg$ code$ )
246 2dup $dnumber if
247 ." HTTP: Bad Status code: " type cr -1 throw
248 then nip nip ( msg$ code )
249 dup d# 200 <> if
250 ." HTTP: Bad Response: " .d type cr -1 throw
251 then ( msg$ code )
252 3drop ( )
253;
254
255\ Get the size of the HTTP message body.
256: http-content-length ( $ -- )
257 htnextfield 2swap 2drop $dnumber ( n false | true )
258 if ." HTTP Content Length Invalid" -1 throw then ( n )
259 to http-transfer-length ( )
260;
261
262\ Process "Content-Type" field of HTTP message header. If this a multipart
263\ message, get the boundary parameter value.
264: http-content-type ( $ -- )
265 htnextfield " multipart/mixed" $case= 0= if 2drop exit then
266 htnextfield " Boundary" $case= 0= if
267 ." Multipart Message Boundary not specified" -1 throw
268 then
269 htnextfield http-part-boundary pack drop 2drop
270 true to http-is-multipart?
271;
272
273\ Get the transfer encoding applied to the message body.
274: http-transfer-encoding ( $ -- )
275 htnextfield 2swap 2drop " chunked" $case= to http-is-chunked?
276;
277
278\ Token table for HTTP message headers.
279create http-headers
280 " Content-Length" false ['] http-content-length token-handler,
281 " Content-Type" false ['] http-content-type token-handler,
282 " Transfer-Encoding" false ['] http-transfer-encoding token-handler,
283 null$ 0 0 token-handler,
284
285\ Process HTTP message headers.
286: http-process-headers ( -- )
287 d# 1000 dup alloc-mem swap ( buf$ )
288 2dup http-read-hdrline ( buf$ line$ )
289 http-check-statusline ( buf$ )
290 begin ( buf$ )
291 2dup http-read-hdrline ( buf$ line$ )
292 dup while ( buf$ line$ )
293 ascii : left-parse-string ( buf$ value$ field$ )
294 http-headers find-token-handler if ( buf$ value$ xt )
295 execute ( buf$ )
296 else ( buf$ value$ )
297 2drop ( buf$ )
298 then ( buf$ )
299 repeat 2drop ( buf$ )
300 free-mem ( )
301;
302
303\ With chunked transfer encoding, the message body is split into one or
304\ more "chunks". A chunk appears as
305\ chunk = chunk-size CRLF
306\ chunk-data CRLF
307\ The chunk-size field indicates the size (in hexadecimal) of the data
308\ in that chunk. The last chunk has a size of zero.
309
310struct
311 d# 80 field >http-chunkline$ \ Buffer to read chunk-size lines
312 /l field >http-chunk-nleft \ Unread data in current chunk
313constant /http-chunkinfo
314
3150 instance value http-chunkinfo
316
317\ Initialize structures to enable chunk decoding
318: http-chunk-init ( -- htchunk )
319 /http-chunkinfo dup alloc-mem tuck swap erase
320;
321
322\ Free chunking data structures
323: http-chunk-free ( htchunk -- )
324 /http-chunkinfo free-mem
325;
326
327\ Get size of unread data in the current chunk
328: htchunk-nleft@ ( -- n ) http-chunkinfo >http-chunk-nleft l@ ;
329
330\ Update size of unread data in current chunk
331: htchunk-nleft! ( n -- ) http-chunkinfo >http-chunk-nleft l! ;
332
333\ Read chunk line (size of chunk or trailing CRLF)
334: http-read-chunkline ( -- line$ )
335 http-chunkinfo >http-chunkline$ d# 80 over 0 2swap bounds ?do
336 http-inbuf i 1 htbuf-read 0= ?leave
337 i c@ linefeed = ?leave
338 i c@ carret <> if 1+ then
339 loop ( adr len )
340;
341
342\ Get size of next chunk
343: http-read-chunksize ( -- n )
344 http-read-chunkline 2dup $hnumber if
345 ." HTTP: Bad Chunk Size " type cr -1 throw
346 then nip nip ( n )
347 dup htchunk-nleft! ( n )
348;
349
350\ Read chunk data. If all data from the previous chunk has been
351\ processed, get the size of the new chunk before reading data.
352\ If all data in the current chunk has been processed as a result
353\ of this read, process the trailing CRLF as well.
354
355: http-read-chunkdata ( adr len -- #read )
356 htchunk-nleft@ 0= if ( adr len )
357 http-read-chunksize 0= if ( adr len )
358 2drop 0 exit ( 0 )
359 then ( adr len )
360 then ( adr len )
361 http-inbuf -rot htchunk-nleft@ min htbuf-read ( #read )
362 htchunk-nleft@ over - htchunk-nleft! ( #read )
363 htchunk-nleft@ 0= if ( #read )
364 http-read-chunkline 2drop ( #read )
365 then ( #read )
366;
367
368\ Decode the chunked transfer-coding to get the message body contents.
369: http-chunked-read ( adr len -- #read )
370 over >r ( adr len ) ( r: adr )
371 begin dup while ( nxt rem )
372 2dup http-read-chunkdata ?dup if ( nxt rem n )
373 tuck - >r ca+ r> ( nxt' rem' )
374 else ( nxt rem )
375 drop r> - exit ( #read )
376 then ( nxt' rem' )
377 repeat ( nxt' rem' )
378 drop r> - ( #read )
379;
380
381\ Read contents from an unencoded message body.
382: http-unencoded-read ( adr len -- #read )
383 over >r ( adr len ) ( r: adr )
384 begin dup while ( nxt rem )
385 2dup http-inbuf -rot htbuf-read ?dup if ( nxt rem nread )
386 tuck - >r ca+ r> ( nxt' rem' )
387 else ( nxt rem )
388 drop r> - exit ( #read )
389 then ( nxt' rem' )
390 repeat ( nxt' rem' )
391 drop r> - ( #read )
392;
393
394\ Read a block of data from the message body.
395: http-read-body ( adr len -- #read )
396 http-is-chunked? if http-chunked-read else http-unencoded-read then
397;
398
399\ Processing body part headers of a multipart message.
400\
401\ Each body part of a multipart message is preceded by a boundary delimiter
402\ line, and the last one is followed by the closing boundary delimiter
403\ line. After its boundary delimiter line, the body part consists of a
404\ header area, a blank line, and a body area.
405\
406\ A boundary delimiter line is of the form "--<boundary>". A closing
407\ boundary delimiter line is of the form "--<boundary>--". The boundary
408\ string is specified in the "Content-Type" field in the HTTP header.
409\
410\ The "Content-Length" and "Content-Type" fields in the body part header
411\ lines provide the length and description of data contained in the
412\ body part.
413\
414\ Since the HTTP message body (which includes body parts of the multipart
415\ message) may be chunked, the transfer-coding must be decoded before
416\ reading body part header lines.
417
418: http-part-content-length ( $ -- )
419 htnextfield 2swap 2drop $dnumber if
420 ." HTTP Bodypart Content Length Invalid" -1 throw
421 then to http-bodypart-length
422;
423
424: http-part-content-type ( $ -- )
425 htnextfield 2swap 2drop ( type$ )
426 2dup " application/octet-stream" $case= 0= if
427 ." HTTP: Unexpected media type " type cr -1 throw
428 then 2drop
429;
430
431\ Token table for body part header fields.
432create http-part-headers
433 " Content-Length" false ['] http-part-content-length token-handler,
434 " Content-Type" false ['] http-part-content-type token-handler,
435 null$ 0 0 token-handler,
436
437: http-process-boundary$ ( boundary$ -- closing-boundary? )
438 over " --" comp 0<> if
439 ." HTTP: Missing Multipart Message Boundary" -1 throw
440 then
441 over 2+ http-part-boundary count comp 0<> if
442 ." HTTP: Multipart Message Boundary Mismatch" -1 throw
443 then
444 drop 2+ http-part-boundary count nip ca+ " --" comp 0=
445;
446
447\ Read next body part header line.
448: http-read-part-hdrline ( adr maxlen -- adr len )
449 over 0 2swap bounds ?do
450 i 1 http-read-body 0= ?leave
451 i c@ linefeed = ?leave
452 i c@ carret <> if 1+ then
453 loop
454;
455
456\ Process body part header lines.
457: http-process-part-headers ( -- )
458 d# 80 dup alloc-mem swap ( buf$ )
459 0 to http-bodypart-length ( buf$ )
460 begin 2dup http-read-part-hdrline dup 0= while ( buf$ line$ )
461 2drop ( buf$ )
462 repeat ( buf$ boundary$ )
463 http-process-boundary$ 0= if ( buf$ )
464 begin 2dup http-read-part-hdrline dup while ( buf$ line$ )
465 ascii : left-parse-string ( buf$ value$ field$ )
466 http-part-headers find-token-handler if ( buf$ value$ xt )
467 execute ( buf$ )
468 else ( buf$ value$ )
469 2drop ( buf$ )
470 then ( buf$ )
471 repeat 2drop ( buf$ )
472 then ( buf$ )
473 free-mem ( )
474;
475
476\ Establish a connection with the HTTP server. The server is accessed
477\ via a proxy server if one was specified.
478
479: http-init ( url$ proxy$ -- )
480 http-init-proxy ( url$ )
481 2dup check-httpurl$-form ( url$ )
482 parse-http-url 2swap 2drop http-init-server ( )
483
484 http-srv-addr ( insock )
485 use-proxy? if ( insock )
486 http-proxy-ip http-proxy-port ( insock proxyip,port )
487 else ( insock )
488 http-server-ip http-server-port ( insock srvip,port )
489 then ( insock ipaddr,port )
490 insock-init ( )
491
492 AF_INET SOCK_STREAM 0 socreate to http-sockid ( )
493 http-sockid http-srv-addr /insock soconnect ( 0 | error# )
494 0<> if ( )
495 http-sockid http-srv-addr ( sockid insock )
496 ." HTTP: Could not connect to " .insock .soerror ( )
497 -1 throw
498 then ( )
499
500 htbuf-alloc to http-inbuf ( )
501 http-chunk-init to http-chunkinfo ( )
502;
503
504\ Close HTTP connection and free resources.
505: http-close ( -- )
506 http-sockid soclose ( )
507 http-inbuf htbuf-free http-chunkinfo http-chunk-free ( )
508;
509
510[ifdef] DEBUG
511: http-load ( adr url$ proxy$ -- )
512 2over 2swap http-init ( adr url$ )
513 http-send-request ( adr )
514 http-process-headers ( adr )
515 h# a00000 http-read-body ( size )
516 http-close ( size )
517;
518[then]
519
520headers