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 + req->response_max_fields * sizeof(nxt_unit_field_t);
338 
339         max_size = 2 * (buf->end - p);
340         if (max_size > nxt_unit_buf_max()) {
341             nxt_unit_req_warn(req, "required max_size is too big: %"PRIu32,
342                 max_size);
343             return NULL;
344         }
345 
346         rc = nxt_unit_response_realloc(req, 2 * req->response_max_fields,
347                                        max_size);
348         if (rc != NXT_UNIT_OK) {
349             nxt_unit_req_warn(req, "reallocation failed: %"PRIu32", %"PRIu32,
350                 2 * req->response_max_fields, max_size);
351             return NULL;
352         }
353     }
354 
355     return req;
356 }
357 
358 
359 static void JNICALL
nxt_java_Response_addIntHeader(JNIEnv * env,jclass cls,jlong req_info_ptr,jarray name,jint value)360 nxt_java_Response_addIntHeader(JNIEnv *env, jclass cls, jlong req_info_ptr,
361     jarray name, jint value)
362 {
363     char                     *name_str;
364     jsize                    name_len;
365     nxt_unit_request_info_t  *req;
366 
367     name_len = (*env)->GetArrayLength(env, name);
368 
369     req = nxt_java_get_response_info(req_info_ptr, 1, name_len + 40);
370     if (req == NULL) {
371         return;
372     }
373 
374     name_str = (*env)->GetPrimitiveArrayCritical(env, name, NULL);
375     if (name_str == NULL) {
376         nxt_unit_req_warn(req, "addIntHeader: failed to get name content");
377         return;
378     }
379 
380     nxt_java_add_int_header(req, name_str, name_len, value);
381 
382     (*env)->ReleasePrimitiveArrayCritical(env, name, name_str, 0);
383 }
384 
385 
386 static void
nxt_java_add_int_header(nxt_unit_request_info_t * req,const char * name,uint8_t name_len,int value)387 nxt_java_add_int_header(nxt_unit_request_info_t *req, const char *name,
388     uint8_t name_len, int value)
389 {
390     char                 *p;
391     nxt_unit_field_t     *f;
392     nxt_unit_response_t  *resp;
393 
394     resp = req->response;
395 
396     f = resp->fields + resp->fields_count;
397     p = req->response_buf->free;
398 
399     f->hash = nxt_unit_field_hash(name, name_len);
400     f->skip = 0;
401     f->name_length = name_len;
402 
403     nxt_unit_sptr_set(&f->name, p);
404     memcpy(p, name, name_len);
405     p += name_len;
406 
407     nxt_unit_sptr_set(&f->value, p);
408     f->value_length = snprintf(p, 40, "%d", (int) value);
409     p += f->value_length + 1;
410 
411     resp->fields_count++;
412     req->response_buf->free = p;
413 
414 }
415 
416 
417 static jboolean JNICALL
nxt_java_Response_containsHeader(JNIEnv * env,jclass cls,jlong req_info_ptr,jarray name)418 nxt_java_Response_containsHeader(JNIEnv *env,
419     jclass cls, jlong req_info_ptr, jarray name)
420 {
421     jboolean                 res;
422     char                     *name_str;
423     jsize                    name_len;
424     nxt_unit_response_t      *resp;
425     nxt_unit_request_info_t  *req;
426 
427     req = nxt_jlong2ptr(req_info_ptr);
428 
429     if (!nxt_unit_response_is_init(req)) {
430         nxt_unit_req_debug(req, "containsHeader: response is not initialized");
431         return 0;
432     }
433 
434     if (nxt_unit_response_is_sent(req)) {
435         nxt_unit_req_debug(req, "containsHeader: response already sent");
436         return 0;
437     }
438 
439     name_len = (*env)->GetArrayLength(env, name);
440 
441     name_str = (*env)->GetPrimitiveArrayCritical(env, name, NULL);
442     if (name_str == NULL) {
443         nxt_unit_req_warn(req, "containsHeader: failed to get name content");
444         return 0;
445     }
446 
447     resp = req->response;
448 
449     res = nxt_java_findHeader(resp->fields,
450                               resp->fields + resp->fields_count,
451                               name_str, name_len) != NULL;
452 
453     (*env)->ReleasePrimitiveArrayCritical(env, name, name_str, 0);
454 
455     return res;
456 }
457 
458 
459 static jstring JNICALL
nxt_java_Response_getHeader(JNIEnv * env,jclass cls,jlong req_info_ptr,jarray name)460 nxt_java_Response_getHeader(JNIEnv *env, jclass cls, jlong req_info_ptr,
461     jarray name)
462 {
463     char                     *name_str;
464     jsize                    name_len;
465     nxt_unit_field_t         *f;
466     nxt_unit_request_info_t  *req;
467 
468     req = nxt_jlong2ptr(req_info_ptr);
469 
470     if (!nxt_unit_response_is_init(req)) {
471         nxt_unit_req_debug(req, "getHeader: response is not initialized");
472         return NULL;
473     }
474 
475     if (nxt_unit_response_is_sent(req)) {
476         nxt_unit_req_debug(req, "getHeader: response already sent");
477         return NULL;
478     }
479 
480     name_len = (*env)->GetArrayLength(env, name);
481 
482     name_str = (*env)->GetPrimitiveArrayCritical(env, name, NULL);
483     if (name_str == NULL) {
484         nxt_unit_req_warn(req, "getHeader: failed to get name content");
485         return NULL;
486     }
487 
488     f = nxt_java_findHeader(req->response->fields,
489                             req->response->fields + req->response->fields_count,
490                             name_str, name_len);
491 
492     (*env)->ReleasePrimitiveArrayCritical(env, name, name_str, 0);
493 
494     if (f == NULL) {
495         return NULL;
496     }
497 
498     return nxt_java_newString(env, nxt_unit_sptr_get(&f->value),
499                               f->value_length);
500 }
501 
502 
503 static jobject JNICALL
nxt_java_Response_getHeaderNames(JNIEnv * env,jclass cls,jlong req_info_ptr)504 nxt_java_Response_getHeaderNames(JNIEnv *env, jclass cls, jlong req_info_ptr)
505 {
506     nxt_unit_request_info_t  *req;
507 
508     req = nxt_jlong2ptr(req_info_ptr);
509 
510     if (!nxt_unit_response_is_init(req)) {
511         nxt_unit_req_debug(req, "getHeaderNames: response is not initialized");
512         return NULL;
513     }
514 
515     if (nxt_unit_response_is_sent(req)) {
516         nxt_unit_req_debug(req, "getHeaderNames: response already sent");
517         return NULL;
518     }
519 
520     return nxt_java_newHeaderNamesEnumeration(env, req->response->fields,
521                                               req->response->fields_count);
522 }
523 
524 
525 static jobject JNICALL
nxt_java_Response_getHeaders(JNIEnv * env,jclass cls,jlong req_info_ptr,jarray name)526 nxt_java_Response_getHeaders(JNIEnv *env, jclass cls,
527     jlong req_info_ptr, jarray name)
528 {
529     char                     *name_str;
530     jsize                    name_len;
531     nxt_unit_field_t         *f;
532     nxt_unit_response_t      *resp;
533     nxt_unit_request_info_t  *req;
534 
535     req = nxt_jlong2ptr(req_info_ptr);
536 
537     if (!nxt_unit_response_is_init(req)) {
538         nxt_unit_req_debug(req, "getHeaders: response is not initialized");
539         return NULL;
540     }
541 
542     if (nxt_unit_response_is_sent(req)) {
543         nxt_unit_req_debug(req, "getHeaders: response already sent");
544         return NULL;
545     }
546 
547     resp = req->response;
548 
549     name_len = (*env)->GetArrayLength(env, name);
550 
551     name_str = (*env)->GetPrimitiveArrayCritical(env, name, NULL);
552     if (name_str == NULL) {
553         nxt_unit_req_warn(req, "getHeaders: failed to get name content");
554         return NULL;
555     }
556 
557     f = nxt_java_findHeader(resp->fields, resp->fields + resp->fields_count,
558                             name_str, name_len);
559 
560     (*env)->ReleasePrimitiveArrayCritical(env, name, name_str, 0);
561 
562     if (f == NULL) {
563         f = resp->fields + resp->fields_count;
564     }
565 
566     return nxt_java_newHeadersEnumeration(env, resp->fields, resp->fields_count,
567                                           f - resp->fields);
568 }
569 
570 
571 static jint JNICALL
nxt_java_Response_getStatus(JNIEnv * env,jclass cls,jlong req_info_ptr)572 nxt_java_Response_getStatus(JNIEnv *env, jclass cls, jlong req_info_ptr)
573 {
574     nxt_unit_request_info_t  *req;
575 
576     req = nxt_jlong2ptr(req_info_ptr);
577 
578     if (!nxt_unit_response_is_init(req)) {
579         nxt_unit_req_debug(req, "getStatus: response is not initialized");
580         return 200;
581     }
582 
583     if (nxt_unit_response_is_sent(req)) {
584         nxt_unit_req_debug(req, "getStatus: response already sent");
585         return 200;
586     }
587 
588     return req->response->status;
589 }
590 
591 
592 static jobject JNICALL
nxt_java_Response_getRequest(JNIEnv * env,jclass cls,jlong req_info_ptr)593 nxt_java_Response_getRequest(JNIEnv *env, jclass cls, jlong req_info_ptr)
594 {
595     nxt_unit_request_info_t  *req;
596     nxt_java_request_data_t  *data;
597 
598     req = nxt_jlong2ptr(req_info_ptr);
599     data = req->data;
600 
601     return data->jreq;
602 }
603 
604 
605 static void JNICALL
nxt_java_Response_commit(JNIEnv * env,jclass cls,jlong req_info_ptr)606 nxt_java_Response_commit(JNIEnv *env, jclass cls, jlong req_info_ptr)
607 {
608     nxt_unit_request_info_t  *req;
609 
610     req = nxt_jlong2ptr(req_info_ptr);
611 
612     nxt_java_OutputStream_flush_buf(env, req);
613 }
614 
615 
616 static void JNICALL
nxt_java_Response_sendRedirect(JNIEnv * env,jclass cls,jlong req_info_ptr,jarray loc)617 nxt_java_Response_sendRedirect(JNIEnv *env, jclass cls,
618     jlong req_info_ptr, jarray loc)
619 {
620     int                      rc;
621     char                     *loc_str;
622     jsize                    loc_len;
623     nxt_unit_request_info_t  *req;
624 
625     static const char        location[] = "Location";
626     static const uint32_t    location_len = sizeof(location) - 1;
627 
628     req = nxt_jlong2ptr(req_info_ptr);
629 
630     if (nxt_unit_response_is_sent(req)) {
631         nxt_java_throw_IllegalStateException(env, "Response already sent");
632 
633         return;
634     }
635 
636     loc_len = (*env)->GetArrayLength(env, loc);
637 
638     req = nxt_java_get_response_info(req_info_ptr, 1,
639                                      location_len + loc_len + 2);
640     if (req == NULL) {
641         return;
642     }
643 
644     loc_str = (*env)->GetPrimitiveArrayCritical(env, loc, NULL);
645     if (loc_str == NULL) {
646         nxt_unit_req_warn(req, "sendRedirect: failed to get loc content");
647         return;
648     }
649 
650     req->response->status = 302;
651 
652     rc = nxt_java_response_set_header(req_info_ptr, location, location_len,
653                                       loc_str, loc_len);
654     if (rc != NXT_UNIT_OK) {
655         // throw
656     }
657 
658     (*env)->ReleasePrimitiveArrayCritical(env, loc, loc_str, 0);
659 
660     nxt_unit_response_send(req);
661 }
662 
663 
664 static int
nxt_java_response_set_header(jlong req_info_ptr,const char * name,jint name_len,const char * value,jint value_len)665 nxt_java_response_set_header(jlong req_info_ptr,
666     const char *name, jint name_len, const char *value, jint value_len)
667 {
668     int                      add_field;
669     char                     *dst;
670     nxt_unit_field_t         *f, *e;
671     nxt_unit_response_t      *resp;
672     nxt_unit_request_info_t  *req;
673 
674     req = nxt_java_get_response_info(req_info_ptr, 0, 0);
675     if (req == NULL) {
676         return NXT_UNIT_ERROR;
677     }
678 
679     resp = req->response;
680 
681     f = resp->fields;
682     e = f + resp->fields_count;
683 
684     add_field = 1;
685 
686     for ( ;; ) {
687         f = nxt_java_findHeader(f, e, name, name_len);
688         if (f == NULL) {
689             break;
690         }
691 
692         if (add_field && f->value_length >= (uint32_t) value_len) {
693             dst = nxt_unit_sptr_get(&f->value);
694             memcpy(dst, value, value_len);
695             dst[value_len] = '\0';
696             f->value_length = value_len;
697 
698             add_field = 0;
699             f->skip = 0;
700 
701         } else {
702             f->skip = 1;
703         }
704 
705         ++f;
706     }
707 
708     if (!add_field) {
709         return NXT_UNIT_OK;
710     }
711 
712     req = nxt_java_get_response_info(req_info_ptr, 1, name_len + value_len + 2);
713     if (req == NULL) {
714         return NXT_UNIT_ERROR;
715     }
716 
717     return nxt_unit_response_add_field(req, name, name_len, value, value_len);
718 }
719 
720 
721 static void JNICALL
nxt_java_Response_setHeader(JNIEnv * env,jclass cls,jlong req_info_ptr,jarray name,jarray value)722 nxt_java_Response_setHeader(JNIEnv *env, jclass cls,
723     jlong req_info_ptr, jarray name, jarray value)
724 {
725     int                      rc;
726     char                     *name_str, *value_str;
727     jsize                    name_len, value_len;
728     nxt_unit_request_info_t  *req;
729 
730     name_str = (*env)->GetPrimitiveArrayCritical(env, name, NULL);
731     if (name_str == NULL) {
732         req = nxt_jlong2ptr(req_info_ptr);
733         nxt_unit_req_warn(req, "setHeader: failed to get name content");
734         return;
735     }
736 
737     value_str = (*env)->GetPrimitiveArrayCritical(env, value, NULL);
738     if (value_str == NULL) {
739         (*env)->ReleasePrimitiveArrayCritical(env, name, name_str, 0);
740 
741         req = nxt_jlong2ptr(req_info_ptr);
742         nxt_unit_req_warn(req, "setHeader: failed to get value content");
743 
744         return;
745     }
746 
747     name_len = (*env)->GetArrayLength(env, name);
748     value_len = (*env)->GetArrayLength(env, value);
749 
750     rc = nxt_java_response_set_header(req_info_ptr, name_str, name_len,
751                                       value_str, value_len);
752     if (rc != NXT_UNIT_OK) {
753         // throw
754     }
755 
756     (*env)->ReleasePrimitiveArrayCritical(env, value, value_str, 0);
757     (*env)->ReleasePrimitiveArrayCritical(env, name, name_str, 0);
758 }
759 
760 
761 static void JNICALL
nxt_java_Response_removeHeader(JNIEnv * env,jclass cls,jlong req_info_ptr,jarray name)762 nxt_java_Response_removeHeader(JNIEnv *env, jclass cls,
763     jlong req_info_ptr, jarray name)
764 {
765     int                      rc;
766     char                     *name_str;
767     jsize                    name_len;
768     nxt_unit_request_info_t  *req;
769 
770     name_len = (*env)->GetArrayLength(env, name);
771 
772     name_str = (*env)->GetPrimitiveArrayCritical(env, name, NULL);
773     if (name_str == NULL) {
774         req = nxt_jlong2ptr(req_info_ptr);
775         nxt_unit_req_warn(req, "setHeader: failed to get name content");
776         return;
777     }
778 
779     rc = nxt_java_response_remove_header(req_info_ptr, name_str, name_len);
780     if (rc != NXT_UNIT_OK) {
781         // throw
782     }
783 
784     (*env)->ReleasePrimitiveArrayCritical(env, name, name_str, 0);
785 }
786 
787 
788 static int
nxt_java_response_remove_header(jlong req_info_ptr,const char * name,jint name_len)789 nxt_java_response_remove_header(jlong req_info_ptr,
790     const char *name, jint name_len)
791 {
792     nxt_unit_field_t         *f, *e;
793     nxt_unit_response_t      *resp;
794     nxt_unit_request_info_t  *req;
795 
796     req = nxt_java_get_response_info(req_info_ptr, 0, 0);
797     if (req == NULL) {
798         return NXT_UNIT_ERROR;
799     }
800 
801     resp = req->response;
802 
803     f = resp->fields;
804     e = f + resp->fields_count;
805 
806     for ( ;; ) {
807         f = nxt_java_findHeader(f, e, name, name_len);
808         if (f == NULL) {
809             break;
810         }
811 
812         f->skip = 1;
813 
814         ++f;
815     }
816 
817     return NXT_UNIT_OK;
818 }
819 
820 
821 static void JNICALL
nxt_java_Response_setIntHeader(JNIEnv * env,jclass cls,jlong req_info_ptr,jarray name,jint value)822 nxt_java_Response_setIntHeader(JNIEnv *env, jclass cls,
823     jlong req_info_ptr, jarray name, jint value)
824 {
825     int    value_len, rc;
826     char   value_str[40];
827     char   *name_str;
828     jsize  name_len;
829 
830     value_len = snprintf(value_str, sizeof(value_str), "%d", (int) value);
831 
832     name_len = (*env)->GetArrayLength(env, name);
833 
834     name_str = (*env)->GetPrimitiveArrayCritical(env, name, NULL);
835     if (name_str == NULL) {
836         nxt_unit_req_warn(nxt_jlong2ptr(req_info_ptr),
837                           "setIntHeader: failed to get name content");
838         return;
839     }
840 
841     rc = nxt_java_response_set_header(req_info_ptr, name_str, name_len,
842                                       value_str, value_len);
843     if (rc != NXT_UNIT_OK) {
844         // throw
845     }
846 
847     (*env)->ReleasePrimitiveArrayCritical(env, name, name_str, 0);
848 }
849 
850 
851 static void JNICALL
nxt_java_Response_setStatus(JNIEnv * env,jclass cls,jlong req_info_ptr,jint sc)852 nxt_java_Response_setStatus(JNIEnv *env, jclass cls, jlong req_info_ptr,
853     jint sc)
854 {
855     nxt_unit_request_info_t  *req;
856 
857     req = nxt_java_get_response_info(req_info_ptr, 0, 0);
858     if (req == NULL) {
859         return;
860     }
861 
862     req->response->status = sc;
863 }
864 
865 
866 static jstring JNICALL
nxt_java_Response_getContentType(JNIEnv * env,jclass cls,jlong req_info_ptr)867 nxt_java_Response_getContentType(JNIEnv *env, jclass cls, jlong req_info_ptr)
868 {
869     nxt_unit_field_t         *f;
870     nxt_unit_request_info_t  *req;
871 
872     req = nxt_jlong2ptr(req_info_ptr);
873 
874     if (!nxt_unit_response_is_init(req)) {
875         nxt_unit_req_debug(req, "getContentType: response is not initialized");
876         return NULL;
877     }
878 
879     if (nxt_unit_response_is_sent(req)) {
880         nxt_unit_req_debug(req, "getContentType: response already sent");
881         return NULL;
882     }
883 
884     f = nxt_java_findHeader(req->response->fields,
885                             req->response->fields + req->response->fields_count,
886                             "Content-Type", sizeof("Content-Type") - 1);
887 
888     if (f == NULL) {
889         return NULL;
890     }
891 
892     return nxt_java_newString(env, nxt_unit_sptr_get(&f->value),
893                               f->value_length);
894 }
895 
896 
897 static jboolean JNICALL
nxt_java_Response_isCommitted(JNIEnv * env,jclass cls,jlong req_info_ptr)898 nxt_java_Response_isCommitted(JNIEnv *env, jclass cls, jlong req_info_ptr)
899 {
900     nxt_unit_request_info_t  *req;
901 
902     req = nxt_jlong2ptr(req_info_ptr);
903 
904     if (nxt_unit_response_is_sent(req)) {
905         return 1;
906     }
907 
908     return 0;
909 }
910 
911 
912 static void JNICALL
nxt_java_Response_reset(JNIEnv * env,jclass cls,jlong req_info_ptr)913 nxt_java_Response_reset(JNIEnv *env, jclass cls, jlong req_info_ptr)
914 {
915     nxt_unit_buf_t           *buf;
916     nxt_unit_request_info_t  *req;
917     nxt_java_request_data_t  *data;
918 
919     req = nxt_jlong2ptr(req_info_ptr);
920 
921     if (nxt_unit_response_is_sent(req)) {
922         nxt_java_throw_IllegalStateException(env, "Response already sent");
923 
924         return;
925     }
926 
927     data = req->data;
928 
929     if (data->buf != NULL && data->buf->free > data->buf->start) {
930         data->buf->free = data->buf->start;
931     }
932 
933     if (nxt_unit_response_is_init(req)) {
934         req->response->status = 200;
935         req->response->fields_count = 0;
936 
937         buf = req->response_buf;
938 
939         buf->free = buf->start + req->response_max_fields
940                                   * sizeof(nxt_unit_field_t);
941     }
942 }
943 
944 
945 static void JNICALL
nxt_java_Response_resetBuffer(JNIEnv * env,jclass cls,jlong req_info_ptr)946 nxt_java_Response_resetBuffer(JNIEnv *env, jclass cls, jlong req_info_ptr)
947 {
948     nxt_unit_request_info_t  *req;
949     nxt_java_request_data_t  *data;
950 
951     req = nxt_jlong2ptr(req_info_ptr);
952     data = req->data;
953 
954     if (data->buf != NULL && data->buf->free > data->buf->start) {
955         data->buf->free = data->buf->start;
956     }
957 }
958 
959 
960 static void JNICALL
nxt_java_Response_setBufferSize(JNIEnv * env,jclass cls,jlong req_info_ptr,jint size)961 nxt_java_Response_setBufferSize(JNIEnv *env, jclass cls, jlong req_info_ptr,
962     jint size)
963 {
964     nxt_unit_request_info_t  *req;
965     nxt_java_request_data_t  *data;
966 
967     req = nxt_jlong2ptr(req_info_ptr);
968     data = req->data;
969 
970     if (data->buf_size == (uint32_t) size) {
971         return;
972     }
973 
974     if (data->buf != NULL && data->buf->free > data->buf->start) {
975         nxt_java_throw_IllegalStateException(env, "Buffer is not empty");
976 
977         return;
978     }
979 
980     data->buf_size = size;
981 
982     if (data->buf_size > nxt_unit_buf_max()) {
983         data->buf_size = nxt_unit_buf_max();
984     }
985 
986     if (data->buf != NULL
987         && (uint32_t) (data->buf->end - data->buf->start) < data->buf_size)
988     {
989         nxt_unit_buf_free(data->buf);
990 
991         data->buf = NULL;
992     }
993 }
994 
995 
996 static jint JNICALL
nxt_java_Response_getBufferSize(JNIEnv * env,jclass cls,jlong req_info_ptr)997 nxt_java_Response_getBufferSize(JNIEnv *env, jclass cls, jlong req_info_ptr)
998 {
999     nxt_unit_request_info_t  *req;
1000     nxt_java_request_data_t  *data;
1001 
1002     req = nxt_jlong2ptr(req_info_ptr);
1003     data = req->data;
1004 
1005     return data->buf_size;
1006 }
1007 
1008 
1009 static void JNICALL
nxt_java_Response_setContentLength(JNIEnv * env,jclass cls,jlong req_info_ptr,jlong len)1010 nxt_java_Response_setContentLength(JNIEnv *env, jclass cls, jlong req_info_ptr,
1011     jlong len)
1012 {
1013     nxt_unit_request_info_t  *req;
1014 
1015     req = nxt_java_get_response_info(req_info_ptr, 0, 0);
1016     if (req == NULL) {
1017         return;
1018     }
1019 
1020     req->response->content_length = len;
1021 }
1022 
1023 
1024 static void JNICALL
nxt_java_Response_setContentType(JNIEnv * env,jclass cls,jlong req_info_ptr,jarray type)1025 nxt_java_Response_setContentType(JNIEnv *env, jclass cls, jlong req_info_ptr,
1026     jarray type)
1027 {
1028     int    rc;
1029     char   *type_str;
1030     jsize  type_len;
1031 
1032     static const char      content_type[] = "Content-Type";
1033     static const uint32_t  content_type_len = sizeof(content_type) - 1;
1034 
1035     type_len = (*env)->GetArrayLength(env, type);
1036 
1037     type_str = (*env)->GetPrimitiveArrayCritical(env, type, NULL);
1038     if (type_str == NULL) {
1039         return;
1040     }
1041 
1042     rc = nxt_java_response_set_header(req_info_ptr,
1043                                       content_type, content_type_len,
1044                                       type_str, type_len);
1045     if (rc != NXT_UNIT_OK) {
1046         // throw
1047     }
1048 
1049     (*env)->ReleasePrimitiveArrayCritical(env, type, type_str, 0);
1050 }
1051 
1052 
1053 static void JNICALL
nxt_java_Response_removeContentType(JNIEnv * env,jclass cls,jlong req_info_ptr)1054 nxt_java_Response_removeContentType(JNIEnv *env, jclass cls, jlong req_info_ptr)
1055 {
1056     nxt_java_response_remove_header(req_info_ptr, "Content-Type",
1057                                     sizeof("Content-Type") - 1);
1058 }
1059 
1060 
1061 static void JNICALL
nxt_java_Response_log(JNIEnv * env,jclass cls,jlong req_info_ptr,jarray msg)1062 nxt_java_Response_log(JNIEnv *env, jclass cls, jlong req_info_ptr, jarray msg)
1063 {
1064     char                     *msg_str;
1065     jsize                    msg_len;
1066     nxt_unit_request_info_t  *req;
1067 
1068     req = nxt_jlong2ptr(req_info_ptr);
1069     msg_len = (*env)->GetArrayLength(env, msg);
1070 
1071     msg_str = (*env)->GetPrimitiveArrayCritical(env, msg, NULL);
1072     if (msg_str == NULL) {
1073         nxt_unit_req_warn(req, "log: failed to get msg content");
1074         return;
1075     }
1076 
1077     nxt_unit_req_log(req, NXT_UNIT_LOG_INFO, "%.*s", msg_len, msg_str);
1078 
1079     (*env)->ReleasePrimitiveArrayCritical(env, msg, msg_str, 0);
1080 }
1081 
1082 
1083 static void JNICALL
nxt_java_Response_trace(JNIEnv * env,jclass cls,jlong req_info_ptr,jarray msg)1084 nxt_java_Response_trace(JNIEnv *env, jclass cls, jlong req_info_ptr, jarray msg)
1085 {
1086 #if (NXT_DEBUG)
1087     char                     *msg_str;
1088     jsize                    msg_len;
1089     nxt_unit_request_info_t  *req;
1090 
1091     req = nxt_jlong2ptr(req_info_ptr);
1092     msg_len = (*env)->GetArrayLength(env, msg);
1093 
1094     msg_str = (*env)->GetPrimitiveArrayCritical(env, msg, NULL);
1095     if (msg_str == NULL) {
1096         nxt_unit_req_warn(req, "trace: failed to get msg content");
1097         return;
1098     }
1099 
1100     nxt_unit_req_debug(req, "%.*s", msg_len, msg_str);
1101 
1102     (*env)->ReleasePrimitiveArrayCritical(env, msg, msg_str, 0);
1103 #endif
1104 }
1105 
1106