Skip to content

Commit 8513de9

Browse files
committed
use buffers
1 parent ac66388 commit 8513de9

4 files changed

Lines changed: 100 additions & 62 deletions

File tree

src/ewe.gleam

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -479,10 +479,7 @@ pub fn start(
479479
let information = information.worker(builder.information_name)
480480

481481
let glisten_supervisor =
482-
glisten.new(
483-
fn(conn) { #(http_.transform_connection(conn), None) },
484-
handler_.loop(handler, on_crash),
485-
)
482+
glisten.new(fn(_conn) { #(Nil, None) }, handler_.loop(handler, on_crash))
486483
|> glisten.bind(builder.interface)
487484
|> fn(glisten_builder) {
488485
case builder.ipv6 {

src/ewe/internal/buffer.gleam

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import gleam/bit_array
2+
import gleam/int
3+
4+
pub type Buffer {
5+
Buffer(data: BitArray, remaining: Int)
6+
}
7+
8+
pub fn new(initial: BitArray) -> Buffer {
9+
Buffer(initial, 0)
10+
}
11+
12+
pub fn sized(buffer: Buffer, size: Int) -> Buffer {
13+
Buffer(buffer.data, size)
14+
}
15+
16+
pub fn empty() -> Buffer {
17+
Buffer(<<>>, 0)
18+
}
19+
20+
pub fn append(buffer: Buffer, data: BitArray) -> Buffer {
21+
let remaining = int.max(0, buffer.remaining - bit_array.byte_size(data))
22+
Buffer(<<buffer.data:bits, data:bits>>, remaining)
23+
}
24+
25+
pub fn append_size(buffer: Buffer, data: BitArray, size: Int) -> Buffer {
26+
let remaining = int.max(0, buffer.remaining - size)
27+
Buffer(<<buffer.data:bits, data:bits>>, remaining)
28+
}

src/ewe/internal/handler.gleam

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import gleam/result
1111
import glisten
1212
import glisten/transport
1313

14+
import ewe/internal/buffer
1415
import ewe/internal/encoder
1516
import ewe/internal/exception
1617
import ewe/internal/http.{
@@ -42,14 +43,15 @@ type Next {
4243
pub fn loop(
4344
handler: fn(Request(Connection)) -> Response(ResponseBody),
4445
on_crash: Response(ResponseBody),
45-
) -> glisten.Loop(Connection, a) {
46-
fn(http_conn, msg, _conn) {
46+
) -> glisten.Loop(Nil, a) {
47+
fn(state, msg, conn) {
4748
let assert glisten.Packet(msg) = msg
49+
let http_conn = http_.transform_connection(conn)
4850

49-
http_.parse_request(http_conn, msg)
51+
http_.parse_request(http_conn, buffer.new(msg))
5052
|> result.map(fn(req) {
5153
case call_handler(req, handler, on_crash) {
52-
Continue -> glisten.continue(http_conn)
54+
Continue -> glisten.continue(state)
5355
Stop(Normal) -> glisten.stop()
5456
Stop(Abnormal(reason)) -> glisten.stop_abnormal(reason)
5557
}

src/ewe/internal/http.gleam

Lines changed: 65 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import glisten/transport
2828

2929
import gramps/websocket as ws
3030

31+
import ewe/internal/buffer.{type Buffer}
3132
import ewe/internal/decoder.{
3233
AbsPath, HttpBin, HttpEoh, HttpHeader, HttpRequest, HttphBin, More, Packet,
3334
}
@@ -74,7 +75,7 @@ pub type Connection {
7475
Connection(
7576
transport: transport.Transport,
7677
socket: socket.Socket,
77-
buffer: BitArray,
78+
buffer: buffer.Buffer,
7879
http_version: option.Option(HttpVersion),
7980
)
8081
}
@@ -99,9 +100,9 @@ pub type UpgradeWebsocketError {
99100

100101
// Chunked body parsing result
101102
type BodyChunk {
102-
Done(rest: BitArray)
103+
Done(rest: Buffer)
103104
Incomplete
104-
Chunk(BitArray, rest: BitArray)
105+
Chunk(BitArray, rest: Buffer)
105106
}
106107

107108
// -----------------------------------------------------------------------------
@@ -120,20 +121,20 @@ pub fn transform_connection(connection: glisten.Connection(a)) -> Connection {
120121
Connection(
121122
transport: connection.transport,
122123
socket: connection.socket,
123-
buffer: <<>>,
124+
buffer: buffer.empty(),
124125
http_version: option.None,
125126
)
126127
}
127128

128129
/// Parses an HTTP request from the given buffer
129130
pub fn parse_request(
130131
connection: Connection,
131-
buffer: BitArray,
132+
buffer: Buffer,
132133
) -> Result(Request(Connection), ParseError) {
133134
let transport = connection.transport
134135
let socket = connection.socket
135136

136-
case decoder.decode_packet(HttpBin, buffer, []) {
137+
case decoder.decode_packet(HttpBin, buffer.data, []) {
137138
Ok(Packet(HttpRequest(atom_method, AbsPath(target), version), rest)) -> {
138139
// Request Line
139140
use method <- try(
@@ -157,7 +158,7 @@ pub fn parse_request(
157158
use #(headers, rest) <- try(parse_headers(
158159
transport,
159160
socket,
160-
rest,
161+
buffer.new(rest),
161162
dict.new(),
162163
))
163164

@@ -185,7 +186,7 @@ pub fn parse_request(
185186
headers: dict.to_list(headers),
186187
body: Connection(
187188
..connection,
188-
buffer: rest,
189+
buffer: buffer.new(rest),
189190
http_version: option.Some(version),
190191
),
191192
scheme:,
@@ -198,15 +199,16 @@ pub fn parse_request(
198199
Ok(More(size)) -> {
199200
let read_size = option.unwrap(size, 0)
200201

201-
use buffer <- try(read_from_socket(
202+
let sized_buffer = buffer.sized(connection.buffer, read_size)
203+
204+
use new_buffer <- try(read_from_socket(
202205
transport,
203206
socket,
204-
amount: read_size,
205-
buffer: connection.buffer,
207+
sized_buffer,
206208
on_error: MalformedRequest,
207209
))
208210

209-
parse_request(connection, buffer)
211+
parse_request(connection, new_buffer)
210212
}
211213
_ -> Error(PacketDiscard)
212214
}
@@ -228,7 +230,7 @@ pub fn read_body(
228230

229231
case transfer_encoding {
230232
Ok("chunked") -> {
231-
use #(body, rest) <- try(read_chunked_body(
233+
use #(body, rest_buffer) <- try(read_chunked_body(
232234
transport,
233235
socket,
234236
req.body.buffer,
@@ -248,7 +250,7 @@ pub fn read_body(
248250
set.insert(set, string.trim(field) |> string.lowercase())
249251
})
250252

251-
Ok(handle_trailers(req, set, rest))
253+
Ok(handle_trailers(req, set, rest_buffer))
252254
}
253255
Error(Nil) -> Ok(req)
254256
}
@@ -261,19 +263,19 @@ pub fn read_body(
261263

262264
use <- bool.guard(content_length > size_limit, Error(BodyTooLarge))
263265

264-
let left = content_length - bit_array.byte_size(req.body.buffer)
266+
let left = content_length - bit_array.byte_size(req.body.buffer.data)
265267

266268
case content_length, left {
267269
0, 0 -> Ok(<<>>)
268-
0, _l | _cl, 0 -> Ok(req.body.buffer)
270+
0, _l | _cl, 0 -> Ok(req.body.buffer.data)
269271
_cl, _l ->
270272
read_from_socket(
271273
transport,
272274
socket,
273-
amount: left,
274-
buffer: req.body.buffer,
275+
buffer: buffer.sized(req.body.buffer, left),
275276
on_error: InvalidBody,
276277
)
278+
|> result.map(fn(buffer) { buffer.data })
277279
}
278280
|> result.map(request.set_body(req, _))
279281
}
@@ -379,23 +381,21 @@ pub fn append_default_headers(
379381
fn read_from_socket(
380382
transport: transport.Transport,
381383
socket: socket.Socket,
382-
amount amount: Int,
383-
buffer buffer: BitArray,
384+
buffer buffer: Buffer,
384385
on_error on_error: ParseError,
385-
) -> Result(BitArray, ParseError) {
386-
let read_size = int.min(amount, max_reading_size)
386+
) -> Result(Buffer, ParseError) {
387+
let read_size = int.min(buffer.remaining, max_reading_size)
387388

388389
use data <- try(
389390
transport.receive_timeout(transport, socket, read_size, 10_000)
390391
|> replace_error(on_error),
391392
)
392393

393-
let amount = amount - read_size
394-
let buffer = <<buffer:bits, data:bits>>
394+
let new_buffer = buffer.append_size(buffer, data, read_size)
395395

396-
case amount > 0 {
397-
True -> read_from_socket(transport, socket, amount:, buffer:, on_error:)
398-
False -> Ok(buffer)
396+
case new_buffer.remaining {
397+
0 -> Ok(new_buffer)
398+
_ -> read_from_socket(transport, socket, new_buffer, on_error:)
399399
}
400400
}
401401

@@ -407,10 +407,10 @@ fn read_from_socket(
407407
fn parse_headers(
408408
transport: transport.Transport,
409409
socket: socket.Socket,
410-
buffer: BitArray,
410+
buffer: Buffer,
411411
headers: Dict(String, String),
412412
) {
413-
case decoder.decode_packet(HttphBin, buffer, []) {
413+
case decoder.decode_packet(HttphBin, buffer.data, []) {
414414
Ok(Packet(HttpEoh, rest)) -> Ok(#(headers, rest))
415415
Ok(Packet(HttpHeader(idx, field, value), rest)) -> {
416416
use field <- try(case decoder.formatted_field_by_idx(idx) {
@@ -427,21 +427,24 @@ fn parse_headers(
427427
|> replace_error(InvalidHeaders),
428428
)
429429

430+
let new_buffer = buffer.new(rest)
431+
430432
insert_header(headers, field, value)
431-
|> parse_headers(transport, socket, rest, _)
433+
|> parse_headers(transport, socket, new_buffer, _)
432434
}
433435
Ok(More(size)) -> {
434436
let read_size = option.unwrap(size, 0)
435437

436-
use buffer <- try(read_from_socket(
438+
let sized_buffer = buffer.sized(buffer, read_size)
439+
440+
use new_buffer <- try(read_from_socket(
437441
transport,
438442
socket,
439-
amount: read_size,
440-
buffer:,
443+
sized_buffer,
441444
on_error: InvalidHeaders,
442445
))
443446

444-
parse_headers(transport, socket, buffer, headers)
447+
parse_headers(transport, socket, new_buffer, headers)
445448
}
446449
_ -> Error(InvalidHeaders)
447450
}
@@ -510,25 +513,31 @@ fn handle_continue(req: Request(Connection)) -> Result(Nil, ParseError) {
510513
fn read_chunked_body(
511514
transport: transport.Transport,
512515
socket: socket.Socket,
513-
buffer: BitArray,
516+
buffer: Buffer,
514517
body: BitArray,
515518
size_limit: Int,
516519
total_size: Int,
517-
) -> Result(#(BitArray, BitArray), ParseError) {
520+
) -> Result(#(BitArray, Buffer), ParseError) {
518521
use <- bool.guard(total_size > size_limit, Error(BodyTooLarge))
519522

520523
case parse_body_chunk(buffer) {
521524
Ok(Done(rest)) -> Ok(#(body, rest))
522525
Ok(Incomplete) -> {
523-
use buffer <- try(read_from_socket(
526+
use new_buffer <- try(read_from_socket(
524527
transport,
525528
socket,
526-
amount: 0,
527-
buffer:,
529+
buffer,
528530
on_error: InvalidBody,
529531
))
530532

531-
read_chunked_body(transport, socket, buffer, body, size_limit, total_size)
533+
read_chunked_body(
534+
transport,
535+
socket,
536+
new_buffer,
537+
body,
538+
size_limit,
539+
total_size,
540+
)
532541
}
533542
Ok(Chunk(chunk, rest)) -> {
534543
let body = <<body:bits, chunk:bits>>
@@ -541,9 +550,9 @@ fn read_chunked_body(
541550
}
542551

543552
/// Parses a single chunk from the chunked body
544-
fn parse_body_chunk(buffer: BitArray) -> Result(BodyChunk, ParseError) {
545-
case split(buffer, <<"\r\n">>, []) {
546-
[<<"0">>, rest] -> Ok(Done(rest))
553+
fn parse_body_chunk(buffer: Buffer) -> Result(BodyChunk, ParseError) {
554+
case split(buffer.data, <<"\r\n">>, []) {
555+
[<<"0">>, rest] -> Ok(Done(buffer.new(rest)))
547556
[chunk_size, rest] -> {
548557
use size <- try(
549558
bit_array.to_string(chunk_size)
@@ -554,7 +563,7 @@ fn parse_body_chunk(buffer: BitArray) -> Result(BodyChunk, ParseError) {
554563
case split(rest, <<"\r\n">>, []) {
555564
[chunk, rest] -> {
556565
case bit_array.byte_size(chunk) == size {
557-
True -> Ok(Chunk(chunk, rest))
566+
True -> Ok(Chunk(chunk, buffer.new(rest)))
558567
False -> Error(InvalidBody)
559568
}
560569
}
@@ -573,27 +582,29 @@ fn parse_body_chunk(buffer: BitArray) -> Result(BodyChunk, ParseError) {
573582
fn handle_trailers(
574583
req: Request(BitArray),
575584
set: set.Set(String),
576-
rest: BitArray,
585+
rest: Buffer,
577586
) -> Request(BitArray) {
578-
case decoder.decode_packet(HttphBin, rest, []) {
587+
case decoder.decode_packet(HttphBin, rest.data, []) {
579588
Ok(Packet(HttpEoh, _)) -> req
580-
Ok(Packet(HttpHeader(idx, field, value), rest)) -> {
581-
let field = case decoder.formatted_field_by_idx(idx) {
582-
Ok(field) -> Ok(field)
589+
Ok(Packet(HttpHeader(idx, field, value), header_rest)) -> {
590+
let field_name = case decoder.formatted_field_by_idx(idx) {
591+
Ok(field_name) -> Ok(field_name)
583592
Error(Nil) -> {
584593
bit_array.to_string(field)
585594
|> result.map(string.lowercase)
586595
}
587596
}
588597

589-
case field {
590-
Ok(field) -> {
591-
case set.contains(set, field) && !is_forbidden_trailer(field) {
598+
case field_name {
599+
Ok(field_name) -> {
600+
case
601+
set.contains(set, field_name) && !is_forbidden_trailer(field_name)
602+
{
592603
True -> {
593604
case bit_array.to_string(value) {
594605
Ok(value) -> {
595-
request.set_header(req, field, value)
596-
|> handle_trailers(set, rest)
606+
request.set_header(req, field_name, value)
607+
|> handle_trailers(set, buffer.new(header_rest))
597608
}
598609
Error(Nil) -> handle_trailers(req, set, rest)
599610
}

0 commit comments

Comments
 (0)