<commit_before>-module(h2_connection).
-include("http2.hrl").
-behaviour(gen_fsm).

%% Start/Stop API
-export([
         start_client_link/5,
         start_ssl_upgrade_link/5,
         start_server_link/3,
         become/1,
         become/2,
         stop/1
        ]).

%% HTTP Operations
-export([
         send_headers/3,
         send_headers/4,
         send_body/3,
         send_body/4,
         is_push/1,
         new_stream/1,
         new_stream/2,
         send_promise/4,
         get_response/2,
         get_peer/1,
         get_streams/1,
         send_window_update/2,
         send_frame/2
        ]).

%% gen_fsm callbacks
-export(
   [
    init/1,
    handle_event/3,
    handle_sync_event/4,
    handle_info/3,
    code_change/4,
    terminate/3
   ]).

%% gen_fsm states
-export([
         listen/2,
         handshake/2,
         connected/2,
         continuation/2,
         closing/2
        ]).

-export([
         go_away/2
        ]).

-record(h2_listening_state, {
          ssl_options   :: [ssl:ssloption()],
          listen_socket :: ssl:sslsocket() | inet:socket(),
          transport     :: gen_tcp | ssl,
          listen_ref    :: non_neg_integer(),
          acceptor_callback = fun chatterbox_sup:start_socket/0 :: fun(),
          server_settings = #settings{} :: settings()
         }).

-record(continuation_state, {
          stream_id                 :: stream_id(),
          promised_id = undefined   :: undefined | stream_id(),
          frames      = queue:new() :: queue:queue(http2_frame:frame()),
          type                      :: headers | push_promise | trailers,
          end_stream  = false       :: boolean(),
          end_headers = false       :: boolean()
}).

-record(connection, {
          type = undefined :: client | server | undefined,
          ssl_options = [],
          listen_ref :: non_neg_integer(),
          socket = undefined :: sock:socket(),
          peer_settings = #settings{} :: settings(),
          self_settings = #settings{} :: settings(),
          send_window_size = ?DEFAULT_INITIAL_WINDOW_SIZE :: integer(),
          recv_window_size = ?DEFAULT_INITIAL_WINDOW_SIZE :: integer(),
          decode_context = hpack:new_context() :: hpack:context(),
          encode_context = hpack:new_context() :: hpack:context(),
          settings_sent = queue:new() :: queue:queue(),
          next_available_stream_id = 2 :: stream_id(),
          streams :: h2_stream_set:stream_set(),
          stream_callback_mod = application:get_env(chatterbox, stream_callback_mod, chatterbox_static_stream) :: module(),
          buffer = empty :: empty | {binary, binary()} | {frame, h2_frame:header(), binary()},
          continuation = undefined :: undefined | #continuation_state{},
          flow_control = auto :: auto | manual
}).

-type connection() :: #connection{}.

-type send_option() :: {send_end_stream, boolean()}.
-type send_opts() :: [send_option()].

-export_type([send_option/0, send_opts/0]).

-spec start_client_link(gen_tcp | ssl,
                        inet:ip_address() | inet:hostname(),
                        inet:port_number(),
                        [ssl:ssloption()],
                        settings()
                       ) ->
                               {ok, pid()} | ignore | {error, term()}.
start_client_link(Transport, Host, Port, SSLOptions, Http2Settings) ->
    gen_fsm:start_link(?MODULE, {client, Transport, Host, Port, SSLOptions, Http2Settings}, []).

-spec start_ssl_upgrade_link(inet:ip_address() | inet:hostname(),
                             inet:port_number(),
                             binary(),
                             [ssl:ssloption()],
                             settings()
                            ) ->
                                    {ok, pid()} | ignore | {error, term()}.
start_ssl_upgrade_link(Host, Port, InitialMessage, SSLOptions, Http2Settings) ->
    gen_fsm:start_link(?MODULE, {client_ssl_upgrade, Host, Port, InitialMessage, SSLOptions, Http2Settings}, []).

-spec start_server_link(socket(),
                        [ssl:ssloption()],
                        #settings{}) ->
                               {ok, pid()} | ignore | {error, term()}.
start_server_link({Transport, ListenSocket}, SSLOptions, Http2Settings) ->
    gen_fsm:start_link(?MODULE, {server, {Transport, ListenSocket}, SSLOptions, Http2Settings}, []).

-spec become(socket()) -> no_return().
become(Socket) ->
    become(Socket, chatterbox:settings(server)).

-spec become(socket(), settings()) -> no_return().
become({Transport, Socket}, Http2Settings) ->
    ok = Transport:setopts(Socket, [{packet, raw}, binary]),
    {_, _, NewState} =
        start_http2_server(Http2Settings,
                           #connection{
                              streams = h2_stream_set:new(server),
                              socket = {Transport, Socket}
                             }),
    gen_fsm:enter_loop(?MODULE,
                       [],
                       handshake,
                       NewState).

%% Init callback
init({client, Transport, Host, Port, SSLOptions, Http2Settings}) ->
    {ok, Socket} = Transport:connect(Host, Port, client_options(Transport, SSLOptions)),
    ok = Transport:setopts(Socket, [{packet, raw}, binary]),
    Transport:send(Socket, <<?PREFACE>>),
    InitialState =
        #connection{
           type = client,
           streams = h2_stream_set:new(client),
           socket = {Transport, Socket},
           next_available_stream_id=1,
           flow_control=application:get_env(chatterbox, client_flow_control, auto)
          },<commit_msg>Change Transport:setopts/2 calls to sock:setopts/2<commit_after>-module(h2_connection).
-include("http2.hrl").
-behaviour(gen_fsm).

%% Start/Stop API
-export([
         start_client_link/5,
         start_ssl_upgrade_link/5,
         start_server_link/3,
         become/1,
         become/2,
         stop/1
        ]).

%% HTTP Operations
-export([
         send_headers/3,
         send_headers/4,
         send_body/3,
         send_body/4,
         is_push/1,
         new_stream/1,
         new_stream/2,
         send_promise/4,
         get_response/2,
         get_peer/1,
         get_streams/1,
         send_window_update/2,
         send_frame/2
        ]).

%% gen_fsm callbacks
-export(
   [
    init/1,
    handle_event/3,
    handle_sync_event/4,
    handle_info/3,
    code_change/4,
    terminate/3
   ]).

%% gen_fsm states
-export([
         listen/2,
         handshake/2,
         connected/2,
         continuation/2,
         closing/2
        ]).

-export([
         go_away/2
        ]).

-record(h2_listening_state, {
          ssl_options   :: [ssl:ssloption()],
          listen_socket :: ssl:sslsocket() | inet:socket(),
          transport     :: gen_tcp | ssl,
          listen_ref    :: non_neg_integer(),
          acceptor_callback = fun chatterbox_sup:start_socket/0 :: fun(),
          server_settings = #settings{} :: settings()
         }).

-record(continuation_state, {
          stream_id                 :: stream_id(),
          promised_id = undefined   :: undefined | stream_id(),
          frames      = queue:new() :: queue:queue(http2_frame:frame()),
          type                      :: headers | push_promise | trailers,
          end_stream  = false       :: boolean(),
          end_headers = false       :: boolean()
}).

-record(connection, {
          type = undefined :: client | server | undefined,
          ssl_options = [],
          listen_ref :: non_neg_integer(),
          socket = undefined :: sock:socket(),
          peer_settings = #settings{} :: settings(),
          self_settings = #settings{} :: settings(),
          send_window_size = ?DEFAULT_INITIAL_WINDOW_SIZE :: integer(),
          recv_window_size = ?DEFAULT_INITIAL_WINDOW_SIZE :: integer(),
          decode_context = hpack:new_context() :: hpack:context(),
          encode_context = hpack:new_context() :: hpack:context(),
          settings_sent = queue:new() :: queue:queue(),
          next_available_stream_id = 2 :: stream_id(),
          streams :: h2_stream_set:stream_set(),
          stream_callback_mod = application:get_env(chatterbox, stream_callback_mod, chatterbox_static_stream) :: module(),
          buffer = empty :: empty | {binary, binary()} | {frame, h2_frame:header(), binary()},
          continuation = undefined :: undefined | #continuation_state{},
          flow_control = auto :: auto | manual
}).

-type connection() :: #connection{}.

-type send_option() :: {send_end_stream, boolean()}.
-type send_opts() :: [send_option()].

-export_type([send_option/0, send_opts/0]).

-spec start_client_link(gen_tcp | ssl,
                        inet:ip_address() | inet:hostname(),
                        inet:port_number(),
                        [ssl:ssloption()],
                        settings()
                       ) ->
                               {ok, pid()} | ignore | {error, term()}.
start_client_link(Transport, Host, Port, SSLOptions, Http2Settings) ->
    gen_fsm:start_link(?MODULE, {client, Transport, Host, Port, SSLOptions, Http2Settings}, []).

-spec start_ssl_upgrade_link(inet:ip_address() | inet:hostname(),
                             inet:port_number(),
                             binary(),
                             [ssl:ssloption()],
                             settings()
                            ) ->
                                    {ok, pid()} | ignore | {error, term()}.
start_ssl_upgrade_link(Host, Port, InitialMessage, SSLOptions, Http2Settings) ->
    gen_fsm:start_link(?MODULE, {client_ssl_upgrade, Host, Port, InitialMessage, SSLOptions, Http2Settings}, []).

-spec start_server_link(socket(),
                        [ssl:ssloption()],
                        #settings{}) ->
                               {ok, pid()} | ignore | {error, term()}.
start_server_link({Transport, ListenSocket}, SSLOptions, Http2Settings) ->
    gen_fsm:start_link(?MODULE, {server, {Transport, ListenSocket}, SSLOptions, Http2Settings}, []).

-spec become(socket()) -> no_return().
become(Socket) ->
    become(Socket, chatterbox:settings(server)).

-spec become(socket(), settings()) -> no_return().
become({Transport, Socket}, Http2Settings) ->
    ok = sock:setopts({Transport, Socket}, [{packet, raw}, binary]),
    {_, _, NewState} =
        start_http2_server(Http2Settings,
                           #connection{
                              streams = h2_stream_set:new(server),
                              socket = {Transport, Socket}
                             }),
    gen_fsm:enter_loop(?MODULE,
                       [],
                       handshake,
                       NewState).

%% Init callback
init({client, Transport, Host, Port, SSLOptions, Http2Settings}) ->
    {ok, Socket} = Transport:connect(Host, Port, client_options(Transport, SSLOptions)),
    ok = sock:setopts({Transport, Socket}, [{packet, raw}, binary]),
    Transport:send(Socket, <<?PREFACE>>),
    InitialState =
        #connection{
           type = client,
           streams = h2_stream_set:new(client),
           socket = {Transport, Socket},
           next_available_stream_id=1,
           flow_control=application:get_env(chatterbox, client_flow_control, auto)
          },
{'fraction_non_alphanumeric': 0.13170688588349685, 'fraction_numerical': 0.009723327145761513, 'mean_word_length': 1.9907480835315887, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 29, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4473576', 'n_tokens_mistral': 3275, 'n_tokens_neox': 3210, 'n_words': 580}