/src/u-boot/lib/efi_loader/efi_http.c
Line | Count | Source |
1 | | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | | /* |
3 | | * An HTTP driver |
4 | | * |
5 | | * HTTP_PROTOCOL |
6 | | * HTTP_SERVICE_BINDING_PROTOCOL |
7 | | * IP4_CONFIG2_PROTOCOL |
8 | | */ |
9 | | |
10 | | #define LOG_CATEGORY LOGC_EFI |
11 | | |
12 | | #include <charset.h> |
13 | | #include <efi_loader.h> |
14 | | #include <image.h> |
15 | | #include <malloc.h> |
16 | | #include <mapmem.h> |
17 | | #include <net.h> |
18 | | |
19 | | static const efi_guid_t efi_http_service_binding_guid = EFI_HTTP_SERVICE_BINDING_PROTOCOL_GUID; |
20 | | static const efi_guid_t efi_http_guid = EFI_HTTP_PROTOCOL_GUID; |
21 | | |
22 | | /** |
23 | | * struct efi_http_instance - EFI object representing an HTTP protocol instance |
24 | | * |
25 | | * @http: EFI_HTTP_PROTOCOL interface |
26 | | * @handle: handle to efi object |
27 | | * @configured: configuration status |
28 | | * @http_load_addr: data buffer |
29 | | * @file_size: size of data |
30 | | * @current_offset: offset in data buffer |
31 | | * @status_code: HTTP status code |
32 | | * @num_headers: number of received headers |
33 | | * @headers: array of headers |
34 | | * @headers_buffer: raw buffer with headers |
35 | | */ |
36 | | struct efi_http_instance { |
37 | | struct efi_http_protocol http; |
38 | | efi_handle_t handle; |
39 | | struct efi_service_binding_protocol *parent; |
40 | | bool configured; |
41 | | void *http_load_addr; |
42 | | ulong file_size; |
43 | | ulong current_offset; |
44 | | u32 status_code; |
45 | | ulong num_headers; |
46 | | struct http_header headers[MAX_HTTP_HEADERS]; |
47 | | char headers_buffer[MAX_HTTP_HEADERS_SIZE]; |
48 | | }; |
49 | | |
50 | | static int num_instances; |
51 | | |
52 | | /* |
53 | | * efi_u32_to_httpstatus() - convert u32 to status |
54 | | * |
55 | | */ |
56 | | enum efi_http_status_code efi_u32_to_httpstatus(u32 status); |
57 | | |
58 | | /* |
59 | | * efi_http_send_data() - sends data to client |
60 | | * |
61 | | * |
62 | | * @client_buffer: client buffer to send data to |
63 | | * @client_buffer_size: size of the client buffer |
64 | | * @inst: HTTP instance for which to send data |
65 | | * |
66 | | * Return: status code |
67 | | */ |
68 | | static efi_status_t efi_http_send_data(void *client_buffer, |
69 | | efi_uintn_t *client_buffer_size, |
70 | | struct efi_http_instance *inst) |
71 | 0 | { |
72 | 0 | efi_status_t ret = EFI_SUCCESS; |
73 | 0 | ulong total_size, transfer_size; |
74 | 0 | uchar *ptr; |
75 | | |
76 | | // Amount of data left; |
77 | 0 | total_size = inst->file_size; |
78 | 0 | transfer_size = total_size - inst->current_offset; |
79 | 0 | debug("efi_http: sending data to client, total size %lu\n", total_size); |
80 | | // Amount of data the client is willing to receive |
81 | 0 | if (transfer_size > *client_buffer_size) |
82 | 0 | transfer_size = *client_buffer_size; |
83 | 0 | else |
84 | 0 | *client_buffer_size = transfer_size; |
85 | 0 | debug("efi_http: transfer size %lu\n", transfer_size); |
86 | 0 | if (!transfer_size) // Ok, only headers |
87 | 0 | goto out; |
88 | | |
89 | 0 | if (!client_buffer) { |
90 | 0 | ret = EFI_INVALID_PARAMETER; |
91 | 0 | goto out; |
92 | 0 | } |
93 | | |
94 | | // Send data |
95 | 0 | ptr = (uchar *)inst->http_load_addr + inst->current_offset; |
96 | 0 | memcpy(client_buffer, ptr, transfer_size); |
97 | |
|
98 | 0 | inst->current_offset += transfer_size; |
99 | | |
100 | | // Whole file served, clean the buffer: |
101 | 0 | if (inst->current_offset == inst->file_size) { |
102 | 0 | efi_free_pool(inst->http_load_addr); |
103 | 0 | inst->http_load_addr = NULL; |
104 | 0 | inst->current_offset = 0; |
105 | 0 | inst->file_size = 0; |
106 | 0 | } |
107 | |
|
108 | 0 | out: |
109 | 0 | return ret; |
110 | 0 | } |
111 | | |
112 | | /* EFI_HTTP_PROTOCOL */ |
113 | | |
114 | | /* |
115 | | * efi_http_get_mode_data() - Gets the current operational status. |
116 | | * |
117 | | * This function implements EFI_HTTP_PROTOCOL.GetModeData(). |
118 | | * See the Unified Extensible Firmware Interface |
119 | | * (UEFI) specification for details. |
120 | | * |
121 | | * @this: pointer to the protocol instance |
122 | | * @data: pointer to the buffer for operational parameters |
123 | | * of this HTTP instance |
124 | | * Return: status code |
125 | | */ |
126 | | static efi_status_t EFIAPI efi_http_get_mode_data(struct efi_http_protocol *this, |
127 | | struct efi_http_config_data *data) |
128 | | { |
129 | | EFI_ENTRY("%p, %p", this, data); |
130 | | |
131 | | efi_status_t ret = EFI_UNSUPPORTED; |
132 | | |
133 | | return EFI_EXIT(ret); |
134 | | } |
135 | | |
136 | | /* |
137 | | * efi_http_configure() - Initializes operational status for this |
138 | | * EFI HTTP instance. |
139 | | * |
140 | | * This function implements EFI_HTTP_PROTOCOL.Configure(). |
141 | | * See the Unified Extensible Firmware Interface |
142 | | * (UEFI) specification for details. |
143 | | * |
144 | | * @this: pointer to the protocol instance |
145 | | * @data: pointer to the buffer for operational parameters of |
146 | | * this HTTP instance |
147 | | * Return: status code |
148 | | */ |
149 | | static efi_status_t EFIAPI efi_http_configure(struct efi_http_protocol *this, |
150 | | struct efi_http_config_data *data) |
151 | | { |
152 | | EFI_ENTRY("%p, %p", this, data); |
153 | | |
154 | | efi_status_t ret = EFI_SUCCESS; |
155 | | enum efi_http_version http_version; |
156 | | struct efi_httpv4_access_point *ipv4_node; |
157 | | struct efi_http_instance *http_instance; |
158 | | |
159 | | if (!this) { |
160 | | ret = EFI_INVALID_PARAMETER; |
161 | | goto out; |
162 | | } |
163 | | |
164 | | http_instance = (struct efi_http_instance *)this; |
165 | | |
166 | | if (!data) { |
167 | | efi_free_pool(http_instance->http_load_addr); |
168 | | http_instance->http_load_addr = NULL; |
169 | | http_instance->current_offset = 0; |
170 | | http_instance->configured = false; |
171 | | |
172 | | goto out; |
173 | | } |
174 | | |
175 | | if (http_instance->configured) { |
176 | | ret = EFI_ALREADY_STARTED; |
177 | | goto out; |
178 | | } |
179 | | |
180 | | http_version = data->http_version; |
181 | | ipv4_node = data->access_point.ipv4_node; |
182 | | |
183 | | if ((http_version != HTTPVERSION10 && |
184 | | http_version != HTTPVERSION11) || |
185 | | data->is_ipv6 || !ipv4_node) { /* Only support ipv4 */ |
186 | | ret = EFI_UNSUPPORTED; |
187 | | goto out; |
188 | | } |
189 | | |
190 | | if (!ipv4_node->use_default_address) { |
191 | | efi_net_set_addr((struct efi_ipv4_address *)&ipv4_node->local_address, |
192 | | (struct efi_ipv4_address *)&ipv4_node->local_subnet, NULL, NULL); |
193 | | } |
194 | | |
195 | | http_instance->current_offset = 0; |
196 | | http_instance->configured = true; |
197 | | |
198 | | out: |
199 | | return EFI_EXIT(ret); |
200 | | } |
201 | | |
202 | | /* |
203 | | * efi_http_request() - Queues an HTTP request to this HTTP instance |
204 | | * |
205 | | * This function implements EFI_HTTP_PROTOCOL.Request(). |
206 | | * See the Unified Extensible Firmware Interface |
207 | | * (UEFI) specification for details. |
208 | | * |
209 | | * @this: pointer to the protocol instance |
210 | | * @token: pointer to storage containing HTTP request token |
211 | | * Return: status code |
212 | | */ |
213 | | static efi_status_t EFIAPI efi_http_request(struct efi_http_protocol *this, |
214 | | struct efi_http_token *token) |
215 | | { |
216 | | EFI_ENTRY("%p, %p", this, token); |
217 | | |
218 | | efi_status_t ret = EFI_SUCCESS; |
219 | | u8 *tmp; |
220 | | u8 url_8[1024]; |
221 | | u16 *url_16; |
222 | | enum efi_http_method current_method; |
223 | | struct efi_http_instance *http_instance; |
224 | | |
225 | | if (!token || !this || !token->message || |
226 | | !token->message->data.request) { |
227 | | ret = EFI_INVALID_PARAMETER; |
228 | | goto out; |
229 | | } |
230 | | |
231 | | http_instance = (struct efi_http_instance *)this; |
232 | | |
233 | | if (!http_instance->configured) { |
234 | | ret = EFI_NOT_STARTED; |
235 | | goto out; |
236 | | } |
237 | | |
238 | | current_method = token->message->data.request->method; |
239 | | url_16 = token->message->data.request->url; |
240 | | |
241 | | /* Parse URL. It comes in UCS-2 encoding and follows RFC3986 */ |
242 | | tmp = url_8; |
243 | | utf16_utf8_strncpy((char **)&tmp, url_16, 1024); |
244 | | |
245 | | ret = efi_net_do_request(url_8, current_method, &http_instance->http_load_addr, |
246 | | &http_instance->status_code, &http_instance->file_size, |
247 | | http_instance->headers_buffer, http_instance->parent); |
248 | | if (ret != EFI_SUCCESS) |
249 | | goto out; |
250 | | |
251 | | // We have a successful request |
252 | | efi_net_parse_headers(&http_instance->num_headers, http_instance->headers); |
253 | | http_instance->current_offset = 0; |
254 | | token->status = EFI_SUCCESS; |
255 | | goto out_signal; |
256 | | |
257 | | out_signal: |
258 | | efi_signal_event(token->event); |
259 | | out: |
260 | | return EFI_EXIT(ret); |
261 | | } |
262 | | |
263 | | /* |
264 | | * efi_http_cancel() - Abort an asynchronous HTTP request or response token |
265 | | * |
266 | | * This function implements EFI_HTTP_PROTOCOL.Cancel(). |
267 | | * See the Unified Extensible Firmware Interface |
268 | | * (UEFI) specification for details. |
269 | | * |
270 | | * @this: pointer to the protocol instance |
271 | | * @token: pointer to storage containing HTTP request token |
272 | | * Return: status code |
273 | | */ |
274 | | static efi_status_t EFIAPI efi_http_cancel(struct efi_http_protocol *this, |
275 | | struct efi_http_token *token) |
276 | | { |
277 | | EFI_ENTRY("%p, %p", this, token); |
278 | | |
279 | | efi_status_t ret = EFI_UNSUPPORTED; |
280 | | |
281 | | return EFI_EXIT(ret); |
282 | | } |
283 | | |
284 | | /* |
285 | | * efi_http_response() - Queues an HTTP response to this HTTP instance |
286 | | * |
287 | | * This function implements EFI_HTTP_PROTOCOL.Response(). |
288 | | * See the Unified Extensible Firmware Interface |
289 | | * (UEFI) specification for details. |
290 | | * |
291 | | * @this: pointer to the protocol instance |
292 | | * @token: pointer to storage containing HTTP request token |
293 | | * Return: status code |
294 | | */ |
295 | | static efi_status_t EFIAPI efi_http_response(struct efi_http_protocol *this, |
296 | | struct efi_http_token *token) |
297 | | { |
298 | | EFI_ENTRY("%p, %p", this, token); |
299 | | |
300 | | efi_status_t ret = EFI_SUCCESS; |
301 | | struct efi_http_instance *http_instance; |
302 | | struct efi_http_header **client_headers; |
303 | | struct efi_http_response_data *response; |
304 | | |
305 | | if (!token || !this || !token->message) { |
306 | | ret = EFI_INVALID_PARAMETER; |
307 | | goto out; |
308 | | } |
309 | | |
310 | | http_instance = (struct efi_http_instance *)this; |
311 | | |
312 | | // Set HTTP status code |
313 | | if (token->message->data.response) { // TODO extra check, see spec. |
314 | | response = token->message->data.response; |
315 | | response->status_code = efi_u32_to_httpstatus(http_instance->status_code); |
316 | | } |
317 | | |
318 | | client_headers = &token->message->headers; |
319 | | |
320 | | ret = efi_allocate_pool(EFI_BOOT_SERVICES_DATA, |
321 | | (http_instance->num_headers) * sizeof(struct efi_http_header), |
322 | | (void **)client_headers); // This is deallocated by the client. |
323 | | if (ret != EFI_SUCCESS) |
324 | | goto out_bad_signal; |
325 | | |
326 | | // Send headers |
327 | | token->message->header_count = http_instance->num_headers; |
328 | | for (int i = 0; i < http_instance->num_headers; i++) { |
329 | | (*client_headers)[i].field_name = http_instance->headers[i].name; |
330 | | (*client_headers)[i].field_value = http_instance->headers[i].value; |
331 | | } |
332 | | |
333 | | ret = efi_http_send_data(token->message->body, &token->message->body_length, http_instance); |
334 | | if (ret != EFI_SUCCESS) |
335 | | goto out_bad_signal; |
336 | | |
337 | | token->status = EFI_SUCCESS; |
338 | | goto out_signal; |
339 | | |
340 | | out_bad_signal: |
341 | | token->status = EFI_ABORTED; |
342 | | out_signal: |
343 | | efi_signal_event(token->event); |
344 | | out: |
345 | | return EFI_EXIT(ret); |
346 | | } |
347 | | |
348 | | /* |
349 | | * efi_http_poll() - Polls for incoming data packets and processes outgoing data packets |
350 | | * |
351 | | * This function implements EFI_HTTP_PROTOCOL.Poll(). |
352 | | * See the Unified Extensible Firmware Interface |
353 | | * (UEFI) specification for details. |
354 | | * |
355 | | * @this: pointer to the protocol instance |
356 | | * @token: pointer to storage containing HTTP request token |
357 | | * Return: status code |
358 | | */ |
359 | | static efi_status_t EFIAPI efi_http_poll(struct efi_http_protocol *this) |
360 | | { |
361 | | EFI_ENTRY("%p", this); |
362 | | |
363 | | efi_status_t ret = EFI_UNSUPPORTED; |
364 | | |
365 | | return EFI_EXIT(ret); |
366 | | } |
367 | | |
368 | | /* EFI_HTTP_SERVICE_BINDING_PROTOCOL */ |
369 | | |
370 | | /* |
371 | | * efi_http_service_binding_create_child() - Creates a child handle |
372 | | * and installs a protocol |
373 | | * |
374 | | * This function implements EFI_HTTP_SERVICE_BINDING.CreateChild(). |
375 | | * See the Unified Extensible Firmware Interface |
376 | | * (UEFI) specification for details. |
377 | | * |
378 | | * @this: pointer to the protocol instance |
379 | | * @child_handle: pointer to child handle |
380 | | * Return: status code |
381 | | */ |
382 | | static efi_status_t EFIAPI efi_http_service_binding_create_child( |
383 | | struct efi_service_binding_protocol *this, |
384 | | efi_handle_t *child_handle) |
385 | | { |
386 | | EFI_ENTRY("%p, %p", this, child_handle); |
387 | | |
388 | | efi_status_t ret = EFI_SUCCESS; |
389 | | struct efi_http_instance *new_instance; |
390 | | |
391 | | if (!child_handle) |
392 | | return EFI_EXIT(EFI_INVALID_PARAMETER); |
393 | | |
394 | | new_instance = calloc(1, sizeof(struct efi_http_instance)); |
395 | | if (!new_instance) { |
396 | | ret = EFI_OUT_OF_RESOURCES; |
397 | | goto failure_to_add_protocol; |
398 | | } |
399 | | |
400 | | if (*child_handle) { |
401 | | new_instance->handle = *child_handle; |
402 | | goto install; |
403 | | } |
404 | | |
405 | | new_instance->handle = calloc(1, sizeof(struct efi_object)); |
406 | | if (!new_instance->handle) { |
407 | | efi_free_pool((void *)new_instance); |
408 | | ret = EFI_OUT_OF_RESOURCES; |
409 | | goto failure_to_add_protocol; |
410 | | } |
411 | | |
412 | | new_instance->parent = this; |
413 | | efi_add_handle(new_instance->handle); |
414 | | *child_handle = new_instance->handle; |
415 | | |
416 | | install: |
417 | | ret = efi_add_protocol(new_instance->handle, &efi_http_guid, |
418 | | &new_instance->http); |
419 | | if (ret != EFI_SUCCESS) |
420 | | goto failure_to_add_protocol; |
421 | | |
422 | | new_instance->http.get_mode_data = efi_http_get_mode_data; |
423 | | new_instance->http.configure = efi_http_configure; |
424 | | new_instance->http.request = efi_http_request; |
425 | | new_instance->http.cancel = efi_http_cancel; |
426 | | new_instance->http.response = efi_http_response; |
427 | | new_instance->http.poll = efi_http_poll; |
428 | | ++num_instances; |
429 | | |
430 | | return EFI_EXIT(EFI_SUCCESS); |
431 | | |
432 | | failure_to_add_protocol: |
433 | | return EFI_EXIT(ret); |
434 | | } |
435 | | |
436 | | /* |
437 | | * efi_http_service_binding_destroy_child() - Destroys a child handle with |
438 | | * a protocol installed on it |
439 | | * |
440 | | * This function implements EFI_HTTP_SERVICE_BINDING.DestroyChild(). |
441 | | * See the Unified Extensible Firmware Interface |
442 | | * (UEFI) specification for details. |
443 | | * |
444 | | * @this: pointer to the protocol instance |
445 | | * @child_handle: child handle |
446 | | * Return: status code |
447 | | */ |
448 | | static efi_status_t EFIAPI efi_http_service_binding_destroy_child( |
449 | | struct efi_service_binding_protocol *this, |
450 | | efi_handle_t child_handle) |
451 | | { |
452 | | EFI_ENTRY("%p, %p", this, child_handle); |
453 | | efi_status_t ret = EFI_SUCCESS; |
454 | | struct efi_http_instance *http_instance; |
455 | | struct efi_handler *phandler; |
456 | | |
457 | | if (num_instances == 0) |
458 | | return EFI_EXIT(EFI_NOT_FOUND); |
459 | | |
460 | | if (!child_handle) |
461 | | return EFI_EXIT(EFI_INVALID_PARAMETER); |
462 | | |
463 | | ret = efi_search_protocol(child_handle, &efi_http_guid, &phandler); |
464 | | if (ret != EFI_SUCCESS) { |
465 | | if (ret != EFI_INVALID_PARAMETER) |
466 | | ret = EFI_UNSUPPORTED; |
467 | | goto out; |
468 | | } |
469 | | |
470 | | ret = efi_delete_handle(child_handle); |
471 | | if (ret != EFI_SUCCESS) |
472 | | goto out; |
473 | | |
474 | | http_instance = phandler->protocol_interface; |
475 | | efi_free_pool(http_instance->http_load_addr); |
476 | | http_instance->http_load_addr = NULL; |
477 | | |
478 | | free(phandler->protocol_interface); |
479 | | |
480 | | num_instances--; |
481 | | out: |
482 | | return EFI_EXIT(ret); |
483 | | } |
484 | | |
485 | | /** |
486 | | * efi_http_register() - register the http protocol |
487 | | * |
488 | | */ |
489 | | efi_status_t efi_http_register(const efi_handle_t handle, |
490 | | struct efi_service_binding_protocol *http_service_binding) |
491 | 0 | { |
492 | 0 | efi_status_t r = EFI_SUCCESS; |
493 | |
|
494 | 0 | r = efi_add_protocol(handle, &efi_http_service_binding_guid, |
495 | 0 | http_service_binding); |
496 | 0 | if (r != EFI_SUCCESS) |
497 | 0 | goto failure_to_add_protocol; |
498 | | |
499 | 0 | http_service_binding->create_child = efi_http_service_binding_create_child; |
500 | 0 | http_service_binding->destroy_child = efi_http_service_binding_destroy_child; |
501 | |
|
502 | 0 | return EFI_SUCCESS; |
503 | 0 | failure_to_add_protocol: |
504 | 0 | return r; |
505 | 0 | } |
506 | | |
507 | | enum efi_http_status_code efi_u32_to_httpstatus(u32 status) |
508 | 0 | { |
509 | 0 | switch (status) { |
510 | 0 | case 100: return HTTP_STATUS_100_CONTINUE; |
511 | 0 | case 101: return HTTP_STATUS_101_SWITCHING_PROTOCOLS; |
512 | 0 | case 200: return HTTP_STATUS_200_OK; |
513 | 0 | case 201: return HTTP_STATUS_201_CREATED; |
514 | 0 | case 202: return HTTP_STATUS_202_ACCEPTED; |
515 | 0 | case 203: return HTTP_STATUS_203_NON_AUTHORITATIVE_INFORMATION; |
516 | 0 | case 204: return HTTP_STATUS_204_NO_CONTENT; |
517 | 0 | case 205: return HTTP_STATUS_205_RESET_CONTENT; |
518 | 0 | case 206: return HTTP_STATUS_206_PARTIAL_CONTENT; |
519 | 0 | case 300: return HTTP_STATUS_300_MULTIPLE_CHOICES; |
520 | 0 | case 301: return HTTP_STATUS_301_MOVED_PERMANENTLY; |
521 | 0 | case 302: return HTTP_STATUS_302_FOUND; |
522 | 0 | case 303: return HTTP_STATUS_303_SEE_OTHER; |
523 | 0 | case 304: return HTTP_STATUS_304_NOT_MODIFIED; |
524 | 0 | case 305: return HTTP_STATUS_305_USE_PROXY; |
525 | 0 | case 307: return HTTP_STATUS_307_TEMPORARY_REDIRECT; |
526 | 0 | case 400: return HTTP_STATUS_400_BAD_REQUEST; |
527 | 0 | case 401: return HTTP_STATUS_401_UNAUTHORIZED; |
528 | 0 | case 402: return HTTP_STATUS_402_PAYMENT_REQUIRED; |
529 | 0 | case 403: return HTTP_STATUS_403_FORBIDDEN; |
530 | 0 | case 404: return HTTP_STATUS_404_NOT_FOUND; |
531 | 0 | case 405: return HTTP_STATUS_405_METHOD_NOT_ALLOWED; |
532 | 0 | case 406: return HTTP_STATUS_406_NOT_ACCEPTABLE; |
533 | 0 | case 407: return HTTP_STATUS_407_PROXY_AUTHENTICATION_REQUIRED; |
534 | 0 | case 408: return HTTP_STATUS_408_REQUEST_TIME_OUT; |
535 | 0 | case 409: return HTTP_STATUS_409_CONFLICT; |
536 | 0 | case 410: return HTTP_STATUS_410_GONE; |
537 | 0 | case 411: return HTTP_STATUS_411_LENGTH_REQUIRED; |
538 | 0 | case 412: return HTTP_STATUS_412_PRECONDITION_FAILED; |
539 | 0 | case 413: return HTTP_STATUS_413_REQUEST_ENTITY_TOO_LARGE; |
540 | 0 | case 414: return HTTP_STATUS_414_REQUEST_URI_TOO_LARGE; |
541 | 0 | case 415: return HTTP_STATUS_415_UNSUPPORTED_MEDIA_TYPE; |
542 | 0 | case 416: return HTTP_STATUS_416_REQUESTED_RANGE_NOT_SATISFIED; |
543 | 0 | case 417: return HTTP_STATUS_417_EXPECTATION_FAILED; |
544 | 0 | case 500: return HTTP_STATUS_500_INTERNAL_SERVER_ERROR; |
545 | 0 | case 501: return HTTP_STATUS_501_NOT_IMPLEMENTED; |
546 | 0 | case 502: return HTTP_STATUS_502_BAD_GATEWAY; |
547 | 0 | case 503: return HTTP_STATUS_503_SERVICE_UNAVAILABLE; |
548 | 0 | case 504: return HTTP_STATUS_504_GATEWAY_TIME_OUT; |
549 | 0 | case 505: return HTTP_STATUS_505_HTTP_VERSION_NOT_SUPPORTED; |
550 | 0 | case 308: return HTTP_STATUS_308_PERMANENT_REDIRECT; |
551 | 0 | default: return HTTP_STATUS_UNSUPPORTED_STATUS; |
552 | 0 | } |
553 | 0 | } |