diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,17 @@
 Changelog
 ==========
 
+## Version 1.5.0
+
+* Switch to faster map data type - all previous usage of
+  Data.Map.Strict and Data.Set is replaced with Data.HashMap.Strict
+  and Data.HashSet.
+* Add `oiUserMetadata` to parse and return user metadata stored with
+  an object.
+* Add `GetObjectResponse` data type for the value returned by
+  `getObject`. It now contains parsed ObjectInfo along with the
+  conduit of object bytes.
+
 ## Version 1.4.0
 
 * Expose runMinioRes and runMinioResWith (#129)
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -25,7 +25,7 @@
 ```
 
 A section of the tests use the remote MinIO Play server at
-`https://play.min.io:9000` by default. For library development,
+`https://play.min.io` by default. For library development,
 using this remote server maybe slow. To run the tests against a
 locally running MinIO live server at `http://localhost:9000`, just set
 the environment `MINIO_LOCAL` to any value (and unset it to switch
@@ -47,7 +47,7 @@
 -- stack --resolver lts-11.1 runghc --package minio-hs --package optparse-applicative --package filepath
 
 --
--- MinIO Haskell SDK, (C) 2017, 2018 MinIO, Inc.
+-- MinIO Haskell SDK, (C) 2017-2019 MinIO, Inc.
 --
 -- Licensed under the Apache License, Version 2.0 (the "License");
 -- you may not use this file except in compliance with the License.
@@ -76,7 +76,7 @@
 import           Prelude
 
 -- | The following example uses minio's play server at
--- https://play.min.io:9000.  The endpoint and associated
+-- https://play.min.io.  The endpoint and associated
 -- credentials are provided via the libary constant,
 --
 -- > minioPlayCI :: ConnectInfo
diff --git a/docs/API.md b/docs/API.md
--- a/docs/API.md
+++ b/docs/API.md
@@ -20,15 +20,15 @@
 
 ```
 
-|Bucket operations|Object Operations|Presigned Operations|
-|:---|:---|:---|
-|[`listBuckets`](#listBuckets) |[`getObject`](#getObject)|[`presignedGetObjectUrl`](#presignedGetObjectUrl)|
-|[`makeBucket`](#makeBucket)|[`putObject`](#putObject)|[`presignedPutObjectUrl`](#presignedPutObjectUrl)|
-|[`removeBucket`](#removeBucket)|[`fGetObject`](#fGetObject)|[`presignedPostPolicy`](#presignedPostPolicy)|
-|[`listObjects`](#listObjects)|[`fPutObject`](#fPutObject)||
-|[`listObjectsV1`](#listObjectsV1)|[`copyObject`](#copyObject)||
-|[`listIncompleteUploads`](#listIncompleteUploads)|[`removeObject`](#removeObject)||
-|[`bucketExists`](#bucketExists)|[`selectObjectContent`](#selectObjectContent)||
+| Bucket operations                                 | Object Operations                             | Presigned Operations                              |
+|:--------------------------------------------------|:----------------------------------------------|:--------------------------------------------------|
+| [`listBuckets`](#listBuckets)                     | [`getObject`](#getObject)                     | [`presignedGetObjectUrl`](#presignedGetObjectUrl) |
+| [`makeBucket`](#makeBucket)                       | [`putObject`](#putObject)                     | [`presignedPutObjectUrl`](#presignedPutObjectUrl) |
+| [`removeBucket`](#removeBucket)                   | [`fGetObject`](#fGetObject)                   | [`presignedPostPolicy`](#presignedPostPolicy)     |
+| [`listObjects`](#listObjects)                     | [`fPutObject`](#fPutObject)                   |                                                   |
+| [`listObjectsV1`](#listObjectsV1)                 | [`copyObject`](#copyObject)                   |                                                   |
+| [`listIncompleteUploads`](#listIncompleteUploads) | [`removeObject`](#removeObject)               |                                                   |
+| [`bucketExists`](#bucketExists)                   | [`selectObjectContent`](#selectObjectContent) |                                                   |
 
 ## 1. Connecting and running operations on the storage service
 
@@ -69,16 +69,16 @@
 
 The parameters in the expression `awsWithRegion region autoDiscover` are:
 
-|Parameter|Type|Description|
-|:---|:---|:---|
-| `region` | _Region_ (alias for `Text`) | The region to connect to by default for all requests. |
-| `autoDiscover` | _Bool_ | If `True`, region discovery will be enabled. If `False`, discovery is disabled, and all requests go the given region only.|
+| Parameter      | Type                        | Description                                                                                                                |
+|:---------------|:----------------------------|:---------------------------------------------------------------------------------------------------------------------------|
+| `region`       | _Region_ (alias for `Text`) | The region to connect to by default for all requests.                                                                      |
+| `autoDiscover` | _Bool_                      | If `True`, region discovery will be enabled. If `False`, discovery is disabled, and all requests go the given region only. |
 
 #### minioPlayCI :: ConnectInfo
 
 This constructor provides connection and authentication information to
 connect to the public MinIO Play server at
-`https://play.min.io:9000/`.
+`https://play.min.io/`.
 
 #### minioCI :: Text -> Int -> Bool -> ConnectInfo
 
@@ -86,25 +86,25 @@
 
 The parameters in the expression `minioCI host port isSecure` are:
 
-|Parameter|Type|Description|
-|:---|:---|:---|
-| `host` | _Text_ | Hostname of the MinIO or other S3-API compatible server |
-| `port` | _Int_ | Port number to connect to|
-| `isSecure` | _Bool_ | Does the server use HTTPS? |
+| Parameter  | Type   | Description                                             |
+|:-----------|:-------|:--------------------------------------------------------|
+| `host`     | _Text_ | Hostname of the MinIO or other S3-API compatible server |
+| `port`     | _Int_  | Port number to connect to                               |
+| `isSecure` | _Bool_ | Does the server use HTTPS?                              |
 
 #### The ConnectInfo fields and Default instance
 
 The following table shows the fields in the `ConnectInfo` record-type:
 
-| Field | Type | Description |
-|:---|:---|:---|
-| `connectHost` | _Text_ | Host name of the server. Defaults to `localhost`. |
-| `connectPort` | _Int_ | Port number on which the server listens. Defaults to `9000`. |
-| `connectAccessKey` | _Text_ | Access key to use in authentication. Defaults to `minio`. |
-| `connectSecretkey` | _Text_ | Secret key to use in authentication. Defaults to `minio123`. |
-| `connectIsSecure` | _Bool_ | Specifies if the server used TLS. Defaults to `False` |
-| `connectRegion` | _Region_ (alias for `Text`) | Specifies the region to use. Defaults to 'us-east-1' |
-| `connectAutoDiscoverRegion` | _Bool_ | Specifies if the library should automatically discover the region of a bucket. Defaults to `True`|
+| Field                       | Type                        | Description                                                                                       |
+|:----------------------------|:----------------------------|:--------------------------------------------------------------------------------------------------|
+| `connectHost`               | _Text_                      | Host name of the server. Defaults to `localhost`.                                                 |
+| `connectPort`               | _Int_                       | Port number on which the server listens. Defaults to `9000`.                                      |
+| `connectAccessKey`          | _Text_                      | Access key to use in authentication. Defaults to `minio`.                                         |
+| `connectSecretkey`          | _Text_                      | Secret key to use in authentication. Defaults to `minio123`.                                      |
+| `connectIsSecure`           | _Bool_                      | Specifies if the server used TLS. Defaults to `False`                                             |
+| `connectRegion`             | _Region_ (alias for `Text`) | Specifies the region to use. Defaults to 'us-east-1'                                              |
+| `connectAutoDiscoverRegion` | _Bool_                      | Specifies if the library should automatically discover the region of a bucket. Defaults to `True` |
 
 The `def` value of type `ConnectInfo` has all the above default
 values.
@@ -148,17 +148,17 @@
 
 __Return Value__
 
-|Return type   |Description   |
-|:---|:---|
-| _Minio [BucketInfo]_| List of buckets |
+| Return type          | Description     |
+|:---------------------|:----------------|
+| _Minio [BucketInfo]_ | List of buckets |
 
 
 __BucketInfo record type__
 
-|Field   |Type   |Description   |
-|:---|:---| :---|
-| `biName` | _Bucket_ (alias of `Text`) | Name of the bucket |
-| `biCreationDate` | _UTCTime_ | Creation time of the bucket |
+| Field            | Type                       | Description                 |
+|:-----------------|:---------------------------|:----------------------------|
+| `biName`         | _Bucket_ (alias of `Text`) | Name of the bucket          |
+| `biCreationDate` | _UTCTime_                  | Creation time of the bucket |
 
 
 <a name="makeBucket"></a>
@@ -171,10 +171,10 @@
 
 In the expression `makeBucket bucketName region` the arguments are:
 
-| Param  | Type  | Description  |
-|---|---|---|
-|`bucketName`  | _Bucket_ (alias for `Text`) | Name of the bucket |
-| `region`  |  _Maybe Region_ | Region where the bucket is to be created. If not specified, default to the region in `ConnectInfo`.|
+| Param        | Type                        | Description                                                                                         |
+|--------------|-----------------------------|-----------------------------------------------------------------------------------------------------|
+| `bucketName` | _Bucket_ (alias for `Text`) | Name of the bucket                                                                                  |
+| `region`     | _Maybe Region_              | Region where the bucket is to be created. If not specified, default to the region in `ConnectInfo`. |
 
 __Example__
 
@@ -200,9 +200,9 @@
 
 In the expression `removeBucket bucketName` the arguments are:
 
-| Param  | Type  | Description  |
-|---|---|---|
-|`bucketName`  | _Bucket_ (alias for `Text`)  | Name of the bucket |
+| Param        | Type                        | Description        |
+|--------------|-----------------------------|--------------------|
+| `bucketName` | _Bucket_ (alias for `Text`) | Name of the bucket |
 
 
 __Example__
@@ -233,26 +233,27 @@
 In the expression `listObjects bucketName prefix recursive` the
 arguments are:
 
-|Param   |Type   |Description   |
-|:---|:---| :---|
-| `bucketName`  | _Bucket_ (alias for `Text`)  | Name of the bucket |
-| `prefix` | _Maybe Text_  | Optional prefix that listed objects should have |
-| `recursive`  | _Bool_  |`True` indicates recursive style listing and `False` indicates directory style listing delimited by '/'.  |
+| Param        | Type                        | Description                                                                                              |
+|:-------------|:----------------------------|:---------------------------------------------------------------------------------------------------------|
+| `bucketName` | _Bucket_ (alias for `Text`) | Name of the bucket                                                                                       |
+| `prefix`     | _Maybe Text_                | Optional prefix that listed objects should have                                                          |
+| `recursive`  | _Bool_                      | `True` indicates recursive style listing and `False` indicates directory style listing delimited by '/'. |
 
 __Return Value__
 
-|Return type   |Description   |
-|:---|:---|
-| _C.ConduitM () ObjectInfo Minio ()_  | A Conduit Producer of `ObjectInfo` values corresponding to each object. |
+| Return type                         | Description                                                             |
+|:------------------------------------|:------------------------------------------------------------------------|
+| _C.ConduitM () ObjectInfo Minio ()_ | A Conduit Producer of `ObjectInfo` values corresponding to each object. |
 
 __ObjectInfo record type__
 
-|Field   |Type   |Description   |
-|:---|:---| :---|
-|`oiObject`  | _Object_ (alias for `Text`) | Name of object |
-|`oiModTime` | _UTCTime_ | Last modified time of the object |
-|`oiETag` | _ETag_ (alias for `Text`) | ETag of the object |
-|`oiSize` | _Int64_ | Size of the object in bytes  |
+| Field        | Type                        | Description                          |
+|:-------------|:----------------------------|:-------------------------------------|
+| `oiObject`   | _Object_ (alias for `Text`) | Name of object                       |
+| `oiModTime`  | _UTCTime_                   | Last modified time of the object     |
+| `oiETag`     | _ETag_ (alias for `Text`)   | ETag of the object                   |
+| `oiSize`     | _Int64_                     | Size of the object in bytes          |
+| `oiMetadata` | _HashMap Text Text_         | Map of key-value user-metadata pairs |
 
 __Example__
 
@@ -265,7 +266,7 @@
 
 
 -- | The following example uses MinIO play server at
--- https://play.min.io:9000.  The endpoint and associated
+-- https://play.min.io.  The endpoint and associated
 -- credentials are provided via the libary constant,
 --
 -- > minioPlayCI :: ConnectInfo
@@ -294,26 +295,26 @@
 In the expression `listObjectsV1 bucketName prefix recursive` the
 arguments are:
 
-|Param   |Type   |Description   |
-|:---|:---| :---|
-| `bucketName`  | _Bucket_ (alias for `Text`)  | Name of the bucket |
-| `prefix` | _Maybe Text_  | Optional prefix that listed objects should have |
-| `recursive`  | _Bool_  |`True` indicates recursive style listing and `False` indicates directory style listing delimited by '/'.  |
+| Param        | Type                        | Description                                                                                              |
+|:-------------|:----------------------------|:---------------------------------------------------------------------------------------------------------|
+| `bucketName` | _Bucket_ (alias for `Text`) | Name of the bucket                                                                                       |
+| `prefix`     | _Maybe Text_                | Optional prefix that listed objects should have                                                          |
+| `recursive`  | _Bool_                      | `True` indicates recursive style listing and `False` indicates directory style listing delimited by '/'. |
 
 __Return Value__
 
-|Return type   |Description   |
-|:---|:---|
-| _C.ConduitM () ObjectInfo Minio ()_  | A Conduit Producer of `ObjectInfo` values corresponding to each object. |
+| Return type                         | Description                                                             |
+|:------------------------------------|:------------------------------------------------------------------------|
+| _C.ConduitM () ObjectInfo Minio ()_ | A Conduit Producer of `ObjectInfo` values corresponding to each object. |
 
 __ObjectInfo record type__
 
-|Field   |Type   |Description   |
-|:---|:---| :---|
-|`oiObject`  | _Object_ (alias for `Text`) | Name of object |
-|`oiModTime` | _UTCTime_ | Last modified time of the object |
-|`oiETag` | _ETag_ (alias for `Text`) | ETag of the object |
-|`oiSize` | _Int64_ | Size of the object in bytes  |
+| Field       | Type                        | Description                      |
+|:------------|:----------------------------|:---------------------------------|
+| `oiObject`  | _Object_ (alias for `Text`) | Name of object                   |
+| `oiModTime` | _UTCTime_                   | Last modified time of the object |
+| `oiETag`    | _ETag_ (alias for `Text`)   | ETag of the object               |
+| `oiSize`    | _Int64_                     | Size of the object in bytes      |
 
 __Example__
 
@@ -326,7 +327,7 @@
 
 
 -- | The following example uses MinIO play server at
--- https://play.min.io:9000.  The endpoint and associated
+-- https://play.min.io.  The endpoint and associated
 -- credentials are provided via the libary constant,
 --
 -- > minioPlayCI :: ConnectInfo
@@ -354,11 +355,11 @@
 In the expression `listIncompleteUploads bucketName prefix recursive`
 the parameters are:
 
-|Param   |Type   |Description   |
-|:---|:---| :---|
-| `bucketName`  | _Bucket_ (alias for `Text`)  | Name of the bucket |
-| `prefix` | _Maybe Text_  | Optional prefix that listed objects should have. |
-| `recursive`  | _Bool_  |`True` indicates recursive style listing and `Talse` indicates directory style listing delimited by '/'.  |
+| Param        | Type                        | Description                                                                                              |
+|:-------------|:----------------------------|:---------------------------------------------------------------------------------------------------------|
+| `bucketName` | _Bucket_ (alias for `Text`) | Name of the bucket                                                                                       |
+| `prefix`     | _Maybe Text_                | Optional prefix that listed objects should have.                                                         |
+| `recursive`  | _Bool_                      | `True` indicates recursive style listing and `Talse` indicates directory style listing delimited by '/'. |
 
 __Return Value__
 
@@ -368,11 +369,11 @@
 
 __UploadInfo record type__
 
-|Field   |Type   |Description   |
-|:---|:---| :---|
-|`uiKey`  | _Object_  |Name of incompletely uploaded object |
-|`uiUploadId` | _String_ |Upload ID of incompletely uploaded object |
-|`uiSize` | _Int64_ |Size of incompletely uploaded object |
+| Field        | Type     | Description                               |
+|:-------------|:---------|:------------------------------------------|
+| `uiKey`      | _Object_ | Name of incompletely uploaded object      |
+| `uiUploadId` | _String_ | Upload ID of incompletely uploaded object |
+| `uiSize`     | _Int64_  | Size of incompletely uploaded object      |
 
 __Example__
 
@@ -384,7 +385,7 @@
 import           Prelude
 
 -- | The following example uses MinIO play server at
--- https://play.min.io:9000.  The endpoint and associated
+-- https://play.min.io.  The endpoint and associated
 -- credentials are provided via the libary constant,
 --
 -- > minioPlayCI :: ConnectInfo
@@ -415,29 +416,29 @@
 In the expression `getObject bucketName objectName opts` the parameters
 are:
 
-|Param   |Type   |Description   |
-|:---|:---| :---|
-| `bucketName`  | _Bucket_ (alias for `Text`)  | Name of the bucket |
-| `objectName` | _Object_ (alias for `Text`)  | Name of the object |
-| `opts`   | _GetObjectOptions_ | Options for GET requests specifying additional options like If-Match, Range |
+| Param        | Type                        | Description                                                                 |
+|:-------------|:----------------------------|:----------------------------------------------------------------------------|
+| `bucketName` | _Bucket_ (alias for `Text`) | Name of the bucket                                                          |
+| `objectName` | _Object_ (alias for `Text`) | Name of the object                                                          |
+| `opts`       | _GetObjectOptions_          | Options for GET requests specifying additional options like If-Match, Range |
 
 __GetObjectOptions record type__
 
-|Field   |Type   |Description   |
-|:---|:---| :---|
-| `gooRange` | `Maybe ByteRanges` | Represents the byte range of object. E.g ByteRangeFromTo 0 9 represents first ten bytes of the object|
-| `gooIfMatch` | `Maybe ETag` (alias for `Text`) | (Optional) ETag of object should match |
-| `gooIfNoneMatch` | `Maybe ETag` (alias for `Text`) | (Optional) ETag of object shouldn't match |
-| `gooIfUnmodifiedSince` | `Maybe UTCTime` | (Optional) Time since object wasn't modified |
-| `gooIfModifiedSince` | `Maybe UTCTime` | (Optional) Time since object was modified |
+| Field                  | Type                            | Description                                                                                           |
+|:-----------------------|:--------------------------------|:------------------------------------------------------------------------------------------------------|
+| `gooRange`             | `Maybe ByteRanges`              | Represents the byte range of object. E.g ByteRangeFromTo 0 9 represents first ten bytes of the object |
+| `gooIfMatch`           | `Maybe ETag` (alias for `Text`) | (Optional) ETag of object should match                                                                |
+| `gooIfNoneMatch`       | `Maybe ETag` (alias for `Text`) | (Optional) ETag of object shouldn't match                                                             |
+| `gooIfUnmodifiedSince` | `Maybe UTCTime`                 | (Optional) Time since object wasn't modified                                                          |
+| `gooIfModifiedSince`   | `Maybe UTCTime`                 | (Optional) Time since object was modified                                                             |
 
 __Return Value__
 
 The return value can be incrementally read to process the contents of
 the object.
-|Return type   |Description   |
-|:---|:---|
-| _Minio (C.ConduitM () ByteString Minio ())_  | A Conduit source of `ByteString` values. |
+| Return type                                 | Description                              |
+|:--------------------------------------------|:-----------------------------------------|
+| _Minio (C.ConduitM () ByteString Minio ())_ | A Conduit source of `ByteString` values. |
 
 __Example__
 
@@ -451,7 +452,7 @@
 import           Prelude
 
 -- | The following example uses MinIO play server at
--- https://play.min.io:9000.  The endpoint and associated
+-- https://play.min.io.  The endpoint and associated
 -- credentials are provided via the libary constant,
 --
 -- > minioPlayCI :: ConnectInfo
@@ -482,13 +483,13 @@
 In the expression `putObject bucketName objectName inputSrc` the parameters
 are:
 
-|Param   |Type   |Description   |
-|:---|:---| :---|
-| `bucketName`  | _Bucket_ (alias for `Text`)  | Name of the bucket |
-| `objectName` | _Object_ (alias for `Text`)  | Name of the object |
-| `inputSrc` | _C.ConduitM () ByteString Minio ()_ | A Conduit producer of `ByteString` values |
-| `size` | _Int64_ | Provide stream size (optional) |
-| `opts` | _PutObjectOptions_ | Optional parameters to provide additional metadata for the object |
+| Param        | Type                                | Description                                                       |
+|:-------------|:------------------------------------|:------------------------------------------------------------------|
+| `bucketName` | _Bucket_ (alias for `Text`)         | Name of the bucket                                                |
+| `objectName` | _Object_ (alias for `Text`)         | Name of the object                                                |
+| `inputSrc`   | _C.ConduitM () ByteString Minio ()_ | A Conduit producer of `ByteString` values                         |
+| `size`       | _Int64_                             | Provide stream size (optional)                                    |
+| `opts`       | _PutObjectOptions_                  | Optional parameters to provide additional metadata for the object |
 
 __Example__
 
@@ -501,7 +502,7 @@
 import           Prelude
 
 -- | The following example uses MinIO play server at
--- https://play.min.io:9000.  The endpoint and associated
+-- https://play.min.io.  The endpoint and associated
 -- credentials are provided via the libary constant,
 --
 -- > minioPlayCI :: ConnectInfo
@@ -533,23 +534,23 @@
 In the expression `fGetObject bucketName objectName inputFile` the parameters
 are:
 
-|Param   |Type   |Description   |
-|:---|:---| :---|
-| `bucketName`  | _Bucket_ (alias for `Text`)  | Name of the bucket |
-| `objectName` | _Object_ (alias for `Text`)  | Name of the object |
-| `inputFile` | _FilePath_ | Path to the file to be uploaded |
-| `opts`      | _GetObjectOptions_ | Options for GET requests specifying additional options like If-Match, Range |
+| Param        | Type                        | Description                                                                 |
+|:-------------|:----------------------------|:----------------------------------------------------------------------------|
+| `bucketName` | _Bucket_ (alias for `Text`) | Name of the bucket                                                          |
+| `objectName` | _Object_ (alias for `Text`) | Name of the object                                                          |
+| `inputFile`  | _FilePath_                  | Path to the file to be uploaded                                             |
+| `opts`       | _GetObjectOptions_          | Options for GET requests specifying additional options like If-Match, Range |
 
 
 __GetObjectOptions record type__
 
-|Field   |Type   |Description   |
-|:---|:---| :---|
-| `gooRange` | `Maybe ByteRanges` | Represents the byte range of object. E.g ByteRangeFromTo 0 9 represents first ten bytes of the object|
-| `gooIfMatch` | `Maybe ETag` (alias for `Text`) | (Optional) ETag of object should match |
-| `gooIfNoneMatch` | `Maybe ETag` (alias for `Text`) | (Optional) ETag of object shouldn't match |
-| `gooIfUnmodifiedSince` | `Maybe UTCTime` | (Optional) Time since object wasn't modified |
-| `gooIfModifiedSince` | `Maybe UTCTime` | (Optional) Time since object was modified |
+| Field                  | Type                            | Description                                                                                           |
+|:-----------------------|:--------------------------------|:------------------------------------------------------------------------------------------------------|
+| `gooRange`             | `Maybe ByteRanges`              | Represents the byte range of object. E.g ByteRangeFromTo 0 9 represents first ten bytes of the object |
+| `gooIfMatch`           | `Maybe ETag` (alias for `Text`) | (Optional) ETag of object should match                                                                |
+| `gooIfNoneMatch`       | `Maybe ETag` (alias for `Text`) | (Optional) ETag of object shouldn't match                                                             |
+| `gooIfUnmodifiedSince` | `Maybe UTCTime`                 | (Optional) Time since object wasn't modified                                                          |
+| `gooIfModifiedSince`   | `Maybe UTCTime`                 | (Optional) Time since object was modified                                                             |
 
 ``` haskell
 
@@ -561,7 +562,7 @@
 import Prelude
 
 -- | The following example uses MinIO play server at
--- https://play.min.io:9000.  The endpoint and associated
+-- https://play.min.io.  The endpoint and associated
 -- credentials are provided via the libary constant,
 --
 -- > minioPlayCI :: ConnectInfo
@@ -592,11 +593,11 @@
 In the expression `fPutObject bucketName objectName inputFile` the parameters
 are:
 
-|Param   |Type   |Description   |
-|:---|:---| :---|
-| `bucketName`  | _Bucket_ (alias for `Text`)  | Name of the bucket |
-| `objectName` | _Object_ (alias for `Text`)  | Name of the object |
-| `inputFile` | _FilePath_ | Path to the file to be uploaded |
+| Param        | Type                        | Description                     |
+|:-------------|:----------------------------|:--------------------------------|
+| `bucketName` | _Bucket_ (alias for `Text`) | Name of the bucket              |
+| `objectName` | _Object_ (alias for `Text`) | Name of the object              |
+| `inputFile`  | _FilePath_                  | Path to the file to be uploaded |
 
 __Example__
 
@@ -629,28 +630,28 @@
 In the expression `copyObject dstInfo srcInfo` the parameters
 are:
 
-|Param   |Type   |Description   |
-|:---|:---| :---|
+| Param     | Type              | Description                                               |
+|:----------|:------------------|:----------------------------------------------------------|
 | `dstInfo` | _DestinationInfo_ | A value representing properties of the destination object |
-| `srcInfo` | _SourceInfo_ | A value representing properties of the source object |
+| `srcInfo` | _SourceInfo_      | A value representing properties of the source object      |
 
 
 __SourceInfo record type__
 
-|Field   |Type   |Description   |
-|:---|:---| :---|
-| `srcBucket` | `Bucket` | Name of source bucket |
-| `srcObject` | `Object` | Name of source object |
-| `srcRange` | `Maybe (Int64, Int64)` | (Optional) Represents the byte range of source object. (0, 9) represents first ten bytes of source object|
-| `srcIfMatch` | `Maybe Text` | (Optional) ETag source object should match |
-| `srcIfNoneMatch` | `Maybe Text` | (Optional) ETag source object shouldn't match |
-| `srcIfUnmodifiedSince` | `Maybe UTCTime` | (Optional) Time since source object wasn't modified |
-| `srcIfModifiedSince` | `Maybe UTCTime` | (Optional) Time since source object was modified |
+| Field                  | Type                   | Description                                                                                               |
+|:-----------------------|:-----------------------|:----------------------------------------------------------------------------------------------------------|
+| `srcBucket`            | `Bucket`               | Name of source bucket                                                                                     |
+| `srcObject`            | `Object`               | Name of source object                                                                                     |
+| `srcRange`             | `Maybe (Int64, Int64)` | (Optional) Represents the byte range of source object. (0, 9) represents first ten bytes of source object |
+| `srcIfMatch`           | `Maybe Text`           | (Optional) ETag source object should match                                                                |
+| `srcIfNoneMatch`       | `Maybe Text`           | (Optional) ETag source object shouldn't match                                                             |
+| `srcIfUnmodifiedSince` | `Maybe UTCTime`        | (Optional) Time since source object wasn't modified                                                       |
+| `srcIfModifiedSince`   | `Maybe UTCTime`        | (Optional) Time since source object was modified                                                          |
 
 __Destination record type__
 
-|Field   |Type   |Description   |
-|:---|:---| :---|
+| Field       | Type     | Description                                          |
+|:------------|:---------|:-----------------------------------------------------|
 | `dstBucket` | `Bucket` | Name of destination bucket in server-side copyObject |
 | `dstObject` | `Object` | Name of destination object in server-side copyObject |
 
@@ -684,10 +685,10 @@
 In the expression `removeObject bucketName objectName` the parameters
 are:
 
-|Param   |Type   |Description   |
-|:---|:---| :---|
-| `bucketName`  | _Bucket_ (alias for `Text`)  | Name of the bucket |
-| `objectName` | _Object_ (alias for `Text`)  | Name of the object |
+| Param        | Type                        | Description        |
+|:-------------|:----------------------------|:-------------------|
+| `bucketName` | _Bucket_ (alias for `Text`) | Name of the bucket |
+| `objectName` | _Object_ (alias for `Text`) | Name of the object |
 
 __Example__
 
@@ -718,10 +719,10 @@
 In the expression `removeIncompleteUpload bucketName objectName` the parameters
 are:
 
-|Param   |Type   |Description   |
-|:---|:---| :---|
-| `bucketName`  | _Bucket_ (alias for `Text`)  | Name of the bucket |
-| `objectName` | _Object_ (alias for `Text`)  | Name of the object |
+| Param        | Type                        | Description        |
+|:-------------|:----------------------------|:-------------------|
+| `bucketName` | _Bucket_ (alias for `Text`) | Name of the bucket |
+| `objectName` | _Object_ (alias for `Text`) | Name of the object |
 
 __Example__
 
@@ -752,11 +753,11 @@
 In the expression `selectObjectContent bucketName objectName selReq`
 the parameters are:
 
-|Param   |Type   |Description   |
-|:---|:---| :---|
-| `bucketName`  | _Bucket_ (alias for `Text`)  | Name of the bucket |
-| `objectName` | _Object_ (alias for `Text`)  | Name of the object |
-| `selReq` | _SelectRequest_   | Select request parameters  |
+| Param        | Type                        | Description               |
+|:-------------|:----------------------------|:--------------------------|
+| `bucketName` | _Bucket_ (alias for `Text`) | Name of the bucket        |
+| `objectName` | _Object_ (alias for `Text`) | Name of the object        |
+| `selReq`     | _SelectRequest_             | Select request parameters |
 
 __SelectRequest record__
 
@@ -804,9 +805,9 @@
 
 In the expression `bucketExists bucketName` the parameters are:
 
-|Param   |Type   |Description   |
-|:---|:---| :---|
-| `bucketName`  | _Bucket_ (alias for `Text`)  | Name of the bucket |
+| Param        | Type                        | Description        |
+|:-------------|:----------------------------|:-------------------|
+| `bucketName` | _Bucket_ (alias for `Text`) | Name of the bucket |
 
 
 ## 4. Presigned operations
@@ -828,22 +829,22 @@
 In the expression `presignedGetObjectUrl bucketName objectName expiry queryParams headers`
 the parameters are:
 
-|Param   |Type   |Description   |
-|:---|:---| :---|
-| `bucketName`  | _Bucket_ (alias for `Text`)  | Name of the bucket |
-| `objectName` | _Object_ (alias for `Text`)  | Name of the object |
-| `expiry` | _UrlExpiry_ (alias for `Int`) | Url expiry time in seconds |
-| `queryParams` | _Query_ (from package `http-types:Network.HTTP.Types`) | Query parameters to add to the URL |
-| `headers` | _RequestHeaders_ (from package `http-types:Network.HTTP.Types` | Request headers that would be used with the URL |
+| Param         | Type                                                           | Description                                     |
+|:--------------|:---------------------------------------------------------------|:------------------------------------------------|
+| `bucketName`  | _Bucket_ (alias for `Text`)                                    | Name of the bucket                              |
+| `objectName`  | _Object_ (alias for `Text`)                                    | Name of the object                              |
+| `expiry`      | _UrlExpiry_ (alias for `Int`)                                  | Url expiry time in seconds                      |
+| `queryParams` | _Query_ (from package `http-types:Network.HTTP.Types`)         | Query parameters to add to the URL              |
+| `headers`     | _RequestHeaders_ (from package `http-types:Network.HTTP.Types` | Request headers that would be used with the URL |
 
 __Return Value__
 
 Returns the generated URL - it will include authentication
 information.
 
-|Return type   |Description   |
-|:---|:---|
-| _ByteString_  | Generated presigned URL |
+| Return type  | Description             |
+|:-------------|:------------------------|
+| _ByteString_ | Generated presigned URL |
 
 __Example__
 
@@ -886,21 +887,21 @@
 In the expression `presignedPutObjectUrl bucketName objectName expiry headers`
 the parameters are:
 
-|Param   |Type   |Description   |
-|:---|:---| :---|
-| `bucketName`  | _Bucket_ (alias for `Text`)  | Name of the bucket |
-| `objectName` | _Object_ (alias for `Text`)  | Name of the object |
-| `expiry` | _UrlExpiry_ (alias for `Int`) | Url expiry time in seconds |
-| `headers` | _RequestHeaders_ (from package `http-types:Network.HTTP.Types` | Request headers that would be used with the URL |
+| Param        | Type                                                           | Description                                     |
+|:-------------|:---------------------------------------------------------------|:------------------------------------------------|
+| `bucketName` | _Bucket_ (alias for `Text`)                                    | Name of the bucket                              |
+| `objectName` | _Object_ (alias for `Text`)                                    | Name of the object                              |
+| `expiry`     | _UrlExpiry_ (alias for `Int`)                                  | Url expiry time in seconds                      |
+| `headers`    | _RequestHeaders_ (from package `http-types:Network.HTTP.Types` | Request headers that would be used with the URL |
 
 __Return Value__
 
 Returns the generated URL - it will include authentication
 information.
 
-|Return type   |Description   |
-|:---|:---|
-| _ByteString_  | Generated presigned URL |
+| Return type  | Description             |
+|:-------------|:------------------------|
+| _ByteString_ | Generated presigned URL |
 
 __Example__
 
@@ -928,7 +929,7 @@
 ```
 
 <a name="presignedPostPolicy"></a>
-### presignedPostPolicy :: PostPolicy -> Minio (ByteString, Map.Map Text ByteString)
+### presignedPostPolicy :: PostPolicy -> Minio (ByteString, HashMap Text ByteString)
 
 Generate a presigned URL and POST policy to upload files via a POST
 request. This is intended for browser uploads and generates form data
@@ -940,10 +941,10 @@
 
 In the expression `newPostPolicy expirationTime conditions` the parameters are:
 
-|Param | Type| Description |
-|:---|:---|:---|
-| `expirationTime` | _UTCTime_ (from package `time:Data.Time.UTCTime`) | The expiration time for the policy |
-| `conditions` | _[PostPolicyConditions]_ | List of conditions to be added to the policy |
+| Param            | Type                                              | Description                                  |
+|:-----------------|:--------------------------------------------------|:---------------------------------------------|
+| `expirationTime` | _UTCTime_ (from package `time:Data.Time.UTCTime`) | The expiration time for the policy           |
+| `conditions`     | _[PostPolicyConditions]_                          | List of conditions to be added to the policy |
 
 The policy conditions are created using various helper functions -
 please refer to the Haddocks for details.
@@ -965,7 +966,7 @@
 
 import qualified Data.ByteString       as B
 import qualified Data.ByteString.Char8 as Char8
-import qualified Data.Map.Strict       as Map
+import qualified Data.HashMap.Strict   as H
 import qualified Data.Text.Encoding    as Enc
 import qualified Data.Time             as Time
 
@@ -1005,7 +1006,7 @@
         let
           formFn (k, v) = B.concat ["-F ", Enc.encodeUtf8 k, "=",
                                     "'", v, "'"]
-          formOptions = B.intercalate " " $ map formFn $ Map.toList formData
+          formOptions = B.intercalate " " $ map formFn $ H.toList formData
 
 
         return $ B.intercalate " " $
diff --git a/examples/BucketExists.hs b/examples/BucketExists.hs
--- a/examples/BucketExists.hs
+++ b/examples/BucketExists.hs
@@ -24,7 +24,7 @@
 import           Prelude
 
 -- | The following example uses minio's play server at
--- https://play.min.io:9000.  The endpoint and associated
+-- https://play.min.io.  The endpoint and associated
 -- credentials are provided via the libary constant,
 --
 -- > minioPlayCI :: ConnectInfo
diff --git a/examples/CopyObject.hs b/examples/CopyObject.hs
--- a/examples/CopyObject.hs
+++ b/examples/CopyObject.hs
@@ -24,7 +24,7 @@
 import           Prelude
 
 -- | The following example uses minio's play server at
--- https://play.min.io:9000.  The endpoint and associated
+-- https://play.min.io.  The endpoint and associated
 -- credentials are provided via the libary constant,
 --
 -- > minioPlayCI :: ConnectInfo
diff --git a/examples/FileUploader.hs b/examples/FileUploader.hs
--- a/examples/FileUploader.hs
+++ b/examples/FileUploader.hs
@@ -31,7 +31,7 @@
 import           Prelude
 
 -- | The following example uses minio's play server at
--- https://play.min.io:9000.  The endpoint and associated
+-- https://play.min.io.  The endpoint and associated
 -- credentials are provided via the libary constant,
 --
 -- > minioPlayCI :: ConnectInfo
diff --git a/examples/GetObject.hs b/examples/GetObject.hs
--- a/examples/GetObject.hs
+++ b/examples/GetObject.hs
@@ -26,7 +26,7 @@
 import           Prelude
 
 -- | The following example uses minio's play server at
--- https://play.min.io:9000.  The endpoint and associated
+-- https://play.min.io.  The endpoint and associated
 -- credentials are provided via the libary constant,
 --
 -- > minioPlayCI :: ConnectInfo
diff --git a/examples/HeadObject.hs b/examples/HeadObject.hs
--- a/examples/HeadObject.hs
+++ b/examples/HeadObject.hs
@@ -24,7 +24,7 @@
 import           Prelude
 
 -- | The following example uses minio's play server at
--- https://play.min.io:9000.  The endpoint and associated
+-- https://play.min.io.  The endpoint and associated
 -- credentials are provided via the libary constant,
 --
 -- > minioPlayCI :: ConnectInfo
diff --git a/examples/ListBuckets.hs b/examples/ListBuckets.hs
--- a/examples/ListBuckets.hs
+++ b/examples/ListBuckets.hs
@@ -24,7 +24,7 @@
 import           Prelude
 
 -- | The following example uses minio's play server at
--- https://play.min.io:9000.  The endpoint and associated
+-- https://play.min.io.  The endpoint and associated
 -- credentials are provided via the libary constant,
 --
 -- > minioPlayCI :: ConnectInfo
diff --git a/examples/ListIncompleteUploads.hs b/examples/ListIncompleteUploads.hs
--- a/examples/ListIncompleteUploads.hs
+++ b/examples/ListIncompleteUploads.hs
@@ -24,7 +24,7 @@
 import           Prelude
 
 -- | The following example uses minio's play server at
--- https://play.min.io:9000.  The endpoint and associated
+-- https://play.min.io.  The endpoint and associated
 -- credentials are provided via the libary constant,
 --
 -- > minioPlayCI :: ConnectInfo
diff --git a/examples/ListObjects.hs b/examples/ListObjects.hs
--- a/examples/ListObjects.hs
+++ b/examples/ListObjects.hs
@@ -25,7 +25,7 @@
 
 
 -- | The following example uses minio's play server at
--- https://play.min.io:9000.  The endpoint and associated
+-- https://play.min.io.  The endpoint and associated
 -- credentials are provided via the libary constant,
 --
 -- > minioPlayCI :: ConnectInfo
diff --git a/examples/MakeBucket.hs b/examples/MakeBucket.hs
--- a/examples/MakeBucket.hs
+++ b/examples/MakeBucket.hs
@@ -24,7 +24,7 @@
 import           Prelude
 
 -- | The following example uses minio's play server at
--- https://play.min.io:9000.  The endpoint and associated
+-- https://play.min.io.  The endpoint and associated
 -- credentials are provided via the libary constant,
 --
 -- > minioPlayCI :: ConnectInfo
diff --git a/examples/PresignedGetObject.hs b/examples/PresignedGetObject.hs
--- a/examples/PresignedGetObject.hs
+++ b/examples/PresignedGetObject.hs
@@ -27,7 +27,7 @@
 import qualified Data.Text.Encoding       as E
 
 -- | The following example uses minio's play server at
--- https://play.min.io:9000.  The endpoint and associated
+-- https://play.min.io.  The endpoint and associated
 -- credentials are provided via the libary constant,
 --
 -- > minioPlayCI :: ConnectInfo
diff --git a/examples/PresignedPostPolicy.hs b/examples/PresignedPostPolicy.hs
--- a/examples/PresignedPostPolicy.hs
+++ b/examples/PresignedPostPolicy.hs
@@ -22,12 +22,12 @@
 
 import qualified Data.ByteString       as B
 import qualified Data.ByteString.Char8 as Char8
-import qualified Data.Map.Strict       as Map
+import qualified Data.HashMap.Strict   as H
 import qualified Data.Text.Encoding    as Enc
 import qualified Data.Time             as Time
 
 -- | The following example uses minio's play server at
--- https://play.min.io:9000.  The endpoint and associated
+-- https://play.min.io.  The endpoint and associated
 -- credentials are provided via the libary constant,
 --
 -- > minioPlayCI :: ConnectInfo
@@ -69,7 +69,7 @@
         let
           formFn (k, v) = B.concat ["-F ", Enc.encodeUtf8 k, "=",
                                     "'", v, "'"]
-          formOptions = B.intercalate " " $ map formFn $ Map.toList formData
+          formOptions = B.intercalate " " $ map formFn $ H.toList formData
 
 
         return $ B.intercalate " " $
diff --git a/examples/PresignedPutObject.hs b/examples/PresignedPutObject.hs
--- a/examples/PresignedPutObject.hs
+++ b/examples/PresignedPutObject.hs
@@ -24,7 +24,7 @@
 import           Data.CaseInsensitive  (original)
 
 -- | The following example uses minio's play server at
--- https://play.min.io:9000.  The endpoint and associated
+-- https://play.min.io.  The endpoint and associated
 -- credentials are provided via the libary constant,
 --
 -- > minioPlayCI :: ConnectInfo
diff --git a/examples/PutObject.hs b/examples/PutObject.hs
--- a/examples/PutObject.hs
+++ b/examples/PutObject.hs
@@ -25,7 +25,7 @@
 import           Prelude
 
 -- | The following example uses minio's play server at
--- https://play.min.io:9000.  The endpoint and associated
+-- https://play.min.io.  The endpoint and associated
 -- credentials are provided via the libary constant,
 --
 -- > minioPlayCI :: ConnectInfo
diff --git a/examples/RemoveBucket.hs b/examples/RemoveBucket.hs
--- a/examples/RemoveBucket.hs
+++ b/examples/RemoveBucket.hs
@@ -24,7 +24,7 @@
 import           Prelude
 
 -- | The following example uses minio's play server at
--- https://play.min.io:9000.  The endpoint and associated
+-- https://play.min.io.  The endpoint and associated
 -- credentials are provided via the libary constant,
 --
 -- > minioPlayCI :: ConnectInfo
diff --git a/examples/RemoveIncompleteUpload.hs b/examples/RemoveIncompleteUpload.hs
--- a/examples/RemoveIncompleteUpload.hs
+++ b/examples/RemoveIncompleteUpload.hs
@@ -23,7 +23,7 @@
 import           Prelude
 
 -- | The following example uses minio's play server at
--- https://play.min.io:9000.  The endpoint and associated
+-- https://play.min.io.  The endpoint and associated
 -- credentials are provided via the libary constant,
 --
 -- > minioPlayCI :: ConnectInfo
diff --git a/minio-hs.cabal b/minio-hs.cabal
--- a/minio-hs.cabal
+++ b/minio-hs.cabal
@@ -1,5 +1,5 @@
 name:                minio-hs
-version:             1.4.0
+version:             1.5.0
 synopsis:            A MinIO Haskell Library for Amazon S3 compatible cloud
                      storage.
 description:         The MinIO Haskell client library provides simple APIs to
@@ -57,7 +57,6 @@
                      , conduit >= 1.3
                      , conduit-extra >= 1.3
                      , connection
-                     , containers >= 0.5
                      , cryptonite >= 0.25
                      , cryptonite-conduit >= 0.2
                      , digest >= 0.0.1
@@ -153,7 +152,6 @@
                      , conduit
                      , conduit-extra
                      , connection
-                     , containers
                      , cryptonite
                      , cryptonite-conduit
                      , digest
@@ -200,7 +198,6 @@
                      , conduit
                      , conduit-extra
                      , connection
-                     , containers
                      , cryptonite
                      , cryptonite-conduit
                      , digest
diff --git a/src/Network/Minio.hs b/src/Network/Minio.hs
--- a/src/Network/Minio.hs
+++ b/src/Network/Minio.hs
@@ -90,6 +90,7 @@
   , oiModTime
   , oiETag
   , oiSize
+  , oiUserMetadata
   , oiMetadata
 
   -- ** Listing incomplete uploads
@@ -146,6 +147,9 @@
   , gooIfModifiedSince
   , gooIfUnmodifiedSince
   , gooSSECKey
+  , GetObjectResponse
+  , gorObjectInfo
+  , gorObjectStream
 
   -- ** Server-side object copying
   , copyObject
@@ -241,7 +245,7 @@
 fGetObject :: Bucket -> Object -> FilePath -> GetObjectOptions -> Minio ()
 fGetObject bucket object fp opts = do
   src <- getObject bucket object opts
-  C.connect src $ CB.sinkFileCautious fp
+  C.connect (gorObjectStream src) $ CB.sinkFileCautious fp
 
 -- | Upload the given file to the given object.
 fPutObject :: Bucket -> Object -> FilePath
@@ -271,11 +275,11 @@
 removeObject :: Bucket -> Object -> Minio ()
 removeObject = deleteObject
 
--- | Get an object from the object store as a resumable source (conduit).
+-- | Get an object from the object store.
 getObject :: Bucket -> Object -> GetObjectOptions
-          -> Minio (C.ConduitM () ByteString Minio ())
-getObject bucket object opts = snd <$> getObject' bucket object []
-                               (gooToHeaders opts)
+          -> Minio GetObjectResponse
+getObject bucket object opts =
+    getObject' bucket object [] $ gooToHeaders opts
 
 -- | Get an object's metadata from the object store. It accepts the
 -- same options as GetObject.
diff --git a/src/Network/Minio/API.hs b/src/Network/Minio/API.hs
--- a/src/Network/Minio/API.hs
+++ b/src/Network/Minio/API.hs
@@ -34,7 +34,7 @@
 import qualified Data.ByteString           as B
 import qualified Data.Char                 as C
 import qualified Data.Conduit              as C
-import qualified Data.Map                  as Map
+import qualified Data.HashMap.Strict       as H
 import qualified Data.Text                 as T
 import qualified Data.Time.Clock           as Time
 import           Network.HTTP.Conduit      (Response)
@@ -95,7 +95,7 @@
 
     if "amazonaws.com" `T.isSuffixOf` connectHost ci
     then maybe (throwIO $ MErrVRegionNotSupported r)
-         return (Map.lookup r awsRegionMap)
+         return (H.lookup r awsRegionMap)
     else return $ connectHost ci
 
 buildRequest :: S3ReqInfo -> Minio NC.Request
diff --git a/src/Network/Minio/Data.hs b/src/Network/Minio/Data.hs
--- a/src/Network/Minio/Data.hs
+++ b/src/Network/Minio/Data.hs
@@ -32,7 +32,6 @@
 import           Data.CaseInsensitive         (mk)
 import qualified Data.HashMap.Strict          as H
 import qualified Data.Ini                     as Ini
-import qualified Data.Map                     as Map
 import           Data.String                  (IsString (..))
 import qualified Data.Text                    as T
 import qualified Data.Text.Encoding           as TE
@@ -74,8 +73,8 @@
 -- type should have a IsString instance to infer the appropriate
 -- constant.
 -- | awsRegionMap - library constant
-awsRegionMap :: Map.Map Text Text
-awsRegionMap = Map.fromList [
+awsRegionMap :: H.HashMap Text Text
+awsRegionMap = H.fromList [
       ("us-east-1", "s3.amazonaws.com")
     , ("us-east-2", "s3-us-east-2.amazonaws.com")
     , ("us-west-1", "s3-us-west-1.amazonaws.com")
@@ -96,7 +95,7 @@
 -- enable the @OverloadedStrings@ language extension and use the
 -- `IsString` instance to provide a URL, for example:
 --
--- > let c :: ConnectInfo = "https://play.min.io:9000"
+-- > let c :: ConnectInfo = "https://play.min.io"
 data ConnectInfo =
     ConnectInfo { connectHost                     :: Text
                 , connectPort                     :: Int
@@ -231,13 +230,13 @@
 awsCI = "https://s3.amazonaws.com"
 
 
--- | <https://play.min.io:9000 MinIO Play Server>
+-- | <https://play.min.io MinIO Play Server>
 -- ConnectInfo. Credentials are already filled in.
 minioPlayCI :: ConnectInfo
 minioPlayCI = let playCreds = Credentials "Q3AM3UQ867SPQQA43P2F" "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG"
               in setCreds playCreds
                  $ setRegion "us-east-1"
-                 "https://play.min.io:9000"
+                 "https://play.min.io"
 
 -- |
 -- Represents a bucket in the object store
@@ -336,14 +335,22 @@
 defaultPutObjectOptions :: PutObjectOptions
 defaultPutObjectOptions = PutObjectOptions Nothing Nothing Nothing Nothing Nothing Nothing [] Nothing Nothing
 
+-- | If the given header name has the @X-Amz-Meta-@ prefix, it is
+-- stripped and a Just is returned.
+userMetadataHeaderNameMaybe :: Text -> Maybe Text
+userMetadataHeaderNameMaybe k =
+    let prefix = T.toCaseFold "X-Amz-Meta-"
+        n = T.length prefix
+    in if T.toCaseFold (T.take n k) == prefix
+       then Just (T.drop n k)
+       else Nothing
+
 addXAmzMetaPrefix :: Text -> Text
-addXAmzMetaPrefix s = do
-  if (T.isPrefixOf "x-amz-meta-" s)
-    then s
-    else T.concat ["x-amz-meta-", s]
+addXAmzMetaPrefix s | isJust (userMetadataHeaderNameMaybe s) = s
+                    | otherwise = "X-Amz-Meta-" <> s
 
 mkHeaderFromMetadata :: [(Text, Text)] -> [HT.Header]
-mkHeaderFromMetadata = map (\(x, y) -> (mk $ encodeUtf8 $ addXAmzMetaPrefix $ T.toLower x, encodeUtf8 y))
+mkHeaderFromMetadata = map (\(x, y) -> (mk $ encodeUtf8 $ addXAmzMetaPrefix $ x, encodeUtf8 y))
 
 pooToHeaders :: PutObjectOptions -> [HT.Header]
 pooToHeaders poo = userMetadata
@@ -436,12 +443,19 @@
 
 -- | Represents information about an object.
 data ObjectInfo = ObjectInfo
-  { oiObject   :: Object -- ^ Object key
-  , oiModTime  :: UTCTime -- ^ Mdification time of the object
-  , oiETag     :: ETag -- ^ ETag of the object
-  , oiSize     :: Int64 -- ^ Size of the object in bytes
-  , oiMetadata :: Map.Map Text Text -- ^ A map of the metadata
-                                    -- key-value pairs
+  { oiObject       :: Object -- ^ Object key
+  , oiModTime      :: UTCTime -- ^ Modification time of the object
+  , oiETag         :: ETag -- ^ ETag of the object
+  , oiSize         :: Int64 -- ^ Size of the object in bytes
+  , oiUserMetadata :: H.HashMap Text Text -- ^ A map of user-metadata
+                                          -- pairs stored with an
+                                          -- object (keys will not
+                                          -- have the @X-Amz-Meta-@
+                                          -- prefix).
+  , oiMetadata     :: H.HashMap Text Text -- ^ A map of metadata
+                                          -- key-value pairs (not
+                                          -- including the
+                                          -- user-metadata pairs)
   } deriving (Show, Eq)
 
 -- | Represents source object in server-side copy object
@@ -523,6 +537,14 @@
     rangeHdr = maybe [] (\a -> [(hRange, HT.renderByteRanges [a])])
                $ gooRange goo
 
+-- | Data type returned by 'getObject' representing the object being
+-- retrieved. Use the @gor*@ functions to access its contents.
+data GetObjectResponse = GetObjectResponse {
+      -- | ObjectInfo of the object being retrieved.
+      gorObjectInfo   :: ObjectInfo
+      -- | A conduit of the bytes of the object.
+    , gorObjectStream :: C.ConduitM () ByteString Minio ()
+    }
 
 -- | A data-type for events that can occur in the object storage
 -- server. Reference:
@@ -928,7 +950,7 @@
 -- seconds. The maximum duration that can be specified is 7 days.
 type UrlExpiry = Int
 
-type RegionMap = Map.Map Bucket Region
+type RegionMap = H.HashMap Bucket Region
 
 -- | The Minio Monad - all computations accessing object storage
 -- happens in it.
@@ -991,7 +1013,7 @@
 -- `MinioConn`.
 mkMinioConn :: ConnectInfo -> NC.Manager -> IO MinioConn
 mkMinioConn ci mgr = do
-    rMapMVar <- M.newMVar Map.empty
+    rMapMVar <- M.newMVar H.empty
     return $ MinioConn ci mgr rMapMVar
 
 -- | Run the Minio action and return the result or an error.
diff --git a/src/Network/Minio/PresignedOperations.hs b/src/Network/Minio/PresignedOperations.hs
--- a/src/Network/Minio/PresignedOperations.hs
+++ b/src/Network/Minio/PresignedOperations.hs
@@ -39,7 +39,7 @@
 import           Data.Aeson                ((.=))
 import qualified Data.Aeson                as Json
 import           Data.ByteString.Builder   (byteString, toLazyByteString)
-import qualified Data.Map.Strict           as Map
+import qualified Data.HashMap.Strict       as H
 import qualified Data.Text                 as T
 import qualified Data.Time                 as Time
 import qualified Network.HTTP.Conduit      as NC
@@ -252,7 +252,7 @@
 -- browser. On success, this function returns a URL and POST
 -- form-data.
 presignedPostPolicy :: PostPolicy
-                    -> Minio (ByteString, Map.Map Text ByteString)
+                    -> Minio (ByteString, H.HashMap Text ByteString)
 presignedPostPolicy p = do
   ci <- asks mcConnInfo
   signTime <- liftIO $ Time.getCurrentTime
@@ -277,12 +277,12 @@
     mkPair (PPCStartsWith k v) = Just (k, v)
     mkPair (PPCEquals k v)     = Just (k, v)
     mkPair _                   = Nothing
-    formFromPolicy = Map.map toS $ Map.fromList $ catMaybes $
+    formFromPolicy = H.map toS $ H.fromList $ catMaybes $
                      mkPair <$> conditions ppWithCreds
-    formData = formFromPolicy `Map.union` signData
+    formData = formFromPolicy `H.union` signData
 
     -- compute POST upload URL
-    bucket = Map.findWithDefault "" "bucket" formData
+    bucket = H.lookupDefault "" "bucket" formData
     scheme = byteString $ bool "http://" "https://" $ connectIsSecure ci
     region = connectRegion ci
 
diff --git a/src/Network/Minio/S3API.hs b/src/Network/Minio/S3API.hs
--- a/src/Network/Minio/S3API.hs
+++ b/src/Network/Minio/S3API.hs
@@ -91,7 +91,6 @@
   , removeAllBucketNotification
   ) where
 
-import qualified Conduit                           as C
 import qualified Data.ByteString                   as BS
 import qualified Data.Text                         as T
 import qualified Network.HTTP.Conduit              as NC
@@ -118,19 +117,37 @@
     }
   parseListBuckets $ NC.responseBody resp
 
--- | GET an object from the service and return the response headers
--- and a conduit source for the object content
+-- Parse headers from getObject and headObject calls.
+parseGetObjectHeaders :: Object -> [HT.Header] -> Maybe ObjectInfo
+parseGetObjectHeaders object headers =
+    let metadataPairs = getMetadata headers
+        userMetadata = getUserMetadataMap metadataPairs
+        metadata = getNonUserMetadataMap metadataPairs
+    in ObjectInfo <$> Just object
+                  <*> getLastModifiedHeader headers
+                  <*> getETagHeader headers
+                  <*> getContentLength headers
+                  <*> Just userMetadata
+                  <*> Just metadata
+
+-- | GET an object from the service and return parsed ObjectInfo and a
+-- conduit source for the object content
 getObject' :: Bucket -> Object -> HT.Query -> [HT.Header]
-           -> Minio ([HT.Header], C.ConduitM () ByteString Minio ())
+           -> Minio GetObjectResponse
 getObject' bucket object queryParams headers = do
-  resp <- mkStreamRequest reqInfo
-  return (NC.responseHeaders resp, NC.responseBody resp)
+    resp <- mkStreamRequest reqInfo
+    let objInfoMaybe = parseGetObjectHeaders object $ NC.responseHeaders resp
+    objInfo <- maybe (throwIO MErrVInvalidObjectInfoResponse) return
+               objInfoMaybe
+    return $ GetObjectResponse { gorObjectInfo = objInfo
+                               , gorObjectStream = NC.responseBody resp
+                               }
   where
     reqInfo = defaultS3ReqInfo { riBucket = Just bucket
-                  , riObject = Just object
-                  , riQueryParams = queryParams
-                  , riHeaders = headers
-                  }
+                               , riObject = Just object
+                               , riQueryParams = queryParams
+                               , riHeaders = headers
+                               }
 
 -- | Creates a bucket via a PUT bucket call.
 putBucket :: Bucket -> Region -> Minio ()
@@ -412,20 +429,13 @@
 headObject :: Bucket -> Object -> [HT.Header] -> Minio ObjectInfo
 headObject bucket object reqHeaders = do
   resp <- executeRequest $ defaultS3ReqInfo { riMethod = HT.methodHead
-                               , riBucket = Just bucket
-                               , riObject = Just object
-                               , riHeaders = reqHeaders
-                               }
-
-  let
-    headers = NC.responseHeaders resp
-    modTime = getLastModifiedHeader headers
-    etag = getETagHeader headers
-    size = getContentLength headers
-    metadata = getMetadataMap headers
+                                            , riBucket = Just bucket
+                                            , riObject = Just object
+                                            , riHeaders = reqHeaders
+                                            }
 
   maybe (throwIO MErrVInvalidObjectInfoResponse) return $
-    ObjectInfo <$> Just object <*> modTime <*> etag <*> size <*> Just metadata
+    parseGetObjectHeaders object $ NC.responseHeaders resp
 
 
 -- | Query the object store if a given bucket exists.
diff --git a/src/Network/Minio/Sign/V4.hs b/src/Network/Minio/Sign/V4.hs
--- a/src/Network/Minio/Sign/V4.hs
+++ b/src/Network/Minio/Sign/V4.hs
@@ -22,8 +22,8 @@
 import qualified Data.ByteString.Char8         as B8
 import           Data.CaseInsensitive          (mk)
 import qualified Data.CaseInsensitive          as CI
-import qualified Data.Map.Strict               as Map
-import qualified Data.Set                      as Set
+import qualified Data.HashMap.Strict           as Map
+import qualified Data.HashSet                  as Set
 import qualified Data.Time                     as Time
 import qualified Network.HTTP.Conduit          as NC
 import           Network.HTTP.Types            (Header, parseQuery)
@@ -39,7 +39,7 @@
 
 -- these headers are not included in the string to sign when signing a
 -- request
-ignoredHeaders :: Set ByteString
+ignoredHeaders :: Set.HashSet ByteString
 ignoredHeaders = Set.fromList $ map CI.foldedCase
                  [ H.hAuthorization
                  , H.hContentType
@@ -178,7 +178,7 @@
 
 getHeadersToSign :: [Header] -> [(ByteString, ByteString)]
 getHeadersToSign !h =
-  filter (flip Set.notMember ignoredHeaders . fst) $
+  filter ((\hdr -> not $ Set.member hdr ignoredHeaders) . fst) $
   map (\(x, y) -> (CI.foldedCase x, stripBS y)) h
 
 mkCanonicalRequest :: Bool -> SignParams -> NC.Request -> [(ByteString, ByteString)]
@@ -234,7 +234,7 @@
 -- and ConnInfo and returns form-data for the POST upload containing
 -- just the signature and the encoded post-policy.
 signV4PostPolicy :: ByteString -> SignParams
-                 -> Map.Map Text ByteString
+                 -> Map.HashMap Text ByteString
 signV4PostPolicy !postPolicyJSON !sp =
   let
     stringToSign = Base64.encode postPolicyJSON
diff --git a/src/Network/Minio/Utils.hs b/src/Network/Minio/Utils.hs
--- a/src/Network/Minio/Utils.hs
+++ b/src/Network/Minio/Utils.hs
@@ -23,8 +23,8 @@
 import qualified Data.ByteString.Lazy          as LB
 import           Data.CaseInsensitive          (mk, original)
 import qualified Data.Conduit.Binary           as CB
+import qualified Data.HashMap.Strict           as H
 import qualified Data.List                     as List
-import qualified Data.Map                      as Map
 import qualified Data.Text                     as T
 import           Data.Text.Encoding.Error      (lenientDecode)
 import           Data.Text.Read                (decimal)
@@ -103,11 +103,26 @@
 getETagHeader hs = decodeUtf8Lenient <$> lookupHeader Hdr.hETag hs
 
 getMetadata :: [HT.Header] -> [(Text, Text)]
-getMetadata = map ((\(x, y) -> (decodeUtf8Lenient $ original x, decodeUtf8Lenient $ stripBS y)))
+getMetadata =
+    map ((\(x, y) -> (decodeUtf8Lenient $ original x, decodeUtf8Lenient $ stripBS y)))
 
-getMetadataMap :: [HT.Header] -> Map Text Text
-getMetadataMap hs = Map.fromList (getMetadata hs)
+toMaybeMetadataHeader :: (Text, Text) -> Maybe (Text, Text)
+toMaybeMetadataHeader (k, v) =
+    (, v) <$> userMetadataHeaderNameMaybe k
 
+getNonUserMetadataMap :: [(Text, Text)] -> H.HashMap Text Text
+getNonUserMetadataMap = H.fromList
+                      . filter ( isNothing
+                               . userMetadataHeaderNameMaybe
+                               . fst
+                               )
+
+-- | This function collects all headers starting with `x-amz-meta-`
+-- and strips off this prefix, and returns a map.
+getUserMetadataMap :: [(Text, Text)] -> H.HashMap Text Text
+getUserMetadataMap = H.fromList
+                   . mapMaybe toMaybeMetadataHeader
+
 getLastModifiedHeader :: [HT.Header] -> Maybe UTCTime
 getLastModifiedHeader hs = do
   modTimebs <- decodeUtf8Lenient <$> lookupHeader Hdr.hLastModified hs
@@ -245,14 +260,14 @@
 lookupRegionCache b = do
     rMVar <- asks mcRegionMap
     rMap <- UM.readMVar rMVar
-    return $ Map.lookup b rMap
+    return $ H.lookup b rMap
 
 addToRegionCache :: Bucket -> Region -> Minio ()
 addToRegionCache b region = do
     rMVar <- asks mcRegionMap
-    UM.modifyMVar_ rMVar $ return . Map.insert b region
+    UM.modifyMVar_ rMVar $ return . H.insert b region
 
 deleteFromRegionCache :: Bucket -> Minio ()
 deleteFromRegionCache b = do
     rMVar <- asks mcRegionMap
-    UM.modifyMVar_ rMVar $ return . Map.delete b
+    UM.modifyMVar_ rMVar $ return . H.delete b
diff --git a/src/Network/Minio/XmlParser.hs b/src/Network/Minio/XmlParser.hs
--- a/src/Network/Minio/XmlParser.hs
+++ b/src/Network/Minio/XmlParser.hs
@@ -30,8 +30,8 @@
   ) where
 
 import qualified Data.ByteString.Lazy as LB
-import           Data.List            (zip3, zip4, zip5)
-import qualified Data.Map             as Map
+import qualified Data.HashMap.Strict  as H
+import           Data.List            (zip3, zip4, zip6)
 import qualified Data.Text            as T
 import           Data.Text.Read       (decimal)
 import           Data.Time
@@ -52,8 +52,8 @@
 uncurry4 :: (a -> b -> c -> d -> e) -> (a, b, c, d) -> e
 uncurry4 f (a, b, c, d) = f a b c d
 
-uncurry5 :: (a -> b -> c -> d -> e -> f) -> (a, b, c, d, e) -> f
-uncurry5 f (a, b, c, d, e) = f a b c d e
+uncurry6 :: (a -> b -> c -> d -> e -> f -> g) -> (a, b, c, d, e, f) -> g
+uncurry6 f (a, b, c, d, e, g) = f a b c d e g
 
 -- | Parse time strings from XML
 parseS3XMLTime :: (MonadIO m) => Text -> m UTCTime
@@ -149,7 +149,8 @@
   sizes <- parseDecimals sizeStr
 
   let
-    objects = map (uncurry5 ObjectInfo) $ zip5 keys modTimes etags sizes (repeat Map.empty)
+    objects = map (uncurry6 ObjectInfo) $
+              zip6 keys modTimes etags sizes (repeat H.empty) (repeat H.empty)
 
   return $ ListObjectsV1Result hasMore nextMarker objects prefixes
 
@@ -178,7 +179,8 @@
   sizes <- parseDecimals sizeStr
 
   let
-    objects = map (uncurry5 ObjectInfo) $ zip5 keys modTimes etags sizes (repeat Map.empty)
+    objects = map (uncurry6 ObjectInfo) $
+              zip6 keys modTimes etags sizes (repeat H.empty) (repeat H.empty)
 
   return $ ListObjectsResult hasMore nextToken objects prefixes
 
diff --git a/test/LiveServer.hs b/test/LiveServer.hs
--- a/test/LiveServer.hs
+++ b/test/LiveServer.hs
@@ -21,13 +21,13 @@
 import           Test.Tasty.QuickCheck                 as QC
 
 import           Conduit                               (replicateC)
+import qualified Conduit                               as C
 import qualified Control.Monad.Trans.Resource          as R
 import qualified Data.ByteString                       as BS
 import           Data.Conduit                          (yield)
-import qualified Data.Conduit                          as C
 import qualified Data.Conduit.Binary                   as CB
 import           Data.Conduit.Combinators              (sinkList)
-import qualified Data.Map.Strict                       as Map
+import qualified Data.HashMap.Strict                   as H
 import qualified Data.Text                             as T
 import           Data.Time                             (fromGregorian)
 import qualified Data.Time                             as Time
@@ -42,6 +42,7 @@
 
 import           Network.Minio
 import           Network.Minio.Data
+import           Network.Minio.Data.Crypto
 import           Network.Minio.PutObject
 import           Network.Minio.S3API
 import           Network.Minio.Utils
@@ -108,6 +109,8 @@
   , putObjectContentTypeTest
   , putObjectContentLanguageTest
   , putObjectStorageClassTest
+  , putObjectUserMetadataTest
+  , getObjectTest
   , copyObjectTests
   , presignedUrlFunTest
   , presignedPostPolicyFunTest
@@ -513,8 +516,8 @@
       "presigned HEAD failed (presignedHeadObjectUrl)"
 
     -- check that header info is accurate
-    let h = Map.fromList $ NC.responseHeaders headResp
-        cLen = Map.findWithDefault "0" HT.hContentLength h
+    let h = H.fromList $ NC.responseHeaders headResp
+        cLen = H.lookupDefault "0" HT.hContentLength h
     liftIO $ (cLen == show size2) @? "Head req returned bad content length"
 
     step "GET object presigned URL - presignedGetObjectUrl"
@@ -580,7 +583,7 @@
       postForm url formData inputFile = do
         req <- NC.parseRequest $ toS url
         let parts = map (\(x, y) -> Form.partBS x y) $
-                    Map.toList formData
+                    H.toList formData
             parts' = parts ++ [Form.partFile "file" inputFile]
         req' <- Form.formDataBody parts' req
         mgr <- NC.newManager NC.tlsManagerSettings
@@ -698,7 +701,7 @@
     let m = oiMetadata oi
 
     step "Validate content-type"
-    liftIO $ assertEqual "Content-Type did not match" (Just "application/javascript") (Map.lookup "Content-Type" m)
+    liftIO $ assertEqual "Content-Type did not match" (Just "application/javascript") (H.lookup "Content-Type" m)
 
     step "upload object with content-encoding set to identity"
     fPutObject bucket object inputFile defaultPutObjectOptions {
@@ -710,7 +713,7 @@
 
     step "Validate content-encoding"
     liftIO $ assertEqual "Content-Encoding did not match" (Just "identity")
-      (Map.lookup "Content-Encoding" m')
+      (H.lookup "Content-Encoding" m')
 
     step "Cleanup actions"
 
@@ -735,11 +738,74 @@
 
     step "Validate content-language"
     liftIO $ assertEqual "content-language did not match" (Just "en-US")
-      (Map.lookup "Content-Language" m)
+      (H.lookup "Content-Language" m)
     step "Cleanup actions"
 
     removeObject bucket object
 
+putObjectUserMetadataTest :: TestTree
+putObjectUserMetadataTest = funTestWithBucket "putObject user-metadata test" $
+  \step bucket -> do
+    step "putObject user-metadata test"
+    let object = "object-with-metadata"
+        size1 = 100 :: Int64
+
+    step "create server object with usermetdata"
+    inputFile <- mkRandFile size1
+
+    fPutObject bucket object inputFile defaultPutObjectOptions {
+      pooUserMetadata = [ ("x-Amz-meta-mykey1", "myval1")
+                        , ("mykey2", "myval2")
+                        ]
+      }
+
+    step "Validate user-metadata"
+    -- retrieve obj info to check
+    oi <- headObject bucket object []
+    let m = oiUserMetadata oi
+        -- need to do a case-insensitive comparison
+        sortedMeta = sort $ map (\(k, v) -> (T.toLower k, T.toLower v)) $
+                     H.toList m
+        ref = sort [("mykey1", "myval1"), ("mykey2", "myval2")]
+
+    liftIO $ (sortedMeta == ref) @? "Metadata mismatch!"
+
+    step "Cleanup actions"
+    removeObject bucket object
+
+getObjectTest :: TestTree
+getObjectTest = funTestWithBucket "getObject test" $
+  \step bucket -> do
+    step "putObject with some metadata"
+    let object = "object-with-metadata"
+        size1 = 100 :: Int64
+
+    inputFile <- mkRandFile size1
+    fPutObject bucket object inputFile defaultPutObjectOptions {
+      pooUserMetadata = [ ("x-Amz-meta-mykey1", "myval1")
+                        , ("mykey2", "myval2")
+                        ]
+      }
+
+    step "get the object - check the metadata matches"
+    -- retrieve obj info to check
+    gor <- getObject bucket object defaultGetObjectOptions
+    let m = oiUserMetadata $ gorObjectInfo gor
+        -- need to do a case-insensitive comparison
+        sortedMeta = sort $ map (\(k, v) -> (T.toLower k, T.toLower v)) $
+                     H.toList m
+        ref = sort [("mykey1", "myval1"), ("mykey2", "myval2")]
+
+    liftIO $ (sortedMeta == ref) @? "Metadata mismatch!"
+
+    step "get the object content"
+    getObjectHash <- hashSHA256FromSource $ gorObjectStream gor
+    inputHash <- hashSHA256FromSource $ C.sourceFile inputFile
+    liftIO $ (getObjectHash == inputHash) @? "Input file and output file mismatched!"
+
+    step "Cleanup actions"
+    removeObject bucket object
+
 putObjectStorageClassTest :: TestTree
 putObjectStorageClassTest = funTestWithBucket "putObject storageClass tests" $
   \step bucket -> do
@@ -771,7 +837,7 @@
 
     step "Validate x-amz-storage-class rrs"
     liftIO $ assertEqual "storageClass did not match" (Just "REDUCED_REDUNDANCY")
-      (Map.lookup "X-Amz-Storage-Class" m')
+      (H.lookup "X-Amz-Storage-Class" m')
 
     fpE <- try $ fPutObject bucket object'' inputFile'' defaultPutObjectOptions {
       pooStorageClass = Just "INVALID_STORAGE_CLASS"
diff --git a/test/Network/Minio/XmlParser/Test.hs b/test/Network/Minio/XmlParser/Test.hs
--- a/test/Network/Minio/XmlParser/Test.hs
+++ b/test/Network/Minio/XmlParser/Test.hs
@@ -19,7 +19,7 @@
   ( xmlParserTests
   ) where
 
-import qualified Data.Map                  as Map
+import qualified Data.HashMap.Strict       as H
 import           Data.Time                 (fromGregorian)
 import           Test.Tasty
 import           Test.Tasty.HUnit
@@ -128,7 +128,7 @@
               \</ListBucketResult>"
 
     expectedListResult = ListObjectsResult True (Just "opaque") [object1] []
-    object1 = ObjectInfo "my-image.jpg" modifiedTime1 "\"fba9dede5f27731c9771645a39863328\"" 434234 Map.empty
+    object1 = ObjectInfo "my-image.jpg" modifiedTime1 "\"fba9dede5f27731c9771645a39863328\"" 434234 H.empty H.empty
     modifiedTime1 = flip UTCTime 64230 $ fromGregorian 2009 10 12
 
   parsedListObjectsResult <- tryValidationErr $ runTestNS $ parseListObjectsResponse xmldata
@@ -155,7 +155,7 @@
               \</ListBucketResult>"
 
     expectedListResult = ListObjectsV1Result True (Just "my-image1.jpg") [object1] []
-    object1 = ObjectInfo "my-image.jpg" modifiedTime1 "\"fba9dede5f27731c9771645a39863328\"" 434234 Map.empty
+    object1 = ObjectInfo "my-image.jpg" modifiedTime1 "\"fba9dede5f27731c9771645a39863328\"" 434234 H.empty H.empty
     modifiedTime1 = flip UTCTime 64230 $ fromGregorian 2009 10 12
 
   parsedListObjectsV1Result <- tryValidationErr $ runTestNS $ parseListObjectsV1Response xmldata
