Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Janitoring #66

Merged
merged 6 commits into from
Dec 9, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
153 changes: 153 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
version: 2.1 # use CircleCI 2.1 instead of CircleCI Classic

x-common:
job-common: &job-common
working_directory: /home/circleci/project
docker:
- image: circleci/elixir:1.11
environment:
MIX_ARCHIVES: /home/circleci/project/.mix/archives
MIX_HOME: /home/circleci/project/.mix
deploy-common: &deploy-common
working_directory: /home/circleci/project
docker:
- image: circleci/buildpack-deps

x-steps:
- &save-deps-cache
save_cache:
key: v1-deps-cache-{{ .Branch }}-{{ checksum "mix.lock" }}
paths: ["deps"]
- &restore-deps-cache
restore_cache:
keys:
- v1-deps-cache-{{ .Branch }}-{{ checksum "mix.lock" }}
- &save-plt-cache
save_cache:
key: v1-plt-cache-{{ .Branch }}-{{ checksum ".dialyzer/cache.plt" }}
paths: [".dialyzer/cache.plt"]
- &restore-plt-cache
restore_cache:
keys:
- v1-plt-cache-{{ .Branch }}-{{ checksum ".dialyzer/cache.plt" }}
- &attach-workspace
attach_workspace:
at: /home/circleci

x-filters:
only-pr: &only-pr
branches:
ignore: /^(master)$/
only-master: &only-master
branches:
only: /^master$/

jobs:
setup:
<<: *job-common
steps:
- checkout
- *restore-deps-cache
- run:
name: Install Hex
command: mix local.hex --force --if-missing
- run:
name: Install rebar
command: mix local.rebar --force --if-missing
- run:
name: Install project's deps
command: mix deps.get
- *save-deps-cache
- persist_to_workspace:
root: /home/circleci/
paths: ["project"]

build:
<<: *job-common
steps:
- *attach-workspace
- run:
name: Build project
command: mix compile --warnings-as-errors
- persist_to_workspace:
root: /home/circleci/
paths: ["project/_build"]

format:
<<: *job-common
steps:
- *attach-workspace
- run:
name: Check formatting rules
command: mix format --check-formatted

dialyzer:
<<: *job-common
steps:
- *attach-workspace
- run:
name: Dialyzer static analysis
command: mix dialyzer

credo:
<<: *job-common
steps:
- *attach-workspace
- run:
name: Check coding rules
command: mix credo

test:
<<: *job-common
steps:
- *attach-workspace
- run: mix test

workflows:
version: 2
on-pull-request:
jobs:
- setup:
filters:
<<: *only-pr
- build:
requires:
- setup
filters:
<<: *only-pr
- format:
requires:
- setup
filters:
<<: *only-pr
- credo:
requires:
- build
filters:
<<: *only-pr
- dialyzer:
requires:
- build
filters:
<<: *only-pr
- test:
requires:
- build
filters:
<<: *only-pr

on-merge:
jobs:
- setup:
filters:
<<: *only-master
- build:
requires:
- setup
filters:
<<: *only-master
- test:
requires:
- setup
filters:
<<: *only-master
1 change: 1 addition & 0 deletions .dialyzer/ignore.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[]
21 changes: 0 additions & 21 deletions .github/workflows/elixir.yml

This file was deleted.

1 change: 0 additions & 1 deletion lib/coap/adapters/phoenix.ex
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ defmodule CoAP.Adapters.Phoenix do
end

defp process(req, {endpoint, opts}) do
# TODO: conn_from_message(message)
case endpoint.__handler__(@connection.conn(req), opts) do
{:plug, conn, handler, opts} ->
%{adapter: {@connection, _req}} =
Expand Down
9 changes: 2 additions & 7 deletions lib/coap/block.ex
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ defmodule CoAP.Block do
@type binary_t_large :: <<_::32>>
@type binary_t :: binary_t_small | binary_t_medium | binary_t_large

# TODO: if more: false, a size_exponent of 0 should be ignored?
# _TODO: if more: false, a size_exponent of 0 should be ignored?
# otherwise size_exponent of 0 results in size: 16

@doc """
Expand Down Expand Up @@ -67,32 +67,27 @@ defmodule CoAP.Block do
}
end

@spec encode(t()) :: binary_t()
@spec encode(t() | tuple_t()) :: binary_t()
def encode(%__MODULE__{} = block), do: encode({block.number, block.more, block.size})
@spec encode(%{number: integer, more: 0 | 1, size: integer}) :: binary_t()
def encode(%{number: number, more: more, size: size}), do: encode({number, more, size})

@spec encode({integer, boolean, 0}) :: binary_t()
def encode({number, more, 0}) do
encode(number, if(more, do: 1, else: 0), 0)
end

@spec encode({integer, boolean, integer}) :: binary_t()
def encode({number, more, size}) do
encode(number, if(more, do: 1, else: 0), trunc(:math.log2(size)) - 4)
end

@spec encode(integer, 0 | 1, integer) :: binary_t_small()
def encode(number, more, size_exponent) when number < 16 do
<<number::size(4), more::size(1), size_exponent::size(3)>>
end

@spec encode(integer, 0 | 1, integer) :: binary_t_medium()
def encode(number, more, size_exponent) when number < 4096 do
<<number::size(12), more::size(1), size_exponent::size(3)>>
end

@spec encode(integer, 0 | 1, integer) :: binary_t_large()
def encode(number, more, size_exponent) do
<<number::size(28), more::size(1), size_exponent::size(3)>>
end
Expand Down
2 changes: 1 addition & 1 deletion lib/coap/client.ex
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ defmodule CoAP.Client do
tag: nil
end

# TODO: options: headers/params?
# _TODO: options: headers/params?

@doc """
Perform a confirmable, GET request to a URL
Expand Down
37 changes: 14 additions & 23 deletions lib/coap/connection.ex
Original file line number Diff line number Diff line change
Expand Up @@ -163,8 +163,6 @@ defmodule CoAP.Connection do

def start_link(args), do: GenServer.start_link(__MODULE__, args)

# TODO: predefined defaults, merged with client/server-specific options
# TODO: default adapter to GenericServer?
@doc """
`init` functions for Server and Client processes

Expand Down Expand Up @@ -214,10 +212,6 @@ defmodule CoAP.Connection do
end

def handle_info({:receive, %Message{} = message}, state) do
# TODO: connection timeouts
# TODO: start timer for conn
# TODO: check if the message_id matches what we may have already sent?

:telemetry.execute(
[:coap_ex, :connection, :data_received],
%{size: message.raw_size},
Expand Down Expand Up @@ -251,7 +245,7 @@ defmodule CoAP.Connection do

def handle_info({:tag, tag}, state), do: {:noreply, %{state | tag: tag}}

# TODO: connection timeout, set to original state?
# _TODO: connection timeout, set to original state?

# def handle_info(:retry, state)

Expand All @@ -260,17 +254,17 @@ defmodule CoAP.Connection do

# RECEIVE ====================================================================
# con -> reset
# TODO: how do we get a nil method, vs a response
# _TODO: how do we get a nil method, vs a response
# defp receive_message(%Message{method: nil, type: :con} = message, %{phase: :idle} = state) do
# TODO: peer ack with reset, next state is peer_ack_sent
# _TODO: peer ack with reset, next state is peer_ack_sent
# Message.response_for(message)
# reply(:reset, message, state[:server])
# end

# TODO: resend reset?
# TODO: what is the message if the client has to re-request after a processing timeout from the app?
# _TODO: resend reset?
# _TODO: what is the message if the client has to re-request after a processing timeout from the app?

# TODO: resend stored message (ack)
# _TODO: resend stored message (ack)
defp receive_message(_message, %{phase: :peer_ack_sent} = state), do: state

# Do nothing if we receive a message from peer during these states; we should be shutting down
Expand Down Expand Up @@ -355,15 +349,15 @@ defmodule CoAP.Connection do
) do
timer = restart_timer(state.timer, ack_timeout(state))

# TODO: respect the number/size from control
# _TODO: respect the number/size from control
# more must be false, must use same size on subsequent request
multipart = Multipart.build(nil, Block.build({number + 1, false, size}))

# alternatively message.verb == nil => client
response =
case message.status do
# client sends original message with new control number
# TODO: what parts of the message are we supposed to send back?
# _TODO: what parts of the message are we supposed to send back?
{:ok, _} ->
Message.next_message(state.message, next_message_id(state.message.message_id))

Expand Down Expand Up @@ -456,25 +450,25 @@ defmodule CoAP.Connection do
defp receive_message(message, %{phase: :awaiting_peer_ack} = state) do
cancel_timer(state.timer)

# TODO: what happens if this is an empty ACK and we get another response later?
# _TODO: what happens if this is an empty ACK and we get another response later?
# Could we go back to being idle?
handle(message, state.handler, peer_for(state))

%{state | phase: next_phase(:awaiting_peer_ack, nil), timer: nil}
end

# DELIVER ====================================================================
# TODO: deliver message for got_non as a NON message, phase becomes :sent_non
# TODO: deliver message for peer_ack_sent as a CON message, phase becomes :awaiting_peer_ack
# _TODO: deliver message for got_non as a NON message, phase becomes :sent_non
# _TODO: deliver message for peer_ack_sent as a CON message, phase becomes :awaiting_peer_ack

defp deliver_message(message, %{phase: :awaiting_app_ack} = state) do
# TODO: does the message include the original request control?
# _TODO: does the message include the original request control?
{bytes, block, payload} = Payload.segment_at(message.payload, @default_payload_size, 0)

# Cancel the app_ack waiting timeout
cancel_timer(state.timer)

# TODO: what happens if the app response is a status code, no code_class/detail tuple?
# _TODO: what happens if the app response is a status code, no code_class/detail tuple?
response =
Message.response_for(
{message.code_class, message.code_detail},
Expand All @@ -501,7 +495,7 @@ defmodule CoAP.Connection do
%Message{type: type, message_id: message_id} = message,
%{phase: :idle, next_message_id: next_message_id} = state
) do
# TODO: get payload size from the request control
# _TODO: get payload size from the request control
{bytes, block, payload} = Payload.segment_at(message.payload, @default_payload_size, 0)

# The server should send back the same message id of the request
Expand Down Expand Up @@ -562,7 +556,6 @@ defmodule CoAP.Connection do
ack_timeout(state)

_ ->
# TODO: exponential backoff
timeout * 2
end

Expand Down Expand Up @@ -676,7 +669,6 @@ defmodule CoAP.Connection do
end

# HANDLER
# TODO: move to CoAP
defp start_handler({adapter, endpoint}), do: start_handler(adapter, endpoint)

defp start_handler(adapter, endpoint) do
Expand All @@ -689,7 +681,6 @@ defmodule CoAP.Connection do
)
end

# TODO: move to CoAP
defp start_socket_for(endpoint, peer) do
DynamicSupervisor.start_child(
CoAP.SocketServerSupervisor,
Expand Down
4 changes: 0 additions & 4 deletions lib/coap/handler.ex
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,6 @@ defmodule CoAP.Handler do
{:ok, {adapter, endpoint}}
end

# TODO: this process is blocked when calling endpoint
# TODO: we may want to instrument/log the queue depth here
# It _should not_ be an issue because this process is already per-peer connection

def handle_info({:request, message, peer, connection}, {adapter, endpoint} = state) do
adapter.request(message, {endpoint, peer}, connection)
{:noreply, state}
Expand Down
Loading