Skip to content

Commit e1e1027

Browse files
committed
WIP errors and cancelling
1 parent d1c8e2e commit e1e1027

File tree

9 files changed

+110
-90
lines changed

9 files changed

+110
-90
lines changed

packages.dhall

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -99,9 +99,7 @@ in upstream
9999
-------------------------------
100100
-}
101101
let upstream =
102-
https://github.com/purescript/package-sets/releases/download/psc-0.15.2-20220621/packages.dhall
103-
sha256:78caab14e4d8ff3886a057f0380c2d4a2500e2ee7ab5c1d32a0f9ce5c71eedd8
102+
https://github.com/purescript/package-sets/releases/download/psc-0.15.4-20221102/packages.dhall
103+
sha256:8628e413718876ce26983db1d0ce9d9e1588129117fa3bb8ed9f618db6914127
104104

105105
in upstream
106-
107-
with node-streams-aff = /home/jbrock/work/me/purescript-node-streams-aff/spago.dhall as Location

spago.dhall

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,13 @@ to generate this file without the comments in this block.
3434
, "options"
3535
, "partial"
3636
, "prelude"
37+
, "record"
3738
, "st"
3839
, "strings"
3940
, "transformers"
4041
, "tuples"
4142
, "unsafe-coerce"
43+
, "web-fetch"
4244
]
4345
, packages = ./packages.dhall
4446
, sources = [ "src/**/*.purs", "test/**/*.purs" ]

src/Node/HTTP2.purs

Lines changed: 31 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
-- | Bindings to the [*Node.js* HTTP/2](https://nodejs.org/docs/latest/api/http2.html) API.
22
-- |
3+
-- | Requires *Node.js v14.17.0* (for
4+
-- | [`AbortController`](https://nodejs.org/docs/latest/api/globals.html#class-abortcontroller))
5+
-- |
36
-- | The __Node.HTTP2.Client__ and __Node.HTTP2.Server__ modules provide a
47
-- | low-level `Effect` callback API.
58
-- |
@@ -13,11 +16,9 @@ module Node.HTTP2
1316
, headerString
1417
, headerArray
1518
, headerStatus
16-
, OptionsObject
19+
, Options
1720
, toOptions
1821
, Flags
19-
, toStringUTF8
20-
, fromStringUTF8
2122
)
2223
where
2324

@@ -27,19 +28,26 @@ import Control.Monad.Except (runExcept)
2728
import Data.Either (hush)
2829
import Data.Maybe (Maybe)
2930
import Data.Traversable (traverse)
30-
import Effect.Class (class MonadEffect, liftEffect)
3131
import Foreign (Foreign, readArray, readInt, readString, unsafeToForeign)
3232
import Foreign.Index (readProp)
33-
import Foreign.Object (Object, keys)
34-
import Node.Buffer (Buffer)
35-
import Node.Buffer as Buffer
36-
import Node.Encoding as Encoding
33+
import Foreign.Object (Object, keys, union)
3734
import Unsafe.Coerce (unsafeCoerce)
3835

3936
-- | HTTP headers object. Construct with the `toHeaders` function.
4037
-- |
4138
-- | https://nodejs.org/docs/latest/api/http2.html#headers-object
42-
foreign import data Headers :: Type
39+
-- |
40+
-- | The `Monoid` instance allows us to merge `Headers`. The headers in the
41+
-- | first, left-side `Headers` will override the second `Headers`.
42+
newtype Headers = Headers Foreign
43+
44+
instance Semigroup Headers where
45+
append l r = unsafeCoerce $ union (unsafeCoerce l) (unsafeCoerce r)
46+
47+
instance Monoid Headers where
48+
mempty = unsafeCoerce {}
49+
50+
4351

4452
-- | https://nodejs.org/docs/latest/api/http2.html#headers-object
4553
-- |
@@ -63,7 +71,7 @@ foreign import data Headers :: Type
6371
-- | }
6472
-- | ```
6573
toHeaders :: forall r. Record r -> Headers
66-
toHeaders = unsafeCoerce
74+
toHeaders = Headers <<< unsafeToForeign
6775

6876
-- | Get all of the keys from a `Headers`.
6977
-- |
@@ -94,19 +102,21 @@ headerStatus h = hush $ runExcept do
94102
-- | https://httpwg.org/specs/rfc7540.html#FrameHeader
95103
type Flags = Int
96104

97-
-- | A type alias for `Foreign` which represents an `Object` with option properties.
105+
-- | A type which represents an `Object` with option properties.
98106
-- |
99107
-- | The “no options” literal is `toOptions {}`.
100-
type OptionsObject = Foreign
108+
-- |
109+
-- | The `Monoid` instance allows us to merge `Options`s. The options
110+
-- | in the first, left-side `Options` will override the
111+
-- | second `Options`.
112+
newtype Options = Options Foreign
101113

102-
-- | Use this function to construct an `OptionsObject`.
103-
toOptions :: forall r. Record r -> OptionsObject
104-
toOptions = unsafeToForeign
114+
instance Semigroup Options where
115+
append l r = unsafeCoerce $ union (unsafeCoerce l) (unsafeCoerce r)
105116

106-
-- | Concatenate an `Array` of UTF-8 encoded `Buffer`s into a `String`.
107-
toStringUTF8 :: forall m. MonadEffect m => Array Buffer -> m String
108-
toStringUTF8 bs = liftEffect $ Buffer.toString Encoding.UTF8 =<< Buffer.concat bs
117+
instance Monoid Options where
118+
mempty = toOptions {}
109119

110-
-- | Encode a `String` as an `Array` containing one UTF-8 encoded `Buffer`.
111-
fromStringUTF8 :: forall m. MonadEffect m => String -> m (Array Buffer)
112-
fromStringUTF8 s = liftEffect $ map pure $ Buffer.fromString s Encoding.UTF8
120+
-- | Use this function to construct an `Options`.
121+
toOptions :: forall r. Record r -> Options
122+
toOptions = Options <<< unsafeToForeign

src/Node/HTTP2/Client.Aff.purs

Lines changed: 29 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -44,19 +44,22 @@ import Prelude
4444

4545
import Data.Either (Either(..))
4646
import Effect.Aff (Aff, effectCanceler, makeAff, nonCanceler)
47-
import Node.HTTP2 (Headers, OptionsObject)
47+
import Effect.Exception (catchException)
48+
import Node.HTTP2 (Headers, Options, toOptions)
4849
import Node.HTTP2.Client (ClientHttp2Session, ClientHttp2Stream)
4950
import Node.HTTP2.Client (ClientHttp2Session, ClientHttp2Stream, toDuplex) as ReClient
5051
import Node.HTTP2.Client as Client
5152
import Node.URL (URL)
53+
import Web.Fetch.AbortController as Web.Fetch.AbortController
5254

5355
-- | Connect a client `Http2Session`.
5456
-- |
5557
-- | See [`http2.connect(authority[, options][, listener])`](https://nodejs.org/docs/latest/api/http2.html#http2connectauthority-options-listener)
56-
connect :: OptionsObject -> URL -> Aff ClientHttp2Session
57-
connect optionsobject url = makeAff \complete -> do
58-
void $ Client.connect url optionsobject
59-
(\session _ -> complete (Right session))
58+
connect :: Options -> URL -> Aff ClientHttp2Session
59+
connect options url = makeAff \complete -> do
60+
catchException (complete <<< Left) do
61+
void $ Client.connect url options
62+
(\session _ -> complete (Right session))
6063
pure nonCanceler
6164

6265
-- | Gracefully closes the `Http2Session`, allowing any existing streams
@@ -66,7 +69,8 @@ connect optionsobject url = makeAff \complete -> do
6669
-- | See [`http2session.close([callback])`](https://nodejs.org/docs/latest/api/http2.html#http2sessionclosecallback)
6770
close :: ClientHttp2Session -> Aff Unit
6871
close session = makeAff \complete -> do
69-
Client.close session (complete (Right unit))
72+
catchException (complete <<< Left) do
73+
Client.close session (complete (Right unit))
7074
pure nonCanceler
7175

7276
-- | Send request headers to the connected server.
@@ -83,22 +87,26 @@ close session = makeAff \complete -> do
8387
-- | `waitResponse`
8488
-- | and
8589
-- | [`Node.Stream.Aff.readAll`](https://pursuit.purescript.org/packages/purescript-node-streams-aff/docs/Node.Stream.Aff#v:readAll).
86-
request :: ClientHttp2Session -> OptionsObject -> Headers -> Aff ClientHttp2Stream
87-
request session optionsobject headers = makeAff \complete -> do
88-
-- how do we get errors out of the request? this is just a guess.
89-
onceErrorCancel <- Client.onceErrorSession session \err -> complete (Left err)
90-
stream <- Client.request session headers optionsobject
91-
onceErrorCancel
92-
complete (Right stream)
93-
pure $ effectCanceler do
94-
-- TODO signal <AbortSignal> An AbortSignal that may be used to abort an ongoing request.
95-
onceErrorCancel
90+
request :: ClientHttp2Session -> Options -> Headers -> Aff ClientHttp2Stream
91+
request session options headers = makeAff \complete -> do
92+
-- how do we get errors out of the request?
93+
-- by catching exceptions? this is just a guess.
94+
-- or maybe like this?
95+
--
96+
-- onceErrorCancel <- Client.onceErrorSession session \err -> complete (Left err)
97+
--
98+
abortcontroller <- Web.Fetch.AbortController.new
99+
let abortsignal = Web.Fetch.AbortController.signal abortcontroller
100+
stream <- catchException (pure <<< Left) do
101+
Right <$> Client.request session headers (toOptions {"signal":abortsignal} <> options)
102+
complete stream
103+
pure $ effectCanceler do
104+
Web.Fetch.AbortController.abort abortcontroller
96105

97106
-- | Wait to receive response headers from the server.
98107
waitResponse :: ClientHttp2Stream -> Aff Headers
99108
waitResponse stream = makeAff \complete -> do
100109
onceResponseCancel <- Client.onceResponse stream \headers _ -> do
101-
-- TODO error?
102110
complete (Right headers)
103111
pure $ effectCanceler do
104112
onceResponseCancel
@@ -118,10 +126,12 @@ waitPush
118126
waitPush session = makeAff \complete -> do
119127
onceStreamCancel <- Client.onceStream session \streamPushed headersRequest _ -> do
120128
_oncePushCancel <- Client.oncePush streamPushed \headersResponse _ ->
121-
complete (Right { headersRequest, headersResponse, streamPushed})
129+
complete (Right {headersRequest, headersResponse, streamPushed})
122130
pure unit
123131
pure $ effectCanceler do
124-
-- TODO _oncePushCancel
132+
-- I'm not sure how to _oncePushCancel.
133+
-- I guess we don't need to _oncePushCancel because we won't really be
134+
-- waiting on oncePush?
125135
onceStreamCancel
126136

127137
-- | Wait for an additional block of headers to be received from a stream,

src/Node/HTTP2/Client.purs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ import Prelude
5858
import Effect (Effect)
5959
import Effect.Exception (Error)
6060
import Node.Buffer (Buffer)
61-
import Node.HTTP2 (Flags, Headers, OptionsObject)
61+
import Node.HTTP2 (Flags, Headers, Options)
6262
import Node.HTTP2.Internal as Internal
6363
import Node.Net.Socket (Socket)
6464
import Node.Stream (Duplex)
@@ -71,15 +71,15 @@ import Unsafe.Coerce (unsafeCoerce)
7171
foreign import data ClientHttp2Session :: Type
7272

7373
-- | https://nodejs.org/docs/latest/api/http2.html#http2connectauthority-options-listener
74-
foreign import connect :: URL -> OptionsObject -> (ClientHttp2Session -> Socket -> Effect Unit) -> Effect ClientHttp2Session
74+
foreign import connect :: URL -> Options -> (ClientHttp2Session -> Socket -> Effect Unit) -> Effect ClientHttp2Session
7575

7676
-- | A client-side `Http2Stream`.
7777
-- |
7878
-- | See [__Class: ClientHttp2Stream__](https://nodejs.org/docs/latest/api/http2.html#class-clienthttp2stream)
7979
foreign import data ClientHttp2Stream :: Type
8080

8181
-- |https://nodejs.org/docs/latest/api/http2.html#clienthttp2sessionrequestheaders-options
82-
foreign import request :: ClientHttp2Session -> Headers -> OptionsObject -> Effect ClientHttp2Stream
82+
foreign import request :: ClientHttp2Session -> Headers -> Options -> Effect ClientHttp2Stream
8383

8484
-- | https://nodejs.org/docs/latest/api/http2.html#destruction
8585
foreign import destroy :: ClientHttp2Stream -> Effect Unit
@@ -122,7 +122,7 @@ foreign import oncePush :: ClientHttp2Stream -> (Headers -> Flags -> Effect Unit
122122
-- -- | https://nodejs.org/docs/latest/api/http2.html#http2streamrespondheaders-options
123123
-- -- |
124124
-- -- | https://www.rfc-editor.org/rfc/rfc7540#section-8.2.2
125-
-- respond :: ClientHttp2Stream -> Headers -> OptionsObject -> Effect Unit
125+
-- respond :: ClientHttp2Stream -> Headers -> Options -> Effect Unit
126126
-- respond = unsafeCoerce Internal.respond
127127

128128
-- | https://nodejs.org/docs/latest/api/http2.html#event-trailers

src/Node/HTTP2/Internal.purs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import Effect (Effect)
99
import Effect.Exception (Error)
1010
import Foreign (Foreign)
1111
import Node.Buffer (Buffer)
12-
import Node.HTTP2 (Flags, Headers, OptionsObject)
12+
import Node.HTTP2 (Flags, Headers, Options)
1313
import Node.HTTP2.Constants (NGHTTP2)
1414

1515
-- | Private type which can be coerced into `ClientHttp2Session`
@@ -76,7 +76,7 @@ foreign import data Http2Stream :: Type
7676
foreign import onceClose :: Http2Stream -> (NGHTTP2 -> Effect Unit) -> Effect (Effect Unit)
7777

7878
-- | https://nodejs.org/docs/latest/api/http2.html#http2streamrespondheaders-options
79-
foreign import respond :: Http2Stream -> Headers -> OptionsObject -> Effect Unit
79+
foreign import respond :: Http2Stream -> Headers -> Options -> Effect Unit
8080

8181
-- | https://nodejs.org/docs/latest/api/http2.html#event-wanttrailers
8282
foreign import onceWantTrailers :: Http2Stream -> Effect Unit -> Effect (Effect Unit)

src/Node/HTTP2/Server.Aff.purs

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ import Data.Nullable (toMaybe)
4747
import Effect.Aff (Aff, Error, effectCanceler, launchAff_, makeAff, nonCanceler)
4848
import Effect.Class (liftEffect)
4949
import Effect.Exception (catchException)
50-
import Node.HTTP2 (Headers, OptionsObject)
50+
import Node.HTTP2 (Headers, Options)
5151
import Node.HTTP2.Server (Http2SecureServer, Http2Server, ServerHttp2Stream)
5252
import Node.HTTP2.Server (Http2Server, Http2SecureServer, ServerHttp2Stream, toDuplex) as ReServer
5353
import Node.HTTP2.Server as Server
@@ -57,10 +57,10 @@ import Node.HTTP2.Server as Server
5757
-- |
5858
-- | Waits until the listening socket is open and then returns the `Http2Server`.
5959
-- |
60-
-- | The first `OptionsObject` is the `createServer` options.
60+
-- | The first `Options` is the `createServer` options.
6161
-- | See [`http2.createServer([options][, onRequestHandler])`](https://nodejs.org/docs/latest/api/http2.html#http2createserveroptions-onrequesthandler)
6262
-- |
63-
-- | The second `OptionsObject` is the `listen` options.
63+
-- | The second `Options` is the `listen` options.
6464
-- | See [`server.listen(options[, callback])`](https://nodejs.org/docs/latest/api/net.html#serverlistenoptions-callback)
6565
-- |
6666
-- | For each new client connection and request, the handler function will
@@ -69,8 +69,8 @@ import Node.HTTP2.Server as Server
6969
-- | For errors raised by the listening socket, there is an error handling
7070
-- | function which will also be invoked by `forkAff`.
7171
listen
72-
:: OptionsObject
73-
-> OptionsObject
72+
:: Options
73+
-> Options
7474
-> (Http2Server -> Error -> Aff Unit)
7575
-> (Http2Server -> Headers -> ServerHttp2Stream -> Aff Unit)
7676
-> Aff Http2Server
@@ -97,8 +97,8 @@ listen optionsserver optionslisten errorhandler handler = makeAff \complete -> d
9797
-- |
9898
-- | Secure version of `listen`.
9999
listenSecure
100-
:: OptionsObject
101-
-> OptionsObject
100+
:: Options
101+
-> Options
102102
-> (Http2SecureServer -> Error -> Aff Unit)
103103
-> (Http2SecureServer -> Headers -> ServerHttp2Stream -> Aff Unit)
104104
-> Aff Http2SecureServer
@@ -129,7 +129,7 @@ listenSecure optionsserver optionslisten errorhandler handler = makeAff \complet
129129
-- |
130130
-- | See
131131
-- | [`http2stream.respond([headers[, options]])`](https://nodejs.org/docs/latest/api/http2.html#http2streamrespondheaders-options)
132-
respond :: ServerHttp2Stream -> OptionsObject -> Headers -> Aff Unit
132+
respond :: ServerHttp2Stream -> Options -> Headers -> Aff Unit
133133
respond stream options headers = makeAff \complete -> do
134134
catchException (complete <<< Left) do
135135
Server.respond stream headers options
@@ -164,9 +164,9 @@ closeSecure server = makeAff \complete -> do
164164
-- | See [`http2stream.pushStream(headers[, options], callback)`](https://nodejs.org/docs/latest/api/http2.html#http2streampushstreamheaders-options-callback)
165165
-- |
166166
-- | > Calling `http2stream.pushStream()` from within a pushed stream is not permitted and will throw an error.
167-
pushStream :: ServerHttp2Stream -> OptionsObject -> Headers -> Aff ServerHttp2Stream
168-
pushStream stream optionsobject headersRequest = makeAff \complete -> do
169-
Server.pushStream stream headersRequest optionsobject \nerr pushedstream _ -> do
167+
pushStream :: ServerHttp2Stream -> Options -> Headers -> Aff ServerHttp2Stream
168+
pushStream stream options headersRequest = makeAff \complete -> do
169+
Server.pushStream stream headersRequest options \nerr pushedstream _ -> do
170170
case toMaybe nerr of
171171
Just err -> complete (Left err)
172172
Nothing -> complete (Right pushedstream)

src/Node/HTTP2/Server.purs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ import Prelude
6161
import Data.Nullable (Nullable)
6262
import Effect (Effect)
6363
import Effect.Exception (Error)
64-
import Node.HTTP2 (Flags, Headers, OptionsObject)
64+
import Node.HTTP2 (Flags, Headers, Options)
6565
import Node.HTTP2.Internal as Internal
6666
import Node.Stream (Duplex)
6767
import Unsafe.Coerce (unsafeCoerce)
@@ -72,10 +72,10 @@ import Unsafe.Coerce (unsafeCoerce)
7272
foreign import data Http2Server :: Type
7373

7474
-- | https://nodejs.org/docs/latest/api/http2.html#http2createserveroptions-onrequesthandler
75-
foreign import createServer :: OptionsObject -> Effect Http2Server
75+
foreign import createServer :: Options -> Effect Http2Server
7676

7777
-- | https://nodejs.org/docs/latest/api/net.html#serverlistenoptions-callback
78-
foreign import listen :: Http2Server -> OptionsObject -> Effect Unit -> Effect Unit
78+
foreign import listen :: Http2Server -> Options -> Effect Unit -> Effect Unit
7979

8080
-- | https://nodejs.org/docs/latest/api/http2.html#serverclosecallback
8181
closeServer :: Http2Server -> Effect Unit -> Effect Unit
@@ -89,10 +89,10 @@ foreign import data Http2SecureServer :: Type
8989
-- | https://nodejs.org/docs/latest/api/http2.html#http2createsecureserveroptions-onrequesthandler
9090
-- |
9191
-- | Required options: `key :: String`, `cert :: String`.
92-
foreign import createSecureServer :: OptionsObject -> Effect Http2SecureServer
92+
foreign import createSecureServer :: Options -> Effect Http2SecureServer
9393

9494
-- | https://nodejs.org/docs/latest/api/net.html#serverlistenoptions-callback
95-
listenSecure :: Http2SecureServer -> OptionsObject -> Effect Unit -> Effect Unit
95+
listenSecure :: Http2SecureServer -> Options -> Effect Unit -> Effect Unit
9696
listenSecure = unsafeCoerce listen
9797

9898
-- | https://nodejs.org/docs/latest/api/http2.html#serverclosecallback
@@ -150,7 +150,7 @@ onStreamSecure :: Http2SecureServer -> (ServerHttp2Stream -> Headers -> Flags ->
150150
onStreamSecure = unsafeCoerce Internal.onStream
151151

152152
-- | https://nodejs.org/docs/latest/api/http2.html#http2streamrespondheaders-options
153-
respond :: ServerHttp2Stream -> Headers -> OptionsObject -> Effect Unit
153+
respond :: ServerHttp2Stream -> Headers -> Options -> Effect Unit
154154
respond = unsafeCoerce Internal.respond
155155

156156
-- | https://nodejs.org/docs/latest/api/http2.html#event-session_1
@@ -167,7 +167,7 @@ foreign import data ServerHttp2Stream :: Type
167167
-- | > Calling `http2stream.pushStream()` from within a pushed stream is not permitted and will throw an error.
168168
-- |
169169
-- | https://www.rfc-editor.org/rfc/rfc7540#section-8.2.1
170-
foreign import pushStream :: ServerHttp2Stream -> Headers -> OptionsObject -> (Nullable Error -> ServerHttp2Stream -> Headers -> Effect Unit) -> Effect Unit
170+
foreign import pushStream :: ServerHttp2Stream -> Headers -> Options -> (Nullable Error -> ServerHttp2Stream -> Headers -> Effect Unit) -> Effect Unit
171171

172172
-- | https://nodejs.org/docs/latest/api/http2.html#http2streampushallowed
173173
foreign import pushAllowed :: ServerHttp2Stream -> Effect Boolean

0 commit comments

Comments
 (0)