Git Product home page Git Product logo

asto's Introduction

Join our Telegramm group

Javadoc License codecov Hits-of-Code Docker Pulls Docker Image Version (latest by date)

Artipie is a binary artifact management tool, similar to Artifactory, Nexus, Archiva, ProGet, and many others. The following set of features makes Artipie unique among all others:

Learn more about Artipie in our Wiki.

Publications about Artipie:

Quickstart

Artipie is distributed as Docker container and as fat jar. The jar file can be downloaded on the GitHub release page and here is a Wiki page describing how to start it. The fastest way to start Artipie is by using Docker container. First, make sure you have already installed Docker Engine. Then, open command line and instruct Docker Engine to run Artipie container:

docker run -it -p 8080:8080 -p 8086:8086 artipie/artipie:latest

It'll start a new Docker container with latest Artipie version, the command includes mapping of two ports: on port 8080 repositories are served and on port 8086 Artipie Rest API and Swagger documentation is provided. A new image generate default configuration, prints a list of running repositories, test credentials and a link to the Swagger documentation to console. To check existing repositories using Artipie Rest API:

  • go to Swagger documentation page http://localhost:8086/api/index.html, choose "Auth token" in "Select a definition" list,
  • generate and copy authentication token for user artipie/artipie,
  • switch to "Repositories" definition, press "Authorize" button and paste the token
  • then perform GET /api/v1/repository/list request. Response should be a json list with three default repositories:
[
  "my-bin",
  "my-docker",
  "my-maven"
]

Artipie server side (repositories) is served on 8080 port and is available on URI http://localhost:8080/{reponame}, where {reponame} is the name of the repository. Let's put some text data into binary repository:

curl -X PUT -d 'Hello world!' http://localhost:8080/my-bin/test.txt

With this request we added file test.txt containing text "Hello world!" into repository. Let's check it's really there:

curl -X GET http://localhost:8080/my-bin/test.txt

"Hello world!" should be printed in console.

To dive in deeper into Artipie configuration, features, explore repositories and storages settings, please, address our Wiki.

Default server configuration in Docker Container refers to /var/artipie/repo to look up for repository configurations. You may want to mount local configurations <your-local-config-dir> to /var/artipie/repo to check and edit it manually.

Important: for provided Artipie docker containers <your-local-config-dir> should have directory ownership set to 2021:2020. To change it correctly use sudo chown -R 2021:2020 <your-local-config-dir>.

If you have any question or suggestions, do not hesitate to create an issue or contact us in Telegram.
Artipie roadmap.

How to contribute

Fork repository, make changes, send us a pull request. We will review your changes and apply them to the master branch shortly, provided they don't violate our quality standards. To avoid frustration, before sending us your pull request please run full Maven build:

$ mvn clean install

To avoid build errors use Maven 3.2+ and please read contributing rules.

Thanks to FreePik for the logo.

How to release

Artipie service is released in several formats:

All these distributions are created by GitHub workflows. To publish release, push tag starting with v into this repository masted branch:

git tag v1.2.0
git push --tags origin

asto's People

Contributors

acheshkov avatar andreoss avatar baudoliver7 avatar brastak avatar dependabot-preview[bot] avatar dependabot[bot] avatar dgarus avatar g4s8 avatar genryxy avatar guseyn avatar olegmoz avatar olenagerasimova avatar rultor avatar sammers21 avatar swizbiz avatar yegor256 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

asto's Issues

FileStorage#save causes IllegalFormatConversionException when info logging is enabled

In case when INFO log level is enabled, any FileStorage#save operation fails with the following stack trace:

java.util.concurrent.ExecutionException: java.util.IllegalFormatConversionException: d != [B

	at java.base/java.util.concurrent.CompletableFuture.reportGet(CompletableFuture.java:395)
	at java.base/java.util.concurrent.CompletableFuture.get(CompletableFuture.java:1999)
	at com.artipie.asto.StorageTest.savesAndLoads(StorageTest.java:67)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:566)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at org.junit.rules.ExternalResource$1.evaluate(ExternalResource.java:48)
	at org.junit.rules.RunRules.evaluate(RunRules.java:20)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
	at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
	at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
	at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
	at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:230)
	at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:58)
Caused by: java.util.IllegalFormatConversionException: d != [B
	at java.base/java.util.Formatter$FormatSpecifier.failConversion(Formatter.java:4426)
	at java.base/java.util.Formatter$FormatSpecifier.printInteger(Formatter.java:2938)
	at java.base/java.util.Formatter$FormatSpecifier.print(Formatter.java:2892)
	at java.base/java.util.Formatter.format(Formatter.java:2673)
	at java.base/java.util.Formatter.format(Formatter.java:2609)
	at java.base/java.lang.String.format(String.java:2897)
	at com.jcabi.log.Logger.format(Logger.java:128)
	at com.jcabi.log.Logger.infoForced(Logger.java:247)
	at com.jcabi.log.Logger.info(Logger.java:232)
	at com.artipie.asto.fs.FileStorage.lambda$save$6(FileStorage.java:120)
	at io.reactivex.rxjava3.internal.operators.completable.CompletableFromAction.subscribeActual(CompletableFromAction.java:35)
	at io.reactivex.rxjava3.core.Completable.subscribe(Completable.java:2279)
	at io.reactivex.rxjava3.internal.operators.single.SingleFlatMapCompletable$FlatMapCompletableObserver.onSuccess(SingleFlatMapCompletable.java:91)
	at io.reactivex.rxjava3.internal.operators.single.SingleMap$MapSingleObserver.onSuccess(SingleMap.java:64)
	at io.reactivex.rxjava3.internal.operators.flowable.FlowableToListSingle$ToListSubscriber.onComplete(FlowableToListSingle.java:102)
	at io.reactivex.rxjava3.internal.operators.flowable.FlowableFromArray$ArraySubscription.fastPath(FlowableFromArray.java:138)
	at io.reactivex.rxjava3.internal.operators.flowable.FlowableFromArray$BaseArraySubscription.request(FlowableFromArray.java:88)
	at io.reactivex.rxjava3.internal.operators.flowable.FlowableToListSingle$ToListSubscriber.onSubscribe(FlowableToListSingle.java:83)
	at io.reactivex.rxjava3.internal.operators.flowable.FlowableFromArray.subscribeActual(FlowableFromArray.java:38)
	at io.reactivex.rxjava3.core.Flowable.subscribe(Flowable.java:14816)
	at io.reactivex.rxjava3.internal.operators.flowable.FlowableToListSingle.subscribeActual(FlowableToListSingle.java:56)
	at io.reactivex.rxjava3.core.Single.subscribe(Single.java:3693)
	at io.reactivex.rxjava3.internal.operators.single.SingleMap.subscribeActual(SingleMap.java:34)
	at io.reactivex.rxjava3.core.Single.subscribe(Single.java:3693)
	at io.reactivex.rxjava3.internal.operators.single.SingleFlatMapCompletable.subscribeActual(SingleFlatMapCompletable.java:44)
	at io.reactivex.rxjava3.core.Completable.subscribe(Completable.java:2279)
	at io.reactivex.rxjava3.core.Completable.subscribe(Completable.java:2353)
	at hu.akarnokd.rxjava3.jdk8interop.CompletableInterop.lambda$await$1(CompletableInterop.java:46)
	at io.reactivex.rxjava3.core.Completable.to(Completable.java:2570)
	at com.artipie.asto.fs.FileStorage.save(FileStorage.java:128)
	at com.artipie.asto.StorageTest.savesAndLoads(StorageTest.java:60)

Validate written data in FileStorage

It might be a good idea, to validate just written file checksum. It may happen that something went wrong writing the file. For example we had a bug when last few bytes of file were missing, something may go wrong with file sytem, etc

Add parent() method to the Key

Key is designed with the hierarchical nature in the mind. We have constructors that:

  • create Key from the parts;
  • create Key from the base Key and the rest parts.

But we don't have the methods to get parent Key from the current.

RxJava Storage wrapper

All of the adapters use old, reactive storage API. To make easy to migrate to the newest version of asto we need a reactive wrapper.

Add ctor to From with UUID

Add ctor to com.artipie.asto.Key.From that accept the UUID parameter.
That ctor could be used in com.artipie.composer.http.Root#put

Assertion messages and matchers

We have a lot of assertions in our tests that doesn't show the proper message when failed or that are using static matchers. We should fix them.

java.lang.NoSuchMethodError: java.nio.ByteBuffer.flip()Ljava/nio/ByteBuffer

RxFile is trying to use ByteBuffer.flip() method which is not available on JDK8, related to #78

java.lang.NoSuchMethodError: java.nio.ByteBuffer.flip()Ljava/nio/ByteBuffer;

	at com.artipie.asto.fs.RxFile.lambda$save$5(RxFile.java:126)
	at java.util.stream.ReduceOps$2ReducingSink.accept(ReduceOps.java:123)
	at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1382)
	at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:482)
	at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:472)
	at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
	at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
	at java.util.stream.ReferencePipeline.reduce(ReferencePipeline.java:546)
	at com.artipie.asto.fs.RxFile.lambda$save$7(RxFile.java:121)
	at io.reactivex.internal.operators.completable.CompletableFromAction.subscribeActual(CompletableFromAction.java:35)
	at io.reactivex.Completable.subscribe(Completable.java:2309)
	at io.reactivex.internal.operators.single.SingleFlatMapCompletable$FlatMapCompletableObserver.onSuccess(SingleFlatMapCompletable.java:91)
	at io.reactivex.internal.operators.single.SingleFromCallable.subscribeActual(SingleFromCallable.java:56)
	at io.reactivex.Single.subscribe(Single.java:3603)
	at io.reactivex.internal.operators.single.SingleFlatMapCompletable.subscribeActual(SingleFlatMapCompletable.java:44)
	at io.reactivex.Completable.subscribe(Completable.java:2309)
	at io.reactivex.Completable.subscribe(Completable.java:2383)
	at hu.akarnokd.rxjava2.interop.CompletableInterop.lambda$await$1(CompletableInterop.java:47)
	at io.reactivex.Completable.to(Completable.java:2597)
	at com.artipie.asto.fs.FileStorage.save(FileStorage.java:138)
	at com.artipie.rpm.RpmTest.addsSingleRpm(RpmTest.java:89)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at org.junit.rules.ExternalResource$1.evaluate(ExternalResource.java:48)
	at org.junit.rules.RunRules.evaluate(RunRules.java:20)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
	at org.junit.runner.JUnitCore.run(JUnitCore.java:137)

FileStorageTest.java:43-46: The combination of RxFile and...

The puzzle 53-9c70e0d7 from #53 has to be resolved:

* @todo #53:30min The combination of RxFile and TempDir Junit5 rule
* doesn't work on Windows. It seems that Junit unable to cleanup
* temporary directory. Fix RxFile implementation and remove disable
* annotation.

The puzzle was created by @g4s8 on 12-Feb-20.

Estimate: 30 minutes, role: DEV.

If you have any technical questions, don't ask me, submit new tickets instead. The task will be "done" when the problem is fixed and the text of the puzzle is removed from the source code. Here is more about PDD and about me.

Key should not allow empty parts

Key parts should not be empty strings. Empty parts make no sense and are problematic to implement in many stores such as FileStroage, S3Storage

Improve FileStorage implementation

Not it works by reading all the bytes from a file and keeping them in memory. It is a simplistic approach. The approach can be improved by keeping only a constant amount of bytes in memory

Storage.list() should accept Key as prefix

Now Storage.list() method accepts String is prefix and expects it to be a file path actually. FileStorage for example verifies last symbol to be OS dependent path separator, which makes it impossible to use Storage without knowing your OS:

final String separator = FileSystems.getDefault().getSeparator();
if (!prefix.endsWith(separator)) {
  throw new IllegalArgumentException(...);
}

MultipartUpload.java:83-85: Refactor MultipartUpload...

The puzzle 87-0ebc33ba from #87 has to be resolved:

* @todo #87:60min Refactor MultipartUpload class, reduce number of fields.
* MultipartUpload class is too big right now in terms of fields and could be decomposed.
* S3Bucket or S3Object classes might be extracted here.

The puzzle was created by @olegmoz on 04-Mar-20.

Estimate: 60 minutes, role: DEV.

If you have any technical questions, don't ask me, submit new tickets instead. The task will be "done" when the problem is fixed and the text of the puzzle is removed from the source code. Here is more about PDD and about me.

S3Storage.java:54-59: Do not await abort to complete if...

The puzzle 87-c49a9b48 from #87 has to be resolved:

* @todo #87:60min Do not await abort to complete if save() failed.
* In case uploading content fails inside {@link S3Storage#save(Key, Content)} method
* we are doing abort() for multipart upload.
* Also whole operation does not complete until abort() is complete.
* It would be better to finish save() operation right away and do abort() in background,
* but it makes testing the method difficult.

The puzzle was created by Oleg Mozzhechkov on 12-Mar-20.

Estimate: 60 minutes, role: DEV.

If you have any technical questions, don't ask me, submit new tickets instead. The task will be "done" when the problem is fixed and the text of the puzzle is removed from the source code. Here is more about PDD and about me.

Storage.list() should accept Key as prefix

Now Storage.list() method accepts String is prefix and expects it to be a file path actually. FileStorage for example verifies last symbol to be OS dependent path separator, which makes it impossible to use Storage without knowing your OS:

final String separator = FileSystems.getDefault().getSeparator();
if (!prefix.endsWith(separator)) {
  throw new IllegalArgumentException(...);
}

Key improvements

Some improvements proposed for Key class:

  • parts field should be of type List, because naturally it is ordered and has limited size.
  • Add Key.From() constructor to create Key from /-delimited string. It is useful to create Key from strings created by Key.string() method and from URI path which is going to be very useful in adapters.
  • Part string should not contain / symbol, otherwise it cannot be to string and from string.

Add delete method to Storage

Delete method should remove particular value from Storage by key. If value not exists then the method should fail. So if it is a file storage it should not allow to delete folders, only files.

FileStorage incorrectly processes list(Key.ROOT) call

Calculation of path prefixes works incorrectly when Key.ROOT is passed to the list method.

Let's assume:

  • base folder is /storage
  • artifact is persisted in /storage/artifact.bin

When FileStorage.list() is called for the ROOT, than prefix length is 8 ("/storage".length() + "".length()) and the rest part of the path is /artifact.bin. We can not build key for this string and method fails (it splits to ["", "artifact.bin"] array and key can not contain empty parts).

So we need either support Key construction from the string with the leading "/" or change FileStorage to handle Key.ROOT a special way

Transactions support

Asto should support transactions in order to guarantee atomic updates of several keys.

BlockingStorage#save is broken

BlockingStorage#save does not work because of underlying CompletableFuture which emits null:

SEVERE: Unhandled exception
java.lang.NullPointerException: The future returned a null value. Null values are generally not allowed in 3.x operators and sources.
	at io.reactivex.rxjava3.internal.util.ExceptionHelper.createNullPointerException(ExceptionHelper.java:164)
	at io.reactivex.rxjava3.internal.operators.flowable.FlowableFromFuture.subscribeActual(FlowableFromFuture.java:52)
	at io.reactivex.rxjava3.core.Flowable.subscribe(Flowable.java:14816)
	at io.reactivex.rxjava3.internal.operators.flowable.FlowableSingleSingle.subscribeActual(FlowableSingleSingle.java:39)
	at io.reactivex.rxjava3.core.Single.subscribe(Single.java:3693)
	at io.reactivex.rxjava3.core.Single.blockingGet(Single.java:2894)
	at com.artipie.asto.blocking.BlockingStorage.save(BlockingStorage.java:96)
	at com.artipie.npm.NpmRegistry.lambda$putPackage$0(NpmRegistry.java:167)
	at io.vertx.reactivex.core.http.HttpServerRequest$2.handle(HttpServerRequest.java:363)
	at io.vertx.reactivex.core.http.HttpServerRequest$2.handle(HttpServerRequest.java:361)
	at io.vertx.core.http.HttpServerRequest.lambda$bodyHandler$0(HttpServerRequest.java:216)
	at io.vertx.core.http.impl.HttpServerRequestImpl.onEnd(HttpServerRequestImpl.java:530)
	at io.vertx.core.http.impl.HttpServerRequestImpl.handleEnd(HttpServerRequestImpl.java:516)
	at io.vertx.core.http.impl.Http1xServerConnection.handleEnd(Http1xServerConnection.java:176)
	at io.vertx.core.http.impl.Http1xServerConnection.handleContent(Http1xServerConnection.java:163)
	at io.vertx.core.http.impl.Http1xServerConnection.handleMessage(Http1xServerConnection.java:140)
	at io.vertx.core.impl.ContextImpl.executeTask(ContextImpl.java:369)
	at io.vertx.core.impl.EventLoopContext.execute(EventLoopContext.java:43)
	at io.vertx.core.impl.ContextImpl.executeFromIO(ContextImpl.java:232)
	at io.vertx.core.net.impl.VertxHandler.channelRead(VertxHandler.java:173)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:374)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:360)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:352)
	at io.netty.channel.ChannelInboundHandlerAdapter.channelRead(ChannelInboundHandlerAdapter.java:93)
	at io.netty.handler.codec.http.websocketx.extensions.WebSocketServerExtensionHandler.channelRead(WebSocketServerExtensionHandler.java:102)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:374)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:360)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:352)
	at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:328)
	at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:302)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:374)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:360)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:352)
	at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1422)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:374)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:360)
	at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:931)
	at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:163)
	at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:700)
	at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:635)
	at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:552)
	at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:514)
	at io.netty.util.concurrent.SingleThreadEventExecutor$6.run(SingleThreadEventExecutor.java:1044)
	at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
	at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
	at java.base/java.lang.Thread.run(Thread.java:834)

FileStorage.exists() should not return `true` for directories

Now FileStorage.exists() returns true for directories, so it is possible to have only key a/b/c in repository, but exists() will return true for key a/b. And at the same time value() methods will fail for this key. This behavior is inconsistent.

Remove duplication of code for ByteBuffer concatination

It is very common in tests (and in production code as well), to concatenate Content which is a Publisher<ByteBuffer> into single byte array. This operation requires a lot of code which is now copied and pasted across all Artipie modules.

Pass value content length along with content bytes in `Storage`

Now Storage.save() method accepts flow of bytes and there is no way to know total content size before reading them all. But actual content length is often known to storage client and might be used by storage implementations for some optimization (such as allocating right amount of memory, choosing right uploading strategy etc).
Same goes for value() method. Storage usually knows content length in advance and may pass it before actual data is read to make things easier for consumer.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.