1 /**
2  *     Copyright 2016-2017 Couchbase, Inc.
3  *
4  *   Licensed under the Apache License, Version 2.0 (the "License");
5  *   you may not use this file except in compliance with the License.
6  *   You may obtain a copy of the License at
7  *
8  *       http://www.apache.org/licenses/LICENSE-2.0
9  *
10  *   Unless required by applicable law or agreed to in writing, software
11  *   distributed under the License is distributed on an "AS IS" BASIS,
12  *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  *   See the License for the specific language governing permissions and
14  *   limitations under the License.
15  */
16 
17 #include "couchbase.h"
18 
19 #define LOGARGS(instance, lvl) LCB_LOG_##lvl, instance, "pcbc/http", __FILE__, __LINE__
20 
21 typedef struct {
22     opcookie_res header;
23     PCBC_ZVAL bytes;
24 } opcookie_http_res;
25 
http_callback(lcb_t instance,int cbtype,const lcb_RESPBASE * rb)26 void http_callback(lcb_t instance, int cbtype, const lcb_RESPBASE *rb)
27 {
28     opcookie_http_res *result = ecalloc(1, sizeof(opcookie_http_res));
29     const lcb_RESPHTTP *resp = (const lcb_RESPHTTP *)rb;
30     TSRMLS_FETCH();
31 
32     result->header.err = resp->rc;
33     if (result->header.err != LCB_SUCCESS) {
34         pcbc_log(LOGARGS(instance, WARN), "Failed to perform HTTP request: rc=%d", (int)resp->rc);
35     }
36 
37     PCBC_ZVAL_ALLOC(result->bytes);
38     if (resp->nbody) {
39         if (((opcookie *)rb->cookie)->json_response) {
40             int last_error;
41 
42             PCBC_JSON_COPY_DECODE(PCBC_P(result->bytes), resp->body, resp->nbody, PHP_JSON_OBJECT_AS_ARRAY, last_error);
43             if (last_error != 0) {
44                 pcbc_log(LOGARGS(instance, WARN), "Failed to decode value as JSON: json_last_error=%d", last_error);
45                 ZVAL_NULL(PCBC_P(result->bytes));
46             }
47         } else {
48             PCBC_STRINGL(result->bytes, resp->body, resp->nbody);
49         }
50     } else {
51         ZVAL_NULL(PCBC_P(result->bytes));
52     }
53 
54     opcookie_push((opcookie *)rb->cookie, &result->header);
55 }
56 
proc_http_results(zval * return_value,opcookie * cookie TSRMLS_DC)57 static lcb_error_t proc_http_results(zval *return_value, opcookie *cookie TSRMLS_DC)
58 {
59     opcookie_http_res *res;
60     lcb_error_t err = LCB_SUCCESS;
61 
62     // Any error should cause everything to fail... for now?
63     err = opcookie_get_first_error(cookie);
64 
65     if (err == LCB_SUCCESS) {
66         int has_value = 0;
67         FOREACH_OPCOOKIE_RES(opcookie_http_res, res, cookie)
68         {
69             if (has_value == 0) {
70                 ZVAL_ZVAL(return_value, PCBC_P(res->bytes), 1, 0);
71                 has_value = 1;
72             } else {
73                 err = LCB_ERROR;
74                 break;
75             }
76         }
77     }
78 
79     FOREACH_OPCOOKIE_RES(opcookie_http_res, res, cookie)
80     {
81         zval_ptr_dtor(&res->bytes);
82     }
83 
84     return err;
85 }
86 
pcbc_http_request(zval * return_value,lcb_t conn,lcb_CMDHTTP * cmd,int json_response TSRMLS_DC)87 void pcbc_http_request(zval *return_value, lcb_t conn, lcb_CMDHTTP *cmd, int json_response TSRMLS_DC)
88 {
89     lcb_error_t err;
90     opcookie *cookie;
91 
92     cookie = opcookie_init();
93     cookie->json_response = json_response;
94     err = lcb_http3(conn, cookie, cmd);
95     if (err == LCB_SUCCESS) {
96         lcb_wait(conn);
97         err = proc_http_results(return_value, cookie TSRMLS_CC);
98     }
99     opcookie_destroy(cookie);
100     if (err != LCB_SUCCESS) {
101         throw_lcb_exception(err);
102     }
103 }
104 
PHP_METHOD(Bucket,http_request)105 PHP_METHOD(Bucket, http_request)
106 {
107     pcbc_bucket_t *obj = Z_BUCKET_OBJ_P(getThis());
108     lcb_CMDHTTP cmd = {0};
109     zval *ztype, *zmethod, *zpath, *zbody, *zcontenttype;
110     int rv;
111 
112     rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zzzzz", &ztype, &zmethod, &zpath, &zbody, &zcontenttype);
113     if (rv == FAILURE) {
114         RETURN_NULL();
115     }
116 
117     if (Z_LVAL_P(ztype) == 1) {
118         cmd.type = LCB_HTTP_TYPE_VIEW;
119     } else if (Z_LVAL_P(ztype) == 2) {
120         cmd.type = LCB_HTTP_TYPE_MANAGEMENT;
121     } else if (Z_LVAL_P(ztype) == 3) {
122         cmd.type = LCB_HTTP_TYPE_N1QL;
123     } else {
124         RETURN_NULL();
125     }
126 
127     if (Z_LVAL_P(zmethod) == 1) {
128         cmd.method = LCB_HTTP_METHOD_GET;
129     } else if (Z_LVAL_P(zmethod) == 2) {
130         cmd.method = LCB_HTTP_METHOD_POST;
131     } else if (Z_LVAL_P(zmethod) == 3) {
132         cmd.method = LCB_HTTP_METHOD_PUT;
133     } else if (Z_LVAL_P(zmethod) == 4) {
134         cmd.method = LCB_HTTP_METHOD_DELETE;
135     } else {
136         RETURN_NULL();
137     }
138 
139     if (Z_LVAL_P(zcontenttype) == 1) {
140         cmd.content_type = "application/json";
141     } else if (Z_LVAL_P(zcontenttype) == 2) {
142         cmd.content_type = "application/x-www-form-urlencoded";
143     } else {
144         RETURN_NULL();
145     }
146 
147     if (Z_TYPE_P(zpath) == IS_STRING) {
148         LCB_CMD_SET_KEY(&cmd, Z_STRVAL_P(zpath), Z_STRLEN_P(zpath));
149     }
150     if (Z_TYPE_P(zbody) == IS_STRING) {
151         cmd.body = Z_STRVAL_P(zbody);
152         cmd.nbody = Z_STRLEN_P(zbody);
153     }
154 
155     pcbc_http_request(return_value, obj->conn->lcb, &cmd, 0 TSRMLS_CC);
156 }
157