Skip to content

Commit 2fb1e83

Browse files
committed
Add client module
1 parent 0bf5bf5 commit 2fb1e83

File tree

6 files changed

+276
-8
lines changed

6 files changed

+276
-8
lines changed

bower.json

+4-3
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,17 @@
99
"bower_components",
1010
"output"
1111
],
12-
"repository": {
13-
"type": "git",
14-
"url": "git://github.com/purescript-node/purescript-node-http.git"
12+
"repository": {
13+
"type": "git",
14+
"url": "git://github.com/purescript-node/purescript-node-http.git"
1515
},
1616
"devDependencies": {
1717
"purescript-console": "^0.1.0"
1818
},
1919
"dependencies": {
2020
"purescript-maps": "^0.5.0",
2121
"purescript-node-streams": "^0.1.1",
22+
"purescript-options": "~0.5.1",
2223
"purescript-unsafe-coerce": "^0.1.0"
2324
}
2425
}

docs/Node/HTTP/Client.md

+136
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
## Module Node.HTTP.Client
2+
3+
This module defines low-level bindings to the Node HTTP client.
4+
5+
#### `Request`
6+
7+
``` purescript
8+
data Request :: *
9+
```
10+
11+
A HTTP request object
12+
13+
#### `Response`
14+
15+
``` purescript
16+
data Response :: *
17+
```
18+
19+
A HTTP response object
20+
21+
#### `RequestHeaders`
22+
23+
``` purescript
24+
newtype RequestHeaders
25+
```
26+
27+
A HTTP request object
28+
29+
##### Instances
30+
``` purescript
31+
instance requestHeadersIsOption :: IsOption RequestHeaders
32+
```
33+
34+
#### `RequestOptions`
35+
36+
``` purescript
37+
data RequestOptions
38+
```
39+
40+
The type of HTTP request options
41+
42+
#### `protocol`
43+
44+
``` purescript
45+
protocol :: Option RequestOptions String
46+
```
47+
48+
The protocol to use
49+
50+
#### `hostname`
51+
52+
``` purescript
53+
hostname :: Option RequestOptions String
54+
```
55+
56+
Domain name or IP
57+
58+
#### `port`
59+
60+
``` purescript
61+
port :: Option RequestOptions Int
62+
```
63+
64+
Port of remote server
65+
66+
#### `method`
67+
68+
``` purescript
69+
method :: Option RequestOptions String
70+
```
71+
72+
The HTTP request method: GET, POST, etc.
73+
74+
#### `path`
75+
76+
``` purescript
77+
path :: Option RequestOptions String
78+
```
79+
80+
The request path, including query string if appropriate.
81+
82+
#### `headers`
83+
84+
``` purescript
85+
headers :: Option RequestOptions String
86+
```
87+
88+
#### `auth`
89+
90+
``` purescript
91+
auth :: Option RequestOptions String
92+
```
93+
94+
Basic authentication
95+
96+
#### `request`
97+
98+
``` purescript
99+
request :: forall eff. Options RequestOptions -> (Response -> Eff (http :: HTTP | eff) Unit) -> Eff (http :: HTTP | eff) Request
100+
```
101+
102+
Make a HTTP request using the specified options and response callback.
103+
104+
#### `requestAsStream`
105+
106+
``` purescript
107+
requestAsStream :: forall eff r a. Request -> Writable r (http :: HTTP | eff) a
108+
```
109+
110+
Create a writable stream from a request object.
111+
112+
#### `responseAsStream`
113+
114+
``` purescript
115+
responseAsStream :: forall eff w a. Response -> Readable w (http :: HTTP | eff) a
116+
```
117+
118+
Create a readable stream from a response object.
119+
120+
#### `httpVersion`
121+
122+
``` purescript
123+
httpVersion :: Response -> String
124+
```
125+
126+
Get the request HTTP version
127+
128+
#### `responseHeaders`
129+
130+
``` purescript
131+
responseHeaders :: Response -> StrMap String
132+
```
133+
134+
Get the response headers as a hash
135+
136+

src/Node/HTTP/Client.js

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
"use strict";
2+
3+
// module Node.HTTP.Client
4+
5+
var http = require('http');
6+
7+
exports.requestImpl = function(opts) {
8+
return function(k) {
9+
return function() {
10+
return http.request(opts, function(res) {
11+
k(res)();
12+
});
13+
};
14+
};
15+
};

src/Node/HTTP/Client.purs

+98
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
-- | This module defines low-level bindings to the Node HTTP client.
2+
3+
module Node.HTTP.Client
4+
( Request()
5+
, Response()
6+
, RequestHeaders()
7+
, RequestOptions()
8+
, protocol
9+
, hostname
10+
, port
11+
, method
12+
, path
13+
, headers
14+
, auth
15+
, request
16+
, requestAsStream
17+
, responseAsStream
18+
, httpVersion
19+
, responseHeaders
20+
) where
21+
22+
import Prelude
23+
24+
import Data.Foreign
25+
import Data.Options
26+
import Data.StrMap (StrMap())
27+
28+
import Node.HTTP (HTTP())
29+
import Node.Stream
30+
31+
import Control.Monad.Eff
32+
33+
import Unsafe.Coerce (unsafeCoerce)
34+
35+
-- | A HTTP request object
36+
foreign import data Request :: *
37+
38+
-- | A HTTP response object
39+
foreign import data Response :: *
40+
41+
-- | A HTTP request object
42+
newtype RequestHeaders = RequestHeaders (StrMap String)
43+
44+
instance requestHeadersIsOption :: IsOption RequestHeaders where
45+
assoc k v = assoc (optionFn k) (unsafeCoerce v :: {})
46+
47+
-- | The type of HTTP request options
48+
data RequestOptions
49+
50+
-- | The protocol to use
51+
protocol :: Option RequestOptions String
52+
protocol = opt "protocol"
53+
54+
-- | Domain name or IP
55+
hostname :: Option RequestOptions String
56+
hostname = opt "hostname"
57+
58+
-- | Port of remote server
59+
port :: Option RequestOptions Int
60+
port = opt "port"
61+
62+
-- | The HTTP request method: GET, POST, etc.
63+
method :: Option RequestOptions String
64+
method = opt "method"
65+
66+
-- | The request path, including query string if appropriate.
67+
path :: Option RequestOptions String
68+
path = opt "path"
69+
70+
headers :: Option RequestOptions String
71+
headers = opt "headers"
72+
73+
-- | Basic authentication
74+
auth :: Option RequestOptions String
75+
auth = opt "auth"
76+
77+
-- | Make a HTTP request using the specified options and response callback.
78+
foreign import requestImpl :: forall eff. Foreign -> (Response -> Eff (http :: HTTP | eff) Unit) -> Eff (http :: HTTP | eff) Request
79+
80+
-- | Make a HTTP request using the specified options and response callback.
81+
request :: forall eff. Options RequestOptions -> (Response -> Eff (http :: HTTP | eff) Unit) -> Eff (http :: HTTP | eff) Request
82+
request = requestImpl <<< options
83+
84+
-- | Create a writable stream from a request object.
85+
requestAsStream :: forall eff r a. Request -> Writable r (http :: HTTP | eff) a
86+
requestAsStream = unsafeCoerce
87+
88+
-- | Create a readable stream from a response object.
89+
responseAsStream :: forall eff w a. Response -> Readable w (http :: HTTP | eff) a
90+
responseAsStream = unsafeCoerce
91+
92+
-- | Get the request HTTP version
93+
httpVersion :: Response -> String
94+
httpVersion = _.httpVersion <<< unsafeCoerce
95+
96+
-- | Get the response headers as a hash
97+
responseHeaders :: Response -> StrMap String
98+
responseHeaders = _.headers <<< unsafeCoerce

test/Main.js

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
"use strict";
2+
3+
// module Test.Main
4+
5+
exports.stdout = process.stdout;

test/Main.purs

+18-5
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,27 @@ module Test.Main where
22

33
import Prelude
44

5+
import Data.Foldable (foldMap)
6+
import Data.Options
7+
58
import Control.Monad.Eff.Console
69

710
import Node.HTTP
11+
import qualified Node.HTTP.Client as Client
812
import Node.Stream
913
import Node.Encoding
1014

15+
foreign import stdout :: forall eff r a. Writable r eff a
16+
1117
main = do
1218
server <- createServer respond
13-
listen server 8080 do
19+
listen server 8080 $ void do
1420
log "Listening on port 8080."
21+
req <- Client.request (Client.hostname := "localhost" <> Client.port := 8080) \response -> void do
22+
log "Response from GET /:"
23+
let responseStream = Client.responseAsStream response
24+
pipe responseStream stdout
25+
end (Client.requestAsStream req) (return unit)
1526
where
1627
respond req res = do
1728
setStatusCode res 200
@@ -20,10 +31,12 @@ main = do
2031
log (requestMethod req <> " " <> requestURL req)
2132
case requestMethod req of
2233
"GET" -> do
23-
let html = "<form method='POST' action='/'>"
24-
<> " <input name='text' type='text'>"
25-
<> " <input type='submit'>"
26-
<> "</form>"
34+
let html = foldMap (<> "\n")
35+
[ "<form method='POST' action='/'>"
36+
, " <input name='text' type='text'>"
37+
, " <input type='submit'>"
38+
, "</form>"
39+
]
2740
setHeader res "Content-Type" "text/html"
2841
writeString outputStream UTF8 html(return unit)
2942
end outputStream (return unit)

0 commit comments

Comments
 (0)