aleph

Ahmed Hassan 2019-03-05T05:58:46.033500Z

How do I approach network programming in Clojure as beginner?

Empperi 2019-03-05T09:02:48.033700Z

With care? :troll:

Empperi 2019-03-05T09:03:02.034Z

Seriously speaking, you need to be more specific than that

Ahmed Hassan 2019-03-05T09:26:03.034200Z

Trying to implement a server to host multimedia like images, videos. I'm a beginner.

Empperi 2019-03-05T12:11:12.034400Z

Ok. And apparently you are planning to use Aleph. Well, the easiest solution is to just return an InputStream object to your media file to the browser

Empperi 2019-03-05T12:11:40.034600Z

This allows streaming the file from filesystem to the browser without you having to first load it into server memory

Empperi 2019-03-05T12:11:50.034800Z

Which would be disastrous especially with videos and multiple concurrent users

Empperi 2019-03-05T12:12:26.035Z

Plus would make it way slower to start the download for the client since server would have to first load the whole file. By returning a stream client will start to receive the file immediately

Empperi 2019-03-05T12:13:09.035200Z

Now when implementing a true video media server then you'd have to take into account also jumping around in the video, this is more about video codecs and formats and how to deal with those

Empperi 2019-03-05T12:13:46.035400Z

But basically the basic idea is the same there: you find the correct position in the video file, then create an InputStream to that file starting at that specific point in time and send that to the client

Empperi 2019-03-05T12:14:35.035600Z

But it gets more complex since you cannot just start streaming from a random byte in a video file: in most codecs there is a concept called "keyframe" which is a point in time where you can rewind into and start streaming from there

Empperi 2019-03-05T12:15:15.035800Z

Basically a time segment between two keyframes is a "full video file" so normal video file is actually a sequence of these

Empperi 2019-03-05T12:15:39.036Z

And this is the actual video part of the file, audio is a story on itself

Empperi 2019-03-05T12:16:06.036200Z

But anyway, you get the point. This is a way too complex topic to cover in detail here but the very basics of it is very simple when it comes to your original question

Empperi 2019-03-05T12:16:53.036400Z

With images however it is very simple, client always needs the whole image. Just send it in whole to the client as a stream

Empperi 2019-03-05T12:17:12.036600Z

Remember to set proper Content-Type header so browser knows what kind of data it is it is receiving

Empperi 2019-03-05T12:17:53.036800Z

All this has nothing to do with Aleph itself really although Aleph can be used as a server for this

Empperi 2019-03-05T12:18:35.037Z

Hope this helps

Ahmed Hassan 2019-03-05T12:40:44.037200Z

Thanks alot 🙂

lispyclouds 2019-03-05T12:55:12.040200Z

Hello, I have a TarArchiveInputStream object and I want to make it available as a download over http. I tried the response on the handler like this

{:status  200
 :headers {"Content-Type"        "archive/tar"
           "Content-Disposition" "inline; filename=download.tar"}
 :body    (get-stream)}
I am able to trigger a download with the correct file names and content types, but the response is of 0 bytes. What am I doing wrong?

Empperi 2019-03-05T13:06:23.040900Z

You need to provide also Content-Length header

Empperi 2019-03-05T13:07:09.041600Z

Aleph cannot deduce that automatically from stream (in order to do that it would first have to read the whole stream which would make the whole stream pretty pointless)

Empperi 2019-03-05T13:07:29.042100Z

Thus you need to retrieve that information from somewhere else,

Empperi 2019-03-05T13:07:37.042400Z

If it is in filesystem, retrieve that data from filesystem

Empperi 2019-03-05T13:07:51.042800Z

if it is in database, record the content-length to database when you store it as a blob

Empperi 2019-03-05T13:08:28.043300Z

Also if your file is in the filesystem you can just pass a File object to aleph and it understands what you want to do

Empperi 2019-03-05T13:08:50.043900Z

Aleph will get the file size from filesystem and will send the FIle as a stream to the client

Empperi 2019-03-05T13:09:09.044400Z

However, if you are calculating the tar file on fly from something else then you need to solve your problem separately

kachayev 2019-03-05T13:10:35.044500Z

@ahmed1hsn Please, check this example I’ve implemented recently: static files server on top of Aleph https://github.com/kachayev/nasus

1👍
kachayev 2019-03-05T13:12:26.044800Z

Other than that… the question is really about details: number of clients, size of files and frequency of changes, geo distribution, goals/plans, hardware/network in terms of bandwidth etc etc etc

1👍
kachayev 2019-03-05T13:13:31.045Z

If you need to implement backend for video- or sound streaming…. that’s an entirely different dimension and different problems

kachayev 2019-03-05T13:26:56.048300Z

@niklas.collin in fact… it’s better not to provide content-length header. In such a case the chance that it would work correctly is actually higher 🙂 If body is represented as an InputStream, Aleph should invoke send-streaming-body that automatically sets “Transfer-Encoding: chunked” header when content length is not provided manually https://github.com/ztellman/aleph/blob/626bec56dea84ddc5930f8bd9852fc1a236e23f7/src/aleph/http/core.clj#L260 (usually you’re not able to tell the size of the stream upfront anyways)

kachayev 2019-03-05T13:28:04.049600Z

@rahul080327 Do you have any debug information about packets sent? (either from browser or wireshark or similar) So we can check raw request/responses?

lispyclouds 2019-03-05T13:31:33.052Z

@kachayev So to give some context im building a clojure system over a lib i created. It exposes a path in a docker container as a TarArchiveInputStream. https://github.com/lispyclouds/clj-docker-client/blob/master/src/clj_docker_client/core.clj#L318 This is beacuse the Spotify docker lib im using does it. I want to expose this over HTTP so that a client can download the stuff from the container

lispyclouds 2019-03-05T13:32:41.052700Z

This is how i call my lib:

(defn get-stream-of
  "Fetches the TarStream of a path from a container."
  [path id]
  (docker/stream-path e/conn id path))

lispyclouds 2019-03-05T13:33:34.053100Z

This is my aleph handler:

(defn stream-artifact
  [name group number artifact]
  (d/let-flow [pipeline (u/name-of name group)
               id       (a/artifact-container-of pipeline number)
               path     (a/get-artifact-path-of pipeline artifact)
               stream   (a/get-stream-of path id)]
    (if (f/failed? stream)
      (res/bad-request {:message "No such artifact."})
      {:status  200
       :headers {"Content-Type"        "archive/tar"
                 "Content-Disposition" (format "inline; filename=%s.tar"
                                               artifact)}
       :body    stream})))

lispyclouds 2019-03-05T13:34:17.053700Z

im able to list all files in if i do it in the REPL. just that i cant send it over http

kachayev 2019-03-05T13:37:35.054300Z

If you want the browser to start downloading, you need attachment not inline, right?

lispyclouds 2019-03-05T13:37:45.054700Z

tried that too

kachayev 2019-03-05T13:38:19.055400Z

inline means that you want this to be shown on the page, which is impossible. Yeah, I known this is not the root of the problem, just noticed

lispyclouds 2019-03-05T13:39:44.055800Z

Just tried a curl, totally empty response

kachayev 2019-03-05T13:40:35.056400Z

Could you please show the result of curl -vvv ...?

lispyclouds 2019-03-05T13:41:14.056900Z

$ curl -vvv <http://localhost:7777/api/pipeline/test/test/1/artifact/source>
*   Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 7777 (#0)
&gt; GET /api/pipeline/test/test/1/artifact/source HTTP/1.1
&gt; Host: localhost:7777
&gt; User-Agent: curl/7.54.0
&gt; Accept: */*
&gt;
&lt; HTTP/1.1 200 OK
&lt; Content-Type: archive/tar
&lt; Content-Disposition: attachement; filename=source.tar
&lt; Server: Aleph/0.4.4
&lt; Connection: Keep-Alive
&lt; Date: Tue, 05 Mar 2019 13:40:48 GMT
&lt; transfer-encoding: chunked
&lt;
• Connection #0 to host localhost left intact

lispyclouds 2019-03-05T13:42:20.057400Z

Dunno if it may help, im using compojure-api for the routing

lispyclouds 2019-03-05T13:43:20.058200Z

https://github.com/bob-cd/bob/blob/master/src/bob/api/routes.clj these are the routes where im creating a new one

kachayev 2019-03-05T13:44:17.058800Z

Any chance to upgrade at least to 0.4.6?

lispyclouds 2019-03-05T13:44:26.059Z

I can

kachayev 2019-03-05T13:44:41.059400Z

Let’s try, this version is ancient 😉

lispyclouds 2019-03-05T13:45:04.059800Z

it is at 0.4.6

kachayev 2019-03-05T13:45:16.060Z

< Server: Aleph/0.4.4

lispyclouds 2019-03-05T13:45:18.060200Z

https://github.com/bob-cd/bob/blob/master/build.boot#L27

kachayev 2019-03-05T13:45:46.060700Z

transitive deps?

kachayev 2019-03-05T13:46:03.061300Z

you’re cURL response indicates another version

lispyclouds 2019-03-05T13:46:03.061400Z

not sure where to put it.

lispyclouds 2019-03-05T13:46:28.061900Z

yeah ive noticed it too. saw it with plain standalone aleph projects too

lispyclouds 2019-03-05T13:47:01.062400Z

but afaik im using 0.4.6

lispyclouds 2019-03-05T13:47:58.062700Z

ls ~/.m2/repository/aleph/aleph/
0.4.6

kachayev 2019-03-05T13:50:26.063Z

Ah, I see

kachayev 2019-03-05T13:50:50.063700Z

https://github.com/ztellman/aleph/blame/626bec56dea84ddc5930f8bd9852fc1a236e23f7/src/aleph/http/server.clj#L113 this was changed only in 0.4.7-alpha2

kachayev 2019-03-05T13:50:57.063900Z

:thisisfine:

lispyclouds 2019-03-05T13:51:23.064100Z

aha 😄

lispyclouds 2019-03-05T13:53:35.065200Z

im not sure if its an aleph problem or something else, have a lot of moving parts in my code. can try it without the compojure-api routing

kachayev 2019-03-05T13:54:23.065800Z

I doubt it’s about routing

kachayev 2019-03-05T13:55:05.066600Z

Nevertheless, I will try to reproduce locally with just a single tar stream to see how it works

lispyclouds 2019-03-05T13:55:43.067400Z

Thanks a lot for the help! 😄 you have all the repos there too. lemme know if you need more info

kachayev 2019-03-05T14:05:22.068500Z

@rahul080327 another question, you said you’re using TarArchiveInputStream but the filename is *.tar… what do you want to archive? read file and return as a tar archive? or read an archive and return as unzipped file?

lispyclouds 2019-03-05T14:06:01.069300Z

I wanna return the file itself. a single download, as the path may contain multiple files

lispyclouds 2019-03-05T14:06:31.069800Z

the docker lib returns a tar stream of the path. i want to return an archive of that path over http

kachayev 2019-03-05T14:11:37.070600Z

if they’re reading directory to create a tar… shouldn’t that return TarArchiveOutputStream?

lispyclouds 2019-03-05T14:13:55.071900Z

should i convert to an output stream? i was under the impression aleph needs an input stream to send stuff over http

kachayev 2019-03-05T14:15:05.072500Z

no, but you don’t need to wrap it in TarArchiveInputStream either

kachayev 2019-03-05T14:15:41.073200Z

https://github.com/spotify/docker-client/blob/14b4464fb7acec25073c8c9c564136188f94562c/src/main/java/com/spotify/docker/client/DockerClient.java#L1348 this returns you an InputStream already (they just do a copy and then open you a stream to a copied archive)

kachayev 2019-03-05T14:16:02.073900Z

You need TarArchiveInputStream *only* in case you want to get raw files (decompressed)

lispyclouds 2019-03-05T14:17:38.075100Z

ah okay. But will this cause an issue for Aleph? TarArchiveInputStream is an InputStream too, so i thought it would be okay

kachayev 2019-03-05T14:17:47.075200Z

this works

kachayev 2019-03-05T14:18:06.075700Z

Tried with FileInputStream, - works fine

kachayev 2019-03-05T14:18:21.076200Z

Try this, let me know if it works

lispyclouds 2019-03-05T14:18:30.076400Z

okay

kachayev 2019-03-05T14:18:47.076600Z

👍

lispyclouds 2019-03-05T14:22:25.077400Z

@kachayev But these are the constructors of FileInputStream FileInputStream(File file) FileInputStream(FileDescriptor fdObj) FileInputStream(String name) and i have a InputStream

kachayev 2019-03-05T14:22:39.077600Z

Right

kachayev 2019-03-05T14:22:49.078Z

I’ve just showed that as an example

kachayev 2019-03-05T14:23:04.078400Z

You can set :body to your InputStream

lispyclouds 2019-03-05T14:23:34.078900Z

okay without the wrapping

kachayev 2019-03-05T14:23:45.079100Z

Yep, exactly

lispyclouds 2019-03-05T14:29:11.079400Z

Ah yesss, it works!!

lispyclouds 2019-03-05T14:29:20.079700Z

Thanks a ton! 😄 😄

kachayev 2019-03-05T14:29:41.080200Z

Sure! Let me know if you have more questions. Glad to help

lispyclouds 2019-03-05T14:30:15.081Z

will do. great learnings and kudos for having aleph around!

1👍