-
-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathpy_buffer.erl
More file actions
107 lines (101 loc) · 3.46 KB
/
py_buffer.erl
File metadata and controls
107 lines (101 loc) · 3.46 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
%% Copyright 2026 Benoit Chesneau
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%% http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
%%% @doc Zero-copy WSGI input buffer for streaming HTTP bodies.
%%%
%%% This module provides a buffer that can be written by Erlang and read
%%% by Python with zero-copy semantics. The buffer is suitable for use
%%% as wsgi.input in WSGI applications.
%%%
%%% == Usage ==
%%%
%%% ```
%%% %% Create a buffer (chunked encoding - unknown size)
%%% {ok, Buf} = py_buffer:new(),
%%%
%%% %% Or with known content length
%%% {ok, Buf} = py_buffer:new(1024),
%%%
%%% %% Write HTTP body chunks
%%% ok = py_buffer:write(Buf, <<"chunk1">>),
%%% ok = py_buffer:write(Buf, <<"chunk2">>),
%%%
%%% %% Signal end of data
%%% ok = py_buffer:close(Buf),
%%%
%%% %% Pass to Python WSGI - buffer is automatically converted
%%% py_context:call(Ctx, <<"myapp">>, <<"handle">>,
%%% [#{<<"wsgi.input">> => Buf}], #{}).
%%% '''
%%%
%%% On the Python side, the buffer provides a file-like interface:
%%%
%%% ```python
%%% def handle(environ):
%%% body = environ['wsgi.input'].read() # Blocks until data ready
%%% # Or use readline(), readlines(), iteration
%%% for line in environ['wsgi.input']:
%%% process(line)
%%% '''
%%%
%%% @end
-module(py_buffer).
-export([
new/0,
new/1,
write/2,
close/1
]).
%% @doc Create a new buffer for chunked/streaming data.
%%
%% Use this when the content length is unknown (chunked transfer encoding).
%% The buffer will grow as needed.
%%
%% @returns {ok, BufferRef} | {error, Reason}
-spec new() -> {ok, reference()} | {error, term()}.
new() ->
py_nif:py_buffer_create(undefined).
%% @doc Create a new buffer with known content length.
%%
%% Pre-allocates the buffer to the specified size for better performance.
%%
%% @param ContentLength Expected total size in bytes, or `undefined' for chunked
%% @returns {ok, BufferRef} | {error, Reason}
-spec new(non_neg_integer() | undefined) -> {ok, reference()} | {error, term()}.
new(undefined) ->
py_nif:py_buffer_create(undefined);
new(ContentLength) when is_integer(ContentLength), ContentLength >= 0 ->
py_nif:py_buffer_create(ContentLength).
%% @doc Write data to the buffer.
%%
%% Appends data to the buffer and signals any waiting Python readers.
%% This function is safe to call from multiple processes, but typically
%% only one process should write to a buffer.
%%
%% @param Ref Buffer reference from new/0 or new/1
%% @param Data Binary data to append
%% @returns ok | {error, Reason}
-spec write(reference(), binary()) -> ok | {error, term()}.
write(Ref, Data) when is_binary(Data) ->
py_nif:py_buffer_write(Ref, Data).
%% @doc Close the buffer (signal end of data).
%%
%% Sets the EOF flag and wakes up any Python threads waiting for data.
%% After calling close, no more data can be written, and Python's read()
%% will return any remaining buffered data followed by empty bytes.
%%
%% @param Ref Buffer reference
%% @returns ok
-spec close(reference()) -> ok.
close(Ref) ->
py_nif:py_buffer_close(Ref).