|
|
@@ -10,6 +10,20 @@
|
|
10
|
10
|
|
|
11
|
11
|
#define TIMER_FREQUENCY 15 * 1000
|
|
12
|
12
|
|
|
|
13
|
+typedef enum {
|
|
|
14
|
+ uploadprogress_state_starting = 0,
|
|
|
15
|
+ uploadprogress_state_error = 1,
|
|
|
16
|
+ uploadprogress_state_done = 2,
|
|
|
17
|
+ uploadprogress_state_uploading = 3,
|
|
|
18
|
+ uploadprogress_state_none
|
|
|
19
|
+} ngx_http_uploadprogress_state_t;
|
|
|
20
|
+
|
|
|
21
|
+typedef struct {
|
|
|
22
|
+ ngx_str_t name;
|
|
|
23
|
+ ngx_http_uploadprogress_state_t idx;
|
|
|
24
|
+ ngx_str_t def;
|
|
|
25
|
+} ngx_http_uploadprogress_state_map_t;
|
|
|
26
|
+
|
|
13
|
27
|
typedef struct ngx_http_uploadprogress_node_s ngx_http_uploadprogress_node_t;
|
|
14
|
28
|
|
|
15
|
29
|
struct ngx_http_uploadprogress_node_s {
|
|
|
@@ -38,6 +52,11 @@ typedef struct {
|
|
38
|
52
|
ngx_http_uploadprogress_node_t list_tail;
|
|
39
|
53
|
} ngx_http_uploadprogress_ctx_t;
|
|
40
|
54
|
|
|
|
55
|
+typedef struct {
|
|
|
56
|
+ ngx_array_t *values;
|
|
|
57
|
+ ngx_array_t *lengths;
|
|
|
58
|
+} ngx_http_uploadprogress_template_t;
|
|
|
59
|
+
|
|
41
|
60
|
typedef struct {
|
|
42
|
61
|
ngx_shm_zone_t *shm_zone;
|
|
43
|
62
|
time_t timeout;
|
|
|
@@ -45,6 +64,7 @@ typedef struct {
|
|
45
|
64
|
ngx_http_handler_pt handler;
|
|
46
|
65
|
u_char track;
|
|
47
|
66
|
ngx_str_t content_type;
|
|
|
67
|
+ ngx_array_t templates;
|
|
48
|
68
|
} ngx_http_uploadprogress_conf_t;
|
|
49
|
69
|
|
|
50
|
70
|
typedef struct {
|
|
|
@@ -61,6 +81,7 @@ static char *ngx_http_uploadprogress_merge_loc_conf(ngx_conf_t *cf, void *parent
|
|
61
|
81
|
static char *ngx_http_track_uploads(ngx_conf_t * cf, ngx_command_t * cmd, void *conf);
|
|
62
|
82
|
static char *ngx_http_report_uploads(ngx_conf_t * cf, ngx_command_t * cmd, void *conf);
|
|
63
|
83
|
static char *ngx_http_upload_progress(ngx_conf_t * cf, ngx_command_t * cmd, void *conf);
|
|
|
84
|
+static char* ngx_http_upload_progress_template(ngx_conf_t * cf, ngx_command_t * cmd, void *conf);
|
|
64
|
85
|
static void ngx_clean_old_connections(ngx_event_t * ev);
|
|
65
|
86
|
static ngx_int_t ngx_http_uploadprogress_content_handler(ngx_http_request_t *r);
|
|
66
|
87
|
|
|
|
@@ -96,6 +117,13 @@ static ngx_command_t ngx_http_uploadprogress_commands[] = {
|
|
96
|
117
|
offsetof(ngx_http_uploadprogress_conf_t, content_type),
|
|
97
|
118
|
NULL},
|
|
98
|
119
|
|
|
|
120
|
+ {ngx_string("upload_progress_template"),
|
|
|
121
|
+ NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_CONF_TAKE2,
|
|
|
122
|
+ ngx_http_upload_progress_template,
|
|
|
123
|
+ NGX_HTTP_LOC_CONF_OFFSET,
|
|
|
124
|
+ offsetof(ngx_http_uploadprogress_conf_t, templates),
|
|
|
125
|
+ NULL},
|
|
|
126
|
+
|
|
99
|
127
|
ngx_null_command
|
|
100
|
128
|
};
|
|
101
|
129
|
|
|
|
@@ -132,6 +160,22 @@ ngx_module_t ngx_http_uploadprogress_module = {
|
|
132
|
160
|
|
|
133
|
161
|
static ngx_str_t x_progress_id = ngx_string("X-Progress-ID");
|
|
134
|
162
|
|
|
|
163
|
+static ngx_http_uploadprogress_state_map_t ngx_http_uploadprogress_state_map[] = {
|
|
|
164
|
+ {ngx_string("starting"), uploadprogress_state_starting,
|
|
|
165
|
+ ngx_string("new Object({ 'state' : 'starting' })\r\n")},
|
|
|
166
|
+
|
|
|
167
|
+ {ngx_string("error"), uploadprogress_state_error,
|
|
|
168
|
+ ngx_string("new Object({ 'state' : 'error', 'status' : $uploadprogress_status })\r\n")},
|
|
|
169
|
+
|
|
|
170
|
+ {ngx_string("done"), uploadprogress_state_done,
|
|
|
171
|
+ ngx_string("new Object({ 'state' : 'done' })\r\n")},
|
|
|
172
|
+
|
|
|
173
|
+ {ngx_string("uploading"), uploadprogress_state_uploading,
|
|
|
174
|
+ ngx_string("new Object({ 'state' : 'uploading', 'received' : $uploadprogress_received, 'size' : $uploadprogress_length })\r\n")},
|
|
|
175
|
+};
|
|
|
176
|
+
|
|
|
177
|
+static ngx_array_t ngx_http_uploadprogress_global_templates;
|
|
|
178
|
+
|
|
135
|
179
|
static ngx_str_t*
|
|
136
|
180
|
get_tracking_id(ngx_http_request_t * r)
|
|
137
|
181
|
{
|
|
|
@@ -400,10 +444,10 @@ static void ngx_http_uploadprogress_event_handler(ngx_http_request_t *r)
|
|
400
|
444
|
static ngx_int_t
|
|
401
|
445
|
ngx_http_reportuploads_handler(ngx_http_request_t * r)
|
|
402
|
446
|
{
|
|
403
|
|
- ngx_str_t *id;
|
|
|
447
|
+ ngx_str_t *id, response;
|
|
404
|
448
|
ngx_buf_t *b;
|
|
405
|
449
|
ngx_chain_t out;
|
|
406
|
|
- ngx_int_t rc, size, found=0, done=0, err_status=0;
|
|
|
450
|
+ ngx_int_t rc, found=0, done=0, err_status=0;
|
|
407
|
451
|
off_t rest=0, length=0;
|
|
408
|
452
|
ngx_uint_t len, i;
|
|
409
|
453
|
ngx_slab_pool_t *shpool;
|
|
|
@@ -411,6 +455,8 @@ ngx_http_reportuploads_handler(ngx_http_request_t * r)
|
|
411
|
455
|
ngx_http_uploadprogress_ctx_t *ctx;
|
|
412
|
456
|
ngx_http_uploadprogress_node_t *up;
|
|
413
|
457
|
ngx_table_elt_t *expires, *cc, **ccp;
|
|
|
458
|
+ ngx_http_uploadprogress_state_t state;
|
|
|
459
|
+ ngx_http_uploadprogress_template_t *t;
|
|
414
|
460
|
|
|
415
|
461
|
if (r->method != NGX_HTTP_GET && r->method != NGX_HTTP_HEAD) {
|
|
416
|
462
|
return NGX_HTTP_NOT_ALLOWED;
|
|
|
@@ -549,55 +595,42 @@ ngx_http_reportuploads_handler(ngx_http_request_t * r)
|
|
549
|
595
|
*/
|
|
550
|
596
|
|
|
551
|
597
|
if (!found) {
|
|
552
|
|
- size = sizeof("new Object({ 'state' : 'starting' })\r\n");
|
|
|
598
|
+ state = uploadprogress_state_starting;
|
|
553
|
599
|
} else if (err_status >= NGX_HTTP_SPECIAL_RESPONSE) {
|
|
554
|
|
- size = sizeof("new Object({ 'state' : 'error', 'status' : ") + NGX_INT_T_LEN + sizeof(" })\r\n");
|
|
|
600
|
+ state = uploadprogress_state_error;
|
|
555
|
601
|
} else if (done) {
|
|
556
|
|
- size = sizeof("new Object({ 'state' : 'done' })\r\n");
|
|
|
602
|
+ state = uploadprogress_state_done;
|
|
557
|
603
|
} else if ( length == 0 && rest == 0 ) {
|
|
558
|
|
- size = sizeof("new Object({ 'state' : 'starting' })\r\n");
|
|
|
604
|
+ state = uploadprogress_state_starting;
|
|
559
|
605
|
} else {
|
|
560
|
|
- size = sizeof("new Object({ 'state' : 'uploading', 'received' : ") + NGX_INT_T_LEN + sizeof(" })\r\n");
|
|
561
|
|
- size += sizeof(", 'size' : ") + NGX_INT_T_LEN;
|
|
|
606
|
+ state = uploadprogress_state_uploading;
|
|
562
|
607
|
}
|
|
563
|
608
|
|
|
564
|
|
- b = ngx_create_temp_buf(r->pool, size);
|
|
565
|
|
- if (b == NULL) {
|
|
|
609
|
+ t = upcf->templates.elts;
|
|
|
610
|
+
|
|
|
611
|
+ if (ngx_http_script_run(r, &response, t[(ngx_uint_t)state].lengths->elts, 0,
|
|
|
612
|
+ t[(ngx_uint_t)state].values->elts) == NULL)
|
|
|
613
|
+ {
|
|
566
|
614
|
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
|
567
|
615
|
}
|
|
568
|
616
|
|
|
569
|
|
- out.buf = b;
|
|
570
|
|
- out.next = NULL;
|
|
|
617
|
+ ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
|
|
618
|
+ "upload progress: state=%d, err_status=%ui, remaining=%uO, length=%uO",
|
|
|
619
|
+ state, err_status, (length - rest), length);
|
|
571
|
620
|
|
|
572
|
|
- if (!found) {
|
|
573
|
|
- b->last = ngx_cpymem(b->last, "new Object({ 'state' : 'starting' })\r\n",
|
|
574
|
|
- sizeof("new Object({ 'state' : 'starting' })\r\n") - 1);
|
|
575
|
|
- ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "reportuploads returning starting");
|
|
576
|
|
- } else if (err_status >= NGX_HTTP_SPECIAL_RESPONSE) {
|
|
577
|
|
- b->last = ngx_cpymem(b->last, "new Object({ 'state' : 'error', 'status' : ",
|
|
578
|
|
- sizeof("new Object({ 'state' : 'error', 'status' : ") - 1);
|
|
579
|
|
- b->last = ngx_sprintf(b->last, "%ui })\r\n", err_status );
|
|
580
|
|
- ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
|
581
|
|
- "reportuploads returning error condition: %ui", err_status);
|
|
582
|
|
- } else if (done) {
|
|
583
|
|
- b->last = ngx_cpymem(b->last, "new Object({ 'state' : 'done' })\r\n",
|
|
584
|
|
- sizeof("new Object({ 'state' : 'done' })\r\n") - 1);
|
|
585
|
|
- ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
|
586
|
|
- "reportuploads returning done");
|
|
587
|
|
- } else if (length == 0 && rest == 0) {
|
|
588
|
|
- b->last = ngx_cpymem(b->last, "new Object({ 'state' : 'starting' })\r\n",
|
|
589
|
|
- sizeof("new Object({ 'state' : 'starting' })\r\n") - 1);
|
|
590
|
|
- ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "reportuploads returning starting");
|
|
591
|
|
- } else {
|
|
592
|
|
- b->last = ngx_cpymem(b->last, "new Object({ 'state' : 'uploading', 'received' : ",
|
|
593
|
|
- sizeof("new Object({ 'state' : 'uploading', 'received' : ") - 1);
|
|
|
621
|
+ b = ngx_calloc_buf(r->pool);
|
|
|
622
|
+ if (b == NULL) {
|
|
|
623
|
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
|
|
624
|
+ }
|
|
594
|
625
|
|
|
595
|
|
- b->last = ngx_sprintf(b->last, "%uO, 'size' : %uO })\r\n", (length - rest), length);
|
|
|
626
|
+ b->pos = b->start = response.data;
|
|
|
627
|
+ b->last = b->end = response.data + response.len;
|
|
596
|
628
|
|
|
597
|
|
- ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
|
598
|
|
- "reportuploads returning %uO / %uO",(length - rest), length );
|
|
599
|
|
- }
|
|
|
629
|
+ b->temporary = 1;
|
|
|
630
|
+ b->memory = 1;
|
|
600
|
631
|
|
|
|
632
|
+ out.buf = b;
|
|
|
633
|
+ out.next = NULL;
|
|
601
|
634
|
|
|
602
|
635
|
r->headers_out.status = NGX_HTTP_OK;
|
|
603
|
636
|
r->headers_out.content_length_n = b->last - b->pos;
|
|
|
@@ -1087,6 +1120,10 @@ ngx_http_uploadprogress_init(ngx_conf_t * cf)
|
|
1087
|
1120
|
{
|
|
1088
|
1121
|
ngx_http_handler_pt *h;
|
|
1089
|
1122
|
ngx_http_core_main_conf_t *cmcf;
|
|
|
1123
|
+ ngx_http_uploadprogress_template_t *t;
|
|
|
1124
|
+ ngx_http_uploadprogress_state_map_t *m;
|
|
|
1125
|
+ ngx_http_script_compile_t sc;
|
|
|
1126
|
+ ssize_t n;
|
|
1090
|
1127
|
|
|
1091
|
1128
|
cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
|
|
1092
|
1129
|
|
|
|
@@ -1108,6 +1145,37 @@ ngx_http_uploadprogress_init(ngx_conf_t * cf)
|
|
1108
|
1145
|
ngx_http_next_header_filter = ngx_http_top_header_filter;
|
|
1109
|
1146
|
ngx_http_top_header_filter = ngx_http_uploadprogress_errortracker;
|
|
1110
|
1147
|
|
|
|
1148
|
+ /*
|
|
|
1149
|
+ * Compile global templates
|
|
|
1150
|
+ */
|
|
|
1151
|
+ if(ngx_array_init(&ngx_http_uploadprogress_global_templates, cf->pool, 4,
|
|
|
1152
|
+ sizeof(ngx_http_uploadprogress_template_t)) != NGX_OK) {
|
|
|
1153
|
+ return NGX_ERROR;
|
|
|
1154
|
+ }
|
|
|
1155
|
+
|
|
|
1156
|
+ m = ngx_http_uploadprogress_state_map;
|
|
|
1157
|
+ t = ngx_http_uploadprogress_global_templates.elts;
|
|
|
1158
|
+
|
|
|
1159
|
+ while(m->name.data != NULL) {
|
|
|
1160
|
+ n = ngx_http_script_variables_count(&m->def);
|
|
|
1161
|
+
|
|
|
1162
|
+ ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
|
|
|
1163
|
+
|
|
|
1164
|
+ sc.cf = cf;
|
|
|
1165
|
+ sc.source = &m->def;
|
|
|
1166
|
+ sc.lengths = &t->lengths;
|
|
|
1167
|
+ sc.values = &t->values;
|
|
|
1168
|
+ sc.variables = n;
|
|
|
1169
|
+ sc.complete_lengths = 1;
|
|
|
1170
|
+ sc.complete_values = 1;
|
|
|
1171
|
+
|
|
|
1172
|
+ if (ngx_http_script_compile(&sc) != NGX_OK) {
|
|
|
1173
|
+ return NGX_ERROR;
|
|
|
1174
|
+ }
|
|
|
1175
|
+
|
|
|
1176
|
+ m++;
|
|
|
1177
|
+ }
|
|
|
1178
|
+
|
|
1111
|
1179
|
return NGX_OK;
|
|
1112
|
1180
|
}
|
|
1113
|
1181
|
|
|
|
@@ -1115,11 +1183,24 @@ static void*
|
|
1115
|
1183
|
ngx_http_uploadprogress_create_loc_conf(ngx_conf_t * cf)
|
|
1116
|
1184
|
{
|
|
1117
|
1185
|
ngx_http_uploadprogress_conf_t *conf;
|
|
|
1186
|
+ ngx_http_uploadprogress_template_t *t;
|
|
|
1187
|
+ ngx_uint_t i;
|
|
1118
|
1188
|
|
|
1119
|
1189
|
conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_uploadprogress_conf_t));
|
|
1120
|
1190
|
if (conf == NULL) {
|
|
1121
|
1191
|
return NGX_CONF_ERROR;
|
|
1122
|
1192
|
}
|
|
|
1193
|
+
|
|
|
1194
|
+ if(ngx_array_init(&conf->templates, cf->pool, 4, sizeof(ngx_http_uploadprogress_template_t)) != NGX_OK) {
|
|
|
1195
|
+ return NGX_CONF_ERROR;
|
|
|
1196
|
+ }
|
|
|
1197
|
+
|
|
|
1198
|
+ t = conf->templates.elts;
|
|
|
1199
|
+ for(i = 0;i < conf->templates.nelts; i++) {
|
|
|
1200
|
+ t[i].values = NULL;
|
|
|
1201
|
+ t[i].lengths = NULL;
|
|
|
1202
|
+ }
|
|
|
1203
|
+
|
|
1123
|
1204
|
return conf;
|
|
1124
|
1205
|
}
|
|
1125
|
1206
|
|
|
|
@@ -1129,6 +1210,8 @@ ngx_http_uploadprogress_merge_loc_conf(ngx_conf_t * cf, void *parent, void *chil
|
|
1129
|
1210
|
{
|
|
1130
|
1211
|
ngx_http_uploadprogress_conf_t *prev = parent;
|
|
1131
|
1212
|
ngx_http_uploadprogress_conf_t *conf = child;
|
|
|
1213
|
+ ngx_http_uploadprogress_template_t *t, *pt, *gt;
|
|
|
1214
|
+ ngx_uint_t i;
|
|
1132
|
1215
|
|
|
1133
|
1216
|
if (conf->shm_zone == NULL) {
|
|
1134
|
1217
|
*conf = *prev;
|
|
|
@@ -1136,6 +1219,23 @@ ngx_http_uploadprogress_merge_loc_conf(ngx_conf_t * cf, void *parent, void *chil
|
|
1136
|
1219
|
|
|
1137
|
1220
|
ngx_conf_merge_str_value(conf->content_type, prev->content_type, "text/javascript");
|
|
1138
|
1221
|
|
|
|
1222
|
+ t = conf->templates.elts;
|
|
|
1223
|
+ pt = prev->templates.elts;
|
|
|
1224
|
+ gt = ngx_http_uploadprogress_global_templates.elts;
|
|
|
1225
|
+
|
|
|
1226
|
+ for(i = 0;i < conf->templates.nelts; i++) {
|
|
|
1227
|
+ if(t[i].values == NULL) {
|
|
|
1228
|
+ if(pt[i].values == NULL) {
|
|
|
1229
|
+ t[i].values = gt[i].values;
|
|
|
1230
|
+ t[i].lengths = gt[i].lengths;
|
|
|
1231
|
+ }
|
|
|
1232
|
+ else{
|
|
|
1233
|
+ t[i].values = pt[i].values;
|
|
|
1234
|
+ t[i].lengths = pt[i].lengths;
|
|
|
1235
|
+ }
|
|
|
1236
|
+ }
|
|
|
1237
|
+ }
|
|
|
1238
|
+
|
|
1139
|
1239
|
return NGX_CONF_OK;
|
|
1140
|
1240
|
}
|
|
1141
|
1241
|
|
|
|
@@ -1287,3 +1387,53 @@ ngx_http_report_uploads(ngx_conf_t * cf, ngx_command_t * cmd, void *conf)
|
|
1287
|
1387
|
|
|
1288
|
1388
|
return NGX_CONF_OK;
|
|
1289
|
1389
|
}
|
|
|
1390
|
+
|
|
|
1391
|
+static char*
|
|
|
1392
|
+ngx_http_upload_progress_template(ngx_conf_t * cf, ngx_command_t * cmd, void *conf)
|
|
|
1393
|
+{
|
|
|
1394
|
+ ngx_http_uploadprogress_conf_t *upcf = conf;
|
|
|
1395
|
+ ssize_t n;
|
|
|
1396
|
+ ngx_str_t *value;
|
|
|
1397
|
+ ngx_http_uploadprogress_state_map_t *m;
|
|
|
1398
|
+ ngx_http_uploadprogress_template_t *t;
|
|
|
1399
|
+ ngx_http_script_compile_t sc;
|
|
|
1400
|
+
|
|
|
1401
|
+ value = cf->args->elts;
|
|
|
1402
|
+
|
|
|
1403
|
+ m = ngx_http_uploadprogress_state_map;
|
|
|
1404
|
+
|
|
|
1405
|
+ while(m->name.data != NULL) {
|
|
|
1406
|
+ if((value[1].len == m->name.len && !ngx_strncmp(value[1].data, m->name.data, m->name.len))
|
|
|
1407
|
+ || (value[1].len == 2 && !ngx_strncmp(value[1].data, m->name.data, 2))) {
|
|
|
1408
|
+ break;
|
|
|
1409
|
+ }
|
|
|
1410
|
+ m++;
|
|
|
1411
|
+ }
|
|
|
1412
|
+
|
|
|
1413
|
+ if (m->name.data == NULL) {
|
|
|
1414
|
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
|
|
1415
|
+ "unknown state \"%V\"", &value[1]);
|
|
|
1416
|
+ return NGX_CONF_ERROR;
|
|
|
1417
|
+ }
|
|
|
1418
|
+
|
|
|
1419
|
+ t = (ngx_http_uploadprogress_template_t*)upcf->templates.elts + (ngx_uint_t)m->idx;
|
|
|
1420
|
+
|
|
|
1421
|
+ n = ngx_http_script_variables_count(&value[2]);
|
|
|
1422
|
+
|
|
|
1423
|
+ ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
|
|
|
1424
|
+
|
|
|
1425
|
+ sc.cf = cf;
|
|
|
1426
|
+ sc.source = &value[2];
|
|
|
1427
|
+ sc.lengths = &t->lengths;
|
|
|
1428
|
+ sc.values = &t->values;
|
|
|
1429
|
+ sc.variables = n;
|
|
|
1430
|
+ sc.complete_lengths = 1;
|
|
|
1431
|
+ sc.complete_values = 1;
|
|
|
1432
|
+
|
|
|
1433
|
+ if (ngx_http_script_compile(&sc) != NGX_OK) {
|
|
|
1434
|
+ return NGX_CONF_ERROR;
|
|
|
1435
|
+ }
|
|
|
1436
|
+
|
|
|
1437
|
+ return NGX_CONF_OK;
|
|
|
1438
|
+}
|
|
|
1439
|
+
|