12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045 |
- @c -*-texinfo-*-
- @c This is part of the GNU Guile Reference Manual.
- @c Copyright (C) 2010, 2011, 2012, 2013, 2015, 2018, 2019, 2020, 2023 Free Software Foundation, Inc.
- @c See the file guile.texi for copying conditions.
- @node Web
- @section @acronym{HTTP}, the Web, and All That
- @cindex Web
- @cindex WWW
- @cindex HTTP
- It has always been possible to connect computers together and share
- information between them, but the rise of the World Wide Web over the
- last couple of decades has made it much easier to do so. The result is
- a richly connected network of computation, in which Guile forms a part.
- By ``the web'', we mean the HTTP protocol@footnote{Yes, the P is for
- protocol, but this phrase appears repeatedly in RFC 2616.} as handled by
- servers, clients, proxies, caches, and the various kinds of messages and
- message components that can be sent and received by that protocol,
- notably HTML.
- On one level, the web is text in motion: the protocols themselves are
- textual (though the payload may be binary), and it's possible to create
- a socket and speak text to the web. But such an approach is obviously
- primitive. This section details the higher-level data types and
- operations provided by Guile: URIs, HTTP request and response records,
- and a conventional web server implementation.
- The material in this section is arranged in ascending order, in which
- later concepts build on previous ones. If you prefer to start with the
- highest-level perspective, @pxref{Web Examples}, and work your way
- back.
- @menu
- * Types and the Web:: Types prevent bugs and security problems.
- * URIs:: Universal Resource Identifiers.
- * HTTP:: The Hyper-Text Transfer Protocol.
- * HTTP Headers:: How Guile represents specific header values.
- * Transfer Codings:: HTTP Transfer Codings.
- * Requests:: HTTP requests.
- * Responses:: HTTP responses.
- * Web Client:: Accessing web resources over HTTP.
- * Web Server:: Serving HTTP to the internet.
- * Web Examples:: How to use this thing.
- @end menu
- @node Types and the Web
- @subsection Types and the Web
- It is a truth universally acknowledged, that a program with good use of
- data types, will be free from many common bugs. Unfortunately, the
- common practice in web programming seems to ignore this maxim. This
- subsection makes the case for expressive data types in web programming.
- By ``expressive data types'', we mean that the data types @emph{say}
- something about how a program solves a problem. For example, if we
- choose to represent dates using SRFI 19 date records (@pxref{SRFI-19}),
- this indicates that there is a part of the program that will always have
- valid dates. Error handling for a number of basic cases, like invalid
- dates, occurs on the boundary in which we produce a SRFI 19 date record
- from other types, like strings.
- With regards to the web, data types are helpful in the two broad phases
- of HTTP messages: parsing and generation.
- Consider a server, which has to parse a request, and produce a response.
- Guile will parse the request into an HTTP request object
- (@pxref{Requests}), with each header parsed into an appropriate Scheme
- data type. This transition from an incoming stream of characters to
- typed data is a state change in a program---the strings might parse, or
- they might not, and something has to happen if they do not. (Guile
- throws an error in this case.) But after you have the parsed request,
- ``client'' code (code built on top of the Guile web framework) will not
- have to check for syntactic validity. The types already make this
- information manifest.
- This state change on the parsing boundary makes programs more robust,
- as they themselves are freed from the need to do a number of common
- error checks, and they can use normal Scheme procedures to handle a
- request instead of ad-hoc string parsers.
- The need for types on the response generation side (in a server) is more
- subtle, though not less important. Consider the example of a POST
- handler, which prints out the text that a user submits from a form.
- Such a handler might include a procedure like this:
- @example
- ;; First, a helper procedure
- (define (para . contents)
- (string-append "<p>" (string-concatenate contents) "</p>"))
- ;; Now the meat of our simple web application
- (define (you-said text)
- (para "You said: " text))
- (display (you-said "Hi!"))
- @print{} <p>You said: Hi!</p>
- @end example
- This is a perfectly valid implementation, provided that the incoming
- text does not contain the special HTML characters @samp{<}, @samp{>}, or
- @samp{&}. But this provision of a restricted character set is not
- reflected anywhere in the program itself: we must @emph{assume} that the
- programmer understands this, and performs the check elsewhere.
- Unfortunately, the short history of the practice of programming does not
- bear out this assumption. A @dfn{cross-site scripting} (@acronym{XSS})
- vulnerability is just such a common error in which unfiltered user input
- is allowed into the output. A user could submit a crafted comment to
- your web site which results in visitors running malicious Javascript,
- within the security context of your domain:
- @example
- (display (you-said "<script src=\"http://bad.com/nasty.js\" />"))
- @print{} <p>You said: <script src="http://bad.com/nasty.js" /></p>
- @end example
- The fundamental problem here is that both user data and the program
- template are represented using strings. This identity means that types
- can't help the programmer to make a distinction between these two, so
- they get confused.
- There are a number of possible solutions, but perhaps the best is to
- treat HTML not as strings, but as native s-expressions: as SXML. The
- basic idea is that HTML is either text, represented by a string, or an
- element, represented as a tagged list. So @samp{foo} becomes
- @samp{"foo"}, and @samp{<b>foo</b>} becomes @samp{(b "foo")}.
- Attributes, if present, go in a tagged list headed by @samp{@@}, like
- @samp{(img (@@ (src "http://example.com/foo.png")))}. @xref{SXML}, for
- more information.
- The good thing about SXML is that HTML elements cannot be confused with
- text. Let's make a new definition of @code{para}:
- @example
- (define (para . contents)
- `(p ,@@contents))
- (use-modules (sxml simple))
- (sxml->xml (you-said "Hi!"))
- @print{} <p>You said: Hi!</p>
- (sxml->xml (you-said "<i>Rats, foiled again!</i>"))
- @print{} <p>You said: <i>Rats, foiled again!</i></p>
- @end example
- So we see in the second example that HTML elements cannot be unwittingly
- introduced into the output. However it is now perfectly acceptable to
- pass SXML to @code{you-said}; in fact, that is the big advantage of SXML
- over everything-as-a-string.
- @example
- (sxml->xml (you-said (you-said "<Hi!>")))
- @print{} <p>You said: <p>You said: <Hi!></p></p>
- @end example
- The SXML types allow procedures to @emph{compose}. The types make
- manifest which parts are HTML elements, and which are text. So you
- needn't worry about escaping user input; the type transition back to a
- string handles that for you. @acronym{XSS} vulnerabilities are a thing
- of the past.
- Well. That's all very nice and opinionated and such, but how do I use
- the thing? Read on!
- @node URIs
- @subsection Universal Resource Identifiers
- Guile provides a standard data type for Universal Resource Identifiers
- (URIs), as defined in RFC 3986.
- The generic URI syntax is as follows:
- @example
- URI-reference := [scheme ":"] ["//" [userinfo "@@"] host [":" port]] path \
- [ "?" query ] [ "#" fragment ]
- @end example
- For example, in the URI, @indicateurl{http://www.gnu.org/help/}, the
- scheme is @code{http}, the host is @code{www.gnu.org}, the path is
- @code{/help/}, and there is no userinfo, port, query, or fragment.
- Userinfo is something of an abstraction, as some legacy URI schemes
- allowed userinfo of the form @code{@var{username}:@var{passwd}}. But
- since passwords do not belong in URIs, the RFC does not want to condone
- this practice, so it calls anything before the @code{@@} sign
- @dfn{userinfo}.
- @example
- (use-modules (web uri))
- @end example
- The following procedures can be found in the @code{(web uri)}
- module. Load it into your Guile, using a form like the above, to have
- access to them.
- The most common way to build a URI from Scheme is with the
- @code{build-uri} function.
- @deffn {Scheme Procedure} build-uri scheme @
- [#:userinfo=@code{#f}] [#:host=@code{#f}] [#:port=@code{#f}] @
- [#:path=@code{""}] [#:query=@code{#f}] [#:fragment=@code{#f}] @
- [#:validate?=@code{#t}]
- Construct a URI. @var{scheme} should be a symbol, @var{port} either a
- positive, exact integer or @code{#f}, and the rest of the fields are
- either strings or @code{#f}. If @var{validate?} is true, also run some
- consistency checks to make sure that the constructed URI is valid.
- @end deffn
- @deffn {Scheme Procedure} uri? obj
- Return @code{#t} if @var{obj} is a URI.
- @end deffn
- Guile, URIs are represented as URI records, with a number of associated
- accessors.
- @deffn {Scheme Procedure} uri-scheme uri
- @deffnx {Scheme Procedure} uri-userinfo uri
- @deffnx {Scheme Procedure} uri-host uri
- @deffnx {Scheme Procedure} uri-port uri
- @deffnx {Scheme Procedure} uri-path uri
- @deffnx {Scheme Procedure} uri-query uri
- @deffnx {Scheme Procedure} uri-fragment uri
- Field accessors for the URI record type. The URI scheme will be a
- symbol, or @code{#f} if the object is a relative-ref (see below). The
- port will be either a positive, exact integer or @code{#f}, and the rest
- of the fields will be either strings or @code{#f} if not present.
- @end deffn
- @deffn {Scheme Procedure} string->uri string
- Parse @var{string} into a URI object. Return @code{#f} if the string
- could not be parsed.
- @end deffn
- @deffn {Scheme Procedure} uri->string uri [#:include-fragment?=@code{#t}]
- Serialize @var{uri} to a string. If the URI has a port that is the
- default port for its scheme, the port is not included in the
- serialization. If @var{include-fragment?} is given as false, the
- resulting string will omit the fragment (if any).
- @end deffn
- @deffn {Scheme Procedure} declare-default-port! scheme port
- Declare a default port for the given URI scheme.
- @end deffn
- @deffn {Scheme Procedure} uri-decode str [#:encoding=@code{"utf-8"}] [#:decode-plus-to-space? #t]
- Percent-decode the given @var{str}, according to @var{encoding}, which
- should be the name of a character encoding.
- Note that this function should not generally be applied to a full URI
- string. For paths, use @code{split-and-decode-uri-path} instead. For
- query strings, split the query on @code{&} and @code{=} boundaries, and
- decode the components separately.
- Note also that percent-encoded strings encode @emph{bytes}, not
- characters. There is no guarantee that a given byte sequence is a valid
- string encoding. Therefore this routine may signal an error if the
- decoded bytes are not valid for the given encoding. Pass @code{#f} for
- @var{encoding} if you want decoded bytes as a bytevector directly.
- @xref{Ports, @code{set-port-encoding!}}, for more information on
- character encodings.
- If @var{decode-plus-to-space?} is true, which is the default, also
- replace instances of the plus character @samp{+} with a space character.
- This is needed when parsing @code{application/x-www-form-urlencoded}
- data.
- Returns a string of the decoded characters, or a bytevector if
- @var{encoding} was @code{#f}.
- @end deffn
- @deffn {Scheme Procedure} uri-encode str [#:encoding=@code{"utf-8"}] [#:unescaped-chars]
- Percent-encode any character not in the character set,
- @var{unescaped-chars}.
- The default character set includes alphanumerics from ASCII, as well as
- the special characters @samp{-}, @samp{.}, @samp{_}, and @samp{~}. Any
- other character will be percent-encoded, by writing out the character to
- a bytevector within the given @var{encoding}, then encoding each byte as
- @code{%@var{HH}}, where @var{HH} is the hexadecimal representation of
- the byte.
- @end deffn
- @deffn {Scheme Procedure} split-and-decode-uri-path path
- Split @var{path} into its components, and decode each component,
- removing empty components.
- For example, @code{"/foo/bar%20baz/"} decodes to the two-element list,
- @code{("foo" "bar baz")}.
- @end deffn
- @deffn {Scheme Procedure} encode-and-join-uri-path parts
- URI-encode each element of @var{parts}, which should be a list of
- strings, and join the parts together with @code{/} as a delimiter.
- For example, the list @code{("scrambled eggs" "biscuits&gravy")} encodes
- as @code{"scrambled%20eggs/biscuits%26gravy"}.
- @end deffn
- @subsubheading Subtypes of URI
- As we noted above, not all URI objects have a scheme. You might have
- noted in the ``generic URI syntax'' example that the left-hand side of
- that grammar definition was URI-reference, not URI. A
- @dfn{URI-reference} is a generalization of a URI where the scheme is
- optional. If no scheme is specified, it is taken to be relative to some
- other related URI. A common use of URI references is when you want to
- be vague regarding the choice of HTTP or HTTPS -- serving a web page
- referring to @code{/foo.css} will use HTTPS if loaded over HTTPS, or
- HTTP otherwise.
- @deffn {Scheme Procedure} build-uri-reference [#:scheme=@code{#f}]@
- [#:userinfo=@code{#f}] [#:host=@code{#f}] [#:port=@code{#f}] @
- [#:path=@code{""}] [#:query=@code{#f}] [#:fragment=@code{#f}] @
- [#:validate?=@code{#t}]
- Like @code{build-uri}, but with an optional scheme.
- @end deffn
- @deffn {Scheme Procedure} uri-reference? obj
- Return @code{#t} if @var{obj} is a URI-reference. This is the most
- general URI predicate, as it includes not only full URIs that have
- schemes (those that match @code{uri?}) but also URIs without schemes.
- @end deffn
- It's also possible to build a @dfn{relative-ref}: a URI-reference that
- explicitly lacks a scheme.
- @deffn {Scheme Procedure} build-relative-ref @
- [#:userinfo=@code{#f}] [#:host=@code{#f}] [#:port=@code{#f}] @
- [#:path=@code{""}] [#:query=@code{#f}] [#:fragment=@code{#f}] @
- [#:validate?=@code{#t}]
- Like @code{build-uri}, but with no scheme.
- @end deffn
- @deffn {Scheme Procedure} relative-ref? obj
- Return @code{#t} if @var{obj} is a ``relative-ref'': a URI-reference
- that has no scheme. Every URI-reference will either match @code{uri?}
- or @code{relative-ref?} (but not both).
- @end deffn
- In case it's not clear from the above, the most general of these URI
- types is the URI-reference, with @code{build-uri-reference} as the most
- general constructor. @code{build-uri} and @code{build-relative-ref}
- enforce enforce specific restrictions on the URI-reference. The most
- generic URI parser is then @code{string->uri-reference}, and there is
- also a parser for when you know that you want a relative-ref.
- Note that @code{uri?} will only return @code{#t} for URI objects that
- have schemes; that is, it rejects relative-refs.
- @deffn {Scheme Procedure} string->uri-reference string
- Parse @var{string} into a URI object, while not requiring a scheme.
- Return @code{#f} if the string could not be parsed.
- @end deffn
- @deffn {Scheme Procedure} string->relative-ref string
- Parse @var{string} into a URI object, while asserting that no scheme is
- present. Return @code{#f} if the string could not be parsed.
- @end deffn
- @node HTTP
- @subsection The Hyper-Text Transfer Protocol
- The initial motivation for including web functionality in Guile, rather
- than rely on an external package, was to establish a standard base on
- which people can share code. To that end, we continue the focus on data
- types by providing a number of low-level parsers and unparsers for
- elements of the HTTP protocol.
- If you are want to skip the low-level details for now and move on to web
- pages, @pxref{Web Client}, and @pxref{Web Server}. Otherwise, load the
- HTTP module, and read on.
- @example
- (use-modules (web http))
- @end example
- The focus of the @code{(web http)} module is to parse and unparse
- standard HTTP headers, representing them to Guile as native data
- structures. For example, a @code{Date:} header will be represented as a
- SRFI-19 date record (@pxref{SRFI-19}), rather than as a string.
- Guile tries to follow RFCs fairly strictly---the road to perdition being
- paved with compatibility hacks---though some allowances are made for
- not-too-divergent texts.
- Header names are represented as lower-case symbols.
- @deffn {Scheme Procedure} string->header name
- Parse @var{name} to a symbolic header name.
- @end deffn
- @deffn {Scheme Procedure} header->string sym
- Return the string form for the header named @var{sym}.
- @end deffn
- For example:
- @example
- (string->header "Content-Length")
- @result{} content-length
- (header->string 'content-length)
- @result{} "Content-Length"
- (string->header "FOO")
- @result{} foo
- (header->string 'foo)
- @result{} "Foo"
- @end example
- Guile keeps a registry of known headers, their string names, and some
- parsing and serialization procedures. If a header is unknown, its
- string name is simply its symbol name in title-case.
- @deffn {Scheme Procedure} known-header? sym
- Return @code{#t} if @var{sym} is a known header, with associated
- parsers and serialization procedures, or @code{#f} otherwise.
- @end deffn
- @deffn {Scheme Procedure} header-parser sym
- Return the value parser for headers named @var{sym}. The result is a
- procedure that takes one argument, a string, and returns the parsed
- value. If the header isn't known to Guile, a default parser is returned
- that passes through the string unchanged.
- @end deffn
- @deffn {Scheme Procedure} header-validator sym
- Return a predicate which returns @code{#t} if the given value is valid
- for headers named @var{sym}. The default validator for unknown headers
- is @code{string?}.
- @end deffn
- @deffn {Scheme Procedure} header-writer sym
- Return a procedure that writes values for headers named @var{sym} to a
- port. The resulting procedure takes two arguments: a value and a port.
- The default writer is @code{display}.
- @end deffn
- For more on the set of headers that Guile knows about out of the box,
- @pxref{HTTP Headers}. To add your own, use the @code{declare-header!}
- procedure:
- @deffn {Scheme Procedure} declare-header! name parser validator writer @
- [#:multiple?=@code{#f}]
- Declare a parser, validator, and writer for a given header.
- @end deffn
- For example, let's say you are running a web server behind some sort of
- proxy, and your proxy adds an @code{X-Client-Address} header, indicating
- the IPv4 address of the original client. You would like for the HTTP
- request record to parse out this header to a Scheme value, instead of
- leaving it as a string. You could register this header with Guile's
- HTTP stack like this:
- @example
- (declare-header! "X-Client-Address"
- (lambda (str)
- (inet-pton AF_INET str))
- (lambda (ip)
- (and (integer? ip) (exact? ip) (<= 0 ip #xffffffff)))
- (lambda (ip port)
- (display (inet-ntop AF_INET ip) port)))
- @end example
- @deffn {Scheme Procedure} declare-opaque-header! name
- A specialised version of @code{declare-header!} for the case in which
- you want a header's value to be returned/written ``as-is''.
- @end deffn
- @deffn {Scheme Procedure} valid-header? sym val
- Return a true value if @var{val} is a valid Scheme value for the header
- with name @var{sym}, or @code{#f} otherwise.
- @end deffn
- Now that we have a generic interface for reading and writing headers, we
- do just that.
- @deffn {Scheme Procedure} read-header port
- Read one HTTP header from @var{port}. Return two values: the header
- name and the parsed Scheme value. May raise an exception if the header
- was known but the value was invalid.
- Returns the end-of-file object for both values if the end of the message
- body was reached (i.e., a blank line).
- @end deffn
- @deffn {Scheme Procedure} parse-header name val
- Parse @var{val}, a string, with the parser for the header named
- @var{name}. Returns the parsed value.
- @end deffn
- @deffn {Scheme Procedure} write-header name val port
- Write the given header name and value to @var{port}, using the writer
- from @code{header-writer}.
- @end deffn
- @deffn {Scheme Procedure} read-headers port
- Read the headers of an HTTP message from @var{port}, returning them
- as an ordered alist.
- @end deffn
- @deffn {Scheme Procedure} write-headers headers port
- Write the given header alist to @var{port}. Doesn't write the final
- @samp{\r\n}, as the user might want to add another header.
- @end deffn
- The @code{(web http)} module also has some utility procedures to read
- and write request and response lines.
- @deffn {Scheme Procedure} parse-http-method str [start] [end]
- Parse an HTTP method from @var{str}. The result is an upper-case symbol,
- like @code{GET}.
- @end deffn
- @deffn {Scheme Procedure} parse-http-version str [start] [end]
- Parse an HTTP version from @var{str}, returning it as a major--minor
- pair. For example, @code{HTTP/1.1} parses as the pair of integers,
- @code{(1 . 1)}.
- @end deffn
- @deffn {Scheme Procedure} parse-request-uri str [start] [end]
- Parse a URI from an HTTP request line. Note that URIs in requests do not
- have to have a scheme or host name. The result is a URI object.
- @end deffn
- @deffn {Scheme Procedure} read-request-line port
- Read the first line of an HTTP request from @var{port}, returning three
- values: the method, the URI, and the version.
- @end deffn
- @deffn {Scheme Procedure} write-request-line method uri version port
- Write the first line of an HTTP request to @var{port}.
- @end deffn
- @deffn {Scheme Procedure} read-response-line port
- Read the first line of an HTTP response from @var{port}, returning three
- values: the HTTP version, the response code, and the ``reason phrase''.
- @end deffn
- @deffn {Scheme Procedure} write-response-line version code reason-phrase port
- Write the first line of an HTTP response to @var{port}.
- @end deffn
- @node HTTP Headers
- @subsection HTTP Headers
- In addition to defining the infrastructure to parse headers, the
- @code{(web http)} module defines specific parsers and unparsers for all
- headers defined in the HTTP/1.1 standard.
- For example, if you receive a header named @samp{Accept-Language} with a
- value @samp{en, es;q=0.8}, Guile parses it as a quality list (defined
- below):
- @example
- (parse-header 'accept-language "en, es;q=0.8")
- @result{} ((1000 . "en") (800 . "es"))
- @end example
- The format of the value for @samp{Accept-Language} headers is defined
- below, along with all other headers defined in the HTTP standard. (If
- the header were unknown, the value would have been returned as a
- string.)
- For brevity, the header definitions below are given in the form,
- @var{Type} @code{@var{name}}, indicating that values for the header
- @code{@var{name}} will be of the given @var{Type}. Since Guile
- internally treats header names in lower case, in this document we give
- types title-cased names. A short description of the each header's
- purpose and an example follow.
- For full details on the meanings of all of these headers, see the HTTP
- 1.1 standard, RFC 2616.
- @subsubsection HTTP Header Types
- Here we define the types that are used below, when defining headers.
- @deftp {HTTP Header Type} Date
- A SRFI-19 date.
- @end deftp
- @deftp {HTTP Header Type} KVList
- A list whose elements are keys or key-value pairs. Keys are parsed to
- symbols. Values are strings by default. Non-string values are the
- exception, and are mentioned explicitly below, as appropriate.
- @end deftp
- @deftp {HTTP Header Type} SList
- A list of strings.
- @end deftp
- @deftp {HTTP Header Type} Quality
- An exact integer between 0 and 1000. Qualities are used to express
- preference, given multiple options. An option with a quality of 870,
- for example, is preferred over an option with quality 500.
- (Qualities are written out over the wire as numbers between 0.0 and
- 1.0, but since the standard only allows three digits after the decimal,
- it's equivalent to integers between 0 and 1000, so that's what Guile
- uses.)
- @end deftp
- @deftp {HTTP Header Type} QList
- A quality list: a list of pairs, the car of which is a quality, and the
- cdr a string. Used to express a list of options, along with their
- qualities.
- @end deftp
- @deftp {HTTP Header Type} ETag
- An entity tag, represented as a pair. The car of the pair is an opaque
- string, and the cdr is @code{#t} if the entity tag is a ``strong'' entity
- tag, and @code{#f} otherwise.
- @end deftp
- @subsubsection General Headers
- General HTTP headers may be present in any HTTP message.
- @deftypevr {HTTP Header} KVList cache-control
- A key-value list of cache-control directives. See RFC 2616, for more
- details.
- If present, parameters to @code{max-age}, @code{max-stale},
- @code{min-fresh}, and @code{s-maxage} are all parsed as non-negative
- integers.
- If present, parameters to @code{private} and @code{no-cache} are parsed
- as lists of header names, as symbols.
- @example
- (parse-header 'cache-control "no-cache,no-store"
- @result{} (no-cache no-store)
- (parse-header 'cache-control "no-cache=\"Authorization,Date\",no-store"
- @result{} ((no-cache . (authorization date)) no-store)
- (parse-header 'cache-control "no-cache=\"Authorization,Date\",max-age=10"
- @result{} ((no-cache . (authorization date)) (max-age . 10))
- @end example
- @end deftypevr
- @deftypevr {HTTP Header} List connection
- A list of header names that apply only to this HTTP connection, as
- symbols. Additionally, the symbol @samp{close} may be present, to
- indicate that the server should close the connection after responding to
- the request.
- @example
- (parse-header 'connection "close")
- @result{} (close)
- @end example
- @end deftypevr
- @deftypevr {HTTP Header} Date date
- The date that a given HTTP message was originated.
- @example
- (parse-header 'date "Tue, 15 Nov 1994 08:12:31 GMT")
- @result{} #<date ...>
- @end example
- @end deftypevr
- @deftypevr {HTTP Header} KVList pragma
- A key-value list of implementation-specific directives.
- @example
- (parse-header 'pragma "no-cache, broccoli=tasty")
- @result{} (no-cache (broccoli . "tasty"))
- @end example
- @end deftypevr
- @deftypevr {HTTP Header} List trailer
- A list of header names which will appear after the message body, instead
- of with the message headers.
- @example
- (parse-header 'trailer "ETag")
- @result{} (etag)
- @end example
- @end deftypevr
- @deftypevr {HTTP Header} List transfer-encoding
- A list of transfer codings, expressed as key-value lists. The only
- transfer coding defined by the specification is @code{chunked}.
- @example
- (parse-header 'transfer-encoding "chunked")
- @result{} ((chunked))
- @end example
- @end deftypevr
- @deftypevr {HTTP Header} List upgrade
- A list of strings, indicating additional protocols that a server could use
- in response to a request.
- @example
- (parse-header 'upgrade "WebSocket")
- @result{} ("WebSocket")
- @end example
- @end deftypevr
- FIXME: parse out more fully?
- @deftypevr {HTTP Header} List via
- A list of strings, indicating the protocol versions and hosts of
- intermediate servers and proxies. There may be multiple @code{via}
- headers in one message.
- @example
- (parse-header 'via "1.0 venus, 1.1 mars")
- @result{} ("1.0 venus" "1.1 mars")
- @end example
- @end deftypevr
- @deftypevr {HTTP Header} List warning
- A list of warnings given by a server or intermediate proxy. Each
- warning is a itself a list of four elements: a code, as an exact integer
- between 0 and 1000, a host as a string, the warning text as a string,
- and either @code{#f} or a SRFI-19 date.
- There may be multiple @code{warning} headers in one message.
- @example
- (parse-header 'warning "123 foo \"core breach imminent\"")
- @result{} ((123 "foo" "core-breach imminent" #f))
- @end example
- @end deftypevr
- @subsubsection Entity Headers
- Entity headers may be present in any HTTP message, and refer to the
- resource referenced in the HTTP request or response.
- @deftypevr {HTTP Header} List allow
- A list of allowed methods on a given resource, as symbols.
- @example
- (parse-header 'allow "GET, HEAD")
- @result{} (GET HEAD)
- @end example
- @end deftypevr
- @deftypevr {HTTP Header} List content-encoding
- A list of content codings, as symbols.
- @example
- (parse-header 'content-encoding "gzip")
- @result{} (gzip)
- @end example
- @end deftypevr
- @deftypevr {HTTP Header} List content-language
- The languages that a resource is in, as strings.
- @example
- (parse-header 'content-language "en")
- @result{} ("en")
- @end example
- @end deftypevr
- @deftypevr {HTTP Header} UInt content-length
- The number of bytes in a resource, as an exact, non-negative integer.
- @example
- (parse-header 'content-length "300")
- @result{} 300
- @end example
- @end deftypevr
- @deftypevr {HTTP Header} URI content-location
- The canonical URI for a resource, in the case that it is also accessible
- from a different URI.
- @example
- (parse-header 'content-location "http://example.com/foo")
- @result{} #<<uri> ...>
- @end example
- @end deftypevr
- @deftypevr {HTTP Header} String content-md5
- The MD5 digest of a resource.
- @example
- (parse-header 'content-md5 "ffaea1a79810785575e29e2bd45e2fa5")
- @result{} "ffaea1a79810785575e29e2bd45e2fa5"
- @end example
- @end deftypevr
- @deftypevr {HTTP Header} List content-range
- Range specification as a list of three elements: the symbol
- @code{bytes}, either the symbol @code{*} or a pair of integers
- indicating the byte range, and either @code{*} or an integer indicating
- the instance length. Used to indicate that a response only includes
- part of a resource.
- @example
- (parse-header 'content-range "bytes 10-20/*")
- @result{} (bytes (10 . 20) *)
- @end example
- @end deftypevr
- @deftypevr {HTTP Header} List content-type
- The MIME type of a resource, as a symbol, along with any parameters.
- @example
- (parse-header 'content-type "text/plain")
- @result{} (text/plain)
- (parse-header 'content-type "text/plain;charset=utf-8")
- @result{} (text/plain (charset . "utf-8"))
- @end example
- Note that the @code{charset} parameter is something of a misnomer, and
- the HTTP specification admits this. It specifies the @emph{encoding} of
- the characters, not the character set.
- @end deftypevr
- @deftypevr {HTTP Header} Date expires
- The date/time after which the resource given in a response is considered
- stale.
- @example
- (parse-header 'expires "Tue, 15 Nov 1994 08:12:31 GMT")
- @result{} #<date ...>
- @end example
- @end deftypevr
- @deftypevr {HTTP Header} Date last-modified
- The date/time on which the resource given in a response was last
- modified.
- @example
- (parse-header 'expires "Tue, 15 Nov 1994 08:12:31 GMT")
- @result{} #<date ...>
- @end example
- @end deftypevr
- @subsubsection Request Headers
- Request headers may only appear in an HTTP request, not in a response.
- @deftypevr {HTTP Header} List accept
- A list of preferred media types for a response. Each element of the
- list is itself a list, in the same format as @code{content-type}.
- @example
- (parse-header 'accept "text/html,text/plain;charset=utf-8")
- @result{} ((text/html) (text/plain (charset . "utf-8")))
- @end example
- Preference is expressed with quality values:
- @example
- (parse-header 'accept "text/html;q=0.8,text/plain;q=0.6")
- @result{} ((text/html (q . 800)) (text/plain (q . 600)))
- @end example
- @end deftypevr
- @deftypevr {HTTP Header} QList accept-charset
- A quality list of acceptable charsets. Note again that what HTTP calls
- a ``charset'' is what Guile calls a ``character encoding''.
- @example
- (parse-header 'accept-charset "iso-8859-5, unicode-1-1;q=0.8")
- @result{} ((1000 . "iso-8859-5") (800 . "unicode-1-1"))
- @end example
- @end deftypevr
- @deftypevr {HTTP Header} QList accept-encoding
- A quality list of acceptable content codings.
- @example
- (parse-header 'accept-encoding "gzip,identity=0.8")
- @result{} ((1000 . "gzip") (800 . "identity"))
- @end example
- @end deftypevr
- @deftypevr {HTTP Header} QList accept-language
- A quality list of acceptable languages.
- @example
- (parse-header 'accept-language "cn,en=0.75")
- @result{} ((1000 . "cn") (750 . "en"))
- @end example
- @end deftypevr
- @deftypevr {HTTP Header} Pair authorization
- Authorization credentials. The car of the pair indicates the
- authentication scheme, like @code{basic}. For basic authentication, the
- cdr of the pair will be the base64-encoded @samp{@var{user}:@var{pass}}
- string. For other authentication schemes, like @code{digest}, the cdr
- will be a key-value list of credentials.
- @example
- (parse-header 'authorization "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=="
- @result{} (basic . "QWxhZGRpbjpvcGVuIHNlc2FtZQ==")
- @end example
- @end deftypevr
- @deftypevr {HTTP Header} List expect
- A list of expectations that a client has of a server. The expectations
- are key-value lists.
- @example
- (parse-header 'expect "100-continue")
- @result{} ((100-continue))
- @end example
- @end deftypevr
- @deftypevr {HTTP Header} String from
- The email address of a user making an HTTP request.
- @example
- (parse-header 'from "bob@@example.com")
- @result{} "bob@@example.com"
- @end example
- @end deftypevr
- @deftypevr {HTTP Header} Pair host
- The host for the resource being requested, as a hostname-port pair. If
- no port is given, the port is @code{#f}.
- @example
- (parse-header 'host "gnu.org:80")
- @result{} ("gnu.org" . 80)
- (parse-header 'host "gnu.org")
- @result{} ("gnu.org" . #f)
- @end example
- @end deftypevr
- @deftypevr {HTTP Header} *|List if-match
- A set of etags, indicating that the request should proceed if and only
- if the etag of the resource is in that set. Either the symbol @code{*},
- indicating any etag, or a list of entity tags.
- @example
- (parse-header 'if-match "*")
- @result{} *
- (parse-header 'if-match "asdfadf")
- @result{} (("asdfadf" . #t))
- (parse-header 'if-match W/"asdfadf")
- @result{} (("asdfadf" . #f))
- @end example
- @end deftypevr
- @deftypevr {HTTP Header} Date if-modified-since
- Indicates that a response should proceed if and only if the resource has
- been modified since the given date.
- @example
- (parse-header 'if-modified-since "Tue, 15 Nov 1994 08:12:31 GMT")
- @result{} #<date ...>
- @end example
- @end deftypevr
- @deftypevr {HTTP Header} *|List if-none-match
- A set of etags, indicating that the request should proceed if and only
- if the etag of the resource is not in the set. Either the symbol
- @code{*}, indicating any etag, or a list of entity tags.
- @example
- (parse-header 'if-none-match "*")
- @result{} *
- @end example
- @end deftypevr
- @deftypevr {HTTP Header} ETag|Date if-range
- Indicates that the range request should proceed if and only if the
- resource matches a modification date or an etag. Either an entity tag,
- or a SRFI-19 date.
- @example
- (parse-header 'if-range "\"original-etag\"")
- @result{} ("original-etag" . #t)
- @end example
- @end deftypevr
- @deftypevr {HTTP Header} Date if-unmodified-since
- Indicates that a response should proceed if and only if the resource has
- not been modified since the given date.
- @example
- (parse-header 'if-not-modified-since "Tue, 15 Nov 1994 08:12:31 GMT")
- @result{} #<date ...>
- @end example
- @end deftypevr
- @deftypevr {HTTP Header} UInt max-forwards
- The maximum number of proxy or gateway hops that a request should be
- subject to.
- @example
- (parse-header 'max-forwards "10")
- @result{} 10
- @end example
- @end deftypevr
- @deftypevr {HTTP Header} Pair proxy-authorization
- Authorization credentials for a proxy connection. See the documentation
- for @code{authorization} above for more information on the format.
- @example
- (parse-header 'proxy-authorization "Digest foo=bar,baz=qux"
- @result{} (digest (foo . "bar") (baz . "qux"))
- @end example
- @end deftypevr
- @deftypevr {HTTP Header} Pair range
- A range request, indicating that the client wants only part of a
- resource. The car of the pair is the symbol @code{bytes}, and the cdr
- is a list of pairs. Each element of the cdr indicates a range; the car
- is the first byte position and the cdr is the last byte position, as
- integers, or @code{#f} if not given.
- @example
- (parse-header 'range "bytes=10-30,50-")
- @result{} (bytes (10 . 30) (50 . #f))
- @end example
- @end deftypevr
- @deftypevr {HTTP Header} URI referer
- The URI of the resource that referred the user to this resource. The
- name of the header is a misspelling, but we are stuck with it.
- @example
- (parse-header 'referer "http://www.gnu.org/")
- @result{} #<uri ...>
- @end example
- @end deftypevr
- @deftypevr {HTTP Header} List te
- A list of transfer codings, expressed as key-value lists. A common
- transfer coding is @code{trailers}.
- @example
- (parse-header 'te "trailers")
- @result{} ((trailers))
- @end example
- @end deftypevr
- @deftypevr {HTTP Header} String user-agent
- A string indicating the user agent making the request. The
- specification defines a structured format for this header, but it is
- widely disregarded, so Guile does not attempt to parse strictly.
- @example
- (parse-header 'user-agent "Mozilla/5.0")
- @result{} "Mozilla/5.0"
- @end example
- @end deftypevr
- @subsubsection Response Headers
- @deftypevr {HTTP Header} List accept-ranges
- A list of range units that the server supports, as symbols.
- @example
- (parse-header 'accept-ranges "bytes")
- @result{} (bytes)
- @end example
- @end deftypevr
- @deftypevr {HTTP Header} UInt age
- The age of a cached response, in seconds.
- @example
- (parse-header 'age "3600")
- @result{} 3600
- @end example
- @end deftypevr
- @deftypevr {HTTP Header} ETag etag
- The entity-tag of the resource.
- @example
- (parse-header 'etag "\"foo\"")
- @result{} ("foo" . #t)
- @end example
- @end deftypevr
- @deftypevr {HTTP Header} URI-reference location
- A URI reference on which a request may be completed. Used in
- combination with a redirecting status code to perform client-side
- redirection.
- @example
- (parse-header 'location "http://example.com/other")
- @result{} #<uri ...>
- @end example
- @end deftypevr
- @deftypevr {HTTP Header} List proxy-authenticate
- A list of challenges to a proxy, indicating the need for authentication.
- @example
- (parse-header 'proxy-authenticate "Basic realm=\"foo\"")
- @result{} ((basic (realm . "foo")))
- @end example
- @end deftypevr
- @deftypevr {HTTP Header} UInt|Date retry-after
- Used in combination with a server-busy status code, like 503, to
- indicate that a client should retry later. Either a number of seconds,
- or a date.
- @example
- (parse-header 'retry-after "60")
- @result{} 60
- @end example
- @end deftypevr
- @deftypevr {HTTP Header} String server
- A string identifying the server.
- @example
- (parse-header 'server "My first web server")
- @result{} "My first web server"
- @end example
- @end deftypevr
- @deftypevr {HTTP Header} *|List vary
- A set of request headers that were used in computing this response.
- Used to indicate that server-side content negotiation was performed, for
- example in response to the @code{accept-language} header. Can also be
- the symbol @code{*}, indicating that all headers were considered.
- @example
- (parse-header 'vary "Accept-Language, Accept")
- @result{} (accept-language accept)
- @end example
- @end deftypevr
- @deftypevr {HTTP Header} List www-authenticate
- A list of challenges to a user, indicating the need for authentication.
- @example
- (parse-header 'www-authenticate "Basic realm=\"foo\"")
- @result{} ((basic (realm . "foo")))
- @end example
- @end deftypevr
- @node Transfer Codings
- @subsection Transfer Codings
- HTTP 1.1 allows for various transfer codings to be applied to message
- bodies. These include various types of compression, and HTTP chunked
- encoding. Currently, only chunked encoding is supported by guile.
- Chunked coding is an optional coding that may be applied to message
- bodies, to allow messages whose length is not known beforehand to be
- returned. Such messages can be split into chunks, terminated by a final
- zero length chunk.
- In order to make dealing with encodings more simple, guile provides
- procedures to create ports that ``wrap'' existing ports, applying
- transformations transparently under the hood.
- These procedures are in the @code{(web http)} module.
- @example
- (use-modules (web http))
- @end example
- @deffn {Scheme Procedure} make-chunked-input-port port [#:keep-alive?=#f]
- Returns a new port, that transparently reads and decodes chunk-encoded
- data from @var{port}. If no more chunk-encoded data is available, it
- returns the end-of-file object. When the port is closed, @var{port} will
- also be closed, unless @var{keep-alive?} is true.
- If the chunked input ends prematurely, a
- @code{&chunked-input-ended-promaturely} exception will be raised.
- @end deffn
- @example
- (use-modules (ice-9 rdelim))
- (define s "5\r\nFirst\r\nA\r\n line\n Sec\r\n8\r\nond line\r\n0\r\n")
- (define p (make-chunked-input-port (open-input-string s)))
- (read-line s)
- @result{} "First line"
- (read-line s)
- @result{} "Second line"
- @end example
- @deffn {Scheme Procedure} make-chunked-output-port port [#:keep-alive?=#f]
- Returns a new port, which transparently encodes data as chunk-encoded
- before writing it to @var{port}. Whenever a write occurs on this port,
- it buffers it, until the port is flushed, at which point it writes a
- chunk containing all the data written so far. When the port is closed,
- the data remaining is written to @var{port}, as is the terminating zero
- chunk. It also causes @var{port} to be closed, unless @var{keep-alive?}
- is true.
- Note. Forcing a chunked output port when there is no data is buffered
- does not write a zero chunk, as this would cause the data to be
- interpreted incorrectly by the client.
- @end deffn
- @example
- (call-with-output-string
- (lambda (out)
- (define out* (make-chunked-output-port out #:keep-alive? #t))
- (display "first chunk" out*)
- (force-output out*)
- (force-output out*) ; note this does not write a zero chunk
- (display "second chunk" out*)
- (close-port out*)))
- @result{} "b\r\nfirst chunk\r\nc\r\nsecond chunk\r\n0\r\n"
- @end example
- @node Requests
- @subsection HTTP Requests
- @example
- (use-modules (web request))
- @end example
- The request module contains a data type for HTTP requests.
- @subsubsection An Important Note on Character Sets
- HTTP requests consist of two parts: the request proper, consisting of a
- request line and a set of headers, and (optionally) a body. The body
- might have a binary content-type, and even in the textual case its
- length is specified in bytes, not characters.
- Therefore, HTTP is a fundamentally binary protocol. However the request
- line and headers are specified to be in a subset of ASCII, so they can
- be treated as text, provided that the port's encoding is set to an
- ASCII-compatible one-byte-per-character encoding. ISO-8859-1 (latin-1)
- is just such an encoding, and happens to be very efficient for Guile.
- So what Guile does when reading requests from the wire, or writing them
- out, is to set the port's encoding to latin-1, and treating the request
- headers as text.
- The request body is another issue. For binary data, the data is
- probably in a bytevector, so we use the R6RS binary output procedures to
- write out the binary payload. Textual data usually has to be written
- out to some character encoding, usually UTF-8, and then the resulting
- bytevector is written out to the port.
- In summary, Guile reads and writes HTTP over latin-1 sockets, without
- any loss of generality.
- @subsubsection Request API
- @deffn {Scheme Procedure} request? obj
- @deffnx {Scheme Procedure} request-method request
- @deffnx {Scheme Procedure} request-uri request
- @deffnx {Scheme Procedure} request-version request
- @deffnx {Scheme Procedure} request-headers request
- @deffnx {Scheme Procedure} request-meta request
- @deffnx {Scheme Procedure} request-port request
- A predicate and field accessors for the request type. The fields are as
- follows:
- @table @code
- @item method
- The HTTP method, for example, @code{GET}.
- @item uri
- The URI as a URI record.
- @item version
- The HTTP version pair, like @code{(1 . 1)}.
- @item headers
- The request headers, as an alist of parsed values.
- @item meta
- An arbitrary alist of other data, for example information returned in
- the @code{sockaddr} from @code{accept} (@pxref{Network Sockets and
- Communication}).
- @item port
- The port on which to read or write a request body, if any.
- @end table
- @end deffn
- @deffn {Scheme Procedure} read-request port [meta='()]
- Read an HTTP request from @var{port}, optionally attaching the given
- metadata, @var{meta}.
- As a side effect, sets the encoding on @var{port} to ISO-8859-1
- (latin-1), so that reading one character reads one byte. See the
- discussion of character sets above, for more information.
- Note that the body is not part of the request. Once you have read a
- request, you may read the body separately, and likewise for writing
- requests.
- @end deffn
- @deffn {Scheme Procedure} build-request uri [#:method='GET] @
- [#:version='(1 . 1)] [#:headers='()] [#:port=#f] [#:meta='()] @
- [#:validate-headers?=#t]
- Construct an HTTP request object. If @var{validate-headers?} is true,
- the headers are each run through their respective validators.
- @end deffn
- @deffn {Scheme Procedure} write-request r port
- Write the given HTTP request to @var{port}.
- Return a new request, whose @code{request-port} will continue writing
- on @var{port}, perhaps using some transfer encoding.
- @end deffn
- @deffn {Scheme Procedure} read-request-body r
- Reads the request body from @var{r}, as a bytevector. Return @code{#f}
- if there was no request body.
- @end deffn
- @deffn {Scheme Procedure} write-request-body r bv
- Write @var{bv}, a bytevector, to the port corresponding to the HTTP
- request @var{r}.
- @end deffn
- The various headers that are typically associated with HTTP requests may
- be accessed with these dedicated accessors. @xref{HTTP Headers}, for
- more information on the format of parsed headers.
- @deffn {Scheme Procedure} request-accept request [default='()]
- @deffnx {Scheme Procedure} request-accept-charset request [default='()]
- @deffnx {Scheme Procedure} request-accept-encoding request [default='()]
- @deffnx {Scheme Procedure} request-accept-language request [default='()]
- @deffnx {Scheme Procedure} request-allow request [default='()]
- @deffnx {Scheme Procedure} request-authorization request [default=#f]
- @deffnx {Scheme Procedure} request-cache-control request [default='()]
- @deffnx {Scheme Procedure} request-connection request [default='()]
- @deffnx {Scheme Procedure} request-content-encoding request [default='()]
- @deffnx {Scheme Procedure} request-content-language request [default='()]
- @deffnx {Scheme Procedure} request-content-length request [default=#f]
- @deffnx {Scheme Procedure} request-content-location request [default=#f]
- @deffnx {Scheme Procedure} request-content-md5 request [default=#f]
- @deffnx {Scheme Procedure} request-content-range request [default=#f]
- @deffnx {Scheme Procedure} request-content-type request [default=#f]
- @deffnx {Scheme Procedure} request-date request [default=#f]
- @deffnx {Scheme Procedure} request-expect request [default='()]
- @deffnx {Scheme Procedure} request-expires request [default=#f]
- @deffnx {Scheme Procedure} request-from request [default=#f]
- @deffnx {Scheme Procedure} request-host request [default=#f]
- @deffnx {Scheme Procedure} request-if-match request [default=#f]
- @deffnx {Scheme Procedure} request-if-modified-since request [default=#f]
- @deffnx {Scheme Procedure} request-if-none-match request [default=#f]
- @deffnx {Scheme Procedure} request-if-range request [default=#f]
- @deffnx {Scheme Procedure} request-if-unmodified-since request [default=#f]
- @deffnx {Scheme Procedure} request-last-modified request [default=#f]
- @deffnx {Scheme Procedure} request-max-forwards request [default=#f]
- @deffnx {Scheme Procedure} request-pragma request [default='()]
- @deffnx {Scheme Procedure} request-proxy-authorization request [default=#f]
- @deffnx {Scheme Procedure} request-range request [default=#f]
- @deffnx {Scheme Procedure} request-referer request [default=#f]
- @deffnx {Scheme Procedure} request-te request [default=#f]
- @deffnx {Scheme Procedure} request-trailer request [default='()]
- @deffnx {Scheme Procedure} request-transfer-encoding request [default='()]
- @deffnx {Scheme Procedure} request-upgrade request [default='()]
- @deffnx {Scheme Procedure} request-user-agent request [default=#f]
- @deffnx {Scheme Procedure} request-via request [default='()]
- @deffnx {Scheme Procedure} request-warning request [default='()]
- Return the given request header, or @var{default} if none was present.
- @end deffn
- @deffn {Scheme Procedure} request-absolute-uri r [default-host=#f] @
- [default-port=#f] [default-scheme=#f]
- A helper routine to determine the absolute URI of a request, using the
- @code{host} header and the default scheme, host and port. If there is
- no default scheme and the URI is not itself absolute, an error is
- signaled.
- @end deffn
- @node Responses
- @subsection HTTP Responses
- @example
- (use-modules (web response))
- @end example
- As with requests (@pxref{Requests}), Guile offers a data type for HTTP
- responses. Again, the body is represented separately from the request.
- @deffn {Scheme Procedure} response? obj
- @deffnx {Scheme Procedure} response-version response
- @deffnx {Scheme Procedure} response-code response
- @deffnx {Scheme Procedure} response-reason-phrase response
- @deffnx {Scheme Procedure} response-headers response
- @deffnx {Scheme Procedure} response-port response
- A predicate and field accessors for the response type. The fields are as
- follows:
- @table @code
- @item version
- The HTTP version pair, like @code{(1 . 1)}.
- @item code
- The HTTP response code, like @code{200}.
- @item reason-phrase
- The reason phrase, or the standard reason phrase for the response's
- code.
- @item headers
- The response headers, as an alist of parsed values.
- @item port
- The port on which to read or write a response body, if any.
- @end table
- @end deffn
- @deffn {Scheme Procedure} read-response port
- Read an HTTP response from @var{port}.
- As a side effect, sets the encoding on @var{port} to ISO-8859-1
- (latin-1), so that reading one character reads one byte. See the
- discussion of character sets in @ref{Responses}, for more information.
- @end deffn
- @deffn {Scheme Procedure} build-response [#:version='(1 . 1)] [#:code=200] [#:reason-phrase=#f] [#:headers='()] [#:port=#f] [#:validate-headers?=#t]
- Construct an HTTP response object. If @var{validate-headers?} is true,
- the headers are each run through their respective validators.
- @end deffn
- @deffn {Scheme Procedure} adapt-response-version response version
- Adapt the given response to a different HTTP version. Return a new HTTP
- response.
- The idea is that many applications might just build a response for the
- default HTTP version, and this method could handle a number of
- programmatic transformations to respond to older HTTP versions (0.9 and
- 1.0). But currently this function is a bit heavy-handed, just updating
- the version field.
- @end deffn
- @deffn {Scheme Procedure} write-response r port
- Write the given HTTP response to @var{port}.
- Return a new response, whose @code{response-port} will continue writing
- on @var{port}, perhaps using some transfer encoding.
- @end deffn
- @deffn {Scheme Procedure} response-must-not-include-body? r
- Some responses, like those with status code 304, are specified as never
- having bodies. This predicate returns @code{#t} for those responses.
- Note also, though, that responses to @code{HEAD} requests must also not
- have a body.
- @end deffn
- @deffn {Scheme Procedure} response-body-port r [#:decode?=#t] [#:keep-alive?=#t]
- Return an input port from which the body of @var{r} can be read. The encoding
- of the returned port is set according to @var{r}'s @code{content-type} header,
- when it's textual, except if @var{decode?} is @code{#f}. Return @code{#f}
- when no body is available.
- When @var{keep-alive?} is @code{#f}, closing the returned port also closes
- @var{r}'s response port.
- @end deffn
- @deffn {Scheme Procedure} read-response-body r
- Read the response body from @var{r}, as a bytevector. Returns @code{#f}
- if there was no response body.
- @end deffn
- @deffn {Scheme Procedure} write-response-body r bv
- Write @var{bv}, a bytevector, to the port corresponding to the HTTP
- response @var{r}.
- @end deffn
- As with requests, the various headers that are typically associated with
- HTTP responses may be accessed with these dedicated accessors.
- @xref{HTTP Headers}, for more information on the format of parsed
- headers.
- @deffn {Scheme Procedure} response-accept-ranges response [default=#f]
- @deffnx {Scheme Procedure} response-age response [default='()]
- @deffnx {Scheme Procedure} response-allow response [default='()]
- @deffnx {Scheme Procedure} response-cache-control response [default='()]
- @deffnx {Scheme Procedure} response-connection response [default='()]
- @deffnx {Scheme Procedure} response-content-encoding response [default='()]
- @deffnx {Scheme Procedure} response-content-language response [default='()]
- @deffnx {Scheme Procedure} response-content-length response [default=#f]
- @deffnx {Scheme Procedure} response-content-location response [default=#f]
- @deffnx {Scheme Procedure} response-content-md5 response [default=#f]
- @deffnx {Scheme Procedure} response-content-range response [default=#f]
- @deffnx {Scheme Procedure} response-content-type response [default=#f]
- @deffnx {Scheme Procedure} response-date response [default=#f]
- @deffnx {Scheme Procedure} response-etag response [default=#f]
- @deffnx {Scheme Procedure} response-expires response [default=#f]
- @deffnx {Scheme Procedure} response-last-modified response [default=#f]
- @deffnx {Scheme Procedure} response-location response [default=#f]
- @deffnx {Scheme Procedure} response-pragma response [default='()]
- @deffnx {Scheme Procedure} response-proxy-authenticate response [default=#f]
- @deffnx {Scheme Procedure} response-retry-after response [default=#f]
- @deffnx {Scheme Procedure} response-server response [default=#f]
- @deffnx {Scheme Procedure} response-trailer response [default='()]
- @deffnx {Scheme Procedure} response-transfer-encoding response [default='()]
- @deffnx {Scheme Procedure} response-upgrade response [default='()]
- @deffnx {Scheme Procedure} response-vary response [default='()]
- @deffnx {Scheme Procedure} response-via response [default='()]
- @deffnx {Scheme Procedure} response-warning response [default='()]
- @deffnx {Scheme Procedure} response-www-authenticate response [default=#f]
- Return the given response header, or @var{default} if none was present.
- @end deffn
- @deffn {Scheme Procedure} text-content-type? @var{type}
- Return @code{#t} if @var{type}, a symbol as returned by
- @code{response-content-type}, represents a textual type such as
- @code{text/plain}.
- @end deffn
- @node Web Client
- @subsection Web Client
- @code{(web client)} provides a simple, synchronous HTTP client, built on
- the lower-level HTTP, request, and response modules.
- @example
- (use-modules (web client))
- @end example
- @deffn {Scheme Procedure} open-socket-for-uri uri [#:verify-certificate? #t]
- Return an open input/output port for a connection to URI. Guile
- dynamically loads Guile-GnuTLS for HTTPS support.
- See the @uref{https://gitlab.com/gnutls/guile/, Web site of Guile-GnuTLS}, and
- @pxref{Guile Preparations,
- how to install the GnuTLS bindings for Guile,, gnutls-guile,
- GnuTLS-Guile}, for more information.
- @cindex certificate verification, for HTTPS
- When @var{verify-certificate?} is true, verify the server's X.509
- certificates against those read from @code{x509-certificate-directory}.
- When an error occurs---e.g., the server's certificate has expired, or
- its host name does not match---raise a @code{tls-certificate-error}
- exception. The arguments to the @code{tls-certificate-error} exception
- are:
- @enumerate
- @item
- a symbol indicating the failure cause, @code{host-mismatch} if the
- certificate's host name does not match the server's host name, and
- @code{invalid-certificate} for other causes;
- @item
- the server's X.509 certificate (@pxref{Guile Reference, GnuTLS Guile
- reference,, gnutls-guile, GnuTLS-Guile});
- @item
- the server's host name (a string);
- @item
- in the case of @code{invalid-certificate} errors, a list of GnuTLS
- certificate status values---one of the @code{certificate-status/}
- constants, such as @code{certificate-status/signer-not-found} or
- @code{certificate-status/revoked}.
- @end enumerate
- @end deffn
- @anchor{http-request}@deffn {Scheme Procedure} http-request @var{uri} @var{arg}@dots{}
- Connect to the server corresponding to @var{uri} and make a request over
- HTTP, using @var{method} (@code{GET}, @code{HEAD}, @code{POST}, etc.).
- The following keyword arguments allow you to modify the requests in
- various ways, for example attaching a body to the request, or setting
- specific headers. The following table lists the keyword arguments and
- their default values.
- @table @code
- @item #:method 'GET
- @item #:body #f
- @item #:verify-certificate? #t
- @item #:port (open-socket-for-uri @var{uri} #:verify-certificate? @var{verify-certificate?})
- @item #:version '(1 . 1)
- @item #:keep-alive? #f
- @item #:headers '()
- @item #:decode-body? #t
- @item #:streaming? #f
- @end table
- If you already have a port open, pass it as @var{port}. Otherwise, a
- connection will be opened to the server corresponding to @var{uri}. Any
- extra headers in the alist @var{headers} will be added to the request.
- If @var{body} is not @code{#f}, a message body will also be sent with
- the HTTP request. If @var{body} is a string, it is encoded according to
- the content-type in @var{headers}, defaulting to UTF-8. Otherwise
- @var{body} should be a bytevector, or @code{#f} for no body. Although a
- message body may be sent with any request, usually only @code{POST} and
- @code{PUT} requests have bodies.
- If @var{decode-body?} is true, as is the default, the body of the
- response will be decoded to string, if it is a textual content-type.
- Otherwise it will be returned as a bytevector.
- However, if @var{streaming?} is true, instead of eagerly reading the
- response body from the server, this function only reads off the headers.
- The response body will be returned as a port on which the data may be
- read.
- Unless @var{keep-alive?} is true, the port will be closed after the full
- response body has been read.
- If @var{port} is false, @var{uri} denotes an HTTPS URL, and @var{verify-certificate?} is
- true, verify X.509 certificates against those available in
- @code{x509-certificate-directory}.
- Returns two values: the response read from the server, and the response
- body as a string, bytevector, #f value, or as a port (if
- @var{streaming?} is true).
- @end deffn
- @deffn {Scheme Procedure} http-get @var{uri} @var{arg}@dots{}
- @deffnx {Scheme Procedure} http-head @var{uri} @var{arg}@dots{}
- @deffnx {Scheme Procedure} http-post @var{uri} @var{arg}@dots{}
- @deffnx {Scheme Procedure} http-put @var{uri} @var{arg}@dots{}
- @deffnx {Scheme Procedure} http-delete @var{uri} @var{arg}@dots{}
- @deffnx {Scheme Procedure} http-trace @var{uri} @var{arg}@dots{}
- @deffnx {Scheme Procedure} http-options @var{uri} @var{arg}@dots{}
- Connect to the server corresponding to @var{uri} and make a request over
- HTTP, using the appropriate method (@code{GET}, @code{HEAD},
- @code{POST}, etc.).
- These procedures are variants of @code{http-request} specialized with a
- specific @var{method} argument, and have the same prototype: a URI
- followed by an optional sequence of keyword arguments.
- @xref{http-request}, for full documentation on the various keyword
- arguments.
- @end deffn
- @defvr {Scheme Parameter} x509-certificate-directory
- @cindex X.509 certificate directory
- @cindex HTTPS, X.509 certificates
- @cindex certificates, for HTTPS
- This parameter gives the name of the directory where X.509 certificates
- for HTTPS connections should be looked for.
- Its default value is one of:
- @itemize
- @item
- @vindex GUILE_TLS_CERTIFICATE_DIRECTORY
- the value of the @env{GUILE_TLS_CERTIFICATE_DIRECTORY} environment
- variable;
- @item
- @vindex SSL_CERT_DIR
- or the value of the @env{SSL_CERT_DIR} environment variable (also
- honored by the OpenSSL library);
- @item
- or, as a last resort, @code{"/etc/ssl/certs"}.
- @end itemize
- X.509 certificates are used when authenticating the identity of a remote
- site, when the @code{#:verify-certificate?} argument to
- @code{open-socket-for-uri}, to @code{http-request}, or to related
- procedures is true.
- @end defvr
- @code{http-get} is useful for making one-off requests to web sites. If
- you are writing a web spider or some other client that needs to handle a
- number of requests in parallel, it's better to build an event-driven URL
- fetcher, similar in structure to the web server (@pxref{Web Server}).
- Another option, good but not as performant, would be to use threads,
- possibly via par-map or futures.
- @deffn {Scheme Parameter} current-http-proxy
- @deffnx {Scheme Parameter} current-https-proxy
- Either @code{#f} or a non-empty string containing the URL of the HTTP
- or HTTPS proxy server to be used by the procedures in the @code{(web client)}
- module, including @code{open-socket-for-uri}. Its initial value is
- based on the @env{http_proxy} and @env{https_proxy} environment variables.
- @example
- (current-http-proxy) @result{} "http://localhost:8123/"
- (parameterize ((current-http-proxy #f))
- (http-get "http://example.com/")) ; temporarily bypass proxy
- (current-http-proxy) @result{} "http://localhost:8123/"
- @end example
- @end deffn
- @node Web Server
- @subsection Web Server
- @code{(web server)} is a generic web server interface, along with a main
- loop implementation for web servers controlled by Guile.
- @example
- (use-modules (web server))
- @end example
- The lowest layer is the @code{<server-impl>} object, which defines a set
- of hooks to open a server, read a request from a client, write a
- response to a client, and close a server. These hooks -- @code{open},
- @code{read}, @code{write}, and @code{close}, respectively -- are bound
- together in a @code{<server-impl>} object. Procedures in this module take a
- @code{<server-impl>} object, if needed.
- A @code{<server-impl>} may also be looked up by name. If you pass the
- @code{http} symbol to @code{run-server}, Guile looks for a variable
- named @code{http} in the @code{(web server http)} module, which should
- be bound to a @code{<server-impl>} object. Such a binding is made by
- instantiation of the @code{define-server-impl} syntax. In this way the
- run-server loop can automatically load other backends if available.
- The life cycle of a server goes as follows:
- @enumerate
- @item
- The @code{open} hook is called, to open the server. @code{open} takes
- zero or more arguments, depending on the backend, and returns an opaque
- server socket object, or signals an error.
- @item
- The @code{read} hook is called, to read a request from a new client.
- The @code{read} hook takes one argument, the server socket. It should
- return three values: an opaque client socket, the request, and the
- request body. The request should be a @code{<request>} object, from
- @code{(web request)}. The body should be a string or a bytevector, or
- @code{#f} if there is no body.
- If the read failed, the @code{read} hook may return #f for the client
- socket, request, and body.
- @item
- A user-provided handler procedure is called, with the request and body
- as its arguments. The handler should return two values: the response,
- as a @code{<response>} record from @code{(web response)}, and the
- response body as bytevector, or @code{#f} if not present.
- The respose and response body are run through @code{sanitize-response},
- documented below. This allows the handler writer to take some
- convenient shortcuts: for example, instead of a @code{<response>}, the
- handler can simply return an alist of headers, in which case a default
- response object is constructed with those headers. Instead of a
- bytevector for the body, the handler can return a string, which will be
- serialized into an appropriate encoding; or it can return a procedure,
- which will be called on a port to write out the data. See the
- @code{sanitize-response} documentation, for more.
- @item
- The @code{write} hook is called with three arguments: the client
- socket, the response, and the body. The @code{write} hook returns no
- values.
- @item
- At this point the request handling is complete. For a loop, we
- loop back and try to read a new request.
- @item
- If the user interrupts the loop, the @code{close} hook is called on
- the server socket.
- @end enumerate
- A user may define a server implementation with the following form:
- @deffn {Scheme Syntax} define-server-impl name open read write close
- Make a @code{<server-impl>} object with the hooks @var{open},
- @var{read}, @var{write}, and @var{close}, and bind it to the symbol
- @var{name} in the current module.
- @end deffn
- @deffn {Scheme Procedure} lookup-server-impl impl
- Look up a server implementation. If @var{impl} is a server
- implementation already, it is returned directly. If it is a symbol, the
- binding named @var{impl} in the @code{(web server @var{impl})} module is
- looked up. Otherwise an error is signaled.
- Currently a server implementation is a somewhat opaque type, useful only
- for passing to other procedures in this module, like @code{read-client}.
- @end deffn
- The @code{(web server)} module defines a number of routines that use
- @code{<server-impl>} objects to implement parts of a web server. Given
- that we don't expose the accessors for the various fields of a
- @code{<server-impl>}, indeed these routines are the only procedures with
- any access to the impl objects.
- @deffn {Scheme Procedure} open-server impl open-params
- Open a server for the given implementation. Return one value, the new
- server object. The implementation's @code{open} procedure is applied to
- @var{open-params}, which should be a list.
- @end deffn
- @deffn {Scheme Procedure} read-client impl server
- Read a new client from @var{server}, by applying the implementation's
- @code{read} procedure to the server. If successful, return three
- values: an object corresponding to the client, a request object, and the
- request body. If any exception occurs, return @code{#f} for all three
- values.
- @end deffn
- @deffn {Scheme Procedure} handle-request handler request body state
- Handle a given request, returning the response and body.
- The response and response body are produced by calling the given
- @var{handler} with @var{request} and @var{body} as arguments.
- The elements of @var{state} are also passed to @var{handler} as
- arguments, and may be returned as additional values. The new
- @var{state}, collected from the @var{handler}'s return values, is then
- returned as a list. The idea is that a server loop receives a handler
- from the user, along with whatever state values the user is interested
- in, allowing the user's handler to explicitly manage its state.
- @end deffn
- @deffn {Scheme Procedure} sanitize-response request response body
- ``Sanitize'' the given response and body, making them appropriate for
- the given request.
- As a convenience to web handler authors, @var{response} may be given as
- an alist of headers, in which case it is used to construct a default
- response. Ensures that the response version corresponds to the request
- version. If @var{body} is a string, encodes the string to a bytevector,
- in an encoding appropriate for @var{response}. Adds a
- @code{content-length} and @code{content-type} header, as necessary.
- If @var{body} is a procedure, it is called with a port as an argument,
- and the output collected as a bytevector. In the future we might try to
- instead use a compressing, chunk-encoded port, and call this procedure
- later, in the write-client procedure. Authors are advised not to rely on
- the procedure being called at any particular time.
- @end deffn
- @deffn {Scheme Procedure} write-client impl server client response body
- Write an HTTP response and body to @var{client}. If the server and
- client support persistent connections, it is the implementation's
- responsibility to keep track of the client thereafter, presumably by
- attaching it to the @var{server} argument somehow.
- @end deffn
- @deffn {Scheme Procedure} close-server impl server
- Release resources allocated by a previous invocation of
- @code{open-server}.
- @end deffn
- Given the procedures above, it is a small matter to make a web server:
- @deffn {Scheme Procedure} serve-one-client handler impl server state
- Read one request from @var{server}, call @var{handler} on the request
- and body, and write the response to the client. Return the new state
- produced by the handler procedure.
- @end deffn
- @deffn {Scheme Procedure} run-server handler @
- [impl='http] [open-params='()] @
- arg @dots{}
- Run Guile's built-in web server.
- @var{handler} should be a procedure that takes two or more arguments,
- the HTTP request and request body, and returns two or more values, the
- response and response body.
- For examples, skip ahead to the next section, @ref{Web Examples}.
- The response and body will be run through @code{sanitize-response}
- before sending back to the client.
- Additional arguments to @var{handler} are taken from @var{arg}
- @enddots{}. These arguments comprise a @dfn{state}. Additional return
- values are accumulated into a new state, which will be used for
- subsequent requests. In this way a handler can explicitly manage its
- state.
- @end deffn
- The default web server implementation is @code{http}, which binds to a
- socket, listening for request on that port.
- @deffn {HTTP Implementation} http [#:host=#f] @
- [#:family=AF_INET] @
- [#:addr=INADDR_LOOPBACK] @
- [#:port 8080] [#:socket]
- The default HTTP implementation. We document it as a function with
- keyword arguments, because that is precisely the way that it is -- all
- of the @var{open-params} to @code{run-server} get passed to the
- implementation's open function.
- @example
- ;; The defaults: localhost:8080
- (run-server handler)
- ;; Same thing
- (run-server handler 'http '())
- ;; On a different port
- (run-server handler 'http '(#:port 8081))
- ;; IPv6
- (run-server handler 'http '(#:family AF_INET6 #:port 8081))
- ;; Custom socket
- (run-server handler 'http `(#:socket ,(sudo-make-me-a-socket)))
- @end example
- @end deffn
- @node Web Examples
- @subsection Web Examples
- Well, enough about the tedious internals. Let's make a web application!
- @subsubsection Hello, World!
- The first program we have to write, of course, is ``Hello, World!''.
- This means that we have to implement a web handler that does what we
- want.
- Now we define a handler, a function of two arguments and two return
- values:
- @example
- (define (handler request request-body)
- (values @var{response} @var{response-body}))
- @end example
- In this first example, we take advantage of a short-cut, returning an
- alist of headers instead of a proper response object. The response body
- is our payload:
- @example
- (define (hello-world-handler request request-body)
- (values '((content-type . (text/plain)))
- "Hello World!"))
- @end example
- Now let's test it, by running a server with this handler. Load up the
- web server module if you haven't yet done so, and run a server with this
- handler:
- @example
- (use-modules (web server))
- (run-server hello-world-handler)
- @end example
- By default, the web server listens for requests on
- @code{localhost:8080}. Visit that address in your web browser to
- test. If you see the string, @code{Hello World!}, sweet!
- @subsubsection Inspecting the Request
- The Hello World program above is a general greeter, responding to all
- URIs. To make a more exclusive greeter, we need to inspect the request
- object, and conditionally produce different results. So let's load up
- the request, response, and URI modules, and do just that.
- @example
- (use-modules (web server)) ; you probably did this already
- (use-modules (web request)
- (web response)
- (web uri))
- (define (request-path-components request)
- (split-and-decode-uri-path (uri-path (request-uri request))))
- (define (hello-hacker-handler request body)
- (if (equal? (request-path-components request)
- '("hacker"))
- (values '((content-type . (text/plain)))
- "Hello hacker!")
- (not-found request)))
- (run-server hello-hacker-handler)
- @end example
- Here we see that we have defined a helper to return the components of
- the URI path as a list of strings, and used that to check for a request
- to @code{/hacker/}. Then the success case is just as before -- visit
- @code{http://localhost:8080/hacker/} in your browser to check.
- You should always match against URI path components as decoded by
- @code{split-and-decode-uri-path}. The above example will work for
- @code{/hacker/}, @code{//hacker///}, and @code{/h%61ck%65r}.
- But we forgot to define @code{not-found}! If you are pasting these
- examples into a REPL, accessing any other URI in your web browser will
- drop your Guile console into the debugger:
- @example
- <unnamed port>:38:7: In procedure module-lookup:
- <unnamed port>:38:7: Unbound variable: not-found
- Entering a new prompt. Type `,bt' for a backtrace or `,q' to continue.
- scheme@@(guile-user) [1]>
- @end example
- So let's define the function, right there in the debugger. As you
- probably know, we'll want to return a 404 response.
- @example
- ;; Paste this in your REPL
- (define (not-found request)
- (values (build-response #:code 404)
- (string-append "Resource not found: "
- (uri->string (request-uri request)))))
- ;; Now paste this to let the web server keep going:
- ,continue
- @end example
- Now if you access @code{http://localhost/foo/}, you get this error
- message. (Note that some popular web browsers won't show
- server-generated 404 messages, showing their own instead, unless the 404
- message body is long enough.)
- @subsubsection Higher-Level Interfaces
- The web handler interface is a common baseline that all kinds of Guile
- web applications can use. You will usually want to build something on
- top of it, however, especially when producing HTML. Here is a simple
- example that builds up HTML output using SXML (@pxref{SXML}).
- First, load up the modules:
- @example
- (use-modules (web server)
- (web request)
- (web response)
- (sxml simple))
- @end example
- Now we define a simple templating function that takes a list of HTML
- body elements, as SXML, and puts them in our super template:
- @example
- (define (templatize title body)
- `(html (head (title ,title))
- (body ,@@body)))
- @end example
- For example, the simplest Hello HTML can be produced like this:
- @example
- (sxml->xml (templatize "Hello!" '((b "Hi!"))))
- @print{}
- <html><head><title>Hello!</title></head><body><b>Hi!</b></body></html>
- @end example
- Much better to work with Scheme data types than to work with HTML as
- strings. Now we define a little response helper:
- @example
- (define* (respond #:optional body #:key
- (status 200)
- (title "Hello hello!")
- (doctype "<!DOCTYPE html>\n")
- (content-type-params '((charset . "utf-8")))
- (content-type 'text/html)
- (extra-headers '())
- (sxml (and body (templatize title body))))
- (values (build-response
- #:code status
- #:headers `((content-type
- . (,content-type ,@@content-type-params))
- ,@@extra-headers))
- (lambda (port)
- (if sxml
- (begin
- (if doctype (display doctype port))
- (sxml->xml sxml port))))))
- @end example
- Here we see the power of keyword arguments with default initializers. By
- the time the arguments are fully parsed, the @code{sxml} local variable
- will hold the templated SXML, ready for sending out to the client.
- Also, instead of returning the body as a string, @code{respond} gives a
- procedure, which will be called by the web server to write out the
- response to the client.
- Now, a simple example using this responder, which lays out the incoming
- headers in an HTML table.
- @example
- (define (debug-page request body)
- (respond
- `((h1 "hello world!")
- (table
- (tr (th "header") (th "value"))
- ,@@(map (lambda (pair)
- `(tr (td (tt ,(with-output-to-string
- (lambda () (display (car pair))))))
- (td (tt ,(with-output-to-string
- (lambda ()
- (write (cdr pair))))))))
- (request-headers request))))))
- (run-server debug-page)
- @end example
- Now if you visit any local address in your web browser, we actually see
- some HTML, finally.
- @subsubsection Conclusion
- Well, this is about as far as Guile's built-in web support goes, for
- now. There are many ways to make a web application, but hopefully by
- standardizing the most fundamental data types, users will be able to
- choose the approach that suits them best, while also being able to
- switch between implementations of the server. This is a relatively new
- part of Guile, so if you have feedback, let us know, and we can take it
- into account. Happy hacking on the web!
- @c Local Variables:
- @c TeX-master: "guile.texi"
- @c End:
|