Skip to content

Commit

Permalink
ReferenceTransaction struct and also fetch the transaction from cache…
Browse files Browse the repository at this point in the history
… in the SNI
  • Loading branch information
bchamagne committed Jan 5, 2023
1 parent b5acfc7 commit ff8a6a2
Show file tree
Hide file tree
Showing 5 changed files with 102 additions and 62 deletions.
44 changes: 4 additions & 40 deletions lib/archethic_web/controllers/api/web_hosting_controller.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,12 @@ defmodule ArchethicWeb.API.WebHostingController do

use ArchethicWeb, :controller

alias Archethic.{
Crypto,
TransactionChain.Transaction,
TransactionChain.Transaction.ValidationStamp,
TransactionChain.TransactionData
}

alias ArchethicCache.LRU
alias Archethic.Crypto

require Logger

alias ArchethicWeb.API.WebHostingController.{Resources, DirectoryListing}
alias ArchethicWeb.ReferenceTransaction

@spec web_hosting(Plug.Conn.t(), params :: map()) :: Plug.Conn.t()
def web_hosting(conn, params = %{"url_path" => []}) do
Expand Down Expand Up @@ -78,16 +72,15 @@ defmodule ArchethicWeb.API.WebHostingController do
| {:error, :invalid_content}
| {:error, :file_not_found}
| {:error, :invalid_encoding}
| {:error, {:is_a_directory, {binary(), map(), DateTime.t()}}}
| {:error, {:is_a_directory, ReferenceTransaction.t()}}
| {:error, any()}

def get_website(params = %{"address" => address}, cache_headers) do
url_path = Map.get(params, "url_path", [])

with {:ok, address} <- Base.decode16(address, case: :mixed),
true <- Crypto.valid_address?(address),
{:ok, last_address} <- Archethic.get_last_transaction_address(address),
{:ok, reference_transaction} <- get_reference_transaction(last_address),
{:ok, reference_transaction} <- ReferenceTransaction.fetch_last(address),
{:ok, file_content, encoding, mime_type, cached?, etag} <-
Resources.load(reference_transaction, url_path, cache_headers) do
{:ok, file_content, encoding, mime_type, cached?, etag}
Expand Down Expand Up @@ -153,33 +146,4 @@ defmodule ArchethicWeb.API.WebHostingController do
else: {conn, file_content}
end
end

# Fetch the reference transaction either from cache, or from the network.
#
# Instead of returning the entire transaction,
# we return a triplet with only the formatted data we need
@spec get_reference_transaction(binary()) ::
{:ok, {binary(), map(), DateTime.t()}} | {:error, term()}
defp get_reference_transaction(address) do
# started by ArchethicWeb.Supervisor
cache_server = :web_hosting_cache_ref_tx
cache_key = address

case LRU.get(cache_server, cache_key) do
nil ->
with {:ok,
%Transaction{
data: %TransactionData{content: content},
validation_stamp: %ValidationStamp{timestamp: timestamp}
}} <- Archethic.search_transaction(address),
{:ok, json_content} <- Jason.decode(content) do
reference_transaction = {address, json_content, timestamp}
LRU.put(cache_server, cache_key, reference_transaction)
{:ok, reference_transaction}
end

reference_transaction ->
{:ok, reference_transaction}
end
end
end
Original file line number Diff line number Diff line change
@@ -1,26 +1,32 @@
defmodule ArchethicWeb.API.WebHostingController.DirectoryListing do
@moduledoc false

alias ArchethicWeb.ReferenceTransaction

require Logger

@spec list(
request_path :: String.t(),
params :: map(),
reference_transaction :: {binary(), map(), DateTime.t()},
reference_transaction :: ReferenceTransaction.t(),
cached_headers :: list()
) ::
{:ok, listing_html :: binary() | nil, encoding :: nil | binary(), mime_type :: binary(),
cached? :: boolean(), etag :: binary()}
def list(
request_path,
params,
{last_address, json_content, timestamp},
%ReferenceTransaction{
address: address,
timestamp: timestamp,
json_content: json_content
},
cache_headers
) do
url_path = Map.get(params, "url_path", [])
mime_type = "text/html"

case get_cache(cache_headers, last_address, url_path) do
case get_cache(cache_headers, address, url_path) do
{cached? = true, etag} ->
{:ok, nil, nil, mime_type, cached?, etag}

Expand All @@ -31,7 +37,7 @@ defmodule ArchethicWeb.API.WebHostingController.DirectoryListing do
url_path,
elem(get_metadata(json_content), 1),
timestamp,
last_address
address
)

{:ok, Phoenix.View.render_to_iodata(ArchethicWeb.DirListingView, "index.html", assigns),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,33 @@ defmodule ArchethicWeb.API.WebHostingController.Resources do

alias Archethic.TransactionChain.{Transaction, TransactionData}
alias ArchethicCache.LRUDisk
alias ArchethicWeb.ReferenceTransaction

require Logger

@spec load(tx :: {binary(), map(), DateTime.t()}, url_path :: list(), cache_headers :: list()) ::
@spec load(
reference_transaction :: ReferenceTransaction.t(),
url_path :: list(),
cache_headers :: list()
) ::
{:ok, file_content :: binary() | nil, encoding :: binary() | nil, mime_type :: binary(),
cached? :: boolean(), etag :: binary()}
| {:error,
:file_not_found
| {:is_a_directory, {binary(), map(), DateTime.t()}}
| {:is_a_directory, ReferenceTransaction.t()}
| :invalid_encoding
| any()}
def load(
reference_transaction = {last_address, json_content, _timestamp},
reference_transaction = %ReferenceTransaction{
address: address,
json_content: json_content
},
url_path,
cache_headers
) do
with {:ok, metadata, _aeweb_version} <- get_metadata(json_content),
{:ok, file, mime_type, resource_path} <- get_file(metadata, url_path),
{cached?, etag} <- get_cache(cache_headers, last_address, url_path),
{cached?, etag} <- get_cache(cache_headers, address, url_path),
{:ok, file_content, encoding} <- get_file_content(file, cached?, resource_path) do
{:ok, file_content, encoding, mime_type, cached?, etag}
else
Expand All @@ -32,8 +40,7 @@ defmodule ArchethicWeb.API.WebHostingController.Resources do
{:error, :file_not_found}

{:error, :get_metadata} ->
{:error,
"Error: Cant access metadata and aewebversion, Reftx: #{Base.encode16(last_address)}"}
{:error, "Error: Cant access metadata and aewebversion, Reftx: #{Base.encode16(address)}"}

{:error, :is_a_directory} ->
{:error, {:is_a_directory, reference_transaction}}
Expand Down
18 changes: 6 additions & 12 deletions lib/archethic_web/domain.ex
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,8 @@ defmodule ArchethicWeb.Domain do

alias Archethic
alias Archethic.Crypto
alias Archethic.TransactionChain.Transaction
alias Archethic.TransactionChain.TransactionData
alias Archethic.TransactionChain.TransactionData.Ownership
alias ArchethicWeb.ReferenceTransaction

require Logger

Expand Down Expand Up @@ -51,16 +50,11 @@ defmodule ArchethicWeb.Domain do
with {:ok, tx_address} <- lookup_dnslink_address(domain),
{:ok, tx_address} <- Base.decode16(tx_address, case: :mixed),
{:ok,
%Transaction{
type: :hosting,
data: %TransactionData{
content: content,
ownerships: [ownership = %Ownership{secret: secret} | _]
}
}} <-
Archethic.get_last_transaction(tx_address),
{:ok, json} <- Jason.decode(content),
{:ok, cert_pem} <- Map.fetch(json, "sslCertificate"),
%ReferenceTransaction{
json_content: json_content,
ownerships: [ownership = %Ownership{secret: secret} | _]
}} <- ReferenceTransaction.fetch_last(tx_address),
{:ok, cert_pem} <- Map.fetch(json_content, "sslCertificate"),
%{extensions: extensions} <- EasySSL.parse_pem(cert_pem),
{:ok, san} <- Map.fetch(extensions, :subjectAltName),
^domain <- String.split(san, ":") |> List.last(),
Expand Down
69 changes: 69 additions & 0 deletions lib/archethic_web/reference_transaction.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
defmodule ArchethicWeb.ReferenceTransaction do
@moduledoc """
ReferenceTransaction is a subset of a transaction
It is meant to be cached so we use a lighter structure
"""
alias Archethic.TransactionChain.Transaction
alias Archethic.TransactionChain.Transaction.ValidationStamp
alias Archethic.TransactionChain.TransactionData
alias Archethic.TransactionChain.TransactionData.Ownership
alias ArchethicCache.LRU

@enforce_keys [:address, :json_content, :timestamp, :ownerships]
defstruct [:address, :json_content, :timestamp, :ownerships]

@type t() :: %__MODULE__{
address: binary(),
json_content: map(),
timestamp: DateTime.t(),
ownerships: list(Ownership.t())
}

@doc """
Fetch the reference transaction either from cache, or from the network.
"""
@spec fetch(binary()) :: {:ok, t()} | {:error, term()}
def fetch(address) do
# started by ArchethicWeb.Supervisor
cache_server = :web_hosting_cache_ref_tx
cache_key = address

case LRU.get(cache_server, cache_key) do
nil ->
with {:ok, transaction} <- Archethic.search_transaction(address),
{:ok, reference_transaction} <- from_transaction(transaction) do
LRU.put(cache_server, cache_key, reference_transaction)
{:ok, reference_transaction}
end

reference_transaction ->
{:ok, reference_transaction}
end
end

@doc """
Fetch the latest reference transaction of the chain, either from cache, or from the network.
"""
@spec fetch_last(binary()) :: {:ok, t()} | {:error, term()}
def fetch_last(address) do
with {:ok, last_address} <- Archethic.get_last_transaction_address(address) do
fetch(last_address)
end
end

defp from_transaction(%Transaction{
address: address,
data: %TransactionData{content: content, ownerships: ownerships},
validation_stamp: %ValidationStamp{timestamp: timestamp}
}) do
with {:ok, json_content} <- Jason.decode(content) do
{:ok,
%__MODULE__{
address: address,
json_content: json_content,
timestamp: timestamp,
ownerships: ownerships
}}
end
end
end

0 comments on commit ff8a6a2

Please sign in to comment.