protected def headersToMap()

in app/helpers/S3Signer.scala [73:152]


  protected def headersToMap(headers: Seq[HttpHeader]) = headers.map(head=>Tuple2(head.name, head.value)).toMap

  /**
    * Asynchronously signs the given [[HttpRequest]] object using the credentials provider given
    * @param req Incoming HttpRequest
    * @param region AWS region to access
    * @param serviceName service name
    * @param credsProvider AWSCredentialsProvider instance (or chain) that gives us credentials
    * @return a Future with the updated [[HttpRequest]]
    */
  def signHttpRequest(req:HttpRequest, region:Region, serviceName:String, credsProvider:AwsCredentialsProvider, timestamp:Option[OffsetDateTime]=None) = {
    import scala.collection.immutable.Seq
    val checksummer = MessageDigest.getInstance("SHA-256")
    val requestTime = timestamp.getOrElse(OffsetDateTime.now(ZoneOffset.UTC))

    val contentHashFuture = if(req.entity.isKnownEmpty()) {
      logger.debug("request entity is empty")
      Future(ByteString(checksummer.digest("".getBytes("UTF-8"))))
    } else {
      logger.debug("request entity has data")
      req.entity.getDataBytes()
        .via(new ContentHashingFlow("SHA-256"))
        .runWith(Sink.reduce[ByteString](_.concat(_)), mat)
    }

    val contentHashHexFuture = contentHashFuture.map(bs=>bs.map("%02x".format(_)).mkString)

    contentHashHexFuture.onComplete({
      case Success(string)=>logger.debug(s"content hash string is $string")
      case Failure(err)=>logger.error(s"failed to generate content hash", err)
    })

    val credentials = credsProvider.resolveCredentials()

    val sessionTokenHeaders = credentials match {
      case session:AwsSessionCredentials=>
        Seq(makeHttpHeader("x-amz-security-token", session.sessionToken()))
      case _=>
        Seq()
    }

    val updatedHeadersFuture = contentHashHexFuture.map(hash=>
      req.headers ++ Seq(
        makeHttpHeader("x-amz-date",requestTime.format(aws_compatible_datetime)),
        makeHttpHeader("x-amz-content-sha256",hash)
      ) ++ sessionTokenHeaders
    )

    val canonStringFuture = updatedHeadersFuture.map(headers=> {
      val hash = contentHashHexFuture.value.get.get //this is safe, because updatedHeadersFuture is mapped from contentHashHexFuture; therefore if we got here, it succeeded.
      calculateCanonicalString(req.method.value, req.uri.path.toString(), convertQueryString(req.uri.rawQueryString), headersToMap(headers), Some(hash))
    })

    canonStringFuture.onComplete({
      case Success(str)=>logger.debug(s"canonicalString is $str")
      case Failure(err)=>logger.error(s"Canonical string failed", err)
    })

    val stringToSignFuture = canonStringFuture.map(cs=>stringToSign(region.getName, serviceName, cs, requestTime))

    stringToSignFuture.onComplete({
      case Success(str)=>logger.debug(s"stringToSign is $str")
      case Failure(err)=>logger.error(s"stringToSign failed", err)
    })

    val signingKeyResult = signingKey(credentials.secretAccessKey(), serviceName, region.getName, requestTime)
    val sig = stringToSignFuture.map(sts=>finalSignature(signingKeyResult, sts))

    Future.sequence(Seq(sig, updatedHeadersFuture)).map(results=>{
      val finalSig = results.head.asInstanceOf[String]
      val signedHeaders = results(1).asInstanceOf[Seq[HttpHeader]]
      val signedHeadersString = signedHeaders.map(_.name().toLowerCase()).sorted.mkString(";")
      val finalHeaders =  signedHeaders ++ Seq(
        makeHttpHeader("Authorization", s"AWS4-HMAC-SHA256 Credential=${credentials.accessKeyId()}/${requestTime.format(aws_compatible_date)}/$region/$serviceName/aws4_request,SignedHeaders=$signedHeadersString,Signature=$finalSig")
      )

      logger.debug(s"Final headers are: $finalHeaders")
      req.withHeaders(finalHeaders)
    })
  }