2002-08-27 05:03:22 -04:00
;;; This file is part of the Scheme Untergrund Networking package.
2002-08-27 05:39:05 -04:00
;;; Copyright (c) 1994 by Brian D. Carlstrom and Olin Shivers.
2002-08-27 05:03:22 -04:00
;;; Copyright (c) 2002 by Mike Sperber.
;;; For copyright information, see the file COPYING which comes with
;;; the distribution.
2003-01-07 07:16:33 -05:00
( define-record-type http-response :http-response
2002-08-26 05:46:11 -04:00
( make-response code message seconds mime extras body )
response?
2004-05-17 12:42:45 -04:00
( code response-code ) ;;HTTP status code
( message response-message ) ;;reason phrase: textual description of
;;status-code, or #f (-> server sends
;;default reason phrase)
( seconds response-seconds ) ;;time the content was created
( mime response-mime ) ;;string indicating the MIME type of the response
( extras response-extras ) ;;assoc list with extra headers to be
;;added to the response; its elements are
;;pairs, each of which consists of a symbol
;;representing the field name and a string
;;representing the field value.
( body response-body ) ) ;; message-body
2002-08-26 05:46:11 -04:00
2004-08-15 07:03:28 -04:00
;;TODO: mime shouldn't be a field in http-response, because it needn't be present for
;;responses which don't include a message-body.
;;Instead treat mime-type like any other header.
;;(Not urgent, as RFC 2616 doesn't prohibit presence of Content-Type header field
;;in body-less responses).
2003-01-14 08:23:29 -05:00
;; This is mainly for nph-... CGI scripts.
;; This means that the body will output the entire MIME message, not
;; just the part after the headers.
( define-record-type http-nph-response :http-nph-response
( make-nph-response body )
nph-response?
( body nph-response-body ) )
2003-02-06 10:05:15 -05:00
( define-record-type http-input-response :http-input-response
( make-input-response body-maker )
input-response?
( body-maker input-response-body-maker ) )
2003-01-07 07:16:33 -05:00
( define-record-type http-writer-body :http-writer-body
2002-08-26 05:46:11 -04:00
( make-writer-body proc )
writer-body?
( proc writer-body-proc ) )
2003-01-07 07:16:33 -05:00
( define-record-type http-reader-writer-body :http-reader-writer-body
2002-08-28 12:44:07 -04:00
( make-reader-writer-body proc )
reader-writer-body?
( proc reader-writer-body-proc ) )
2002-09-02 09:42:10 -04:00
2003-01-07 07:16:33 -05:00
( define-record-type http-redirect-body :http-redirect-body
2002-09-02 09:42:10 -04:00
( make-redirect-body location )
redirect-body?
( location redirect-body-location ) )
2002-08-28 12:44:07 -04:00
2004-08-15 07:03:28 -04:00
;; type for responses which MUST NOT include a body (101, 204, 304)
( define-enumerated-type no-body :no-body
no-body?
no-body-elements
no-body-name
no-body-index
( none ) )
2002-08-28 12:44:07 -04:00
( define ( display-http-body body iport oport options )
( cond
( ( writer-body? body )
( ( writer-body-proc body ) oport options ) )
( ( reader-writer-body? body )
( ( reader-writer-body-proc body ) iport oport options ) ) ) )
2002-08-26 05:46:11 -04:00
2003-01-09 10:05:30 -05:00
( define-finite-type status-code :http-status-code
( number message )
status-code?
status-codes
status-code-name
status-code-index
( number status-code-number )
( message status-code-message )
(
2004-05-27 10:47:46 -04:00
( continue 100 "Continue" )
( switch-protocol 101 "Switching Protocols" )
2003-01-09 10:05:30 -05:00
( ok 200 "OK" )
( created 201 "Created" )
( accepted 202 "Accepted" )
2004-05-27 10:47:46 -04:00
( non-author-info 203 "Non-Authoritative Information" )
2003-01-09 10:05:30 -05:00
( no-content 204 "No Content" )
2004-05-27 10:47:46 -04:00
( reset-content 205 "Reset Content" )
( partial-content 206 "Partial Content" )
2003-01-09 10:05:30 -05:00
( mult-choice 300 "Multiple Choices" )
( moved-perm 301 "Moved Permanently" )
2004-05-27 10:47:46 -04:00
( found 302 "Found" ) ;;use 303 or 307 for unambiguity;
;;use 302 for compatibility with
;;pre-1.1-clients
( see-other 303 "See other" ) ;;client is expected to
;;perform a GET on new URI
2003-01-09 10:05:30 -05:00
( not-mod 304 "Not Modified" )
2004-05-27 10:47:46 -04:00
( use-proxy 305 "Use Proxy" )
( temp-redirect 307 "Temporary Redirect" ) ;;analogous to "302
;;Moved Temporarily"
;;in RFC1945
2003-01-09 10:05:30 -05:00
( bad-request 400 "Bad Request" )
( unauthorized 401 "Unauthorized" )
2004-05-27 10:47:46 -04:00
( payment-required 402 "Payment Required" )
2003-01-09 10:05:30 -05:00
( forbidden 403 "Forbidden" )
( not-found 404 "Not Found" )
( method-not-allowed 405 "Method Not Allowed" )
2004-05-27 10:47:46 -04:00
( not-acceptable 406 "Not Acceptable" )
2003-01-09 10:05:30 -05:00
( proxy-auth-required 407 "Proxy Authentication Required" )
( timeout 408 "Request Timeout" )
( conflict 409 "Conflict" )
2004-05-27 10:47:46 -04:00
( gone 410 "Gone" )
( length-required 411 "Length Required" )
( precon-failed 412 "Precondition Failed" )
( req-ent-too-large 413 "Request Entity Too Large" )
( req-uri-too-large 414 "Request URI Too Large" )
( unsupp-media-type 415 "Unsupported Media Type" )
( req-range-not-sat 416 "Requested Range Not Satisfiable" )
( expectation-failed 417 "Expectation Failed" )
2003-01-09 10:05:30 -05:00
( internal-error 500 "Internal Server Error" )
( not-implemented 501 "Not Implemented" )
( bad-gateway 502 "Bad Gateway" )
( service-unavailable 503 "Service Unavailable" )
( gateway-timeout 504 "Gateway Timeout" )
2004-05-27 10:47:46 -04:00
( version-not-supp 505 "HTTP Version Not Supported" )
2003-01-09 10:05:30 -05:00
( redirect -301 "Internal redirect" ) ) )
2003-01-10 04:57:41 -05:00
( define ( name->status-code name )
( if ( not ( symbol? name ) )
( call-error name->status-code ( list name ) )
( let loop ( ( i 0 ) )
( cond ( ( = i ( vector-length status-codes ) )
#f )
( ( eq? name
( status-code-name ( vector-ref status-codes i ) ) )
( vector-ref status-codes i ) )
( else
( loop ( + i 1 ) ) ) ) ) ) )
2003-01-15 08:37:05 -05:00
( define ( number->status-code number )
( if ( not ( number? number ) )
( call-error number->status-code ( list number ) )
( let loop ( ( i 0 ) )
( cond ( ( = i ( vector-length status-codes ) )
#f )
( ( = number
( status-code-number ( vector-ref status-codes i ) ) )
( vector-ref status-codes i ) )
( else
( loop ( + i 1 ) ) ) ) ) ) )
2002-08-26 05:59:14 -04:00
2004-07-30 18:25:03 -04:00
;;; (make-error-response status-code req [extras])
2002-08-27 05:39:05 -04:00
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;; As a special case, request REQ is allowed to be #f, meaning we haven't
2004-08-10 10:26:50 -04:00
;;; even had a chance to parse and construct the request. This can be the case for
;;; internal-error, bad-request, (possibly bad-gateway and ...?)
2002-08-27 05:39:05 -04:00
2004-07-30 18:25:03 -04:00
( define ( make-error-response code req . extras )
2004-08-10 10:26:50 -04:00
( let*
;;catch server internal errors coming off by calls of make-error-response with too few arguments
( ( assert ( lambda ( n )
( if ( < ( length extras ) n )
( make-error-response ( status-code internal-error ) req
"Too few arguments to make-error-response" ) ) ) )
( generic-title ( lambda ( port )
2004-07-30 18:25:03 -04:00
( title-html port
( status-code-message code ) ) ) )
( close-html ( lambda ( port args )
( if ( not ( null? args ) )
( format port "<br/>~%Further Information:~%" ) )
( for-each ( lambda ( x ) ( format port "<br/>~%~s~%" x ) ) args )
2004-08-13 11:37:31 -04:00
( format port "</p>~%</body>~%</html>~%" ) ) )
2004-08-15 07:03:28 -04:00
2004-07-30 18:25:03 -04:00
( create-response
2004-08-15 07:03:28 -04:00
( lambda ( headers body )
2004-07-30 18:25:03 -04:00
( make-response code
#f
( time )
"text/html"
headers
2004-08-15 07:03:28 -04:00
body ) ) )
( create-writer-body-response
( lambda ( headers writer-proc )
( create-response headers ( make-writer-body writer-proc ) ) ) )
( create-no-body-response
( lambda ( headers )
( create-response headers ( no-body none ) ) ) ) )
2002-08-27 05:39:05 -04:00
( cond
2004-08-15 07:03:28 -04:00
;;this response requires one arg:
;;the value of the Upgrade field header,
;;which must be a string listing the protocols which are being switched
;;for example "HTTP/2.0, IRC/6.9"
( ( eq? code ( status-code switch-protocol ) ) ;; server currently doesn't have ability to switch protocols
( assert 1 )
( create-no-body-response
( list ( cons 'upgrade ( car extras ) )
( cons 'connection "upgrade" ) ) ) ) ;; need this, because Upgrade header field only applies to immediate connection
( ( eq? code ( status-code no-content ) )
( create-no-body-response ' ( ) ) )
2004-07-29 12:08:30 -04:00
;; This error response requires one arg:
2004-07-30 18:25:03 -04:00
;; the value of the Location field header,
;; which must be a single absolute URI
2004-07-29 12:08:30 -04:00
( ( or ( eq? code ( status-code found ) ) ;302
( eq? code ( status-code see-other ) ) ;303
( eq? code ( status-code temp-redirect ) ) ;307
( eq? code ( status-code moved-perm ) ) ) ;301
2004-08-10 10:26:50 -04:00
( assert 1 )
2004-08-15 07:03:28 -04:00
( create-writer-body-response
2004-07-30 18:25:03 -04:00
( list ( cons 'location ( car extras ) ) )
2002-08-27 05:39:05 -04:00
( lambda ( port options )
( title-html port "Document moved" )
( format port
2004-08-11 06:17:14 -04:00
"The requested resource has moved ~A to a <a href=\"~A\">new location</a>.~%"
2004-07-29 12:08:30 -04:00
( if ( eq? code ( status-code moved-perm ) )
"permanently"
"temporarily" )
2004-07-30 18:25:03 -04:00
( car extras ) )
( close-html port ( cdr extras ) ) ) ) )
2002-08-27 05:39:05 -04:00
2004-08-15 07:03:28 -04:00
( ( eq? code ( status-code not-mod ) )
( create-no-body-response ' ( ) ) ) ;;see RCF 2616 10.3.5: this is only a valid answer if the server never sends
;;any of the headers Expires, Cache-Control, Vary for this resource
2004-07-30 18:25:03 -04:00
( ( eq? code ( status-code bad-request ) )
2004-08-15 07:03:28 -04:00
( create-writer-body-response
2002-08-27 05:39:05 -04:00
' ( )
2004-07-30 18:25:03 -04:00
( lambda ( port options )
( generic-title port )
2004-08-13 11:37:31 -04:00
( format port "The request the client sent could not be understood by this server due to malformed syntax.~% Report to client maintainer.~%" )
2004-07-30 18:25:03 -04:00
( close-html port extras ) ) ) )
2002-08-27 05:39:05 -04:00
2004-08-11 06:17:14 -04:00
;; This error response requires one arg:
;; the value of the Allow field header,
2004-08-11 10:48:11 -04:00
;; which must be a string listing the valid methods for the requested resource
;; Ex.: "GET, HEAD, POST"
2003-04-22 09:49:49 -04:00
( ( eq? code ( status-code method-not-allowed ) )
2004-08-11 06:17:14 -04:00
( assert 1 )
2004-08-15 07:03:28 -04:00
( create-writer-body-response
2004-08-11 06:17:14 -04:00
( list ( cons 'allow ( car extras ) ) )
2003-04-22 09:49:49 -04:00
( lambda ( port options )
( generic-title port )
2004-08-11 10:48:11 -04:00
( format port "The method ~A is not allowed on the requested resource ~A.~%"
2005-04-06 07:44:28 -04:00
( request-method req ) ( http-url->url-string ( request-url req ) ) )
2004-08-11 06:17:14 -04:00
( close-html port ( cdr extras ) ) ) ) )
2003-04-22 09:49:49 -04:00
2004-07-30 18:25:03 -04:00
;; This error response requires one arg:
;; the value of the WWW-Authenticate header field,
;; which must be a challenge (as described in RFC 2617)
2003-01-09 10:05:30 -05:00
( ( eq? code ( status-code unauthorized ) )
2004-08-10 10:26:50 -04:00
( assert 1 )
2004-08-15 07:03:28 -04:00
( create-writer-body-response
2004-07-30 18:25:03 -04:00
( list ( cons 'WWW-Authenticate ( car extras ) ) )
2002-08-27 05:39:05 -04:00
( lambda ( port options )
2004-07-30 18:25:03 -04:00
( title-html port "Authentication Required" )
2004-08-13 11:37:31 -04:00
( format port "Client not authentication-capable or authentication failed.~%" )
2004-07-30 18:25:03 -04:00
( close-html port ( cdr extras ) ) ) ) )
2002-08-27 05:39:05 -04:00
2003-01-09 10:05:30 -05:00
( ( eq? code ( status-code forbidden ) )
2004-08-15 07:03:28 -04:00
( create-writer-body-response
2002-08-27 05:39:05 -04:00
' ( )
( lambda ( port options )
( title-html port "Request not allowed." )
2004-08-13 11:37:31 -04:00
( format port "The request the client sent is not allowed.~% Retrying won't help.~%" )
2004-07-30 18:25:03 -04:00
( close-html port extras ) ) ) )
2002-08-27 05:39:05 -04:00
2003-01-09 10:05:30 -05:00
( ( eq? code ( status-code not-found ) )
2004-08-15 07:03:28 -04:00
( create-writer-body-response
2002-08-27 05:39:05 -04:00
' ( )
( lambda ( port options )
2004-07-30 18:25:03 -04:00
( title-html port "Resource not found" )
2004-08-13 11:37:31 -04:00
( format port "The requested resource ~A was not found on this server.~%"
2005-04-06 07:44:28 -04:00
( http-url->url-string ( request-url req ) ) )
2004-07-30 18:25:03 -04:00
( close-html port extras ) ) ) )
2002-08-27 05:39:05 -04:00
2003-01-09 10:05:30 -05:00
( ( eq? code ( status-code internal-error ) )
2004-08-15 07:03:28 -04:00
( create-writer-body-response
2002-08-27 05:39:05 -04:00
' ( )
( lambda ( port options )
( generic-title port )
2004-07-30 18:25:03 -04:00
( format port "This server encountered an internal error or misconfiguration and was unable to complete your request.~%<br/>~%Please inform the server administrator ~A of the circumstances leading to the error, and the time it occured.~%"
2002-11-03 09:41:43 -05:00
( or ( httpd-options-server-admin options )
"[no mail address available]" ) )
2004-07-30 18:25:03 -04:00
( close-html port extras ) ) ) )
2003-01-09 10:05:30 -05:00
( ( eq? code ( status-code not-implemented ) )
2004-08-15 07:03:28 -04:00
( create-writer-body-response
2002-08-27 05:39:05 -04:00
' ( )
( lambda ( port options )
( generic-title port )
2004-08-13 11:37:31 -04:00
( format port "This server does not recognize or does not implement the requested method ~A.~%"
2004-08-11 06:17:14 -04:00
( request-method req ) )
( close-html port extras ) ) ) )
2002-09-03 08:45:39 -04:00
2003-01-09 10:05:30 -05:00
( ( eq? code ( status-code bad-gateway ) )
2004-08-15 07:03:28 -04:00
( create-writer-body-response
2002-09-03 08:45:39 -04:00
' ( )
( lambda ( port options )
( generic-title port )
2004-08-11 15:38:16 -04:00
( format port "This server received an invalid response from the upstream server it accessed in attempting to fulfill the request.~%" )
2004-07-30 18:25:03 -04:00
( close-html port extras ) ) ) ) ) ) )
2002-08-27 05:39:05 -04:00
2004-08-13 11:37:31 -04:00
2002-08-27 05:39:05 -04:00
( define ( title-html out message )
2004-07-30 18:25:03 -04:00
;;produce valid XHTML 1.0 Strict
2004-08-13 11:37:31 -04:00
( emit-prolog out )
( emit-tag out 'html xmlnsdecl-attr )
( format out "~%<head>~%<title>~%~A~%</title>~%</head>~%" message )
2004-07-30 18:25:03 -04:00
( format out "<body>~%<h1>~A</h1>~%<p>~%" message ) )
2002-08-27 05:39:05 -04:00
2003-01-24 10:34:37 -05:00
;; Creates a redirect response. The server will serve the new file
;; indicated by NEW-LOCATION. NEW-LOCATION must be uri-encoded and
;; begin with a slash. This is intended for CGI scripts. Note that
;; the browser won't notice the redirect. Thus, it will keep the
;; original URL. For "real" redirections, use
;; (make-error-response (status-code moved-perm) req
2004-07-29 12:08:30 -04:00
;; "new-location").
2002-09-02 09:42:10 -04:00
( define ( make-redirect-response new-location )
( make-response
2003-01-09 10:05:30 -05:00
( status-code redirect )
#f
2002-09-02 09:42:10 -04:00
( time )
""
' ( )
2003-01-10 04:52:35 -05:00
( make-redirect-body new-location ) ) )