Performs a callback by sending a request that contains callback parameters to Object Storage Service (OSS). This topic describes how to configure callbacks.

Note

Step 1: Construct parameters

  • Callback parameter

    A callback parameter is a Base64-encoded string that contains multiple fields in the JSON format. To construct a callback parameter, you must specify the URL (callbackUrl) of the server to which the callback request is sent and the content (callbackBody) of the callback request.

    The following table describes the fields in the JSON format.

    Field Description Required
    callbackUrl
    • The URL of the server to which OSS sends a callback request. After you upload an object, OSS uses POST to send a callback request to the URL. The body of the request is the content that is specified in callbackBody. In most cases, the server whose URL is used returns the HTTP/1.1 200 OK response. The response body must be in the JSON format, and the value of the Content-Length response header must be valid and smaller than 3 MB in size.
    • You can configure up to five URLs. Separate the URLs with semicolons (;). OSS sends requests to each URL until a successful response is returned.
    • If you do not configure this field or if you leave this field empty, the callback is not configured.
    • HTTPS URLs are supported.
    • To ensure that Chinese characters can be correctly processed, the callback URL must be encoded.
    Yes
    callbackHost
    • The value of the Host header in the callback request. The value must comply with the naming conventions for domain names and IP addresses. This field takes effect only when callbackUrl is specified.
    • If you do not configure this field, the host values are resolved from the URLs of the callbackUrl field and are specified as the value of callbackHost.
    No
    callbackBody
    • The value of the callback request body. Example: key=${object}&etag=${etag}&my_var=${x:my_var}.
    • OSS system variables, custom variables, and constants are supported. The following table describes the system variables that are supported. You can specify custom variables by using the callback-var parameter in the PutObject and CompleteMultipartUpload operations and by using form fields in the PostObject operation.
    Yes
    callbackBodyType
    • The Content-Type header in the callback request. Valid values: application/x-www-form-urlencoded and application/json. Default value: application/x-www-form-urlencoded.
    • If you set callbackBodyType to application/x-www-form-urlencoded, the variables in the callbackBody field are replaced with URL-encoded values. If you set callbackBodyType to application/json, the variables in the callbackBody field are replaced with values in the JSON format.
    No

    Examples of the JSON fields:

    {
    "callbackUrl":"172.16.XX.XX/test.php",
    "callbackHost":"oss-cn-hangzhou.aliyuncs.com",
    "callbackBody":"{\"mimeType\":${mimeType},\"size\":${size}}",
    "callbackBodyType":"application/json"
    }
    {
    "callbackUrl":"172.16.XX.XX:23456/index.html",
    "callbackBody":"bucket=${bucket}&object=${object}&etag=${etag}&size=${size}&mimeType=${mimeType}&imageInfo.height=${imageInfo.height}&imageInfo.width=${imageInfo.width}&imageInfo.format=${imageInfo.format}&my_var=${x:my_var}"
    }

    You can use system parameters for the callbackBody field. The following table describes the parameters.

    System parameter Description
    bucket Storage capacity.
    object The object that is stored in the OSS bucket.
    etag The ETag field that is configured for the object and returned to the requester.
    size The size of the requested object, which is the total size of the object in CompleteMultipartUpload operations.
    mimeType The resource type. For example, the resource type of JPEG images is image/jpeg.
    imageInfo.height The height of the image.
    imageInfo.width The width of the image.
    imageInfo.format The format of the image. Example: JPG and PNG.
    Note The imageInfo parameters take effect only on image objects. If the object is not an image, the imageInfo.height, imageInfo.width, and imageInfo.format parameters are empty.
  • Construct custom parameters by using callback-var

    You can configure custom parameters by using the callback-var parameter. Custom parameters are key-value pairs in a map. You can add the required parameters to the map. When a POST callback request is initiated, OSS adds the custom parameters and the system parameters that are described in the preceding table to the body of the POST request. This way, the requester can obtain these parameters.

    You can create a custom parameter in the same manner in which you create a callback parameter. Configure the custom parameters in the JSON format. The JSON string is a map that consists of key-value pairs of all custom parameters.

    Note The key of a custom parameter must start with x: and must be specified in lowercase letters. If the key does not start with x: and is not specified in lowercase letters, the system cannot correctly assign a value to the custom parameter even if HTTP status code 200 is returned.

    For example, you configure two custom parameters named x:var1 and x:var2. The value of x:var1 is value1, and the value of x:var2 is value2. The following JSON string is created:

    {
    "x:var1":"value1",
    "x:var2":"value2"
    }
Note If the callback parameter or callback-var parameter that you imported is invalid, HTTP status code 400 and the InvalidArgument error code are returned. This error occurs in the following scenarios:
  • You specified URLs and headers for the callback parameter (x-oss-callback) or the callback-var parameter (x-oss-callback-var) in PutObject and CompleteMultipartUpload operations.
  • The size of the callback parameter or callback-var parameter exceeds 5 KB. PostObject() does not include the callback-var parameter. When you use PostObject(), no limits are imposed for operations.
  • The callback parameter or callback-var parameter is not Base64-encoded, or the parameter is not in the valid JSON format after you decode the parameter.
  • The callbackUrl field that you decoded from the callback parameter includes more than five URLs or the port in the URL is invalid. Example:
    {"callbackUrl":"172.16.XX.XX:test",
        "callbackBody":"test"}
  • The callbackBody field that you decoded from the callback parameter is empty.
  • The value of the callbackBodyType field that you decoded from the callback parameter is not application/x-www-form-urlencoded or application/json.
  • The variables in the callbackBody field that you decoded from the callback parameter are not in the ${var} format.
  • The variables that you decoded from the callback-var parameter are not in the following JSON format: {"x:var1":"value1","x:var2":"value2"...}.

Step 2: Configure a callback request

After you construct the callback and callback-var parameters, you must add the parameters to the callback request that is sent to OSS.

You can use one of the following methods to add the parameters:

  • Add the parameters to a URL.
  • Add the parameters to the header.
  • Add the parameters to the form fields in the body of a POST request.
    Note You can use only this method to specify callback parameters when you upload objects by calling the PostObject operation.

You can use only one of the preceding three methods. If you use more than one method, OSS returns the InvalidArgument error code.

To add the parameters to a request that you want OSS to send, you must use Base64 to encode the JSON strings that you created in the preceding section, and then add the parameters.

  • To add the parameters to a URL, add callback=[CallBack] or callback-var=[CallBackVar] to the request as a URL parameter. The callback paramter or callback-var parameter is used as a subresource to calculate the signature for the CanonicalizedResource field.
  • To add the parameters to the header, add x-oss-callback=[CallBack] or x-oss-callback-var=[CallBackVar] to the request as a header. Add the x-oss-callback-var parameter and x-oss-callback parameter in the header to calculate the signature for the CanonicalizedOSSHeaders field. Example:
    PUT /test.txt HTTP/1.1
    Host: callback-test.oss-test.aliyun-inc.com
    Accept-Encoding: identity
    Content-Length: 5
    x-oss-callback-var: eyJ4Om15X3ZhciI6ImZvci1jYWxsYmFjay10ZXN0In0=
    User-Agent: aliyun-sdk-python/0.4.0 (Linux/2.6.32-220.23.2.ali1089.el5.x86_64/x86_64;2.5.4)
    x-oss-callback: eyJjYWxsYmFja1VybCI6IjEyMS40My4xMTMuODoyMzQ1Ni9pbmRleC5odG1sIiwgICJjYWxsYmFja0JvZHkiOiJidWNrZXQ9JHtidWNrZXR9Jm9iamVjdD0ke29iamVjdH0mZXRhZz0ke2V0YWd9JnNpemU9JHtzaXplfSZtaW1lVHlwZT0ke21pbWVUeXBlfSZpbWFnZUluZm8uaGVpZ2h0PSR7aW1hZ2VJbmZvLmhlaWdodH0maW1hZ2VJbmZvLndpZHRoPSR7aW1hZ2VJbmZvLndpZHRofSZpbWFnZUluZm8uZm9ybWF0PSR7aW1hZ2VJbmZvLmZvcm1hdH0mbXlfdmFyPSR7eDpteV92YXJ9In0=
    Host: callback-test.oss-test.aliyun-inc.com
    Expect: 100-Continue
    Date: Mon, 14 Sep 2015 12:37:27 GMT
    Content-Type: text/plain
    Authorization: OSS mlepou3zr4u7b14:5a74vhd4UXpmyuudV14Kaen5****
    Test
  • Add the parameters to the form fields in the body of a POST request.
    • If you use POST to upload an object, add the callback parameter by using a different form field. Example:
      --9431149156168
      Content-Disposition: form-data; name="callback"
      eyJjYWxsYmFja1VybCI6IjEwLjEwMS4xNjYuMzA6ODA4My9jYWxsYmFjay5waHAiLCJjYWxsYmFja0hvc3QiOiIxMC4xMDEuMTY2LjMwIiwiY2FsbGJhY2tCb2R5IjoiZmlsZW5hbWU9JChmaWxlbmFtZSkmdGFibGU9JHt4OnRhYmxlfSIsImNhbGxiYWNrQm9keVR5cGUiOiJhcHBsaWNhdGlvbi94LXd3dy1mb3JtLXVybGVuY29kZWQifQ==
    • Each custom parameter uses a different form field. You cannot add the callback-var parameter to existing fields. Examples of JSON fields that you can configure for custom parameters:
      {
      "x:var1":"value1",
      "x:var2":"value2"
      }

      Examples of form fields in the POST request:

      --9431149156168
      Content-Disposition: form-data; name="callback"
      eyJjYWxsYmFja1VybCI6IjEwLjEwMS4xNjYuMzA6ODA4My9jYWxsYmFjay5waHAiLCJjYWxsYmFja0hvc3QiOiIxMC4xMDEuMTY2LjMwIiwiY2FsbGJhY2tCb2R5IjoiZmlsZW5hbWU9JChmaWxlbmFtZSkmdGFibGU9JHt4OnRhYmxlfSIsImNhbGxiYWNrQm9keVR5cGUiOiJhcHBsaWNhdGlvbi94LXd3dy1mb3JtLXVybGVuY29kZWQifQ==
      --9431149156168
      Content-Disposition: form-data; name="x:var1"
      value1
      --9431149156168
      Content-Disposition: form-data; name="x:var2"
      value2

      You can add callback conditions to the policy. If you do not add callback conditions, the callback parameters are not verified for the upload. Example:

      { "expiration": "2021-12-01T12:00:00.000Z",
        "conditions": [
          {"bucket": "johnsmith" },
          {"callback": "eyJjYWxsYmFja1VybCI6IjEwLjEwMS4xNjYuMzA6ODA4My9jYWxsYmFjay5waHAiLCJjYWxsYmFja0hvc3QiOiIxMC4xMDEuMTY2LjMwIiwiY2FsbGJhY2tCb2R5IjoiZmlsZW5hbWU9JChmaWxlbmFtZSkiLCJjYWxsYmFja0JvZHlUeXBlIjoiYXBwbGljYXRpb24veC13d3ctZm9ybS11cmxlbmNvZGVkIn0="},
          ["starts-with", "$key", "user/eric/"],
        ]
      }

Step 3: Initiate a callback request

If you upload an object, OSS sends the content that you specified in the callback parameter and callback-var parameter in the request to the application server by using the POST method. Example:

POST /index.html HTTP/1.0
Host: 172.16.XX.XX
Connection: close
Content-Length: 181
Content-Type: application/x-www-form-urlencoded
User-Agent: http-client/0.0.1
bucket=callback-test&object=test.txt&etag=D8E8FCA2DC0F896FD7CB4CB0031BA249&size=5&mimeType=text%2Fplain&imageInfo.height=&imageInfo.width=&imageInfo.format=&x:var1=for-callback-test

(Optional) Step 4: Sign the callback request

If you include the callback parameter in the request, OSS uses the POST method to send a callback request to the application server based on the specified callback URL. To check whether the callback request that is received by the application server is initiated by OSS, you can sign the callback request.

  • Generate a signature

    OSS uses the RSA asymmetric algorithm to sign a callback request.

    To generate a signature, encrypt the callback string by using a private key. Example:

    authorization = base64_encode(rsa_sign(private_key, url_decode(path) + query_string + '\n' + body, md5))
    Note In the preceding code, private_key is a private key that is known only to OSS, path is the resource path that is included in the callback request, query_string is the query string, and body is the message body that is specified in the callback request.

    To sign a callback request, perform the following steps:

    1. Obtain the callback string that you want to sign. The string consists of the resource path that is obtained by decoding the URL, the original query string, a carriage return, and the callback message body.
    2. Sign the callback string by using the RSA encryption algorithm. Use the private key to encrypt the signature string. The MD5 hash function is used for the signature.
    3. Use Base64 to encode the signed result to obtain the final signature. Then, add the signature to the Authorization header in the callback request.

    Example:

    POST /index.php?id=1&index=2 HTTP/1.0
    Host: 172.16.XX.XX
    Connection: close
    Content-Length: 18
    authorization: kKQeGTRccDKyHB3H9vF+xYMSrmhMZjzzl2/kdD1ktNVgbWEfYTQG0G2SU/RaHBovRCE8OkQDjC3uG33esH2t****
    Content-Type: application/x-www-form-urlencoded
    User-Agent: http-client/0.0.1
    x-oss-pub-key-url: aHR0cDovL2dvc3NwdWJsaWMuYWxpY2RuLmNvbS9jYWxsYmFja19wdWJfa2V5X3YxLn****==
    bucket=yonghu-test

    In the preceding code, path is set to /index.php, query_string is set to ?id=1&index=2, and the body is set to bucket=yonghu-test. The final signature is kKQeGTRccDKyHB3H9vF+xYMSrmhMZjzzl2/kdD1ktNVgbWEfYTQG0G2SU/RaHBovRCE8OkQDjC3uG33esH2txA==.

  • Verify the signature

    The process for verifying a signature is the inverse of the process for signing a request. The signature is verified by the application server. Process:

    Result = rsa_verify(public_key, md5(url_decode(path) + query_string + '\n' + body), base64_decode(authorization))

    The fields in the preceding code and the fields that are used to sign the request are similar. public_key specifies the public key, and authorization specifies the signature that you want to include in the callback request header. To verify the signature, perform the following steps:

    1. The x-oss-pub-key-url header in the callback request stores the Base64-encoded URL of the public key. Decode the Base64-encoded URL to obtain the public key.
      public_key = urlopen(base64_decode(x-oss-pub-key-url header value))
      Note To ensure that the public key is issued by OSS, check whether the value of the x-oss-pub-key-url header starts with http://gosspublic.alicdn.com/ or https://gosspublic.alicdn.com/.
    2. Obtain the signature that you decoded in Base64.
      signature = base64_decode(authorization header value)
    3. Obtain the string to sign by using the same procedure that you used to obtain the string when the callback request is signed.
      sign_str = url_decode(path) + query_string + '\n' + body
    4. Verify the signature.
      result = rsa_verify(public_key, md5(sign_str), signature)

    Complete signature verification:

    1. Obtain the URL of the public key by decoding aHR0cDovL2dvc3NwdWJsaWMuYWxpY2RuLmNvbS9jYWxsYmFja19wdWJfa2V5X3YxLnBlbQ== in Base64. The decoded URL is http://gosspublic.alicdn.com/callback_pub_key_v1.pem.
    2. Decode kKQeGTRccDKyHB3H9vF+xYMSrmhMZjzzl2/kdD1ktNVgbWEfYTQG0G2SU/RaHBovRCE8OkQDjC3uG33esH2txA== in Base64. The decoded result cannot be displayed because the result is a non-printable string.
    3. Obtain the string to sign and perform MD5 verification. The string to sign is concatenated as shown in the following sample code: url_decode("index.php") + "?id=1&index=2" + "\n" + "bucket=yonghu-test".
    4. Verify the signature.
  • Verify the signature by using an application server

    The following sample code in Python shows how an application server verifies a signature. Before you run the code, install the M2Crypto library.

    import httplib
    import base64
    import md5
    import urllib2
    from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
    from M2Crypto import RSA
    from M2Crypto import BIO
    def get_local_ip():
        try:
            csock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
            csock.connect(('8.8.8.8', 80))
            (addr, port) = csock.getsockname()
            csock.close()
            return addr
        except socket.error:
            return ""
    class MyHTTPRequestHandler(BaseHTTPRequestHandler):
        '''
        def log_message(self, format, *args):
            return
        '''
        def do_POST(self):
            #get public key
            pub_key_url = ''
            try:
                pub_key_url_base64 = self.headers['x-oss-pub-key-url']
                pub_key_url = pub_key_url_base64.decode('base64')
                if not pub_key_url.startswith("http://gosspublic.alicdn.com/") and not pub_key_url.startswith("https://gosspublic.alicdn.com/"):
                    self.send_response(400)
                    self.end_headers()
                    return
                url_reader = urllib2.urlopen(pub_key_url)
                #you can cache it
                pub_key = url_reader.read() 
            except:
                print 'pub_key_url : ' + pub_key_url
                print 'Get pub key failed!'
                self.send_response(400)
                self.end_headers()
                return
            #get authorization
            authorization_base64 = self.headers['authorization']
            authorization = authorization_base64.decode('base64')
            #get callback body
            content_length = self.headers['content-length']
            callback_body = self.rfile.read(int(content_length))
            #compose authorization string
            auth_str = ''
            pos = self.path.find('?')
            if -1 == pos:
                auth_str = urllib2.unquote(self.path) + '\n' + callback_body
            else:
                auth_str = urllib2.unquote(self.path[0:pos]) + self.path[pos:] + '\n' + callback_body
            print auth_str
            #verify authorization
            auth_md5 = md5.new(auth_str).digest()
            bio = BIO.MemoryBuffer(pub_key)
            rsa_pub = RSA.load_pub_key_bio(bio)
            try:
                result = rsa_pub.verify(auth_md5, authorization, 'md5')
            except:
                result = False
            if not result:
                print 'Authorization verify failed!'
                print 'Public key : %s' % (pub_key)
                print 'Auth string : %s' % (auth_str)
                self.send_response(400)
                self.end_headers()
                return
            #do something according to callback_body
            #response to OSS
            resp_body = '{"Status":"OK"}'
            self.send_response(200)
            self.send_header('Content-Type', 'application/json')
            self.send_header('Content-Length', str(len(resp_body)))
            self.end_headers()
            self.wfile.write(resp_body)
    class MyHTTPServer(HTTPServer):
        def __init__(self, host, port):
            HTTPServer.__init__(self, (host, port), MyHTTPRequestHandler)
    if '__main__' == __name__:
        server_ip = get_local_ip()
    server_port = 23451
    server = MyHTTPServer(server_ip, server_port)
    server.serve_forever()
    The following table describes the code in other programming languages that you can use to verify a signature on the server.
    Programming language Description
    Java
    • Download link: Java
    • Running method: Decompress the package and run java -jar oss-callback-server-demo.jar 9000. The port number is 9000. You can change the port number.
    Python
    • Download link: Python
    • Running method: Decompress the package and run python callback_app_server.py. Before you run the code, install RSA dependencies.
    Go
    • Download link: Go
    • Running method: Decompress the package and follow the instructions in README.md.
    PHP
    • Download link: PHP
    • Running method: Deploy the code to an Apache environment to ensure that specific headers in the code can use the environment as a dependency. You can modify the sample code based on the environment.
    .NET
    • Download link: .NET
    • Running method: Decompress the package and follow the instructions in README.md.
    Node.js
    • Download link: Node.js
    • Running method: Decompress the package and run node example.js.
    Ruby
    • Download link: Ruby
    • Running method: Run ruby aliyun_oss_callback_server.rb.

Step 5: Return the callback result

The application server returns a response to OSS.

The following example shows a response that is returned for a callback request:

HTTP/1.0 200 OK
Server: BaseHTTP/0.3 Python/2.7.6
Date: Mon, 14 Sep 2015 12:37:27 GMT
Content-Type: application/json
Content-Length: 9
{"a":"b"}
Note The response that is returned by the application server to OSS must contain the Content-Length header. The size of the response body cannot exceed 1 MB.

Step 6: Return the upload result

OSS returns the information that is returned by the application server to the user.

The following example shows a response that is returned:

HTTP/1.1 200 OK
Date: Mon, 14 Sep 2015 12:37:27 GMT
Content-Type: application/json
Content-Length: 9
Connection: keep-alive
ETag: "D8E8FCA2DC0F896FD7CB4CB0031BA249"
Server: AliyunOSS
x-oss-bucket-version: 1442231779
x-oss-request-id: 55F6BF87207FB30F2640C548
{"a":"b"}
Note
  • The response bodies for some requests such as CompleteMultipartUpload contains content such as data in the XML format. If you use the upload callback feature, the original body content such as {"a":"b"} is overwritten. Exercise caution when you configure upload callbacks.
  • If the upload callback fails, HTTP status code 203 and the CallbackFailed error code are returned. This response indicates that the object is uploaded to OSS and the callback fails. A callback failure indicates that OSS does not receive the expected callback response. However, this type of failure does not indicate that the application server does not receive a callback request. For example, a callback failure occurs if the response that is returned by the application server is not in the JSON format.