From 88cdc8f6c8ccb180a55ad6f8da507bee5df6d202 Mon Sep 17 00:00:00 2001 From: "Pierre R. Mai" Date: Tue, 25 May 1999 10:25:20 +0000 Subject: [PATCH] Basic development mostly done, need to do HTTP functionality now. We might deprecate CLASH-SYS (both package and system) and incorporate that into the driver itself. For now we've removed all dependencies. --- src/main/conditions.cl | 42 +++ src/main/status-codes.cl | 607 +++++++++++++++++++++++++++++++++++++++ src/main/url.cl | 185 ++++++++++++ src/package.cl | 4 +- 4 files changed, 836 insertions(+), 2 deletions(-) create mode 100644 src/main/conditions.cl create mode 100644 src/main/status-codes.cl create mode 100644 src/main/url.cl diff --git a/src/main/conditions.cl b/src/main/conditions.cl new file mode 100644 index 0000000..605521a --- /dev/null +++ b/src/main/conditions.cl @@ -0,0 +1,42 @@ +;;;; CLASH --- The Common Lisp Adaptable Simple HTTP server +;;;; This is copyrighted software. See documentation for terms. +;;;; +;;;; conditions.cl --- CLASH- and HTTP-specific conditions +;;;; +;;;; Checkout Tag: $Name$ +;;;; $Id$ + +(in-package :CLASH) + +;;;; %File Description: +;;;; +;;;; This file defines a number of CLASH- and/or HTTP-specific +;;;; conditions, which are used in the rest of CLASH for error +;;;; signalling and handling. +;;;; + +(defun report-clash-error + (condition stream &optional (fmt "Unspecified error.") args) + (format stream + "CLASH error: ~?~& Possible HTTP status code: ~D (~A)." + fmt args + (clash-error-code condition) + (HTTP-code-description (clash-error-code condition)))) + +(define-condition clash-error (error) + ((code :initarg :code :initform +HTTP-Code-Internal-Server-Error+ + :type fixnum + :reader clash-error-code + #+NIL + :documentation + #+NIL + "HTTP status code that might be returned to the client, if +this makes sense.")) + (:report report-clash-error)) + +(define-condition simple-clash-error (simple-condition clash-error) + () + (:report (lambda (condition stream) + (report-clash-error condition stream + (simple-condition-format-control condition) + (simple-condition-format-arguments condition))))) diff --git a/src/main/status-codes.cl b/src/main/status-codes.cl new file mode 100644 index 0000000..b082995 --- /dev/null +++ b/src/main/status-codes.cl @@ -0,0 +1,607 @@ +;;;; CLASH --- The Common Lisp Adaptable Simple HTTP server +;;;; This is copyrighted software. See documentation for terms. +;;;; +;;;; status-codes.cl --- HTTP-mandated status codes. +;;;; +;;;; Checkout Tag: $Name$ +;;;; $Id$ + +(in-package :CLASH) + +;;;; %File Description: +;;;; +;;;; This file defines constants for all the status codes prescribed +;;;; by RFC 1945 (HTTP/1.0) and RFC 2068 (HTTP/1.1). Documentation +;;;; from RFC 2068 has been added where appropriate. +;;;; + +#| +10.1 Informational 1xx + + This class of status code indicates a provisional response, + consisting only of the Status-Line and optional headers, and is + terminated by an empty line. Since HTTP/1.0 did not define any 1xx + status codes, servers MUST NOT send a 1xx response to an HTTP/1.0 + client except under experimental conditions. +|# + +(defconstant +HTTP-Code-Continue+ 100 + "HTTP/1.1:10.1 Informational:10.1.1 100 Continue + + The client may continue with its request. This interim response is + used to inform the client that the initial part of the request has + been received and has not yet been rejected by the server. The client + SHOULD continue by sending the remainder of the request or, if the + request has already been completed, ignore this response. The server + MUST send a final response after the request has been completed.") + +(defconstant +HTTP-Code-Switching-Protocols+ 101 + "HTTP/1.1:10.1 Informational:10.1.2 101 Switching Protocols + + The server understands and is willing to comply with the client's + request, via the Upgrade message header field (section 14.41), for a + change in the application protocol being used on this connection. The + server will switch protocols to those defined by the response's + Upgrade header field immediately after the empty line which + terminates the 101 response. + + The protocol should only be switched when it is advantageous to do + so. For example, switching to a newer version of HTTP is + advantageous over older versions, and switching to a real-time, + synchronous protocol may be advantageous when delivering resources + that use such features.") + +#| +10.2 Successful 2xx + + This class of status code indicates that the client's request was + successfully received, understood, and accepted. +|# + +(defconstant +HTTP-Code-OK+ 200 + "HTTP/1.0:10.2 Successful:10.2.1 200 OK + + The request has succeeded. The information returned with the response + is dependent on the method used in the request, for example: + + GET an entity corresponding to the requested resource is sent in the + response; + + HEAD the entity-header fields corresponding to the requested resource + are sent in the response without any message-body; + + POST an entity describing or containing the result of the action; + + TRACE an entity containing the request message as received by the end + server.") + +(defconstant +HTTP-Code-Created+ 201 + "HTTP/1.0:10.2 Successful:10.2.2 201 Created + + The request has been fulfilled and resulted in a new resource being + created. The newly created resource can be referenced by the URI(s) + returned in the entity of the response, with the most specific URL + for the resource given by a Location header field. The origin server + MUST create the resource before returning the 201 status code. If the + action cannot be carried out immediately, the server should respond + with 202 (Accepted) response instead.") + +(defconstant +HTTP-Code-Accepted+ 202 + "HTTP/1.0:10.2 Successful:10.2.3 202 Accepted + + The request has been accepted for processing, but the processing has + not been completed. The request MAY or MAY NOT eventually be acted + upon, as it MAY be disallowed when processing actually takes place. + There is no facility for re-sending a status code from an + asynchronous operation such as this. + + The 202 response is intentionally non-committal. Its purpose is to + allow a server to accept a request for some other process (perhaps a + batch-oriented process that is only run once per day) without + requiring that the user agent's connection to the server persist + until the process is completed. The entity returned with this + response SHOULD include an indication of the request's current status + and either a pointer to a status monitor or some estimate of when the + user can expect the request to be fulfilled.") + +(defconstant +HTTP-Code-Non-Authoritative-Information+ 203 + "HTTP/1.1:10.2 Successful:10.2.4 203 Non-Authoritative Information + + The returned metainformation in the entity-header is not the + definitive set as available from the origin server, but is gathered + from a local or a third-party copy. The set presented MAY be a subset + or superset of the original version. For example, including local + annotation information about the resource MAY result in a superset of + the metainformation known by the origin server. Use of this response + code is not required and is only appropriate when the response would + otherwise be 200 (OK).") + +(defconstant +HTTP-Code-No-Content+ 204 + "HTTP/1.0:10.2 Successful:10.2.5 204 No Content + + The server has fulfilled the request but there is no new information + to send back. If the client is a user agent, it SHOULD NOT change its + document view from that which caused the request to be sent. This + response is primarily intended to allow input for actions to take + place without causing a change to the user agent's active document + view. The response MAY include new metainformation in the form of + entity-headers, which SHOULD apply to the document currently in the + user agent's active view. + + The 204 response MUST NOT include a message-body, and thus is always + terminated by the first empty line after the header fields.") + +(defconstant +HTTP-Code-Reset-Content+ 205 + "HTTP/1.1:10.2 Successful:10.2.6 205 Reset Content + + The server has fulfilled the request and the user agent SHOULD reset + the document view which caused the request to be sent. This response + is primarily intended to allow input for actions to take place via + user input, followed by a clearing of the form in which the input is + given so that the user can easily initiate another input action. The + response MUST NOT include an entity.") + +(defconstant +HTTP-Code-Partial-Content+ 206 + "HTTP/1.1:10.2 Successful:10.2.7 206 Partial Content + + The server has fulfilled the partial GET request for the resource. + The request must have included a Range header field (section 14.36) + indicating the desired range. The response MUST include either a + Content-Range header field (section 14.17) indicating the range + included with this response, or a multipart/byteranges Content-Type + including Content-Range fields for each part. If multipart/byteranges + is not used, the Content-Length header field in the response MUST + match the actual number of OCTETs transmitted in the message-body. + + A cache that does not support the Range and Content-Range headers + MUST NOT cache 206 (Partial) responses.") + +#| +10.3 Redirection 3xx + + This class of status code indicates that further action needs to be + taken by the user agent in order to fulfill the request. The action + required MAY be carried out by the user agent without interaction + with the user if and only if the method used in the second request is + GET or HEAD. A user agent SHOULD NOT automatically redirect a request + more than 5 times, since such redirections usually indicate an + infinite loop. +|# + +(defconstant +HTTP-Code-Multiple-Choices+ 300 + "HTTP/1.0:10.3 Redirection:10.3.1 300 Multiple Choices + + The requested resource corresponds to any one of a set of + representations, each with its own specific location, and agent- + driven negotiation information (section 12) is being provided so that + the user (or user agent) can select a preferred representation and + redirect its request to that location. + + Unless it was a HEAD request, the response SHOULD include an entity + containing a list of resource characteristics and location(s) from + which the user or user agent can choose the one most appropriate. The + entity format is specified by the media type given in the Content- + Type header field. Depending upon the format and the capabilities of + the user agent, selection of the most appropriate choice may be + performed automatically. However, this specification does not define + any standard for such automatic selection. + + If the server has a preferred choice of representation, it SHOULD + include the specific URL for that representation in the Location + field; user agents MAY use the Location field value for automatic + redirection. This response is cachable unless indicated otherwise.") + +(defconstant +HTTP-Code-Moved-Permanently+ 301 + "HTTP/1.0:10.3 Redirection:10.3.2 301 Moved Permanently + + The requested resource has been assigned a new permanent URI and any + future references to this resource SHOULD be done using one of the + returned URIs. Clients with link editing capabilities SHOULD + automatically re-link references to the Request-URI to one or more of + the new references returned by the server, where possible. This + response is cachable unless indicated otherwise. + + If the new URI is a location, its URL SHOULD be given by the Location + field in the response. Unless the request method was HEAD, the entity + of the response SHOULD contain a short hypertext note with a + hyperlink to the new URI(s). + + If the 301 status code is received in response to a request other + than GET or HEAD, the user agent MUST NOT automatically redirect the + request unless it can be confirmed by the user, since this might + change the conditions under which the request was issued. + + Note: When automatically redirecting a POST request after receiving + a 301 status code, some existing HTTP/1.0 user agents will + erroneously change it into a GET request.") + +(defconstant +HTTP-Code-Moved-Temporarily+ 302 + "HTTP/1.0:10.3 Redirection:10.3.3 302 Moved Temporarily + + The requested resource resides temporarily under a different URI. + Since the redirection may be altered on occasion, the client SHOULD + continue to use the Request-URI for future requests. This response is + only cachable if indicated by a Cache-Control or Expires header + field. + + If the new URI is a location, its URL SHOULD be given by the Location + field in the response. Unless the request method was HEAD, the entity + of the response SHOULD contain a short hypertext note with a + hyperlink to the new URI(s). + + If the 302 status code is received in response to a request other + than GET or HEAD, the user agent MUST NOT automatically redirect the + request unless it can be confirmed by the user, since this might + change the conditions under which the request was issued. + + Note: When automatically redirecting a POST request after receiving + a 302 status code, some existing HTTP/1.0 user agents will + erroneously change it into a GET request.") + +(defconstant +HTTP-Code-See-Other+ 303 + "HTTP/1.1:10.3 Redirection:10.3.4 303 See Other + + The response to the request can be found under a different URI and + SHOULD be retrieved using a GET method on that resource. This method + exists primarily to allow the output of a POST-activated script to + redirect the user agent to a selected resource. The new URI is not a + substitute reference for the originally requested resource. The 303 + response is not cachable, but the response to the second (redirected) + request MAY be cachable. + + If the new URI is a location, its URL SHOULD be given by the Location + field in the response. Unless the request method was HEAD, the entity + of the response SHOULD contain a short hypertext note with a + hyperlink to the new URI(s).") + +(defconstant +HTTP-Code-Not-Modified+ 304 + "HTTP/1.0:10.3 Redirection:10.3.5 304 Not Modified + + If the client has performed a conditional GET request and access is + allowed, but the document has not been modified, the server SHOULD + respond with this status code. The response MUST NOT contain a + message-body. + + The response MUST include the following header fields: + + o Date + + o ETag and/or Content-Location, if the header would have been sent in + a 200 response to the same request + + o Expires, Cache-Control, and/or Vary, if the field-value might + differ from that sent in any previous response for the same variant + + If the conditional GET used a strong cache validator (see section + 13.3.3), the response SHOULD NOT include other entity-headers. + Otherwise (i.e., the conditional GET used a weak validator), the + response MUST NOT include other entity-headers; this prevents + inconsistencies between cached entity-bodies and updated headers. + + If a 304 response indicates an entity not currently cached, then the + cache MUST disregard the response and repeat the request without the + conditional. + + If a cache uses a received 304 response to update a cache entry, the + cache MUST update the entry to reflect any new field values given in + the response. + + The 304 response MUST NOT include a message-body, and thus is always + terminated by the first empty line after the header fields.") + +(defconstant +HTTP-Code-Use-Proxy+ 305 + "HTTP/1.1:10.3 Redirection:10.3.6 305 Use Proxy + + The requested resource MUST be accessed through the proxy given by + the Location field. The Location field gives the URL of the proxy. + The recipient is expected to repeat the request via the proxy.") + +#| +10.4 Client Error 4xx + + The 4xx class of status code is intended for cases in which the + client seems to have erred. Except when responding to a HEAD request, + the server SHOULD include an entity containing an explanation of the + error situation, and whether it is a temporary or permanent + condition. These status codes are applicable to any request method. + User agents SHOULD display any included entity to the user. + + Note: If the client is sending data, a server implementation using + TCP should be careful to ensure that the client acknowledges + receipt of the packet(s) containing the response, before the server + closes the input connection. If the client continues sending data + to the server after the close, the server's TCP stack will send a + reset packet to the client, which may erase the client's + unacknowledged input buffers before they can be read and + interpreted by the HTTP application. +|# + +(defconstant +HTTP-Code-Bad-Request+ 400 + "HTTP/1.0:10.4 Client Error:10.4.1 400 Bad Request + + The request could not be understood by the server due to malformed + syntax. The client SHOULD NOT repeat the request without + modifications.") + +(defconstant +HTTP-Code-Unauthorized+ 401 + "HTTP/1.0:10.4 Client Error:10.4.2 401 Unauthorized + + The request requires user authentication. The response MUST include a + WWW-Authenticate header field (section 14.46) containing a challenge + applicable to the requested resource. The client MAY repeat the + request with a suitable Authorization header field (section 14.8). If + the request already included Authorization credentials, then the 401 + response indicates that authorization has been refused for those + credentials. If the 401 response contains the same challenge as the + prior response, and the user agent has already attempted + authentication at least once, then the user SHOULD be presented the + entity that was given in the response, since that entity MAY include + relevant diagnostic information. HTTP access authentication is + explained in section 11.") + +(defconstant +HTTP-Code-Payment-Required+ 402 + "HTTP/1.1:10.4 Client Error:10.4.3 402 Payment Required + + This code is reserved for future use.") + +(defconstant +HTTP-Code-Forbidden+ 403 + "HTTP/1.0:10.4 Client Error:10.4.4 403 Forbidden + + The server understood the request, but is refusing to fulfill it. + Authorization will not help and the request SHOULD NOT be repeated. + If the request method was not HEAD and the server wishes to make + public why the request has not been fulfilled, it SHOULD describe the + reason for the refusal in the entity. This status code is commonly + used when the server does not wish to reveal exactly why the request + has been refused, or when no other response is applicable.") + +(defconstant +HTTP-Code-Not-Found+ 404 + "HTTP/1.0:10.4 Client Error:10.4.5 404 Not Found + + The server has not found anything matching the Request-URI. No + indication is given of whether the condition is temporary or + permanent. + + If the server does not wish to make this information available to the + client, the status code 403 (Forbidden) can be used instead. The 410 + (Gone) status code SHOULD be used if the server knows, through some + internally configurable mechanism, that an old resource is + permanently unavailable and has no forwarding address.") + +(defconstant +HTTP-Code-Method-Not-Allowed+ 405 + "HTTP/1.1:10.4 Client Error:10.4.6 405 Method Not Allowed + + The method specified in the Request-Line is not allowed for the + resource identified by the Request-URI. The response MUST include an + Allow header containing a list of valid methods for the requested + resource.") + +(defconstant +HTTP-Code-Not-Acceptable+ 406 + "HTTP/1.1:10.4 Client Error:10.4.7 406 Not Acceptable + + The resource identified by the request is only capable of generating + response entities which have content characteristics not acceptable + according to the accept headers sent in the request. + + Unless it was a HEAD request, the response SHOULD include an entity + containing a list of available entity characteristics and location(s) + from which the user or user agent can choose the one most + appropriate. The entity format is specified by the media type given + in the Content-Type header field. Depending upon the format and the + capabilities of the user agent, selection of the most appropriate + choice may be performed automatically. However, this specification + does not define any standard for such automatic selection. + + Note: HTTP/1.1 servers are allowed to return responses which are + not acceptable according to the accept headers sent in the request. + In some cases, this may even be preferable to sending a 406 + response. User agents are encouraged to inspect the headers of an + incoming response to determine if it is acceptable. If the response + could be unacceptable, a user agent SHOULD temporarily stop receipt + of more data and query the user for a decision on further actions.") + +(defconstant +HTTP-Code-Proxy-Authentication-Required+ 407 + "HTTP/1.1:10.4 Client Error:10.4.8 407 Proxy Authentication Required + + This code is similar to 401 (Unauthorized), but indicates that the + client MUST first authenticate itself with the proxy. The proxy MUST + return a Proxy-Authenticate header field (section 14.33) containing a + challenge applicable to the proxy for the requested resource. The + client MAY repeat the request with a suitable Proxy-Authorization + header field (section 14.34). HTTP access authentication is explained + in section 11.") + +(defconstant +HTTP-Code-Request-Timeout+ 408 + "HTTP/1.1:10.4 Client Error:10.4.9 408 Request Timeout + + The client did not produce a request within the time that the server + was prepared to wait. The client MAY repeat the request without + modifications at any later time.") + +(defconstant +HTTP-Code-Conflict+ 409 + "HTTP/1.1:10.4 Client Error:10.4.10 409 Conflict + + The request could not be completed due to a conflict with the current + state of the resource. This code is only allowed in situations where + it is expected that the user might be able to resolve the conflict + and resubmit the request. The response body SHOULD include enough + information for the user to recognize the source of the conflict. + Ideally, the response entity would include enough information for the + user or user agent to fix the problem; however, that may not be + possible and is not required. + + Conflicts are most likely to occur in response to a PUT request. If + versioning is being used and the entity being PUT includes changes to + a resource which conflict with those made by an earlier (third-party) + request, the server MAY use the 409 response to indicate that it + can't complete the request. In this case, the response entity SHOULD + contain a list of the differences between the two versions in a + format defined by the response Content-Type.") + +(defconstant +HTTP-Code-Gone+ 410 + "HTTP/1.1:10.4 Client Error:10.4.11 410 Gone + + The requested resource is no longer available at the server and no + forwarding address is known. This condition SHOULD be considered + permanent. Clients with link editing capabilities SHOULD delete + references to the Request-URI after user approval. If the server does + not know, or has no facility to determine, whether or not the + condition is permanent, the status code 404 (Not Found) SHOULD be + used instead. This response is cachable unless indicated otherwise. + + The 410 response is primarily intended to assist the task of web + maintenance by notifying the recipient that the resource is + intentionally unavailable and that the server owners desire that + remote links to that resource be removed. Such an event is common for + limited-time, promotional services and for resources belonging to + individuals no longer working at the server's site. It is not + necessary to mark all permanently unavailable resources as \"gone\" or + to keep the mark for any length of time -- that is left to the + discretion of the server owner.") + +(defconstant +HTTP-Code-Length-Required+ 411 + "HTTP/1.1:10.4 Client Error:10.4.12 411 Length Required + + The server refuses to accept the request without a defined Content- + Length. The client MAY repeat the request if it adds a valid + Content-Length header field containing the length of the message-body + in the request message.") + +(defconstant +HTTP-Code-Precondition-Failed+ 412 + "HTTP/1.1:10.4 Client Error:10.4.13 412 Precondition Failed + + The precondition given in one or more of the request-header fields + evaluated to false when it was tested on the server. This response + code allows the client to place preconditions on the current resource + metainformation (header field data) and thus prevent the requested + method from being applied to a resource other than the one intended.") + +(defconstant +HTTP-Code-Request-Entity-Too-Large+ 413 + "HTTP/1.1:10.4 Client Error:10.4.14 413 Request Entity Too Large + + The server is refusing to process a request because the request + entity is larger than the server is willing or able to process. The + server may close the connection to prevent the client from continuing + the request. + + If the condition is temporary, the server SHOULD include a Retry- + After header field to indicate that it is temporary and after what + time the client may try again.") + +(defconstant +HTTP-Code-Request-URI-Too-Long+ 414 + "HTTP/1.1:10.4 Client Error:10.4.15 414 Request-URI Too Long + + The server is refusing to service the request because the Request-URI + is longer than the server is willing to interpret. This rare + condition is only likely to occur when a client has improperly + converted a POST request to a GET request with long query + information, when the client has descended into a URL \"black hole\" of + redirection (e.g., a redirected URL prefix that points to a suffix of + itself), or when the server is under attack by a client attempting to + exploit security holes present in some servers using fixed-length + buffers for reading or manipulating the Request-URI.") + +(defconstant +HTTP-Code-Unsupported-Media-Type+ 415 + "HTTP/1.1:10.4 Client Error:10.4.16 415 Unsupported Media Type + + The server is refusing to service the request because the entity of + the request is in a format not supported by the requested resource + for the requested method.") + +#| +10.5 Server Error 5xx + + Response status codes beginning with the digit "5" indicate cases in + which the server is aware that it has erred or is incapable of + performing the request. Except when responding to a HEAD request, the + server SHOULD include an entity containing an explanation of the + error situation, and whether it is a temporary or permanent + condition. User agents SHOULD display any included entity to the + user. These response codes are applicable to any request method. +|# + +(defconstant +HTTP-Code-Internal-Server-Error+ 500 + "HTTP/1.0:10.5 Server Error:10.5.1 500 Internal Server Error + + The server encountered an unexpected condition which prevented it + from fulfilling the request.") + +(defconstant +HTTP-Code-Not-Implemented+ 501 + "HTTP/1.0:10.5 Server Error:10.5.2 501 Not Implemented + + The server does not support the functionality required to fulfill the + request. This is the appropriate response when the server does not + recognize the request method and is not capable of supporting it for + any resource.") + +(defconstant +HTTP-Code-Bad-Gateway+ 502 + "HTTP/1.0:10.5 Server Error:10.5.3 502 Bad Gateway + + The server, while acting as a gateway or proxy, received an invalid + response from the upstream server it accessed in attempting to + fulfill the request.") + +(defconstant +HTTP-Code-Service-Unavailable+ 503 + "HTTP/1.0:10.5 Server Error:10.5.4 503 Service Unavailable + + The server is currently unable to handle the request due to a + temporary overloading or maintenance of the server. The implication + is that this is a temporary condition which will be alleviated after + some delay. If known, the length of the delay may be indicated in a + Retry-After header. If no Retry-After is given, the client SHOULD + handle the response as it would for a 500 response. + + Note: The existence of the 503 status code does not imply that a + server must use it when becoming overloaded. Some servers may wish + to simply refuse the connection.") + +(defconstant +HTTP-Code-Gateway-Timeout+ 504 + "HTTP/1.1:10.5 Server Error:10.5.5 504 Gateway Timeout + + The server, while acting as a gateway or proxy, did not receive a + timely response from the upstream server it accessed in attempting to + complete the request.") + +(defconstant +HTTP-Code-HTTP-Version-Not-Supported+ 505 + "HTTP/1.1:10.5 Server Error:10.5.6 505 HTTP Version Not Supported + + The server does not support, or refuses to support, the HTTP protocol + version that was used in the request message. The server is + indicating that it is unable or unwilling to complete the request + using the same major version as the client, as described in section + 3.1, other than with this error message. The response SHOULD contain + an entity describing why that version is not supported and what other + protocols are supported by that server.") + + +(defconstant +HTTP-Code-Symbols+ + '(+HTTP-CODE-NO-CONTENT+ +HTTP-CODE-GATEWAY-TIMEOUT+ + +HTTP-CODE-REQUEST-URI-TOO-LONG+ +HTTP-CODE-NON-AUTHORITATIVE-INFORMATION+ + +HTTP-CODE-USE-PROXY+ +HTTP-CODE-SERVICE-UNAVAILABLE+ + +HTTP-CODE-BAD-REQUEST+ +HTTP-CODE-MULTIPLE-CHOICES+ + +HTTP-CODE-SWITCHING-PROTOCOLS+ +HTTP-CODE-CREATED+ +HTTP-CODE-UNAUTHORIZED+ + +HTTP-CODE-BAD-GATEWAY+ +HTTP-CODE-FORBIDDEN+ +HTTP-CODE-CONFLICT+ + +HTTP-CODE-CONTINUE+ +HTTP-CODE-INTERNAL-SERVER-ERROR+ + +HTTP-CODE-RESET-CONTENT+ +HTTP-CODE-PRECONDITION-FAILED+ + +HTTP-CODE-UNSUPPORTED-MEDIA-TYPE+ +HTTP-CODE-OK+ + +HTTP-CODE-METHOD-NOT-ALLOWED+ +HTTP-CODE-LENGTH-REQUIRED+ + +HTTP-CODE-REQUEST-ENTITY-TOO-LARGE+ +HTTP-CODE-MOVED-TEMPORARILY+ + +HTTP-CODE-NOT-FOUND+ +HTTP-CODE-NOT-MODIFIED+ + +HTTP-CODE-HTTP-VERSION-NOT-SUPPORTED+ +HTTP-CODE-PARTIAL-CONTENT+ + +HTTP-CODE-PAYMENT-REQUIRED+ +HTTP-CODE-PROXY-AUTHENTICATION-REQUIRED+ + +HTTP-CODE-ACCEPTED+ +HTTP-CODE-NOT-ACCEPTABLE+ + +HTTP-CODE-MOVED-PERMANENTLY+ +HTTP-CODE-REQUEST-TIMEOUT+ + +HTTP-CODE-NOT-IMPLEMENTED+ +HTTP-CODE-SEE-OTHER+ +HTTP-CODE-GONE+) + "List of all HTTP-Code symbols.") + +(defun HTTP-Code-Description (code) + (loop for sym in +HTTP-Code-Symbols+ + do + (when (= (symbol-value sym) code) + (let* ((doc (documentation sym 'variable)) + (end (position #\Newline doc)) + (code-string (format nil "~3D " code)) + (start1 (search code-string doc :end2 end :from-end t))) + (return (if start1 + (subseq doc (+ start1 (length code-string)) end) + (subseq doc 0 end))))))) diff --git a/src/main/url.cl b/src/main/url.cl new file mode 100644 index 0000000..8e2a11e --- /dev/null +++ b/src/main/url.cl @@ -0,0 +1,185 @@ +;;;; CLASH --- The Common Lisp Adaptable Simple HTTP server +;;;; This is copyrighted software. See documentation for terms. +;;;; +;;;; url.cl --- URL parsing/unparsing +;;;; +;;;; Checkout Tag: $Name$ +;;;; $Id$ + +(in-package :CLASH) + +;;;; %File Description: +;;;; +;;;; This file provides all the facilities for the parsing of URLs +;;;; into their component parts, as well as the mapping mechanism from +;;;; URLs to resources. +;;;; + +;;; Conditions +(define-condition url-error (clash-error) + ((url-string :initarg :url-string :reader url-error-url-string)) + (:default-initargs :code +HTTP-Code-Not-Found+) + (:report (lambda (condition stream) + (report-clash-error condition stream + "Unspecified URL parsing error on URL ~A" + (list (url-error-url-string condition)))))) + +(define-condition url-unknown-scheme-error (url-error) + ((scheme :initarg :scheme :reader url-unknown-scheme-error-scheme)) + (:report (lambda (condition stream) + (report-clash-error condition stream + "Unknown URL scheme ~A while parsing URL ~A" + (list + (url-unknown-scheme-error-scheme condition) + (url-error-url-string condition)))))) + +;;; Constants + +(defconstant +url-scheme-separator+ #\: + "Separator Character for URL schemes.") + +(defconstant +url-fragment-separator+ #\# + "Separator Character for URL fragments, which are not part of URLs.") + +(defconstant +url-host-port-separator+ #\: + "Host:Port Separator Character for some URL schemes.") + +(defconstant +url-path-separator+ #\/ + "Path Separator Character for some URL schemes.") + +(defconstant +url-query-separator+ #\? + "Query Separator Character for some URL schemes.") + +(defconstant +url-port-host-indicator+ "//" + "String that when present at the beginning of some scheme-specific parts +indicates the presence of host and/or port information.") + +;;; URLs + +(defclass url () + ((source-string :initarg :source-string :reader url-source-string + :initform nil))) + +(defgeneric url-string (url) + (:documentation + "Renders the given URL object into a new string and returns that.")) + +(defgeneric url-scheme (url) + (:documentation "Return a string with the URLs canonical scheme.")) + +(defgeneric url-specific-part (url) + (:documentation "Return a string with the URLs scheme-specific part.")) + +(defgeneric url-significant-string (url) + (:documentation + "Return a string which contains those parts of the URL, that are necessary +to identify a resource for the HTTP server to look-up the provider of that +resource. For example with HTTP-URLs this excludes the search-key part.")) + +(defmethod url-string ((url url)) + (format nil "~A:~A" + (url-scheme url) + (url-specific-part url))) + +(defmethod print-object ((obj url) stream) + (print-unreadable-object (obj stream :type t :identity nil) + (format stream "~S" (url-specific-part obj)))) + +;;; The following are some commonly used mix-ins for URLs. + +(defclass url-host-port-mixin () + ((host :initarg :host :initform nil :reader url-host) + (port :initarg :port :initform nil :reader url-port))) + +(defclass url-path-mixin () + ((path :initarg :path :initform nil :reader url-path))) + +(defclass url-parameter-mixin () + ((parameters :initarg :parameters :initform nil :reader url-parameters))) + +(defclass url-search-mixin () + ((searchpart :initarg :searchpart :initform nil :reader url-searchpart))) + +;;; Schemes and Parsers + +(defvar *default-url-scheme* "http" + "This specifies the default scheme to use for parsing of URLs that +don't provide a scheme by themselves.") + +(defvar *scheme-parser-map* (make-hash-table :test #'equal) + "Mapping from schemes to parsers.") + +(defun register-scheme (scheme parser-function) + "Registers a new URL scheme." + (let ((scheme (string-downcase scheme))) + (setf (gethash scheme *scheme-parser-map*) parser-function))) + +(defun unregister-scheme (scheme) + "Unregisters an URL scheme." + (let ((scheme (string-downcase scheme))) + (remhash scheme *scheme-parser-map*))) + +(defun reset-schemes () + "Clear the list of recognized schemes." + (clrhash *scheme-parser-map*)) + +;;; Parsing of URLs + +(defun parse-url-from-string (string &optional clear-fragment) + (let* ((string (if (and clear-fragment + (position +url-fragment-separator+ + string)) + (subseq string 0 + (position +url-fragment-separator+ + string)) + string)) + (scheme-end (position +url-scheme-separator+ string)) + (scheme (if scheme-end + (string-downcase + (subseq string 0 scheme-end)) + *default-url-scheme*)) + (parser (gethash scheme *scheme-parser-map* nil))) + (unless parser + (error 'url-unknown-scheme-error :scheme scheme :url-string string)) + (funcall parser scheme string (if scheme-end (1+ scheme-end) 0)))) + +;;; The specific URL schemes: + +(defclass url-http (url url-host-port-mixin url-path-mixin url-search-mixin) + ((scheme :allocation :class :initform "http" :reader url-scheme))) + +(defmethod url-specific-part ((url url-http)) + (format nil "~A~@[?~A~]" + (url-significant-string url) + (url-searchpart url))) + +(defmethod url-significant-string ((url url-http)) + (format nil "~:[~;//~A~@[:~A~]/~]~@[~A~]" + (or (url-host url) (url-port url)) + (url-host url) + (url-port url) + (url-path url))) + +(defun url-http-parser (scheme url start) + (declare (ignore scheme)) + (let ((rest (subseq url start)) + host port path searchpart) + (when (equal "//" (subseq rest 0 2)) + (let* ((rest-pos (position +url-path-separator+ rest :start 2)) + (port-pos (position +url-host-port-separator+ rest :start 2 + :end rest-pos))) + (setq host (subseq rest 2 (or port-pos rest-pos))) + (when port-pos + (setq port (subseq rest (1+ port-pos) rest-pos))) + (setq rest (subseq rest (1+ rest-pos))))) + (let ((searchpart-pos (position +url-query-separator+ rest))) + (if searchpart-pos + (setq searchpart (subseq rest (1+ searchpart-pos)) + path (subseq rest 0 searchpart-pos)) + (setq path rest))) + (make-instance 'url-http + :source-string url + :host host :port port :path path + :searchpart searchpart))) + +(register-scheme "http" #'url-http-parser) diff --git a/src/package.cl b/src/package.cl index 56b3a91..6effc74 100644 --- a/src/package.cl +++ b/src/package.cl @@ -14,5 +14,5 @@ ;;;; (defpackage "CLASH" - (:USE :CL :CLASH-SYS) - (:EXPORT )) + (:USE :CL) + (:EXPORT "+HTTP-CODE-NO-CONTENT+"))