1
2 /*
3 * Copyright (C) NGINX, Inc.
4 */
5
6 #include <nxt_auto_config.h>
7
8 #include <jni.h>
9 #include <nxt_unit.h>
10
11 #include "nxt_jni.h"
12 #include "nxt_jni_OutputStream.h"
13 #include "nxt_jni_URLClassLoader.h"
14
15
16 static void JNICALL nxt_java_OutputStream_writeByte(JNIEnv *env, jclass cls,
17 jlong req_info_ptr, jint b);
18 static nxt_unit_buf_t *nxt_java_OutputStream_req_buf(JNIEnv *env,
19 nxt_unit_request_info_t *req);
20 static void JNICALL nxt_java_OutputStream_write(JNIEnv *env, jclass cls,
21 jlong req_info_ptr, jarray b, jint off, jint len);
22 static void JNICALL nxt_java_OutputStream_flush(JNIEnv *env, jclass cls,
23 jlong req_info_ptr);
24 static void JNICALL nxt_java_OutputStream_close(JNIEnv *env, jclass cls,
25 jlong req_info_ptr);
26
27
28 static jclass nxt_java_OutputStream_class;
29
30
31 int
nxt_java_initOutputStream(JNIEnv * env,jobject cl)32 nxt_java_initOutputStream(JNIEnv *env, jobject cl)
33 {
34 int res;
35 jclass cls;
36
37 cls = nxt_java_loadClass(env, cl, "nginx.unit.OutputStream");
38 if (cls == NULL) {
39 return NXT_UNIT_ERROR;
40 }
41
42 nxt_java_OutputStream_class = (*env)->NewGlobalRef(env, cls);
43 (*env)->DeleteLocalRef(env, cls);
44
45 cls = nxt_java_OutputStream_class;
46
47 JNINativeMethod os_methods[] = {
48 { (char *) "write",
49 (char *) "(JI)V",
50 nxt_java_OutputStream_writeByte },
51
52 { (char *) "write",
53 (char *) "(J[BII)V",
54 nxt_java_OutputStream_write },
55
56 { (char *) "flush",
57 (char *) "(J)V",
58 nxt_java_OutputStream_flush },
59
60 { (char *) "close",
61 (char *) "(J)V",
62 nxt_java_OutputStream_close },
63 };
64
65 res = (*env)->RegisterNatives(env, nxt_java_OutputStream_class,
66 os_methods,
67 sizeof(os_methods) / sizeof(os_methods[0]));
68
69 nxt_unit_debug(NULL, "registered OutputStream methods: %d", res);
70
71 if (res != 0) {
72 (*env)->DeleteGlobalRef(env, cls);
73 return NXT_UNIT_ERROR;
74 }
75
76 return NXT_UNIT_OK;
77 }
78
79
80 static void JNICALL
nxt_java_OutputStream_writeByte(JNIEnv * env,jclass cls,jlong req_info_ptr,jint b)81 nxt_java_OutputStream_writeByte(JNIEnv *env, jclass cls, jlong req_info_ptr,
82 jint b)
83 {
84 nxt_unit_buf_t *buf;
85 nxt_unit_request_info_t *req;
86 nxt_java_request_data_t *data;
87
88 req = nxt_jlong2ptr(req_info_ptr);
89 data = req->data;
90
91 buf = nxt_java_OutputStream_req_buf(env, req);
92 if (buf == NULL) {
93 return;
94 }
95
96 *buf->free++ = b;
97
98 if ((uint32_t) (buf->free - buf->start) >= data->buf_size) {
99 nxt_java_OutputStream_flush_buf(env, req);
100 }
101 }
102
103
104 int
nxt_java_OutputStream_flush_buf(JNIEnv * env,nxt_unit_request_info_t * req)105 nxt_java_OutputStream_flush_buf(JNIEnv *env, nxt_unit_request_info_t *req)
106 {
107 int rc;
108 nxt_java_request_data_t *data;
109
110 data = req->data;
111
112 if (!nxt_unit_response_is_init(req)) {
113 rc = nxt_unit_response_init(req, 200, 0, 0);
114 if (rc != NXT_UNIT_OK) {
115 nxt_java_throw_IOException(env, "Failed to allocate response");
116
117 return rc;
118 }
119 }
120
121 if (!nxt_unit_response_is_sent(req)) {
122 rc = nxt_unit_response_send(req);
123 if (rc != NXT_UNIT_OK) {
124 nxt_java_throw_IOException(env, "Failed to send response headers");
125
126 return rc;
127 }
128 }
129
130 if (data->buf != NULL) {
131 rc = nxt_unit_buf_send(data->buf);
132 if (rc != NXT_UNIT_OK) {
133 nxt_java_throw_IOException(env, "Failed to send buffer");
134
135 } else {
136 data->buf = NULL;
137 }
138
139 } else {
140 rc = NXT_UNIT_OK;
141 }
142
143 return rc;
144 }
145
146
147 static nxt_unit_buf_t *
nxt_java_OutputStream_req_buf(JNIEnv * env,nxt_unit_request_info_t * req)148 nxt_java_OutputStream_req_buf(JNIEnv *env, nxt_unit_request_info_t *req)
149 {
150 uint32_t size;
151 nxt_unit_buf_t *buf;
152 nxt_java_request_data_t *data;
153
154 data = req->data;
155 buf = data->buf;
156
157 if (buf == NULL || buf->free >= buf->end) {
158 size = data->buf_size == 0 ? nxt_unit_buf_min() : data->buf_size;
159
160 buf = nxt_unit_response_buf_alloc(req, size);
161 if (buf == NULL) {
162 nxt_java_throw_IOException(env, "Failed to allocate buffer");
163
164 return NULL;
165 }
166
167 data->buf = buf;
168 }
169
170 return buf;
171 }
172
173
174 static void JNICALL
nxt_java_OutputStream_write(JNIEnv * env,jclass cls,jlong req_info_ptr,jarray b,jint off,jint len)175 nxt_java_OutputStream_write(JNIEnv *env, jclass cls, jlong req_info_ptr,
176 jarray b, jint off, jint len)
177 {
178 int rc;
179 jint copy;
180 uint8_t *ptr;
181 nxt_unit_buf_t *buf;
182 nxt_unit_request_info_t *req;
183 nxt_java_request_data_t *data;
184
185 req = nxt_jlong2ptr(req_info_ptr);
186 data = req->data;
187
188 ptr = (*env)->GetPrimitiveArrayCritical(env, b, NULL);
189
190 while (len > 0) {
191 buf = nxt_java_OutputStream_req_buf(env, req);
192 if (buf == NULL) {
193 return;
194 }
195
196 copy = buf->end - buf->free;
197 copy = copy < len ? copy : len;
198
199 memcpy(buf->free, ptr + off, copy);
200 buf->free += copy;
201
202 len -= copy;
203 off += copy;
204
205 if ((uint32_t) (buf->free - buf->start) >= data->buf_size) {
206 rc = nxt_java_OutputStream_flush_buf(env, req);
207 if (rc != NXT_UNIT_OK) {
208 break;
209 }
210 }
211 }
212
213 (*env)->ReleasePrimitiveArrayCritical(env, b, ptr, 0);
214 }
215
216
217 static void JNICALL
nxt_java_OutputStream_flush(JNIEnv * env,jclass cls,jlong req_info_ptr)218 nxt_java_OutputStream_flush(JNIEnv *env, jclass cls, jlong req_info_ptr)
219 {
220 nxt_unit_request_info_t *req;
221 nxt_java_request_data_t *data;
222
223 req = nxt_jlong2ptr(req_info_ptr);
224 data = req->data;
225
226 if (data->buf != NULL && data->buf->free > data->buf->start) {
227 nxt_java_OutputStream_flush_buf(env, req);
228 }
229 }
230
231
232 static void JNICALL
nxt_java_OutputStream_close(JNIEnv * env,jclass cls,jlong req_info_ptr)233 nxt_java_OutputStream_close(JNIEnv *env, jclass cls, jlong req_info_ptr)
234 {
235 nxt_java_OutputStream_flush_buf(env, nxt_jlong2ptr(req_info_ptr));
236 }
237