1
1
use cirru_edn:: Edn ;
2
2
use std:: collections:: HashMap ;
3
3
use std:: sync:: Arc ;
4
- use tiny_http:: { Response , Server } ;
4
+ use tiny_http:: { Method , Response , Server } ;
5
+
6
+ struct HttpServerOptions {
7
+ port : u16 ,
8
+ host : String ,
9
+ }
10
+
11
+ struct ResponseSkeleton {
12
+ code : u8 ,
13
+ headers : HashMap < String , String > ,
14
+ body : String ,
15
+ }
16
+
17
+ #[ no_mangle]
18
+ pub fn abi_version ( ) -> String {
19
+ String :: from ( "0.0.1" )
20
+ }
5
21
6
22
#[ no_mangle]
7
23
pub fn serve_http (
8
24
args : Vec < Edn > ,
9
- handler : Arc < dyn Fn ( Edn ) -> Result < Edn , String > > ,
25
+ handler : Arc < dyn Fn ( Vec < Edn > ) -> Result < Edn , String > > ,
26
+ _finish : Box < dyn FnOnce ( ) > ,
10
27
) -> Result < Edn , String > {
11
- println ! ( "TODO args: {:?}" , args) ;
12
- let server = Server :: http ( "0.0.0.0:8000" ) . unwrap ( ) ;
28
+ if args. is_empty ( ) {
29
+ return Err ( format ! ( "expected an option, got nothing: {:?}" , args) ) ;
30
+ }
31
+ let options = parse_options ( & args[ 0 ] ) ?;
32
+ let server = Server :: http ( & format ! ( "{}:{}" , options. host, options. port) ) . unwrap ( ) ;
33
+ println ! ( "Server started at {}:{}" , options. host, options. port) ;
13
34
14
- for request in server. incoming_requests ( ) {
35
+ for mut request in server. incoming_requests ( ) {
15
36
// println!(
16
37
// "received request! method: {:?}, url: {:?}, headers: {:?}",
17
38
// request.method(),
@@ -22,18 +43,117 @@ pub fn serve_http(
22
43
let mut m: HashMap < Edn , Edn > = HashMap :: new ( ) ;
23
44
m. insert (
24
45
Edn :: Keyword ( String :: from ( "method" ) ) ,
25
- Edn :: Str ( request. method ( ) . to_string ( ) ) ,
46
+ Edn :: Keyword ( request. method ( ) . to_string ( ) ) ,
26
47
) ;
27
48
m. insert (
28
49
Edn :: Keyword ( String :: from ( "url" ) ) ,
29
50
Edn :: Str ( request. url ( ) . to_string ( ) ) ,
30
51
) ;
52
+
53
+ let mut headers: HashMap < Edn , Edn > = HashMap :: new ( ) ;
54
+
55
+ for pair in request. headers ( ) {
56
+ headers. insert (
57
+ Edn :: Keyword ( pair. field . to_string ( ) ) ,
58
+ Edn :: Str ( pair. value . to_string ( ) ) ,
59
+ ) ;
60
+ }
61
+ m. insert ( Edn :: Keyword ( String :: from ( "headers" ) ) , Edn :: Map ( headers) ) ;
62
+
63
+ if request. method ( ) != & Method :: Get {
64
+ let mut content = String :: new ( ) ;
65
+ request. as_reader ( ) . read_to_string ( & mut content) . unwrap ( ) ;
66
+ m. insert (
67
+ Edn :: Keyword ( String :: from ( "body" ) ) ,
68
+ Edn :: Str ( content. to_string ( ) ) ,
69
+ ) ;
70
+ }
71
+
31
72
let info = Edn :: Map ( m) ;
32
- let result = handler ( info) ?;
73
+ let result = handler ( vec ! [ info] ) ?;
74
+ let res = parse_response ( & result) ?;
75
+
76
+ let mut response = Response :: from_string ( res. body . to_string ( ) ) . with_status_code ( res. code ) ;
33
77
34
- let response = Response :: from_string ( result. to_string ( ) ) ;
78
+ for ( field, value) in res. headers {
79
+ response. add_header (
80
+ format ! ( "{}: {}" , field, value)
81
+ . parse :: < tiny_http:: Header > ( )
82
+ . unwrap ( ) ,
83
+ ) ;
84
+ }
35
85
request. respond ( response) . map_err ( |x| x. to_string ( ) ) ?;
36
86
}
37
87
38
88
Ok ( Edn :: Nil )
39
89
}
90
+
91
+ fn parse_options ( d : & Edn ) -> Result < HttpServerOptions , String > {
92
+ match d {
93
+ Edn :: Nil => Ok ( HttpServerOptions {
94
+ port : 4000 ,
95
+ host : String :: from ( "0.0.0.0" ) ,
96
+ } ) ,
97
+ Edn :: Map ( m) => {
98
+ let mut options = HttpServerOptions {
99
+ port : 4000 ,
100
+ host : String :: from ( "0.0.0.0" ) ,
101
+ } ;
102
+ options. port = match m. get ( & Edn :: Keyword ( String :: from ( "port" ) ) ) {
103
+ Some ( Edn :: Number ( port) ) => * port as u16 ,
104
+ None => 4000 ,
105
+ a => return Err ( format ! ( "invalid config for port: {:?}" , a) ) ,
106
+ } ;
107
+ options. host = match m. get ( & Edn :: Keyword ( String :: from ( "host" ) ) ) {
108
+ Some ( Edn :: Str ( host) ) => host. to_owned ( ) ,
109
+ None => String :: from ( "0.0.0.0" ) ,
110
+ a => return Err ( format ! ( "invalid config for host: {:?}" , a) ) ,
111
+ } ;
112
+ Ok ( options)
113
+ }
114
+ _ => Err ( format ! ( "invalid data for options: {}" , d) ) ,
115
+ }
116
+ }
117
+
118
+ /// from user response
119
+ fn parse_response ( info : & Edn ) -> Result < ResponseSkeleton , String > {
120
+ if let Edn :: Map ( m) = info {
121
+ let mut res = ResponseSkeleton {
122
+ code : 200 ,
123
+ headers : HashMap :: new ( ) ,
124
+ body : String :: from ( "" ) ,
125
+ } ;
126
+ res. code = match m. get ( & Edn :: Keyword ( String :: from ( "code" ) ) ) {
127
+ Some ( Edn :: Number ( n) ) => * n as u8 ,
128
+ None => 200 ,
129
+ a => return Err ( format ! ( "invalid code: {:?}" , a) ) ,
130
+ } ;
131
+ res. body = match m. get ( & Edn :: Keyword ( String :: from ( "body" ) ) ) {
132
+ Some ( Edn :: Str ( s) ) => s. to_owned ( ) ,
133
+ Some ( a) => a. to_string ( ) ,
134
+ None => String :: from ( "" ) ,
135
+ } ;
136
+ res. headers = match m. get ( & Edn :: Keyword ( String :: from ( "headers" ) ) ) {
137
+ Some ( Edn :: Map ( m) ) => {
138
+ let mut hs: HashMap < String , String > = HashMap :: new ( ) ;
139
+ for ( k, v) in m {
140
+ if let Edn :: Keyword ( s) = k {
141
+ if let Edn :: Str ( s2) = v {
142
+ hs. insert ( s. to_owned ( ) , s2. to_owned ( ) ) ;
143
+ } else {
144
+ hs. insert ( s. to_owned ( ) , v. to_string ( ) ) ;
145
+ }
146
+ } else {
147
+ return Err ( format ! ( "invalid head entry: {}" , k) ) ;
148
+ }
149
+ }
150
+ hs
151
+ }
152
+ Some ( a) => return Err ( format ! ( "invalid data for headers: {}" , a) ) ,
153
+ None => HashMap :: new ( ) ,
154
+ } ;
155
+ Ok ( res)
156
+ } else {
157
+ Err ( format ! ( "invalid response shape: {}" , info) )
158
+ }
159
+ }
0 commit comments