5
5
//! Support code for generated clients.
6
6
7
7
use std:: ops:: { Deref , DerefMut } ;
8
+ use std:: str:: FromStr ;
8
9
9
10
use bytes:: Bytes ;
10
11
use futures_core:: Stream ;
@@ -14,6 +15,9 @@ use serde::{de::DeserializeOwned, Serialize};
14
15
type InnerByteStream =
15
16
std:: pin:: Pin < Box < dyn Stream < Item = reqwest:: Result < Bytes > > + Send + Sync > > ;
16
17
18
+ // used for Websockets requests
19
+ type HttpBareRequest = http:: Request < ( ) > ;
20
+
17
21
/// Untyped byte stream used for both success and error responses.
18
22
pub struct ByteStream ( InnerByteStream ) ;
19
23
@@ -195,6 +199,65 @@ impl<T: std::fmt::Debug> std::fmt::Debug for ResponseValue<T> {
195
199
}
196
200
}
197
201
202
+ /// Value returned by generated client methods for Websocket channels.
203
+ ///
204
+ /// Currently, the only interface available for Dropshot websocket channels is
205
+ /// providing their raw, unmodeled form (that is, Dropshot does not yet define
206
+ /// higher-level constructs for modeling the structure of Websocket messages
207
+ /// themselves).
208
+ ///
209
+ /// The user is responsible for passing it to a websocket implementation, i.e.:
210
+ /// ```ignore
211
+ /// let (request, tcp_stream) = my_progenitor_client
212
+ /// .my_websocket_channel()
213
+ /// .answer(42)
214
+ /// .send()
215
+ /// .await?
216
+ /// .into_request_and_tcp_stream();
217
+ /// let ws_client = tokio_tungstenite::client_async(request, tcp_stream).await?;
218
+ /// ```
219
+ ///
220
+ /// (As no request has been *made*, returning a [ResponseValue] would be inappropriate.)
221
+ pub struct WebsocketReactants {
222
+ request : HttpBareRequest ,
223
+ tcp_stream : tokio:: net:: TcpStream ,
224
+ }
225
+
226
+ impl WebsocketReactants {
227
+ pub fn new < T > (
228
+ rqw : reqwest:: Request ,
229
+ tcp_stream : tokio:: net:: TcpStream ,
230
+ ) -> Result < Self , Error < T > > {
231
+ // rebuild as http::Request, which tungstenite re-exports as its
232
+ // "IntoClientRequest" type.
233
+ // FIXME: this is obviously a hack, the better thing to do would be to
234
+ // implement using http::Request::builder() in the proc macro
235
+ let mut rb = http:: Request :: builder ( )
236
+ . method ( rqw. method ( ) )
237
+ . version ( rqw. version ( ) )
238
+ . uri (
239
+ http:: Uri :: from_str ( rqw. url ( ) . as_str ( ) )
240
+ . map_err ( |e| Error :: InvalidRequest ( format ! ( "{:?}" , e) ) ) ?,
241
+ ) ;
242
+ for ( k, v) in rqw. headers ( ) . iter ( ) {
243
+ rb = rb. header ( k, v) ;
244
+ }
245
+ let request = rb
246
+ . body ( ( ) )
247
+ . map_err ( |e| Error :: InvalidRequest ( e. to_string ( ) ) ) ?;
248
+ Ok ( Self {
249
+ request,
250
+ tcp_stream,
251
+ } )
252
+ }
253
+
254
+ pub fn into_request_and_tcp_stream (
255
+ self ,
256
+ ) -> ( HttpBareRequest , tokio:: net:: TcpStream ) {
257
+ ( self . request , self . tcp_stream )
258
+ }
259
+ }
260
+
198
261
/// Error produced by generated client methods.
199
262
///
200
263
/// The type parameter may be a struct if there's a single expected error type
@@ -207,6 +270,9 @@ pub enum Error<E = ()> {
207
270
/// A server error either due to the data, or with the connection.
208
271
CommunicationError ( reqwest:: Error ) ,
209
272
273
+ /// A fundamental input/output error has occurred (e.g. unable to make a socket connection)
274
+ IoError ( std:: io:: Error ) ,
275
+
210
276
/// A documented, expected error response.
211
277
ErrorResponse ( ResponseValue < E > ) ,
212
278
@@ -225,6 +291,7 @@ impl<E> Error<E> {
225
291
match self {
226
292
Error :: InvalidRequest ( _) => None ,
227
293
Error :: CommunicationError ( e) => e. status ( ) ,
294
+ Error :: IoError ( _) => None ,
228
295
Error :: ErrorResponse ( rv) => Some ( rv. status ( ) ) ,
229
296
Error :: InvalidResponsePayload ( e) => e. status ( ) ,
230
297
Error :: UnexpectedResponse ( r) => Some ( r. status ( ) ) ,
@@ -239,6 +306,7 @@ impl<E> Error<E> {
239
306
match self {
240
307
Error :: InvalidRequest ( s) => Error :: InvalidRequest ( s) ,
241
308
Error :: CommunicationError ( e) => Error :: CommunicationError ( e) ,
309
+ Error :: IoError ( e) => Error :: IoError ( e) ,
242
310
Error :: ErrorResponse ( ResponseValue {
243
311
inner : _,
244
312
status,
@@ -262,6 +330,12 @@ impl<E> From<reqwest::Error> for Error<E> {
262
330
}
263
331
}
264
332
333
+ impl < E > From < std:: io:: Error > for Error < E > {
334
+ fn from ( e : std:: io:: Error ) -> Self {
335
+ Self :: IoError ( e)
336
+ }
337
+ }
338
+
265
339
impl < E > std:: fmt:: Display for Error < E >
266
340
where
267
341
ResponseValue < E > : ErrorFormat ,
@@ -274,6 +348,9 @@ where
274
348
Error :: CommunicationError ( e) => {
275
349
write ! ( f, "Communication Error: {}" , e)
276
350
}
351
+ Error :: IoError ( e) => {
352
+ write ! ( f, "Input/Output Error: {}" , e)
353
+ }
277
354
Error :: ErrorResponse ( rve) => {
278
355
write ! ( f, "Error Response: " ) ?;
279
356
rve. fmt_info ( f)
@@ -377,3 +454,8 @@ impl<E> RequestBuilderExt<E> for RequestBuilder {
377
454
} ) ?) )
378
455
}
379
456
}
457
+
458
+ #[ doc( hidden) ]
459
+ pub fn generate_websocket_key ( ) -> String {
460
+ base64:: encode ( rand:: random :: < [ u8 ; 16 ] > ( ) )
461
+ }
0 commit comments