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