I can't test this as I am not at home but there are multiple form marshallers, one for url encoding and one for multipart called, I think, Multipart.FormData. sorry if you already know this.
Hi Eric! :P
AWS/S3 HTTP Post with a policy requires an HTTP post with multipart-mime -- one part for the file and one part, each, for various form parameters. When I use akka.http and attempt to marshal FormData to an Entity and use that to create a multi-part mime message, all the form data go into one mime-part using the query string format (foo=bar&baz=quux). AWS rejects such a request as it doesn't want all form parameters in a single www-url-encoded-form-data part, but rather separate mime parts for each parameter. How to I cause a Form to be martialled in this way using akka.http?
--
>>>>>>>>>> Read the docs: http://akka.io/docs/
>>>>>>>>>> Check the FAQ: http://doc.akka.io/docs/akka/current/additional/faq.html
>>>>>>>>>> Search the archives: https://groups.google.com/group/akka-user
---
You received this message because you are subscribed to the Google Groups "Akka User List" group.
To unsubscribe from this group and stop receiving emails from it, send an email to akka-user+...@googlegroups.com.
To post to this group, send email to akka...@googlegroups.com.
Visit this group at http://groups.google.com/group/akka-user.
For more options, visit https://groups.google.com/d/optout.
httpEntity <- Marshal(form).to[HttpEntity]
strictEntity <- httpEntity.toStrict(timeout)
} yield Multipart.FormData.BodyPart("foo", strictEntity)
gives me one body part with Content-Type x-www-url-encoded, and the set of all fields are encoded as in a query string.If you can point me to the "other" way of marshalling a form to a set of mime body parts, I'd appreciate it. I had pretty much given up (and googling and using stack overflow have turned up nothing) and was going to generate the parts manually. i'm sure there must be a correct way to do this. Thanks. -- Eric
val m = Map(
"key" -> "upload/quux.txt",
"acl" -> acl,
"policy" -> policy,
"X-amz-algorithm" -> "AWS4-HMAC-SHA256",
"X-amz-credential" -> s"$awsAccessKeyId/$shortDate/$region/$service/aws4_request",
"X-amz-date" -> date,
"X-amz-expires" -> expires.toString,
"X-amz-signature" -> signature
)
implicit val timeout: FiniteDuration = 10.seconds
val uri = s"http://$bucket.$service-$region.amazonaws.com"
// create request entities for each of the POST parameters. Note that generating Strict entities is an asynchronous
// operation (returns a future). Strict entities are NOT chunked. It appears AWS is not happy with chunked MIME
// body parts.
val strictFormDataEntityFuture = m.map { case (k, v) => (k, HttpEntity(ContentTypes.NoContentType, v).toStrict(timeout)) }
val responseFuture = for {
strictEntityMap <- Future.sequence(strictFormDataEntityFuture.map(entry => entry._2.map(i => (entry._1, i)))).map(_.toMap) recoverWith { case t: Throwable => throw t }
formDataBodyPartList = strictEntityMap.map {e => Multipart.FormData.BodyPart.Strict(e._1, e._2)}.toList
fileEntity <- HttpEntity(ContentTypes.`application/octet-stream`, 3, Source.single(ByteString("foo"))).toStrict(timeout) recoverWith { case t: Throwable => throw t }
multipartForm = Multipart.FormData(Source(formDataBodyPartList ++List(Multipart.FormData.BodyPart("file", fileEntity, Map("filename" -> "foo.bin")))))
requestEntity <- Marshal(multipartForm).to[RequestEntity] recoverWith { case t: Throwable => throw t }
strictRequestEntity <- requestEntity.toStrict(timeout) recoverWith { case t: Throwable => throw t }
response <- Http().singleRequest(HttpRequest(method = HttpMethods.POST, uri = uri, entity = strictRequestEntity)) recoverWith { case t: Throwable => throw t }
responseEntity <- Unmarshal(response.entity).to[String] recoverWith { case t: Throwable => throw t }
} yield (responseEntity, response)
If anyone can suggest a better approach, I'd appreciate it. Thanks.
You received this message because you are subscribed to a topic in the Google Groups "Akka User List" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/akka-user/ChTzhtmTvyU/unsubscribe.
To unsubscribe from this group and all its topics, send an email to akka-user+...@googlegroups.com.
AWS/S3 doesn’t want the form data parts, nor the whole HTTP POST to be chunked. So those entities have to be made “strict”. So I had to make each MIME part be strict, as well as the final Multipart.FormData entity.
The point of my original post was that it seems the kaka-http library has made a policy decision
that the only way to convert a FormData to an entity (in a multi-part MIME body) is to create a single part, using www-url-encoding, and include all fields in query string syntax. This doesn’t work for AWS and therefore it seems a limiting policy decision.
It should be equally possible/easy to convert a FormData to a collection of parts.
The requirement that there should be a Content-Type header in these parts, also should not be dictated by the framework.
Have you seen/tried http://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-streaming.html? It seems to suggest that chunked transfer-encoding is supported, though in a bit complicated format where on top of chunked transfer-encoding AWS seems to introduce their own chunked content-encoding layer where each chunk is signed separately.
The point of my original post was that it seems the kaka-http library has made a policy decisionWDYM? akka-http supports both `FormData` = www-url-encoding and `multipart/formdata` encodings, so, I wonder which "policy decision" you mean?
that the only way to convert a FormData to an entity (in a multi-part MIME body) is to create a single part, using www-url-encoding, and include all fields in query string syntax. This doesn’t work for AWS and therefore it seems a limiting policy decision.Yes, because `scaladsl.model.FormData` models www-url-encoding and not multipart/formdata. You seem to suggest that there should be a more general encoding that would allow marshalling to either of both variants. In any case, there's no policy decision but, as explained above, two different models for two different kinds of modelling things.
It should be equally possible/easy to convert a FormData to a collection of parts.Yes, that could be useful, or otherwise a more general representation of form data that unifies both kinds.The requirement that there should be a Content-Type header in these parts, also should not be dictated by the framework.I see that you have to fight a bit to get akka-http to render exactly what you want, but the reason seems to be mostly that AWS has very specific rules and interpretations of how to use HTTP.
akka-http is a general purpose HTTP library and cannot foresee all possible deviations or additional constraints HTTP APIs try to enforce. So, in the end, akka-http should make it possible to connect to these APIs (as long as they support HTTP to a large enough degree) but it may mean that the extra complexity the API enforces lies in your code. You could see that as a feature.
That said, I'm pretty sure that there's some sugar missing in the APIs that would help you build those requests more easily. If you can distill the generic helpers that are missing from your particular use case we could discuss how to improve things.Here are some things I can see:* you can directly build a `Multipart.FormData.Strict` which can be directly converted into a Strict entity, I guess one problem that we could solve is that there's only a non-strict FormData.BodyPart.fromFile` which builds a streamed part to prevent loading the complete file into memory. There's no `FormData.BodyPart.fromFile` that would actually load the file and create a strict version of it. We could add that (even if it wouldn't be recommended to load files into memory...)* having to run marshalling and unmarshalling manually could be replaced by some sugar
* dealing with those chains of `T => Future[U]` functions is cumbersome, for this and the previous point, we had the simple `pipelining` DSL in spray which seems to have been lost in parts in the transition to akka-http
Btw. I don't think your code is too bad, if you break it down into methods for every step and put it into a utility object, you can reuse it and don't need to deal with it any more.