]> gitweb @ CieloNegro.org - Lucu.git/blob - Network/HTTP/Lucu/Resource.hs
getRemoteAddr and getRemoteAddr'
[Lucu.git] / Network / HTTP / Lucu / Resource.hs
1 -- #prune
2
3 -- |This is the Resource Monad; monadic actions to define the behavior
4 -- of each resources. The 'Resource' Monad is a kind of IO Monad thus
5 -- it implements MonadIO class. It is also a state machine.
6 -- 
7 -- Request Processing Flow:
8 --
9 --   1. A client issues an HTTP request.
10 --
11 --   2. If the URI of it matches to any resource, the corresponding
12 --      'Resource' Monad starts running on a newly spawned thread.
13 --
14 --   3. The 'Resource' Monad looks at the request header, find (or not
15 --      find) an entity, receive the request body (if any), decide the
16 --      response header, and decide the response body. This process
17 --      will be discussed later.
18 --
19 --   4. The 'Resource' Monad and its thread stops running. The client
20 --      may or may not be sending us the next request at this point.
21 --
22 -- 'Resource' Monad takes the following states. The initial state is
23 -- /Examining Request/ and the final state is /Done/.
24 --
25 --   [/Examining Request/] In this state, a 'Resource' looks at the
26 --   request header and thinks about an entity for it. If there is a
27 --   suitable entity, the 'Resource' tells the system an entity tag
28 --   and its last modification time ('foundEntity'). If it found no
29 --   entity, it tells the system so ('foundNoEntity'). In case it is
30 --   impossible to decide the existence of entity, which is a typical
31 --   case for POST requests, 'Resource' does nothing in this state.
32 --
33 --   [/Getting Body/] A 'Resource' asks the system to receive a
34 --   request body from client. Before actually reading from the
35 --   socket, the system sends \"100 Continue\" to the client if need
36 --   be. When a 'Resource' transits to the next state without
37 --   receiving all or part of request body, the system still reads it
38 --   and just throws it away.
39 --
40 --   [/Deciding Header/] A 'Resource' makes a decision of status code
41 --   and response header. When it transits to the next state, the
42 --   system checks the validness of response header and then write
43 --   them to the socket.
44 --
45 --   [/Deciding Body/] In this state, a 'Resource' asks the system to
46 --   write some response body to the socket. When it transits to the
47 --   next state without writing any response body, the system
48 --   completes it depending on the status code.
49 --
50 --   [/Done/] Everything is over. A 'Resource' can do nothing for the
51 --   HTTP interaction anymore.
52 --
53 -- Note that the state transition is one-way: for instance, it is an
54 -- error to try to read a request body after writing some
55 -- response. This limitation is for efficiency. We don't want to read
56 -- the entire request before starting 'Resource', nor we don't want to
57 -- postpone writing the entire response till the end of 'Resource'
58 -- computation.
59
60 module Network.HTTP.Lucu.Resource
61     (
62     -- * Monad
63     Resource
64
65     -- * Actions
66
67     -- ** Getting request header
68
69     -- |These actions can be computed regardless of the current state,
70     -- and they don't change the state.
71     , getConfig
72     , getRemoteAddr
73     , getRemoteAddr'
74     , getRequest
75     , getMethod
76     , getRequestURI
77     , getResourcePath
78     , getPathInfo
79     , getQueryForm
80     , getHeader
81     , getAccept
82     , getContentType
83
84     -- ** Finding an entity
85
86     -- |These actions can be computed only in the /Examining Request/
87     -- state. After the computation, the 'Resource' transits to
88     -- /Getting Body/ state.
89     , foundEntity
90     , foundETag
91     , foundTimeStamp
92     , foundNoEntity
93
94     -- ** Getting a request body
95
96     -- |Computation of these actions changes the state to /Getting
97     -- Body/.
98     , input
99     , inputChunk
100     , inputBS
101     , inputChunkBS
102     , inputForm
103     , defaultLimit
104
105     -- ** Setting response headers
106     
107     -- |Computation of these actions changes the state to /Deciding
108     -- Header/.
109     , setStatus
110     , setHeader
111     , redirect
112     , setContentType
113     , setLocation
114
115     -- ** Writing a response body
116
117     -- |Computation of these actions changes the state to /Deciding
118     -- Body/.
119     , output
120     , outputChunk
121     , outputBS
122     , outputChunkBS
123
124     , driftTo
125     )
126     where
127
128 import           Control.Concurrent.STM
129 import           Control.Monad.Reader
130 import           Data.Bits
131 import qualified Data.ByteString.Lazy.Char8 as B
132 import           Data.ByteString.Lazy.Char8 (ByteString)
133 import           Data.List
134 import           Data.Maybe
135 import           GHC.Conc (unsafeIOToSTM)
136 import           Network.HTTP.Lucu.Abortion
137 import           Network.HTTP.Lucu.Config
138 import           Network.HTTP.Lucu.DefaultPage
139 import           Network.HTTP.Lucu.ETag
140 import qualified Network.HTTP.Lucu.Headers as H
141 import           Network.HTTP.Lucu.HttpVersion
142 import           Network.HTTP.Lucu.Interaction
143 import           Network.HTTP.Lucu.Parser
144 import           Network.HTTP.Lucu.Postprocess
145 import           Network.HTTP.Lucu.RFC1123DateTime
146 import           Network.HTTP.Lucu.Request
147 import           Network.HTTP.Lucu.Response
148 import           Network.HTTP.Lucu.MIMEType
149 import           Network.HTTP.Lucu.Utils
150 import           Network.Socket
151 import           Network.URI
152 import           System.Time
153
154 -- |The 'Resource' monad. /Interaction/ is an internal state thus it
155 -- is not exposed to users. This monad implements 'MonadIO' so it can
156 -- do any IO actions.
157 type Resource a = ReaderT Interaction IO a
158
159 -- |Get the 'Network.HTTP.Lucu.Config.Config' value which is used for
160 -- the httpd.
161 getConfig :: Resource Config
162 getConfig = do itr <- ask
163                return $ itrConfig itr
164
165
166 -- |Get the SockAddr of the remote host. If you want a string
167 -- representation instead of SockAddr, use 'getRemoteAddr''.
168 getRemoteAddr :: Resource SockAddr
169 getRemoteAddr = do itr <- ask
170                    return $ itrRemoteAddr itr
171
172
173 -- |Get the string representation of the address of remote host. If
174 -- you want a SockAddr instead of String, use 'getRemoteAddr'.
175 getRemoteAddr' :: Resource String
176 getRemoteAddr' = do addr <- getRemoteAddr
177                     case addr of
178                       -- Network.Socket は IPv6 を考慮してゐないやうだ…
179                       (SockAddrInet _ v4addr)
180                           -> let b1 = (v4addr `shiftR` 24) .&. 0xFF
181                                  b2 = (v4addr `shiftR` 16) .&. 0xFF
182                                  b3 = (v4addr `shiftR`  8) .&. 0xFF
183                                  b4 =  v4addr              .&. 0xFF
184                              in
185                                return $ concat $ intersperse "." $ map show [b1, b2, b3, b4]
186                       (SockAddrUnix path)
187                           -> return path
188
189
190 -- |Get the 'Network.HTTP.Lucu.Request.Request' value which represents
191 -- the request header. In general you don't have to use this action.
192 getRequest :: Resource Request
193 getRequest = do itr <- ask
194                 req <- liftIO $ atomically $ readItr itr itrRequest fromJust
195                 return req
196
197 -- |Get the 'Network.HTTP.Lucu.Request.Method' value of the request.
198 getMethod :: Resource Method
199 getMethod = do req <- getRequest
200                return $ reqMethod req
201
202 -- |Get the URI of the request.
203 getRequestURI :: Resource URI
204 getRequestURI = do req <- getRequest
205                    return $ reqURI req
206
207 -- |Get the path of this 'Resource' (to be exact,
208 -- 'Network.HTTP.Lucu.Resource.Tree.ResourceDef') in the
209 -- 'Network.HTTP.Lucu.Resource.Tree.ResTree'. The result of this
210 -- action is the exact path in the tree even if the
211 -- 'Network.HTTP.Lucu.Resource.Tree.ResourceDef' is greedy.
212 --
213 -- Example:
214 --
215 -- > main = let tree = mkResTree [ (["foo"], resFoo) ]
216 -- >        in runHttpd defaultConfig tree
217 -- >
218 -- > resFoo = ResourceDef {
219 -- >     resIsGreedy = True
220 -- >   , resGet = Just $ do requestURI   <- getRequestURI
221 -- >                        resourcePath <- getResourcePath
222 -- >                        pathInfo     <- getPathInfo
223 -- >                        -- uriPath requestURI == "/foo/bar/baz"
224 -- >                        -- resourcePath       == ["foo"]
225 -- >                        -- pathInfo           == ["bar", "baz"]
226 -- >                        ...
227 -- >   , ...
228 -- >   }
229 getResourcePath :: Resource [String]
230 getResourcePath = do itr <- ask
231                      return $ fromJust $ itrResourcePath itr
232
233
234 -- |This is an analogy of CGI PATH_INFO. Its result is always @[]@ if
235 -- the 'Network.HTTP.Lucu.Resource.Tree.ResourceDef' is not
236 -- greedy. See 'getResourcePath'.
237 getPathInfo :: Resource [String]
238 getPathInfo = do rsrcPath <- getResourcePath
239                  reqURI   <- getRequestURI
240                  let reqPathStr = uriPath reqURI
241                      reqPath    = [x | x <- splitBy (== '/') reqPathStr, x /= ""]
242                  -- rsrcPath と reqPath の共通する先頭部分を reqPath か
243                  -- ら全部取り除くと、それは PATH_INFO のやうなものにな
244                  -- る。rsrcPath は全部一致してゐるに決まってゐる(でな
245                  -- ければこの Resource が撰ばれた筈が無い)ので、
246                  -- rsrcPath の長さの分だけ削除すれば良い。
247                  return $ drop (length rsrcPath) reqPath
248
249 -- | Assume the query part of request URI as
250 -- application\/x-www-form-urlencoded, and parse it. This action
251 -- doesn't parse the request body. See 'inputForm'.
252 getQueryForm :: Resource [(String, String)]
253 getQueryForm = do reqURI <- getRequestURI
254                   return $ parseWWWFormURLEncoded $ uriQuery reqURI
255
256 -- |Get a value of given request header. Comparison of header name is
257 -- case-insensitive. Note that this action is not intended to be used
258 -- so frequently: there should be actions like 'getContentType' for
259 -- every common headers.
260 getHeader :: String -> Resource (Maybe String)
261 getHeader name = do req <- getRequest
262                     return $ H.getHeader name req
263
264 -- |Get a list of 'Network.HTTP.Lucu.MIMEType.MIMEType' enumerated on
265 -- header \"Accept\".
266 getAccept :: Resource [MIMEType]
267 getAccept = do accept <- getHeader "Accept"
268                if accept == Nothing then
269                    return []
270                  else
271                    case parseStr mimeTypeListP $ fromJust accept of
272                      (Success xs, _) -> return xs
273                      _               -> return []
274
275 -- |Get the header \"Content-Type\" as
276 -- 'Network.HTTP.Lucu.MIMEType.MIMEType'.
277 getContentType :: Resource (Maybe MIMEType)
278 getContentType = do cType <- getHeader "Content-Type"
279                     if cType == Nothing then
280                         return Nothing
281                       else
282                         case parseStr mimeTypeP $ fromJust cType of
283                           (Success t, _) -> return $ Just t
284                           _              -> return Nothing
285
286
287
288 {- ExaminingRequest 時に使用するアクション群 -}
289
290 -- |Tell the system that the 'Resource' found an entity for the
291 -- request URI. If this is a GET or HEAD request, a found entity means
292 -- a datum to be replied. If this is a PUT or DELETE request, it means
293 -- a datum which was stored for the URI up to now. It is an error to
294 -- compute 'foundEntity' if this is a POST request.
295 --
296 -- Computation of 'foundEntity' performs \"If-Match\" test or
297 -- \"If-None-Match\" test if possible. When those tests fail, the
298 -- computation of 'Resource' immediately aborts with status \"412
299 -- Precondition Failed\" or \"304 Not Modified\" depending on the
300 -- situation.
301 --
302 -- If this is a GET or HEAD request, 'foundEntity' automatically puts
303 -- \"ETag\" and \"Last-Modified\" headers into the response.
304 foundEntity :: ETag -> ClockTime -> Resource ()
305 foundEntity tag timeStamp
306     = do driftTo ExaminingRequest
307
308          method <- getMethod
309          when (method == GET || method == HEAD)
310                   $ setHeader' "Last-Modified" $ formatHTTPDateTime timeStamp
311          when (method == POST)
312                   $ abort InternalServerError []
313                         (Just "Illegal computation of foundEntity for POST request.")
314          foundETag tag
315
316          driftTo GettingBody
317
318 -- |Tell the system that the 'Resource' found an entity for the
319 -- request URI. The only difference from 'foundEntity' is that
320 -- 'foundETag' doesn't (and can't) put \"Last-Modified\" header into
321 -- the response.
322 --
323 -- This action is not preferred. You should use 'foundEntity' when
324 -- possible.
325 foundETag :: ETag -> Resource ()
326 foundETag tag
327     = do driftTo ExaminingRequest
328       
329          method <- getMethod
330          when (method == GET || method == HEAD)
331                   $ setHeader' "ETag" $ show tag
332          when (method == POST)
333                   $ abort InternalServerError []
334                         (Just "Illegal computation of foundETag for POST request.")
335
336          -- If-Match があればそれを見る。
337          ifMatch <- getHeader "If-Match"
338          case ifMatch of
339            Nothing   -> return ()
340            Just "*"  -> return ()
341            Just list -> case parseStr eTagListP list of
342                           (Success tags, _)
343                               -- tags の中に一致するものが無ければ
344                               -- PreconditionFailed で終了。
345                               -> when (not $ any (== tag) tags)
346                                  $ abort PreconditionFailed []
347                                        $ Just ("The entity tag doesn't match: " ++ list)
348                           _   -> abort BadRequest [] $ Just ("Unparsable If-Match: " ++ fromJust ifMatch)
349
350          let statusForNoneMatch = if method == GET || method == HEAD then
351                                       NotModified
352                                   else
353                                       PreconditionFailed
354
355          -- If-None-Match があればそれを見る。
356          ifNoneMatch <- getHeader "If-None-Match"
357          case ifNoneMatch of
358            Nothing   -> return ()
359            Just "*"  -> abort statusForNoneMatch [] $ Just ("The entity tag matches: *")
360            Just list -> case parseStr eTagListP list of
361                           (Success tags, _)
362                               -> when (any (== tag) tags)
363                                  $ abort statusForNoneMatch [] $ Just ("The entity tag matches: " ++ list)
364                           _   -> abort BadRequest [] $ Just ("Unparsable If-None-Match: " ++ list)
365
366          driftTo GettingBody
367
368 -- |Tell the system that the 'Resource' found an entity for the
369 -- request URI. The only difference from 'foundEntity' is that
370 -- 'foundTimeStamp' performs \"If-Modified-Since\" test or
371 -- \"If-Unmodified-Since\" test instead of \"If-Match\" test or
372 -- \"If-None-Match\" test. Be aware that any tests based on last
373 -- modification time are unsafe because it is possible to mess up such
374 -- tests by modifying the entity twice in a second.
375 --
376 -- This action is not preferred. You should use 'foundEntity' when
377 -- possible.
378 foundTimeStamp :: ClockTime -> Resource ()
379 foundTimeStamp timeStamp
380     = do driftTo ExaminingRequest
381
382          method <- getMethod
383          when (method == GET || method == HEAD)
384                   $ setHeader' "Last-Modified" $ formatHTTPDateTime timeStamp
385          when (method == POST)
386                   $ abort InternalServerError []
387                         (Just "Illegal computation of foundTimeStamp for POST request.")
388
389          let statusForIfModSince = if method == GET || method == HEAD then
390                                        NotModified
391                                    else
392                                        PreconditionFailed
393
394          -- If-Modified-Since があればそれを見る。
395          ifModSince <- getHeader "If-Modified-Since"
396          case ifModSince of
397            Just str -> case parseHTTPDateTime str of
398                          Just lastTime
399                              -> when (timeStamp <= lastTime)
400                                 $ abort statusForIfModSince []
401                                       $ Just ("The entity has not been modified since " ++ str)
402                          Nothing
403                              -> return () -- 不正な時刻は無視
404            Nothing  -> return ()
405
406          -- If-Unmodified-Since があればそれを見る。
407          ifUnmodSince <- getHeader "If-Unmodified-Since"
408          case ifUnmodSince of
409            Just str -> case parseHTTPDateTime str of
410                          Just lastTime
411                              -> when (timeStamp > lastTime)
412                                 $ abort PreconditionFailed []
413                                       $ Just  ("The entity has not been modified since " ++ str)
414                          Nothing
415                              -> return () -- 不正な時刻は無視
416            Nothing  -> return ()
417
418          driftTo GettingBody
419
420 -- | Computation of @'foundNoEntity' mStr@ tells the system that the
421 -- 'Resource' found no entity for the request URI. @mStr@ is an
422 -- optional error message to be replied to the client.
423 --
424 -- If this is a PUT request, 'foundNoEntity' performs \"If-Match\"
425 -- test and aborts with status \"412 Precondition Failed\" when it
426 -- failed. If this is a GET, HEAD, POST or DELETE request,
427 -- 'foundNoEntity' always aborts with status \"404 Not Found\".
428 foundNoEntity :: Maybe String -> Resource ()
429 foundNoEntity msgM
430     = do driftTo ExaminingRequest
431
432          method <- getMethod
433          when (method /= PUT)
434                   $ abort NotFound [] msgM
435
436          -- エンティティが存在しないと云ふ事は、"*" も含めたどのやうな
437          -- If-Match: 條件も滿たさない。
438          ifMatch <- getHeader "If-Match"
439          when (ifMatch /= Nothing)
440                   $ abort PreconditionFailed [] msgM
441
442          driftTo GettingBody
443
444
445 {- GettingBody 時に使用するアクション群 -}
446
447 -- | Computation of @'input' limit@ attempts to read the request body
448 -- up to @limit@ bytes, and then make the 'Resource' transit to
449 -- /Deciding Header/ state. When the actual size of body is larger
450 -- than @limit@ bytes, computation of 'Resource' immediately aborts
451 -- with status \"413 Request Entity Too Large\". When the request has
452 -- no body, 'input' returns an empty string.
453 --
454 -- @limit@ may be less than or equal to zero. In this case, the
455 -- default limitation value
456 -- ('Network.HTTP.Lucu.Config.cnfMaxEntityLength') is used. See
457 -- 'defaultLimit'.
458 --
459 -- Note that 'inputBS' is more efficient than 'input' so you should
460 -- use it whenever possible.
461 input :: Int -> Resource String
462 input limit = inputBS limit >>= return . B.unpack
463
464
465 -- | This is mostly the same as 'input' but is more
466 -- efficient. 'inputBS' returns a lazy ByteString but it's not really
467 -- lazy: reading from the socket just happens at the computation of
468 -- 'inputBS', not at the lazy evaluation of the ByteString. The same
469 -- goes for 'inputChunkBS'.
470 inputBS :: Int -> Resource ByteString
471 inputBS limit
472     = do driftTo GettingBody
473          itr     <- ask
474          hasBody <- liftIO $ atomically $ readItr itr itrRequestHasBody id
475          chunk   <- if hasBody then
476                         askForInput itr
477                     else
478                         do driftTo DecidingHeader
479                            return B.empty
480          return chunk
481     where
482       askForInput :: Interaction -> Resource ByteString
483       askForInput itr
484           = do let defaultLimit = cnfMaxEntityLength $ itrConfig itr
485                    actualLimit  = if limit <= 0 then
486                                       defaultLimit
487                                   else
488                                       limit
489                when (actualLimit <= 0)
490                         $ fail ("inputBS: limit must be positive: " ++ show actualLimit)
491                -- Reader にリクエスト
492                liftIO $ atomically
493                           $ do chunkLen <- readItr itr itrReqChunkLength id
494                                writeItr itr itrWillReceiveBody True
495                                if fmap (> actualLimit) chunkLen == Just True then
496                                    -- 受信前から多過ぎる事が分かってゐる
497                                    tooLarge actualLimit
498                                  else
499                                    writeItr itr itrReqBodyWanted $ Just actualLimit
500                -- 應答を待つ。トランザクションを分けなければ當然デッドロック。
501                chunk <- liftIO $ atomically
502                         $ do chunk       <- readItr itr itrReceivedBody id
503                              chunkIsOver <- readItr itr itrReqChunkIsOver id
504                              if B.length chunk < fromIntegral actualLimit then
505                                  -- 要求された量に滿たなくて、まだ殘り
506                                  -- があるなら再試行。
507                                  unless chunkIsOver
508                                             $ retry
509                                else
510                                  -- 制限値一杯まで讀むやうに指示したの
511                                  -- にまだ殘ってゐるなら、それは多過ぎ
512                                  -- る。
513                                  unless chunkIsOver
514                                             $ tooLarge actualLimit
515                              -- 成功。itr 内にチャンクを置いたままにす
516                              -- るとメモリの無駄になるので除去。
517                              writeItr itr itrReceivedBody B.empty
518                              return chunk
519                driftTo DecidingHeader
520                return chunk
521
522       tooLarge :: Int -> STM ()
523       tooLarge lim = abortSTM RequestEntityTooLarge []
524                      $ Just ("Request body must be smaller than "
525                              ++ show lim ++ " bytes.")
526          
527 -- | Computation of @'inputChunk' limit@ attempts to read a part of
528 -- request body up to @limit@ bytes. You can read any large request by
529 -- repeating computation of this action. When you've read all the
530 -- request body, 'inputChunk' returns an empty string and then make
531 -- the 'Resource' transit to /Deciding Header/ state.
532 --
533 -- @limit@ may be less than or equal to zero. In this case, the
534 -- default limitation value
535 -- ('Network.HTTP.Lucu.Config.cnfMaxEntityLength') is used. See
536 -- 'defaultLimit'.
537 --
538 -- Note that 'inputChunkBS' is more efficient than 'inputChunk' so you
539 -- should use it whenever possible.
540 inputChunk :: Int -> Resource String
541 inputChunk limit = inputChunkBS limit >>= return . B.unpack
542
543
544 -- | This is mostly the same as 'inputChunk' but is more
545 -- efficient. See 'inputBS'.
546 inputChunkBS :: Int -> Resource ByteString
547 inputChunkBS limit
548     = do driftTo GettingBody
549          itr <- ask
550          hasBody <- liftIO $ atomically $ readItr itr itrRequestHasBody id
551          chunk   <- if hasBody then
552                         askForInput itr
553                     else
554                         do driftTo DecidingHeader
555                            return B.empty
556          return chunk
557     where
558       askForInput :: Interaction -> Resource ByteString
559       askForInput itr
560           = do let defaultLimit = cnfMaxEntityLength $ itrConfig itr
561                    actualLimit  = if limit < 0 then
562                                       defaultLimit
563                                   else
564                                       limit
565                when (actualLimit <= 0)
566                         $ fail ("inputChunkBS: limit must be positive: " ++ show actualLimit)
567                -- Reader にリクエスト
568                liftIO $ atomically
569                           $ do writeItr itr itrReqBodyWanted $ Just actualLimit
570                                writeItr itr itrWillReceiveBody True
571                -- 應答を待つ。トランザクションを分けなければ當然デッドロック。
572                chunk <- liftIO $ atomically
573                         $ do chunk <- readItr itr itrReceivedBody id
574                              -- 要求された量に滿たなくて、まだ殘りがあ
575                              -- るなら再試行。
576                              when (B.length chunk < fromIntegral actualLimit)
577                                       $ do chunkIsOver <- readItr itr itrReqChunkIsOver id
578                                            unless chunkIsOver
579                                                       $ retry
580                              -- 成功
581                              writeItr itr itrReceivedBody B.empty
582                              return chunk
583                when (B.null chunk)
584                         $ driftTo DecidingHeader
585                return chunk
586
587 -- | Computation of @'inputForm' limit@ attempts to read the request
588 -- body with 'input' and parse it as
589 -- application\/x-www-form-urlencoded. If the request header
590 -- \"Content-Type\" is not application\/x-www-form-urlencoded,
591 -- 'inputForm' makes 'Resource' abort with status \"415 Unsupported
592 -- Media Type\". If the request has no \"Content-Type\", it aborts
593 -- with \"400 Bad Request\".
594 --
595 -- This action should also support multipart\/form-data somehow, but
596 -- it is not (yet) done.
597 inputForm :: Int -> Resource [(String, String)]
598 inputForm limit
599     = do cTypeM <- getContentType
600          case cTypeM of
601            Nothing
602                -> abort BadRequest [] (Just "Missing Content-Type")
603            Just (MIMEType "application" "x-www-form-urlencoded" _)
604                -> readWWWFormURLEncoded
605            Just (MIMEType "multipart" "form-data" _)
606                -> readMultipartFormData
607            Just cType
608                -> abort UnsupportedMediaType [] (Just $ "Unsupported media type: "
609                                                           ++ show cType)
610     where
611       readWWWFormURLEncoded
612           = do src <- input limit
613                return $ parseWWWFormURLEncoded src
614
615       readMultipartFormData -- FIXME: 未對應
616           = abort UnsupportedMediaType []
617             (Just $ "Sorry, inputForm does not currently support multipart/form-data.")
618
619 -- | This is just a constant -1. It's better to say @'input'
620 -- 'defaultLimit'@ than to say @'input' (-1)@ but these are exactly
621 -- the same.
622 defaultLimit :: Int
623 defaultLimit = (-1)
624
625
626
627 {- DecidingHeader 時に使用するアクション群 -}
628
629 -- | Set the response status code. If you omit to compute this action,
630 -- the status code will be defaulted to \"200 OK\".
631 setStatus :: StatusCode -> Resource ()
632 setStatus code
633     = do driftTo DecidingHeader
634          itr <- ask
635          liftIO $ atomically $ updateItr itr itrResponse
636                     $ \ res -> res {
637                                  resStatus = code
638                                }
639
640 -- | Set a value of given resource header. Comparison of header name
641 -- is case-insensitive. Note that this action is not intended to be
642 -- used so frequently: there should be actions like 'setContentType'
643 -- for every common headers.
644 --
645 -- Some important headers (especially \"Content-Length\" and
646 -- \"Transfer-Encoding\") may be silently dropped or overwritten by
647 -- the system not to corrupt the interaction with client at the
648 -- viewpoint of HTTP protocol layer. For instance, if we are keeping
649 -- the connection alive, without this process it causes a catastrophe
650 -- to send a header \"Content-Length: 10\" and actually send a body of
651 -- 20 bytes long. In this case the client shall only accept the first
652 -- 10 bytes of response body and thinks that the residual 10 bytes is
653 -- a part of header of the next response.
654 setHeader :: String -> String -> Resource ()
655 setHeader name value
656     = driftTo DecidingHeader >> setHeader' name value
657          
658
659 setHeader' :: String -> String -> Resource()
660 setHeader' name value
661     = do itr <- ask
662          liftIO $ atomically
663                     $ updateItr itr itrResponse
664                           $ H.setHeader name value
665
666 -- | Computation of @'redirect' code uri@ sets the response status to
667 -- @code@ and \"Location\" header to @uri@. @code@ must satisfy
668 -- 'Network.HTTP.Lucu.Response.isRedirection' or it causes an error.
669 redirect :: StatusCode -> URI -> Resource ()
670 redirect code uri
671     = do when (code == NotModified || not (isRedirection code))
672                   $ abort InternalServerError []
673                         $ Just ("Attempted to redirect with status " ++ show code)
674          setStatus code
675          setLocation uri
676
677 -- | Computation of @'setContentType' mType@ sets the response header
678 -- \"Content-Type\" to @mType@.
679 setContentType :: MIMEType -> Resource ()
680 setContentType mType
681     = setHeader "Content-Type" $ show mType
682
683 -- | Computation of @'setLocation' uri@ sets the response header
684 -- \"Location\" to @uri@.
685 setLocation :: URI -> Resource ()
686 setLocation uri
687     = setHeader "Location" $ uriToString id uri $ ""
688
689
690 {- DecidingBody 時に使用するアクション群 -}
691
692 -- | Computation of @'output' str@ writes @str@ as a response body,
693 -- and then make the 'Resource' transit to /Done/ state. It is safe to
694 -- apply 'output' to an infinite string, such as a lazy stream of
695 -- \/dev\/random.
696 --
697 -- Note that 'outputBS' is more efficient than 'output' so you should
698 -- use it whenever possible.
699 output :: String -> Resource ()
700 output = outputBS . B.pack
701
702 -- | This is mostly the same as 'output' but is more efficient.
703 outputBS :: ByteString -> Resource ()
704 outputBS str = do outputChunkBS str
705                   driftTo Done
706
707 -- | Computation of @'outputChunk' str@ writes @str@ as a part of
708 -- response body. You can compute this action multiple times to write
709 -- a body little at a time. It is safe to apply 'outputChunk' to an
710 -- infinite string.
711 --
712 -- Note that 'outputChunkBS' is more efficient than 'outputChunk' so
713 -- you should use it whenever possible.
714 outputChunk :: String -> Resource ()
715 outputChunk = outputChunkBS . B.pack
716
717 -- | This is mostly the same as 'outputChunk' but is more efficient.
718 outputChunkBS :: ByteString -> Resource ()
719 outputChunkBS str
720     = do driftTo DecidingBody
721          itr <- ask
722          
723          let limit = cnfMaxOutputChunkLength $ itrConfig itr
724          when (limit <= 0)
725                   $ fail ("cnfMaxOutputChunkLength must be positive: "
726                           ++ show limit)
727
728          discardBody <- liftIO $ atomically $
729                         readItr itr itrWillDiscardBody id
730
731          unless (discardBody)
732                     $ sendChunks str limit
733
734          unless (B.null str)
735                     $ liftIO $ atomically $
736                       writeItr itr itrBodyIsNull False
737     where
738       {- チャンクの大きさは Config で制限されてゐる。もし例へば
739          /dev/zero を B.readFile して作った ByteString をそのまま
740          ResponseWriter に渡したりすると大變な事が起こる。何故なら
741          ResponseWriter はTransfer-Encoding: chunked の時、ヘッダを書く
742          爲にチャンクの大きさを測るから、その時に起こるであらう事は言ふ
743          までも無い。 -}
744       sendChunks :: ByteString -> Int -> Resource ()
745       sendChunks str limit
746           | B.null str = return ()
747           | otherwise  = do let (chunk, remaining) = B.splitAt (fromIntegral limit) str
748                             itr <- ask
749                             liftIO $ atomically $ 
750                                    do buf <- readItr itr itrBodyToSend id
751                                       if B.null buf then
752                                           -- バッファが消化された
753                                           writeItr itr itrBodyToSend chunk
754                                         else
755                                           -- 消化されるのを待つ
756                                           retry
757                             -- 殘りのチャンクについて繰り返す
758                             sendChunks remaining limit
759
760 {-
761
762   [GettingBody からそれ以降の状態に遷移する時]
763   
764   body を讀み終へてゐなければ、殘りの body を讀み捨てる。
765
766
767   [DecidingHeader からそれ以降の状態に遷移する時]
768
769   postprocess する。
770
771
772   [Done に遷移する時]
773
774   bodyIsNull が False ならば何もしない。True だった場合は出力補完す
775   る。
776
777 -}
778
779 driftTo :: InteractionState -> Resource ()
780 driftTo newState
781     = do itr <- ask
782          liftIO $ atomically $ do oldState <- readItr itr itrState id
783                                   if newState < oldState then
784                                       throwStateError oldState newState
785                                     else
786                                       do let a = [oldState .. newState]
787                                              b = tail a
788                                              c = zip a b
789                                          mapM_ (uncurry $ drift itr) c
790                                          writeItr itr itrState newState
791     where
792       throwStateError :: Monad m => InteractionState -> InteractionState -> m a
793
794       throwStateError Done DecidingBody
795           = fail "It makes no sense to output something after finishing to output."
796
797       throwStateError old new
798           = fail ("state error: " ++ show old ++ " ==> " ++ show new)
799
800
801       drift :: Interaction -> InteractionState -> InteractionState -> STM ()
802
803       drift itr GettingBody _
804           = writeItr itr itrReqBodyWasteAll True
805
806       drift itr DecidingHeader _
807           = postprocess itr
808
809       drift itr _ Done
810           = do bodyIsNull <- readItr itr itrBodyIsNull id
811                when bodyIsNull
812                         $ writeDefaultPage itr
813
814       drift _ _ _
815           = return ()