@@ -16,39 +16,92 @@ socket: net.StreamServer,
16
16
17
17
/// An interface to either a plain or TLS connection.
18
18
pub const Connection = struct {
19
+ pub const buffer_size = std .crypto .tls .max_ciphertext_record_len ;
20
+ pub const Protocol = enum { plain };
21
+
19
22
stream : net.Stream ,
20
23
protocol : Protocol ,
21
24
22
25
closing : bool = true ,
23
26
24
- pub const Protocol = enum { plain };
27
+ read_buf : [buffer_size ]u8 = undefined ,
28
+ read_start : u16 = 0 ,
29
+ read_end : u16 = 0 ,
25
30
26
- pub fn read (conn : * Connection , buffer : []u8 ) ReadError ! usize {
31
+ pub fn rawReadAtLeast (conn : * Connection , buffer : []u8 , len : usize ) ReadError ! usize {
27
32
return switch (conn .protocol ) {
28
- .plain = > conn .stream .read (buffer ),
29
- // .tls => return conn.tls_client.read(conn.stream, buffer),
30
- } catch | err | switch (err ) {
31
- error .ConnectionTimedOut = > return error .ConnectionTimedOut ,
32
- error .ConnectionResetByPeer , error .BrokenPipe = > return error .ConnectionResetByPeer ,
33
- else = > return error .UnexpectedReadFailure ,
33
+ .plain = > conn .stream .readAtLeast (buffer , len ),
34
+ // .tls => conn.tls_client.readAtLeast(conn.stream, buffer, len),
35
+ } catch | err | {
36
+ switch (err ) {
37
+ error .ConnectionResetByPeer , error .BrokenPipe = > return error .ConnectionResetByPeer ,
38
+ else = > return error .UnexpectedReadFailure ,
39
+ }
34
40
};
35
41
}
36
42
43
+ pub fn fill (conn : * Connection ) ReadError ! void {
44
+ if (conn .read_end != conn .read_start ) return ;
45
+
46
+ const nread = try conn .rawReadAtLeast (conn .read_buf [0.. ], 1 );
47
+ if (nread == 0 ) return error .EndOfStream ;
48
+ conn .read_start = 0 ;
49
+ conn .read_end = @intCast (u16 , nread );
50
+ }
51
+
52
+ pub fn peek (conn : * Connection ) []const u8 {
53
+ return conn .read_buf [conn .read_start .. conn .read_end ];
54
+ }
55
+
56
+ pub fn drop (conn : * Connection , num : u16 ) void {
57
+ conn .read_start += num ;
58
+ }
59
+
37
60
pub fn readAtLeast (conn : * Connection , buffer : []u8 , len : usize ) ReadError ! usize {
38
- return switch (conn .protocol ) {
39
- .plain = > conn .stream .readAtLeast (buffer , len ),
40
- // .tls => return conn.tls_client.readAtLeast(conn.stream, buffer, len),
41
- } catch | err | switch (err ) {
42
- error .ConnectionTimedOut = > return error .ConnectionTimedOut ,
43
- error .ConnectionResetByPeer , error .BrokenPipe = > return error .ConnectionResetByPeer ,
44
- else = > return error .UnexpectedReadFailure ,
45
- };
61
+ assert (len <= buffer .len );
62
+
63
+ var out_index : u16 = 0 ;
64
+ while (out_index < len ) {
65
+ const available_read = conn .read_end - conn .read_start ;
66
+ const available_buffer = buffer .len - out_index ;
67
+
68
+ if (available_read > available_buffer ) { // partially read buffered data
69
+ @memcpy (buffer [out_index .. ], conn .read_buf [conn .read_start .. conn .read_end ][0.. available_buffer ]);
70
+ out_index += @intCast (u16 , available_buffer );
71
+ conn .read_start += @intCast (u16 , available_buffer );
72
+
73
+ break ;
74
+ } else if (available_read > 0 ) { // fully read buffered data
75
+ @memcpy (buffer [out_index .. ][0.. available_read ], conn .read_buf [conn .read_start .. conn .read_end ]);
76
+ out_index += available_read ;
77
+ conn .read_start += available_read ;
78
+
79
+ if (out_index >= len ) break ;
80
+ }
81
+
82
+ const leftover_buffer = available_buffer - available_read ;
83
+ const leftover_len = len - out_index ;
84
+
85
+ if (leftover_buffer > conn .read_buf .len ) {
86
+ // skip the buffer if the output is large enough
87
+ return conn .rawReadAtLeast (buffer [out_index .. ], leftover_len );
88
+ }
89
+
90
+ try conn .fill ();
91
+ }
92
+
93
+ return out_index ;
94
+ }
95
+
96
+ pub fn read (conn : * Connection , buffer : []u8 ) ReadError ! usize {
97
+ return conn .readAtLeast (buffer , 1 );
46
98
}
47
99
48
100
pub const ReadError = error {
49
101
ConnectionTimedOut ,
50
102
ConnectionResetByPeer ,
51
103
UnexpectedReadFailure ,
104
+ EndOfStream ,
52
105
};
53
106
54
107
pub const Reader = std .io .Reader (* Connection , ReadError , read );
@@ -93,112 +146,6 @@ pub const Connection = struct {
93
146
}
94
147
};
95
148
96
- /// A buffered (and peekable) Connection.
97
- pub const BufferedConnection = struct {
98
- pub const buffer_size = std .crypto .tls .max_ciphertext_record_len ;
99
-
100
- conn : Connection ,
101
- read_buf : [buffer_size ]u8 = undefined ,
102
- read_start : u16 = 0 ,
103
- read_end : u16 = 0 ,
104
-
105
- write_buf : [buffer_size ]u8 = undefined ,
106
- write_end : u16 = 0 ,
107
-
108
- pub fn fill (bconn : * BufferedConnection ) ReadError ! void {
109
- if (bconn .read_end != bconn .read_start ) return ;
110
-
111
- const nread = try bconn .conn .read (bconn .read_buf [0.. ]);
112
- if (nread == 0 ) return error .EndOfStream ;
113
- bconn .read_start = 0 ;
114
- bconn .read_end = @intCast (u16 , nread );
115
- }
116
-
117
- pub fn peek (bconn : * BufferedConnection ) []const u8 {
118
- return bconn .read_buf [bconn .read_start .. bconn .read_end ];
119
- }
120
-
121
- pub fn drop (bconn : * BufferedConnection , num : u16 ) void {
122
- bconn .read_start += num ;
123
- }
124
-
125
- pub fn readAtLeast (bconn : * BufferedConnection , buffer : []u8 , len : usize ) ReadError ! usize {
126
- var out_index : u16 = 0 ;
127
- while (out_index < len ) {
128
- const available = bconn .read_end - bconn .read_start ;
129
- const left = buffer .len - out_index ;
130
-
131
- if (available > 0 ) {
132
- const can_read = @intCast (u16 , @min (available , left ));
133
-
134
- @memcpy (buffer [out_index .. ][0.. can_read ], bconn .read_buf [bconn .read_start .. ][0.. can_read ]);
135
- out_index += can_read ;
136
- bconn .read_start += can_read ;
137
-
138
- continue ;
139
- }
140
-
141
- if (left > bconn .read_buf .len ) {
142
- // skip the buffer if the output is large enough
143
- return bconn .conn .read (buffer [out_index .. ]);
144
- }
145
-
146
- try bconn .fill ();
147
- }
148
-
149
- return out_index ;
150
- }
151
-
152
- pub fn read (bconn : * BufferedConnection , buffer : []u8 ) ReadError ! usize {
153
- return bconn .readAtLeast (buffer , 1 );
154
- }
155
-
156
- pub const ReadError = Connection .ReadError || error {EndOfStream };
157
- pub const Reader = std .io .Reader (* BufferedConnection , ReadError , read );
158
-
159
- pub fn reader (bconn : * BufferedConnection ) Reader {
160
- return Reader { .context = bconn };
161
- }
162
-
163
- pub fn writeAll (bconn : * BufferedConnection , buffer : []const u8 ) WriteError ! void {
164
- if (bconn .write_buf .len - bconn .write_end >= buffer .len ) {
165
- @memcpy (bconn .write_buf [bconn .write_end .. ][0.. buffer .len ], buffer );
166
- bconn .write_end += @intCast (u16 , buffer .len );
167
- } else {
168
- try bconn .flush ();
169
- try bconn .conn .writeAll (buffer );
170
- }
171
- }
172
-
173
- pub fn write (bconn : * BufferedConnection , buffer : []const u8 ) WriteError ! usize {
174
- if (bconn .write_buf .len - bconn .write_end >= buffer .len ) {
175
- @memcpy (bconn .write_buf [bconn .write_end .. ][0.. buffer .len ], buffer );
176
- bconn .write_end += @intCast (u16 , buffer .len );
177
-
178
- return buffer .len ;
179
- } else {
180
- try bconn .flush ();
181
- return try bconn .conn .write (buffer );
182
- }
183
- }
184
-
185
- pub fn flush (bconn : * BufferedConnection ) WriteError ! void {
186
- defer bconn .write_end = 0 ;
187
- return bconn .conn .writeAll (bconn .write_buf [0.. bconn .write_end ]);
188
- }
189
-
190
- pub const WriteError = Connection .WriteError ;
191
- pub const Writer = std .io .Writer (* BufferedConnection , WriteError , write );
192
-
193
- pub fn writer (bconn : * BufferedConnection ) Writer {
194
- return Writer { .context = bconn };
195
- }
196
-
197
- pub fn close (bconn : * BufferedConnection ) void {
198
- bconn .conn .close ();
199
- }
200
- };
201
-
202
149
/// The mode of transport for responses.
203
150
pub const ResponseTransfer = union (enum ) {
204
151
content_length : u64 ,
@@ -351,7 +298,7 @@ pub const Response = struct {
351
298
352
299
allocator : Allocator ,
353
300
address : net.Address ,
354
- connection : BufferedConnection ,
301
+ connection : Connection ,
355
302
356
303
headers : http.Headers ,
357
304
request : Request ,
@@ -388,7 +335,7 @@ pub const Response = struct {
388
335
389
336
if (! res .request .parser .done ) {
390
337
// If the response wasn't fully read, then we need to close the connection.
391
- res .connection .conn . closing = true ;
338
+ res .connection .closing = true ;
392
339
return .closing ;
393
340
}
394
341
@@ -402,9 +349,9 @@ pub const Response = struct {
402
349
const req_connection = res .request .headers .getFirstValue ("connection" );
403
350
const req_keepalive = req_connection != null and ! std .ascii .eqlIgnoreCase ("close" , req_connection .? );
404
351
if (req_keepalive and (res_keepalive or res_connection == null )) {
405
- res .connection .conn . closing = false ;
352
+ res .connection .closing = false ;
406
353
} else {
407
- res .connection .conn . closing = true ;
354
+ res .connection .closing = true ;
408
355
}
409
356
410
357
switch (res .request .compression ) {
@@ -434,14 +381,14 @@ pub const Response = struct {
434
381
.parser = res .request .parser ,
435
382
};
436
383
437
- if (res .connection .conn . closing ) {
384
+ if (res .connection .closing ) {
438
385
return .closing ;
439
386
} else {
440
387
return .reset ;
441
388
}
442
389
}
443
390
444
- pub const DoError = BufferedConnection .WriteError || error { UnsupportedTransferEncoding , InvalidContentLength };
391
+ pub const DoError = Connection .WriteError || error { UnsupportedTransferEncoding , InvalidContentLength };
445
392
446
393
/// Send the response headers.
447
394
pub fn do (res : * Response ) ! void {
@@ -450,7 +397,8 @@ pub const Response = struct {
450
397
.first , .start , .responded , .finished = > unreachable ,
451
398
}
452
399
453
- const w = res .connection .writer ();
400
+ var buffered = std .io .bufferedWriter (res .connection .writer ());
401
+ const w = buffered .writer ();
454
402
455
403
try w .writeAll (@tagName (res .version ));
456
404
try w .writeByte (' ' );
@@ -508,10 +456,10 @@ pub const Response = struct {
508
456
509
457
try w .writeAll ("\r \n " );
510
458
511
- try res . connection .flush ();
459
+ try buffered .flush ();
512
460
}
513
461
514
- pub const TransferReadError = BufferedConnection .ReadError || proto .HeadersParser .ReadError ;
462
+ pub const TransferReadError = Connection .ReadError || proto .HeadersParser .ReadError ;
515
463
516
464
pub const TransferReader = std .io .Reader (* Response , TransferReadError , transferRead );
517
465
@@ -532,7 +480,7 @@ pub const Response = struct {
532
480
return index ;
533
481
}
534
482
535
- pub const WaitError = BufferedConnection .ReadError || proto .HeadersParser .CheckCompleteHeadError || Request .ParseError || error { CompressionInitializationFailed , CompressionNotSupported };
483
+ pub const WaitError = Connection .ReadError || proto .HeadersParser .CheckCompleteHeadError || Request .ParseError || error { CompressionInitializationFailed , CompressionNotSupported };
536
484
537
485
/// Wait for the client to send a complete request head.
538
486
pub fn wait (res : * Response ) WaitError ! void {
@@ -637,7 +585,7 @@ pub const Response = struct {
637
585
return index ;
638
586
}
639
587
640
- pub const WriteError = BufferedConnection .WriteError || error { NotWriteable , MessageTooLong };
588
+ pub const WriteError = Connection .WriteError || error { NotWriteable , MessageTooLong };
641
589
642
590
pub const Writer = std .io .Writer (* Response , WriteError , write );
643
591
@@ -692,8 +640,6 @@ pub const Response = struct {
692
640
.content_length = > | len | if (len != 0 ) return error .MessageNotCompleted ,
693
641
.none = > {},
694
642
}
695
-
696
- try res .connection .flush ();
697
643
}
698
644
};
699
645
@@ -742,10 +688,10 @@ pub fn accept(server: *Server, options: AcceptOptions) AcceptError!Response {
742
688
return Response {
743
689
.allocator = options .allocator ,
744
690
.address = in .address ,
745
- .connection = .{ . conn = .{
691
+ .connection = .{
746
692
.stream = in .stream ,
747
693
.protocol = .plain ,
748
- } } ,
694
+ },
749
695
.headers = .{ .allocator = options .allocator },
750
696
.request = .{
751
697
.version = undefined ,
0 commit comments