xref: /unit/src/java/nxt_jni_Response.c (revision 2561:0e6d01d0c23b)
1 
2 /*
3  * Copyright (C) NGINX, Inc.
4  */
5 
6 #include <nxt_auto_config.h>
7 
8 #include <nxt_unit.h>
9 #include <nxt_unit_response.h>
10 #include <jni.h>
11 #include <stdio.h>
12 
13 #include "nxt_jni.h"
14 #include "nxt_jni_Response.h"
15 #include "nxt_jni_HeadersEnumeration.h"
16 #include "nxt_jni_HeaderNamesEnumeration.h"
17 #include "nxt_jni_OutputStream.h"
18 #include "nxt_jni_URLClassLoader.h"
19 
20 
21 static jclass     nxt_java_Response_class;
22 static jmethodID  nxt_java_Response_ctor;
23 
24 
25 static void JNICALL nxt_java_Response_addHeader(JNIEnv *env, jclass cls,
26     jlong req_info_ptr, jarray name, jarray value);
27 
28 static nxt_unit_request_info_t *nxt_java_get_response_info(
29     jlong req_info_ptr, uint32_t extra_fields, uint32_t extra_data);
30 
31 static void JNICALL nxt_java_Response_addIntHeader(JNIEnv *env, jclass cls,
32     jlong req_info_ptr, jarray name, jint value);
33 
34 static void nxt_java_add_int_header(nxt_unit_request_info_t *req,
35     const char *name, uint8_t name_len, int value);
36 
37 static jboolean JNICALL nxt_java_Response_containsHeader(JNIEnv *env,
38     jclass cls, jlong req_info_ptr, jarray name);
39 
40 static jstring JNICALL nxt_java_Response_getHeader(JNIEnv *env, jclass cls,
41     jlong req_info_ptr, jarray name);
42 
43 static jobject JNICALL nxt_java_Response_getHeaderNames(JNIEnv *env,
44     jclass cls, jlong req_info_ptr);
45 
46 static jobject JNICALL nxt_java_Response_getHeaders(JNIEnv *env, jclass cls,
47     jlong req_info_ptr, jarray name);
48 
49 static jint JNICALL nxt_java_Response_getStatus(JNIEnv *env, jclass cls,
50     jlong req_info_ptr);
51 
52 static jobject JNICALL nxt_java_Response_getRequest(JNIEnv *env, jclass cls,
53     jlong req_info_ptr);
54 
55 static void JNICALL nxt_java_Response_commit(JNIEnv *env, jclass cls,
56     jlong req_info_ptr);
57 
58 static void JNICALL nxt_java_Response_sendRedirect(JNIEnv *env, jclass cls,
59     jlong req_info_ptr, jarray loc);
60 
61 static int nxt_java_response_set_header(jlong req_info_ptr,
62     const char *name, jint name_len, const char *value, jint value_len);
63 
64 static void JNICALL nxt_java_Response_setHeader(JNIEnv *env, jclass cls,
65     jlong req_info_ptr, jarray name, jarray value);
66 
67 static void JNICALL nxt_java_Response_removeHeader(JNIEnv *env, jclass cls,
68     jlong req_info_ptr, jarray name);
69 
70 static int nxt_java_response_remove_header(jlong req_info_ptr,
71     const char *name, jint name_len);
72 
73 static void JNICALL nxt_java_Response_setIntHeader(JNIEnv *env, jclass cls,
74     jlong req_info_ptr, jarray name, jint value);
75 
76 static void JNICALL nxt_java_Response_setStatus(JNIEnv *env, jclass cls,
77     jlong req_info_ptr, jint sc);
78 
79 static jstring JNICALL nxt_java_Response_getContentType(JNIEnv *env,
80     jclass cls, jlong req_info_ptr);
81 
82 static jboolean JNICALL nxt_java_Response_isCommitted(JNIEnv *env, jclass cls,
83     jlong req_info_ptr);
84 
85 static void JNICALL nxt_java_Response_reset(JNIEnv *env, jclass cls,
86     jlong req_info_ptr);
87 
88 static void JNICALL nxt_java_Response_resetBuffer(JNIEnv *env, jclass cls,
89     jlong req_info_ptr);
90 
91 static void JNICALL nxt_java_Response_setBufferSize(JNIEnv *env, jclass cls,
92     jlong req_info_ptr, jint size);
93 
94 static jint JNICALL nxt_java_Response_getBufferSize(JNIEnv *env, jclass cls,
95     jlong req_info_ptr);
96 
97 static void JNICALL nxt_java_Response_setContentLength(JNIEnv *env, jclass cls,
98     jlong req_info_ptr, jlong len);
99 
100 static void JNICALL nxt_java_Response_setContentType(JNIEnv *env, jclass cls,
101     jlong req_info_ptr, jarray type);
102 
103 static void JNICALL nxt_java_Response_removeContentType(JNIEnv *env, jclass cls,
104     jlong req_info_ptr);
105 
106 static void JNICALL nxt_java_Response_log(JNIEnv *env, jclass cls,
107     jlong req_info_ptr, jarray msg);
108 
109 static void JNICALL nxt_java_Response_trace(JNIEnv *env, jclass cls,
110     jlong req_info_ptr, jarray msg);
111 
112 int
nxt_java_initResponse(JNIEnv * env,jobject cl)113 nxt_java_initResponse(JNIEnv *env, jobject cl)
114 {
115     int     res;
116     jclass  cls;
117 
118     cls = nxt_java_loadClass(env, cl, "nginx.unit.Response");
119     if (cls == NULL) {
120         return NXT_UNIT_ERROR;
121     }
122 
123     nxt_java_Response_class = (*env)->NewGlobalRef(env, cls);
124     (*env)->DeleteLocalRef(env, cls);
125     cls = nxt_java_Response_class;
126 
127     nxt_java_Response_ctor = (*env)->GetMethodID(env, cls, "<init>", "(J)V");
128     if (nxt_java_Response_ctor == NULL) {
129         (*env)->DeleteGlobalRef(env, cls);
130         return NXT_UNIT_ERROR;
131     }
132 
133     JNINativeMethod resp_methods[] = {
134         { (char *) "addHeader",
135           (char *) "(J[B[B)V",
136           nxt_java_Response_addHeader },
137 
138         { (char *) "addIntHeader",
139           (char *) "(J[BI)V",
140           nxt_java_Response_addIntHeader },
141 
142         { (char *) "containsHeader",
143           (char *) "(J[B)Z",
144           nxt_java_Response_containsHeader },
145 
146         { (char *) "getHeader",
147           (char *) "(J[B)Ljava/lang/String;",
148           nxt_java_Response_getHeader },
149 
150         { (char *) "getHeaderNames",
151           (char *) "(J)Ljava/util/Enumeration;",
152           nxt_java_Response_getHeaderNames },
153 
154         { (char *) "getHeaders",
155           (char *) "(J[B)Ljava/util/Enumeration;",
156           nxt_java_Response_getHeaders },
157 
158         { (char *) "getStatus",
159           (char *) "(J)I",
160           nxt_java_Response_getStatus },
161 
162         { (char *) "getRequest",
163           (char *) "(J)Lnginx/unit/Request;",
164           nxt_java_Response_getRequest },
165 
166         { (char *) "commit",
167           (char *) "(J)V",
168           nxt_java_Response_commit },
169 
170         { (char *) "sendRedirect",
171           (char *) "(J[B)V",
172           nxt_java_Response_sendRedirect },
173 
174         { (char *) "setHeader",
175           (char *) "(J[B[B)V",
176           nxt_java_Response_setHeader },
177 
178         { (char *) "removeHeader",
179           (char *) "(J[B)V",
180           nxt_java_Response_removeHeader },
181 
182         { (char *) "setIntHeader",
183           (char *) "(J[BI)V",
184           nxt_java_Response_setIntHeader },
185 
186         { (char *) "setStatus",
187           (char *) "(JI)V",
188           nxt_java_Response_setStatus },
189 
190         { (char *) "getContentType",
191           (char *) "(J)Ljava/lang/String;",
192           nxt_java_Response_getContentType },
193 
194         { (char *) "isCommitted",
195           (char *) "(J)Z",
196           nxt_java_Response_isCommitted },
197 
198         { (char *) "reset",
199           (char *) "(J)V",
200           nxt_java_Response_reset },
201 
202         { (char *) "resetBuffer",
203           (char *) "(J)V",
204           nxt_java_Response_resetBuffer },
205 
206         { (char *) "setBufferSize",
207           (char *) "(JI)V",
208           nxt_java_Response_setBufferSize },
209 
210         { (char *) "getBufferSize",
211           (char *) "(J)I",
212           nxt_java_Response_getBufferSize },
213 
214         { (char *) "setContentLength",
215           (char *) "(JJ)V",
216           nxt_java_Response_setContentLength },
217 
218         { (char *) "setContentType",
219           (char *) "(J[B)V",
220           nxt_java_Response_setContentType },
221 
222         { (char *) "removeContentType",
223           (char *) "(J)V",
224           nxt_java_Response_removeContentType },
225 
226         { (char *) "log",
227           (char *) "(J[B)V",
228           nxt_java_Response_log },
229 
230         { (char *) "trace",
231           (char *) "(J[B)V",
232           nxt_java_Response_trace },
233 
234     };
235 
236     res = (*env)->RegisterNatives(env, nxt_java_Response_class,
237                                   resp_methods,
238                                   sizeof(resp_methods)
239                                       / sizeof(resp_methods[0]));
240 
241     nxt_unit_debug(NULL, "registered Response methods: %d", res);
242 
243     if (res != 0) {
244         (*env)->DeleteGlobalRef(env, cls);
245         return NXT_UNIT_ERROR;
246     }
247 
248     return NXT_UNIT_OK;
249 }
250 
251 
252 jobject
nxt_java_newResponse(JNIEnv * env,nxt_unit_request_info_t * req)253 nxt_java_newResponse(JNIEnv *env, nxt_unit_request_info_t *req)
254 {
255     return (*env)->NewObject(env, nxt_java_Response_class,
256                              nxt_java_Response_ctor, nxt_ptr2jlong(req));
257 }
258 
259 
260 static void JNICALL
nxt_java_Response_addHeader(JNIEnv * env,jclass cls,jlong req_info_ptr,jarray name,jarray value)261 nxt_java_Response_addHeader(JNIEnv *env, jclass cls, jlong req_info_ptr,
262     jarray name, jarray value)
263 {
264     int                      rc;
265     char                     *name_str, *value_str;
266     jsize                    name_len, value_len;
267     nxt_unit_request_info_t  *req;
268 
269     name_len = (*env)->GetArrayLength(env, name);
270     value_len = (*env)->GetArrayLength(env, value);
271 
272     req = nxt_java_get_response_info(req_info_ptr, 1, name_len + value_len + 2);
273     if (req == NULL) {
274         return;
275     }
276 
277     name_str = (*env)->GetPrimitiveArrayCritical(env, name, NULL);
278     if (name_str == NULL) {
279         nxt_unit_req_warn(req, "addHeader: failed to get name content");
280         return;
281     }
282 
283     value_str = (*env)->GetPrimitiveArrayCritical(env, value, NULL);
284     if (value_str == NULL) {
285         (*env)->ReleasePrimitiveArrayCritical(env, name, name_str, 0);
286         nxt_unit_req_warn(req, "addHeader: failed to get value content");
287 
288         return;
289     }
290 
291     rc = nxt_unit_response_add_field(req, name_str, name_len,
292                                      value_str, value_len);
293     if (rc != NXT_UNIT_OK) {
294         // throw
295     }
296 
297     (*env)->ReleasePrimitiveArrayCritical(env, value, value_str, 0);
298     (*env)->ReleasePrimitiveArrayCritical(env, name, name_str, 0);
299 }
300 
301 
302 static nxt_unit_request_info_t *
nxt_java_get_response_info(jlong req_info_ptr,uint32_t extra_fields,uint32_t extra_data)303 nxt_java_get_response_info(jlong req_info_ptr, uint32_t extra_fields,
304     uint32_t extra_data)
305 {
306     int                      rc;
307     char                     *p;
308     uint32_t                 max_size;
309     nxt_unit_buf_t           *buf;
310     nxt_unit_request_info_t  *req;
311     nxt_java_request_data_t  *data;
312 
313     req = nxt_jlong2ptr(req_info_ptr);
314 
315     if (nxt_unit_response_is_sent(req)) {
316         return NULL;
317     }
318 
319     data = req->data;
320 
321     if (!nxt_unit_response_is_init(req)) {
322         max_size = nxt_unit_buf_max();
323         max_size = max_size < data->header_size ? max_size : data->header_size;
324 
325         rc = nxt_unit_response_init(req, 200, 16, max_size);
326         if (rc != NXT_UNIT_OK) {
327             return NULL;
328         }
329     }
330 
331     buf = req->response_buf;
332 
333     if (extra_fields > req->response_max_fields
334                        - req->response->fields_count
335         || extra_data > (uint32_t) (buf->end - buf->free))
336     {
337         p = buf->start + sizeof(nxt_unit_response_t)
338             + req->response_max_fields * sizeof(nxt_unit_field_t);
339 
340         max_size = 2 * (buf->end - p);
341         if (max_size > nxt_unit_buf_max()) {
342             nxt_unit_req_warn(req, "required max_size is too big: %"PRIu32,
343                 max_size);
344             return NULL;
345         }
346 
347         rc = nxt_unit_response_realloc(req, 2 * req->response_max_fields,
348                                        max_size);
349         if (rc != NXT_UNIT_OK) {
350             nxt_unit_req_warn(req, "reallocation failed: %"PRIu32", %"PRIu32,
351                 2 * req->response_max_fields, max_size);
352             return NULL;
353         }
354     }
355 
356     return req;
357 }
358 
359 
360 static void JNICALL
nxt_java_Response_addIntHeader(JNIEnv * env,jclass cls,jlong req_info_ptr,jarray name,jint value)361 nxt_java_Response_addIntHeader(JNIEnv *env, jclass cls, jlong req_info_ptr,
362     jarray name, jint value)
363 {
364     char                     *name_str;
365     jsize                    name_len;
366     nxt_unit_request_info_t  *req;
367 
368     name_len = (*env)->GetArrayLength(env, name);
369 
370     req = nxt_java_get_response_info(req_info_ptr, 1, name_len + 40);
371     if (req == NULL) {
372         return;
373     }
374 
375     name_str = (*env)->GetPrimitiveArrayCritical(env, name, NULL);
376     if (name_str == NULL) {
377         nxt_unit_req_warn(req, "addIntHeader: failed to get name content");
378         return;
379     }
380 
381     nxt_java_add_int_header(req, name_str, name_len, value);
382 
383     (*env)->ReleasePrimitiveArrayCritical(env, name, name_str, 0);
384 }
385 
386 
387 static void
nxt_java_add_int_header(nxt_unit_request_info_t * req,const char * name,uint8_t name_len,int value)388 nxt_java_add_int_header(nxt_unit_request_info_t *req, const char *name,
389     uint8_t name_len, int value)
390 {
391     char                 *p;
392     nxt_unit_field_t     *f;
393     nxt_unit_response_t  *resp;
394 
395     resp = req->response;
396 
397     f = resp->fields + resp->fields_count;
398     p = req->response_buf->free;
399 
400     f->hash = nxt_unit_field_hash(name, name_len);
401     f->skip = 0;
402     f->name_length = name_len;
403 
404     nxt_unit_sptr_set(&f->name, p);
405     memcpy(p, name, name_len);
406     p += name_len;
407 
408     nxt_unit_sptr_set(&f->value, p);
409     f->value_length = snprintf(p, 40, "%d", (int) value);
410     p += f->value_length + 1;
411 
412     resp->fields_count++;
413     req->response_buf->free = p;
414 
415 }
416 
417 
418 static jboolean JNICALL
nxt_java_Response_containsHeader(JNIEnv * env,jclass cls,jlong req_info_ptr,jarray name)419 nxt_java_Response_containsHeader(JNIEnv *env,
420     jclass cls, jlong req_info_ptr, jarray name)
421 {
422     jboolean                 res;
423     char                     *name_str;
424     jsize                    name_len;
425     nxt_unit_response_t      *resp;
426     nxt_unit_request_info_t  *req;
427 
428     req = nxt_jlong2ptr(req_info_ptr);
429 
430     if (!nxt_unit_response_is_init(req)) {
431         nxt_unit_req_debug(req, "containsHeader: response is not initialized");
432         return 0;
433     }
434 
435     if (nxt_unit_response_is_sent(req)) {
436         nxt_unit_req_debug(req, "containsHeader: response already sent");
437         return 0;
438     }
439 
440     name_len = (*env)->GetArrayLength(env, name);
441 
442     name_str = (*env)->GetPrimitiveArrayCritical(env, name, NULL);
443     if (name_str == NULL) {
444         nxt_unit_req_warn(req, "containsHeader: failed to get name content");
445         return 0;
446     }
447 
448     resp = req->response;
449 
450     res = nxt_java_findHeader(resp->fields,
451                               resp->fields + resp->fields_count,
452                               name_str, name_len) != NULL;
453 
454     (*env)->ReleasePrimitiveArrayCritical(env, name, name_str, 0);
455 
456     return res;
457 }
458 
459 
460 static jstring JNICALL
nxt_java_Response_getHeader(JNIEnv * env,jclass cls,jlong req_info_ptr,jarray name)461 nxt_java_Response_getHeader(JNIEnv *env, jclass cls, jlong req_info_ptr,
462     jarray name)
463 {
464     char                     *name_str;
465     jsize                    name_len;
466     nxt_unit_field_t         *f;
467     nxt_unit_request_info_t  *req;
468 
469     req = nxt_jlong2ptr(req_info_ptr);
470 
471     if (!nxt_unit_response_is_init(req)) {
472         nxt_unit_req_debug(req, "getHeader: response is not initialized");
473         return NULL;
474     }
475 
476     if (nxt_unit_response_is_sent(req)) {
477         nxt_unit_req_debug(req, "getHeader: response already sent");
478         return NULL;
479     }
480 
481     name_len = (*env)->GetArrayLength(env, name);
482 
483     name_str = (*env)->GetPrimitiveArrayCritical(env, name, NULL);
484     if (name_str == NULL) {
485         nxt_unit_req_warn(req, "getHeader: failed to get name content");
486         return NULL;
487     }
488 
489     f = nxt_java_findHeader(req->response->fields,
490                             req->response->fields + req->response->fields_count,
491                             name_str, name_len);
492 
493     (*env)->ReleasePrimitiveArrayCritical(env, name, name_str, 0);
494 
495     if (f == NULL) {
496         return NULL;
497     }
498 
499     return nxt_java_newString(env, nxt_unit_sptr_get(&f->value),
500                               f->value_length);
501 }
502 
503 
504 static jobject JNICALL
nxt_java_Response_getHeaderNames(JNIEnv * env,jclass cls,jlong req_info_ptr)505 nxt_java_Response_getHeaderNames(JNIEnv *env, jclass cls, jlong req_info_ptr)
506 {
507     nxt_unit_request_info_t  *req;
508 
509     req = nxt_jlong2ptr(req_info_ptr);
510 
511     if (!nxt_unit_response_is_init(req)) {
512         nxt_unit_req_debug(req, "getHeaderNames: response is not initialized");
513         return NULL;
514     }
515 
516     if (nxt_unit_response_is_sent(req)) {
517         nxt_unit_req_debug(req, "getHeaderNames: response already sent");
518         return NULL;
519     }
520 
521     return nxt_java_newHeaderNamesEnumeration(env, req->response->fields,
522                                               req->response->fields_count);
523 }
524 
525 
526 static jobject JNICALL
nxt_java_Response_getHeaders(JNIEnv * env,jclass cls,jlong req_info_ptr,jarray name)527 nxt_java_Response_getHeaders(JNIEnv *env, jclass cls,
528     jlong req_info_ptr, jarray name)
529 {
530     char                     *name_str;
531     jsize                    name_len;
532     nxt_unit_field_t         *f;
533     nxt_unit_response_t      *resp;
534     nxt_unit_request_info_t  *req;
535 
536     req = nxt_jlong2ptr(req_info_ptr);
537 
538     if (!nxt_unit_response_is_init(req)) {
539         nxt_unit_req_debug(req, "getHeaders: response is not initialized");
540         return NULL;
541     }
542 
543     if (nxt_unit_response_is_sent(req)) {
544         nxt_unit_req_debug(req, "getHeaders: response already sent");
545         return NULL;
546     }
547 
548     resp = req->response;
549 
550     name_len = (*env)->GetArrayLength(env, name);
551 
552     name_str = (*env)->GetPrimitiveArrayCritical(env, name, NULL);
553     if (name_str == NULL) {
554         nxt_unit_req_warn(req, "getHeaders: failed to get name content");
555         return NULL;
556     }
557 
558     f = nxt_java_findHeader(resp->fields, resp->fields + resp->fields_count,
559                             name_str, name_len);
560 
561     (*env)->ReleasePrimitiveArrayCritical(env, name, name_str, 0);
562 
563     if (f == NULL) {
564         f = resp->fields + resp->fields_count;
565     }
566 
567     return nxt_java_newHeadersEnumeration(env, resp->fields, resp->fields_count,
568                                           f - resp->fields);
569 }
570 
571 
572 static jint JNICALL
nxt_java_Response_getStatus(JNIEnv * env,jclass cls,jlong req_info_ptr)573 nxt_java_Response_getStatus(JNIEnv *env, jclass cls, jlong req_info_ptr)
574 {
575     nxt_unit_request_info_t  *req;
576 
577     req = nxt_jlong2ptr(req_info_ptr);
578 
579     if (!nxt_unit_response_is_init(req)) {
580         nxt_unit_req_debug(req, "getStatus: response is not initialized");
581         return 200;
582     }
583 
584     if (nxt_unit_response_is_sent(req)) {
585         nxt_unit_req_debug(req, "getStatus: response already sent");
586         return 200;
587     }
588 
589     return req->response->status;
590 }
591 
592 
593 static jobject JNICALL
nxt_java_Response_getRequest(JNIEnv * env,jclass cls,jlong req_info_ptr)594 nxt_java_Response_getRequest(JNIEnv *env, jclass cls, jlong req_info_ptr)
595 {
596     nxt_unit_request_info_t  *req;
597     nxt_java_request_data_t  *data;
598 
599     req = nxt_jlong2ptr(req_info_ptr);
600     data = req->data;
601 
602     return data->jreq;
603 }
604 
605 
606 static void JNICALL
nxt_java_Response_commit(JNIEnv * env,jclass cls,jlong req_info_ptr)607 nxt_java_Response_commit(JNIEnv *env, jclass cls, jlong req_info_ptr)
608 {
609     nxt_unit_request_info_t  *req;
610 
611     req = nxt_jlong2ptr(req_info_ptr);
612 
613     nxt_java_OutputStream_flush_buf(env, req);
614 }
615 
616 
617 static void JNICALL
nxt_java_Response_sendRedirect(JNIEnv * env,jclass cls,jlong req_info_ptr,jarray loc)618 nxt_java_Response_sendRedirect(JNIEnv *env, jclass cls,
619     jlong req_info_ptr, jarray loc)
620 {
621     int                      rc;
622     char                     *loc_str;
623     jsize                    loc_len;
624     nxt_unit_request_info_t  *req;
625 
626     static const char        location[] = "Location";
627     static const uint32_t    location_len = sizeof(location) - 1;
628 
629     req = nxt_jlong2ptr(req_info_ptr);
630 
631     if (nxt_unit_response_is_sent(req)) {
632         nxt_java_throw_IllegalStateException(env, "Response already sent");
633 
634         return;
635     }
636 
637     loc_len = (*env)->GetArrayLength(env, loc);
638 
639     req = nxt_java_get_response_info(req_info_ptr, 1,
640                                      location_len + loc_len + 2);
641     if (req == NULL) {
642         return;
643     }
644 
645     loc_str = (*env)->GetPrimitiveArrayCritical(env, loc, NULL);
646     if (loc_str == NULL) {
647         nxt_unit_req_warn(req, "sendRedirect: failed to get loc content");
648         return;
649     }
650 
651     req->response->status = 302;
652 
653     rc = nxt_java_response_set_header(req_info_ptr, location, location_len,
654                                       loc_str, loc_len);
655     if (rc != NXT_UNIT_OK) {
656         // throw
657     }
658 
659     (*env)->ReleasePrimitiveArrayCritical(env, loc, loc_str, 0);
660 
661     nxt_unit_response_send(req);
662 }
663 
664 
665 static int
nxt_java_response_set_header(jlong req_info_ptr,const char * name,jint name_len,const char * value,jint value_len)666 nxt_java_response_set_header(jlong req_info_ptr,
667     const char *name, jint name_len, const char *value, jint value_len)
668 {
669     int                      add_field;
670     char                     *dst;
671     nxt_unit_field_t         *f, *e;
672     nxt_unit_response_t      *resp;
673     nxt_unit_request_info_t  *req;
674 
675     req = nxt_java_get_response_info(req_info_ptr, 0, 0);
676     if (req == NULL) {
677         return NXT_UNIT_ERROR;
678     }
679 
680     resp = req->response;
681 
682     f = resp->fields;
683     e = f + resp->fields_count;
684 
685     add_field = 1;
686 
687     for ( ;; ) {
688         f = nxt_java_findHeader(f, e, name, name_len);
689         if (f == NULL) {
690             break;
691         }
692 
693         if (add_field && f->value_length >= (uint32_t) value_len) {
694             dst = nxt_unit_sptr_get(&f->value);
695             memcpy(dst, value, value_len);
696             dst[value_len] = '\0';
697             f->value_length = value_len;
698 
699             add_field = 0;
700             f->skip = 0;
701 
702         } else {
703             f->skip = 1;
704         }
705 
706         ++f;
707     }
708 
709     if (!add_field) {
710         return NXT_UNIT_OK;
711     }
712 
713     req = nxt_java_get_response_info(req_info_ptr, 1, name_len + value_len + 2);
714     if (req == NULL) {
715         return NXT_UNIT_ERROR;
716     }
717 
718     return nxt_unit_response_add_field(req, name, name_len, value, value_len);
719 }
720 
721 
722 static void JNICALL
nxt_java_Response_setHeader(JNIEnv * env,jclass cls,jlong req_info_ptr,jarray name,jarray value)723 nxt_java_Response_setHeader(JNIEnv *env, jclass cls,
724     jlong req_info_ptr, jarray name, jarray value)
725 {
726     int                      rc;
727     char                     *name_str, *value_str;
728     jsize                    name_len, value_len;
729     nxt_unit_request_info_t  *req;
730 
731     name_str = (*env)->GetPrimitiveArrayCritical(env, name, NULL);
732     if (name_str == NULL) {
733         req = nxt_jlong2ptr(req_info_ptr);
734         nxt_unit_req_warn(req, "setHeader: failed to get name content");
735         return;
736     }
737 
738     value_str = (*env)->GetPrimitiveArrayCritical(env, value, NULL);
739     if (value_str == NULL) {
740         (*env)->ReleasePrimitiveArrayCritical(env, name, name_str, 0);
741 
742         req = nxt_jlong2ptr(req_info_ptr);
743         nxt_unit_req_warn(req, "setHeader: failed to get value content");
744 
745         return;
746     }
747 
748     name_len = (*env)->GetArrayLength(env, name);
749     value_len = (*env)->GetArrayLength(env, value);
750 
751     rc = nxt_java_response_set_header(req_info_ptr, name_str, name_len,
752                                       value_str, value_len);
753     if (rc != NXT_UNIT_OK) {
754         // throw
755     }
756 
757     (*env)->ReleasePrimitiveArrayCritical(env, value, value_str, 0);
758     (*env)->ReleasePrimitiveArrayCritical(env, name, name_str, 0);
759 }
760 
761 
762 static void JNICALL
nxt_java_Response_removeHeader(JNIEnv * env,jclass cls,jlong req_info_ptr,jarray name)763 nxt_java_Response_removeHeader(JNIEnv *env, jclass cls,
764     jlong req_info_ptr, jarray name)
765 {
766     int                      rc;
767     char                     *name_str;
768     jsize                    name_len;
769     nxt_unit_request_info_t  *req;
770 
771     name_len = (*env)->GetArrayLength(env, name);
772 
773     name_str = (*env)->GetPrimitiveArrayCritical(env, name, NULL);
774     if (name_str == NULL) {
775         req = nxt_jlong2ptr(req_info_ptr);
776         nxt_unit_req_warn(req, "setHeader: failed to get name content");
777         return;
778     }
779 
780     rc = nxt_java_response_remove_header(req_info_ptr, name_str, name_len);
781     if (rc != NXT_UNIT_OK) {
782         // throw
783     }
784 
785     (*env)->ReleasePrimitiveArrayCritical(env, name, name_str, 0);
786 }
787 
788 
789 static int
nxt_java_response_remove_header(jlong req_info_ptr,const char * name,jint name_len)790 nxt_java_response_remove_header(jlong req_info_ptr,
791     const char *name, jint name_len)
792 {
793     nxt_unit_field_t         *f, *e;
794     nxt_unit_response_t      *resp;
795     nxt_unit_request_info_t  *req;
796 
797     req = nxt_java_get_response_info(req_info_ptr, 0, 0);
798     if (req == NULL) {
799         return NXT_UNIT_ERROR;
800     }
801 
802     resp = req->response;
803 
804     f = resp->fields;
805     e = f + resp->fields_count;
806 
807     for ( ;; ) {
808         f = nxt_java_findHeader(f, e, name, name_len);
809         if (f == NULL) {
810             break;
811         }
812 
813         f->skip = 1;
814 
815         ++f;
816     }
817 
818     return NXT_UNIT_OK;
819 }
820 
821 
822 static void JNICALL
nxt_java_Response_setIntHeader(JNIEnv * env,jclass cls,jlong req_info_ptr,jarray name,jint value)823 nxt_java_Response_setIntHeader(JNIEnv *env, jclass cls,
824     jlong req_info_ptr, jarray name, jint value)
825 {
826     int    value_len, rc;
827     char   value_str[40];
828     char   *name_str;
829     jsize  name_len;
830 
831     value_len = snprintf(value_str, sizeof(value_str), "%d", (int) value);
832 
833     name_len = (*env)->GetArrayLength(env, name);
834 
835     name_str = (*env)->GetPrimitiveArrayCritical(env, name, NULL);
836     if (name_str == NULL) {
837         nxt_unit_req_warn(nxt_jlong2ptr(req_info_ptr),
838                           "setIntHeader: failed to get name content");
839         return;
840     }
841 
842     rc = nxt_java_response_set_header(req_info_ptr, name_str, name_len,
843                                       value_str, value_len);
844     if (rc != NXT_UNIT_OK) {
845         // throw
846     }
847 
848     (*env)->ReleasePrimitiveArrayCritical(env, name, name_str, 0);
849 }
850 
851 
852 static void JNICALL
nxt_java_Response_setStatus(JNIEnv * env,jclass cls,jlong req_info_ptr,jint sc)853 nxt_java_Response_setStatus(JNIEnv *env, jclass cls, jlong req_info_ptr,
854     jint sc)
855 {
856     nxt_unit_request_info_t  *req;
857 
858     req = nxt_java_get_response_info(req_info_ptr, 0, 0);
859     if (req == NULL) {
860         return;
861     }
862 
863     req->response->status = sc;
864 }
865 
866 
867 static jstring JNICALL
nxt_java_Response_getContentType(JNIEnv * env,jclass cls,jlong req_info_ptr)868 nxt_java_Response_getContentType(JNIEnv *env, jclass cls, jlong req_info_ptr)
869 {
870     nxt_unit_field_t         *f;
871     nxt_unit_request_info_t  *req;
872 
873     req = nxt_jlong2ptr(req_info_ptr);
874 
875     if (!nxt_unit_response_is_init(req)) {
876         nxt_unit_req_debug(req, "getContentType: response is not initialized");
877         return NULL;
878     }
879 
880     if (nxt_unit_response_is_sent(req)) {
881         nxt_unit_req_debug(req, "getContentType: response already sent");
882         return NULL;
883     }
884 
885     f = nxt_java_findHeader(req->response->fields,
886                             req->response->fields + req->response->fields_count,
887                             "Content-Type", sizeof("Content-Type") - 1);
888 
889     if (f == NULL) {
890         return NULL;
891     }
892 
893     return nxt_java_newString(env, nxt_unit_sptr_get(&f->value),
894                               f->value_length);
895 }
896 
897 
898 static jboolean JNICALL
nxt_java_Response_isCommitted(JNIEnv * env,jclass cls,jlong req_info_ptr)899 nxt_java_Response_isCommitted(JNIEnv *env, jclass cls, jlong req_info_ptr)
900 {
901     nxt_unit_request_info_t  *req;
902 
903     req = nxt_jlong2ptr(req_info_ptr);
904 
905     if (nxt_unit_response_is_sent(req)) {
906         return 1;
907     }
908 
909     return 0;
910 }
911 
912 
913 static void JNICALL
nxt_java_Response_reset(JNIEnv * env,jclass cls,jlong req_info_ptr)914 nxt_java_Response_reset(JNIEnv *env, jclass cls, jlong req_info_ptr)
915 {
916     nxt_unit_buf_t           *buf;
917     nxt_unit_request_info_t  *req;
918     nxt_java_request_data_t  *data;
919 
920     req = nxt_jlong2ptr(req_info_ptr);
921 
922     if (nxt_unit_response_is_sent(req)) {
923         nxt_java_throw_IllegalStateException(env, "Response already sent");
924 
925         return;
926     }
927 
928     data = req->data;
929 
930     if (data->buf != NULL && data->buf->free > data->buf->start) {
931         data->buf->free = data->buf->start;
932     }
933 
934     if (nxt_unit_response_is_init(req)) {
935         req->response->status = 200;
936         req->response->fields_count = 0;
937 
938         buf = req->response_buf;
939 
940         buf->free = buf->start + sizeof(nxt_unit_response_t)
941                     + req->response_max_fields * sizeof(nxt_unit_field_t);
942     }
943 }
944 
945 
946 static void JNICALL
nxt_java_Response_resetBuffer(JNIEnv * env,jclass cls,jlong req_info_ptr)947 nxt_java_Response_resetBuffer(JNIEnv *env, jclass cls, jlong req_info_ptr)
948 {
949     nxt_unit_request_info_t  *req;
950     nxt_java_request_data_t  *data;
951 
952     req = nxt_jlong2ptr(req_info_ptr);
953     data = req->data;
954 
955     if (data->buf != NULL && data->buf->free > data->buf->start) {
956         data->buf->free = data->buf->start;
957     }
958 }
959 
960 
961 static void JNICALL
nxt_java_Response_setBufferSize(JNIEnv * env,jclass cls,jlong req_info_ptr,jint size)962 nxt_java_Response_setBufferSize(JNIEnv *env, jclass cls, jlong req_info_ptr,
963     jint size)
964 {
965     nxt_unit_request_info_t  *req;
966     nxt_java_request_data_t  *data;
967 
968     req = nxt_jlong2ptr(req_info_ptr);
969     data = req->data;
970 
971     if (data->buf_size == (uint32_t) size) {
972         return;
973     }
974 
975     if (data->buf != NULL && data->buf->free > data->buf->start) {
976         nxt_java_throw_IllegalStateException(env, "Buffer is not empty");
977 
978         return;
979     }
980 
981     data->buf_size = size;
982 
983     if (data->buf_size > nxt_unit_buf_max()) {
984         data->buf_size = nxt_unit_buf_max();
985     }
986 
987     if (data->buf != NULL
988         && (uint32_t) (data->buf->end - data->buf->start) < data->buf_size)
989     {
990         nxt_unit_buf_free(data->buf);
991 
992         data->buf = NULL;
993     }
994 }
995 
996 
997 static jint JNICALL
nxt_java_Response_getBufferSize(JNIEnv * env,jclass cls,jlong req_info_ptr)998 nxt_java_Response_getBufferSize(JNIEnv *env, jclass cls, jlong req_info_ptr)
999 {
1000     nxt_unit_request_info_t  *req;
1001     nxt_java_request_data_t  *data;
1002 
1003     req = nxt_jlong2ptr(req_info_ptr);
1004     data = req->data;
1005 
1006     return data->buf_size;
1007 }
1008 
1009 
1010 static void JNICALL
nxt_java_Response_setContentLength(JNIEnv * env,jclass cls,jlong req_info_ptr,jlong len)1011 nxt_java_Response_setContentLength(JNIEnv *env, jclass cls, jlong req_info_ptr,
1012     jlong len)
1013 {
1014     nxt_unit_request_info_t  *req;
1015 
1016     req = nxt_java_get_response_info(req_info_ptr, 0, 0);
1017     if (req == NULL) {
1018         return;
1019     }
1020 
1021     req->response->content_length = len;
1022 }
1023 
1024 
1025 static void JNICALL
nxt_java_Response_setContentType(JNIEnv * env,jclass cls,jlong req_info_ptr,jarray type)1026 nxt_java_Response_setContentType(JNIEnv *env, jclass cls, jlong req_info_ptr,
1027     jarray type)
1028 {
1029     int    rc;
1030     char   *type_str;
1031     jsize  type_len;
1032 
1033     static const char      content_type[] = "Content-Type";
1034     static const uint32_t  content_type_len = sizeof(content_type) - 1;
1035 
1036     type_len = (*env)->GetArrayLength(env, type);
1037 
1038     type_str = (*env)->GetPrimitiveArrayCritical(env, type, NULL);
1039     if (type_str == NULL) {
1040         return;
1041     }
1042 
1043     rc = nxt_java_response_set_header(req_info_ptr,
1044                                       content_type, content_type_len,
1045                                       type_str, type_len);
1046     if (rc != NXT_UNIT_OK) {
1047         // throw
1048     }
1049 
1050     (*env)->ReleasePrimitiveArrayCritical(env, type, type_str, 0);
1051 }
1052 
1053 
1054 static void JNICALL
nxt_java_Response_removeContentType(JNIEnv * env,jclass cls,jlong req_info_ptr)1055 nxt_java_Response_removeContentType(JNIEnv *env, jclass cls, jlong req_info_ptr)
1056 {
1057     nxt_java_response_remove_header(req_info_ptr, "Content-Type",
1058                                     sizeof("Content-Type") - 1);
1059 }
1060 
1061 
1062 static void JNICALL
nxt_java_Response_log(JNIEnv * env,jclass cls,jlong req_info_ptr,jarray msg)1063 nxt_java_Response_log(JNIEnv *env, jclass cls, jlong req_info_ptr, jarray msg)
1064 {
1065     char                     *msg_str;
1066     jsize                    msg_len;
1067     nxt_unit_request_info_t  *req;
1068 
1069     req = nxt_jlong2ptr(req_info_ptr);
1070     msg_len = (*env)->GetArrayLength(env, msg);
1071 
1072     msg_str = (*env)->GetPrimitiveArrayCritical(env, msg, NULL);
1073     if (msg_str == NULL) {
1074         nxt_unit_req_warn(req, "log: failed to get msg content");
1075         return;
1076     }
1077 
1078     nxt_unit_req_log(req, NXT_UNIT_LOG_INFO, "%.*s", msg_len, msg_str);
1079 
1080     (*env)->ReleasePrimitiveArrayCritical(env, msg, msg_str, 0);
1081 }
1082 
1083 
1084 static void JNICALL
nxt_java_Response_trace(JNIEnv * env,jclass cls,jlong req_info_ptr,jarray msg)1085 nxt_java_Response_trace(JNIEnv *env, jclass cls, jlong req_info_ptr, jarray msg)
1086 {
1087 #if (NXT_DEBUG)
1088     char                     *msg_str;
1089     jsize                    msg_len;
1090     nxt_unit_request_info_t  *req;
1091 
1092     req = nxt_jlong2ptr(req_info_ptr);
1093     msg_len = (*env)->GetArrayLength(env, msg);
1094 
1095     msg_str = (*env)->GetPrimitiveArrayCritical(env, msg, NULL);
1096     if (msg_str == NULL) {
1097         nxt_unit_req_warn(req, "trace: failed to get msg content");
1098         return;
1099     }
1100 
1101     nxt_unit_req_debug(req, "%.*s", msg_len, msg_str);
1102 
1103     (*env)->ReleasePrimitiveArrayCritical(env, msg, msg_str, 0);
1104 #endif
1105 }
1106 
1107