Commit 78e3e598 authored by Thomas Milius's avatar Thomas Milius Committed by ROOL
Browse files

Add ability to register user defined methods

Detail:
  Two new SWIs (see Docs/AddMethds) allow a client to register a method which AcornHTTP
  will then handle on its behalf based on some flags. In many cases the method can just
  follow a GET or PUT in operation.

Version 1.05. Tagged as 'HTTP-1_05'
parent 9cb08bd8
Extra HTTP Methods Details
==========================
AcornHTTP is designed to be very flexible but it is limited to the classical
HTTP methods.
Since its original design a couple of other HTTP methods have been defined for
various purposes e.g. for WebDAV, CalDAV etc. which are more or less popular.
Many methods are in fact similar to the standard set (GET/PUT etc) and so
can be handled by changing AcornHTTP quite simply.
Every client which wants to use AcornHTTP with a non standard HTTP method
registers the method details via a protocol specific SWI.
When a client no longer makes usage of the registered HTTP non standard
method especally when qutting it has to deregister its method(s). This allows
other applications to reuse the method number for their own purposes.
SWI HTTP_RegisterMethod
=======================
On entry:
R0 - pointer to the name of non standard HTTP method (eg. REPORT)
R1 - method flags
Bit
0-1 type
0 - Caller must setup everything
1 - Behave like PUT/POST
2 - Behave like GET
3 - Unused
2-31 reserved (0)
On exit:
R2 - opaque method number to use within other AcornHTTP SWIs
Any number in method range 1-127 may be returned as valid result except the
method number which are fixed assigned to HTTP standard methods (GET, HEAD,
POST, PUT, OPTIONS, TRACE, DELETE).
SWI HTTP_DeregisterMethod
=========================
On entry:
R0 - pointer to the name of non standard HTTP method (eg. REPORT)
R1 - method flags (as were set when registering)
No exit defined
......@@ -9,3 +9,5 @@ E07:Unable to initiate fetch
E08:Unable to parse URL
E09:Remote proxy not found. Please check the proxy settings
E0a:Security support module not present
E0b:Invalid parameter passed to SWI
E0c:Insufficient internal resources to handle SWI
/* (1.04)
/* (1.05)
*
* This file is automatically maintained by srccommit, do not edit manually.
*
*/
#define Module_MajorVersion_CMHG 1.04
#define Module_MajorVersion_CMHG 1.05
#define Module_MinorVersion_CMHG
#define Module_Date_CMHG 22 Apr 2020
#define Module_Date_CMHG 03 Jul 2021
#define Module_MajorVersion "1.04"
#define Module_Version 104
#define Module_MajorVersion "1.05"
#define Module_Version 105
#define Module_MinorVersion ""
#define Module_Date "22 Apr 2020"
#define Module_Date "03 Jul 2021"
#define Module_ApplicationDate "22-Apr-20"
#define Module_ApplicationDate "03-Jul-21"
#define Module_ComponentName "HTTP"
#define Module_FullVersion "1.04"
#define Module_HelpVersion "1.04 (22 Apr 2020)"
#define Module_LibraryVersionInfo "1:4"
#define Module_FullVersion "1.05"
#define Module_HelpVersion "1.05 (03 Jul 2021)"
#define Module_LibraryVersionInfo "1:5"
......@@ -36,6 +36,8 @@
#include "dns.h"
#include "config.h"
#include "security.h"
#include "start.h"
#include "Httperror.h"
static volatile int callback_pending_flag = 0;
......@@ -58,31 +60,6 @@ int __errno;
#endif
#endif
/*************************************************************/
/* So the real error number that we have to return is: */
/*************************************************************/
#define HTTP_ERROR_NUM (ERROR_BASE+HTTP_OFFSET)
/*************************************************************/
/* The error message definitions */
/*************************************************************/
typedef enum {
HTTP_HOST_NOT_FOUND = HTTP_ERROR_NUM + 0, /* DNS lookup failed */
HTTP_HOST_CONNECT_ERROR = HTTP_ERROR_NUM + 1, /* connect() failed */
HTTP_DATA_READ_ERROR = HTTP_ERROR_NUM + 2, /* unexpected read error on socket */
HTTP_GENERAL_ERROR = HTTP_ERROR_NUM + 3, /* Misc. - not helpful */
HTTP_BAD_SESSION_ERROR = HTTP_ERROR_NUM + 4, /* Failed to locate session in session table */
HTTP_CONNECTION_FAILED = HTTP_ERROR_NUM + 5, /* Failed to establish connection */
HTTP_METHOD_UNSUPPORTED = HTTP_ERROR_NUM + 6, /* Unknown method in R2 */
HTTP_METHOD_INIT_ERR = HTTP_ERROR_NUM + 7, /* Failed to start a fetch completely */
HTTP_BAD_URL_PARSE = HTTP_ERROR_NUM + 8, /* Unable to parse URL (URL module too old?) */
HTTP_PROXY_NOT_FOUND = HTTP_ERROR_NUM + 9, /* Unable to contact the proxy */
HTTP_NO_SECURITY = HTTP_ERROR_NUM + 10 /* No SSL support present */
} http_internal_error_codes;
/* Sets clibrary_realloc_routine_is_buggy non-zero if RISC OS 3.1 or earlier (ie. ROM realloc is broken) */
static int clibrary_realloc_routine_is_buggy;
static void module_check_os_version(void)
......@@ -203,6 +180,7 @@ _kernel_oserror *module_init(const char *cmd_tail, int podule_base, void *pw)
hosttrack_init();
connpool_init();
dns_init();
http_start_init();
e = register_with_url();
if (e == NULL) {
......@@ -303,6 +281,7 @@ _kernel_oserror *module_kill(int fatal, int podule, void *pw)
ses_kill_all();
session_init();
http_start_exit();
hosttrack_exit();
connpool_exit();
http_free_agent();
......@@ -381,6 +360,12 @@ static _kernel_oserror *swi_handler_2(int swi_offset, _kernel_swi_regs *r, void
return(make_error(HTTP_NO_SECURITY, 0));
}
case HTTP_RegisterMethod - HTTP_00:
return(http_register_method(r));
case HTTP_DeregisterMethod - HTTP_00:
return(http_deregister_method(r));
/* HTTP specific SWIs (at the upper end of the SWI range */
#ifdef COOKIE
case HTTP_EnumerateCookies - HTTP_00:
......
......@@ -37,6 +37,22 @@
#include "URLclient.h"
#include "security.h"
#include "connect.h"
#include "Httperror.h"
#include "utils.h"
#include "start.h"
#define HTTP_OTHER_METHOD_FLAG_TYPE_USER 0
#define HTTP_OTHER_METHOD_FLAG_TYPE_POST 1
#define HTTP_OTHER_METHOD_FLAG_TYPE_GET 2
#define HTTP_OTHER_METHOD_FLAG_MASK_TYPE 3
typedef struct {
char *name;
unsigned long flags;
unsigned long usage_counter;
} http_other_method;
#define HTTP_OTHER_METHODS 128 /* Methods 0 & 255 reserved, 1-127 for us, 128-254 for the user */
static http_other_method http_other_methods[HTTP_OTHER_METHODS];
static int http_generate_request(Session *ses, _kernel_swi_regs *r);
static Session *http_new_session(_kernel_swi_regs *r);
......@@ -45,6 +61,140 @@ static const char *http_method_text(const int);
static int http_parse_url(Session *ses, char *proxy);
static _kernel_oserror *http_start_main(_kernel_swi_regs *r);
/* Called at start of module */
void http_start_init(void)
{
int i;
for (i = 0; i < HTTP_OTHER_METHODS; i++) {
http_other_methods[i].name = NULL;
http_other_methods[i].flags = 0;
http_other_methods[i].usage_counter = 0;
}
/* Mark reserved methods as used */
http_other_methods[0].usage_counter = 1;
/* Mark HTTP standard methods as used */
http_other_methods[method_HTTP_GET].usage_counter = 1;
http_other_methods[method_HTTP_HEAD].usage_counter = 1;
http_other_methods[method_HTTP_POST].usage_counter = 1;
http_other_methods[method_HTTP_PUT].usage_counter = 1;
http_other_methods[method_HTTP_OPTIONS].usage_counter = 1;
http_other_methods[method_HTTP_TRACE].usage_counter = 1;
http_other_methods[method_HTTP_DELETE].usage_counter = 1;
}
/* Called at finalisation of module */
void http_start_exit(void)
{
int i;
for (i = 0; i < HTTP_OTHER_METHODS; i++) {
if (http_other_methods[i].name != NULL) {
free(http_other_methods[i].name);
http_other_methods[i].name = NULL;
}
http_other_methods[i].flags = 0;
http_other_methods[i].usage_counter = 0;
}
}
/*************************************************************/
/* _kernel_oserror http_register_method(_kernel_swi_regs *r) */
/*************************************************************/
/*************************************************************/
/* Must be called by application to register a certain non */
/* standard HTTP method. The values in the registers are: */
/* r0 = Pointer to 0 delimited HTTP method name */
/* r1 = Method flags */
/* On exit: */
/* r2 = method to use */
/*************************************************************/
_kernel_oserror *http_register_method(_kernel_swi_regs *r)
{
int i = 0;
int first_unused_method, len;
char *buffer;
if (r->r[0] == NULL || (r->r[1] & ~HTTP_OTHER_METHOD_FLAG_MASK_TYPE)) {
return make_error(HTTP_BAD_PARAMETER, 0);
}
len = strlen((const char *)r->r[0]);
if (len == 0) return make_error(HTTP_BAD_PARAMETER, 0);
first_unused_method = -1;
while (i < HTTP_OTHER_METHODS) {
if (first_unused_method == -1 &&
http_other_methods[i].usage_counter == 0) {
/* Note first unused method in case the requested
* method doesn't already exist
*/
first_unused_method = i;
}
else {
if (http_other_methods[i].usage_counter > 0 &&
http_other_methods[i].name != NULL &&
http_other_methods[i].flags == (unsigned long)r->r[1] &&
strcmp(http_other_methods[i].name, (const char *)r->r[0]) == 0) {
/* Method already exists and is now used by
* an additional application
*/
http_other_methods[i].usage_counter++;
r->r[2] = i;
return NULL;
}
}
i++;
}
if (first_unused_method == -1) return make_error(HTTP_NO_RESOURCES, 0);
/* Method doesn't exist yet and there is a free method we can use for it */
buffer = malloc(len + 1);
if (buffer == NULL) return make_error(HTTP_NO_RESOURCES, 0);
http_other_methods[first_unused_method].name = buffer;
strcpy(buffer, (const char *)r->r[0]);
http_other_methods[first_unused_method].usage_counter = 1;
http_other_methods[first_unused_method].flags = (unsigned long)r->r[1];
r->r[2] = first_unused_method;
return NULL;
}
/***************************************************************/
/* _kernel_oserror http_deregister_method(_kernel_swi_regs *r) */
/***************************************************************/
/***************************************************************/
/* Must be called by application to deregister a certain non */
/* standard HTTP method previously registered with */
/* http_register_method. The values in the registers are: */
/* r0 = Pointer to 0 delimited HTTP method name */
/* r1 = Method Flags */
/***************************************************************/
_kernel_oserror *http_deregister_method(_kernel_swi_regs *r)
{
int i = 0;
if (r->r[0] == NULL) return make_error(HTTP_BAD_PARAMETER,0);
while (i < HTTP_OTHER_METHODS) {
if (http_other_methods[i].usage_counter > 0 &&
http_other_methods[i].name != NULL &&
http_other_methods[i].flags == r->r[1] &&
strcmp(http_other_methods[i].name, (const char *)r->r[0]) == 0) {
/* Match found, decrement usage */
http_other_methods[i].usage_counter--;
if (http_other_methods[i].usage_counter == 0) {
/* Can be used for other methods now. */
free(http_other_methods[i].name);
http_other_methods[i].name = NULL;
http_other_methods[i].flags = 0;
}
return NULL;
}
i++;
}
return make_error(HTTP_METHOD_UNSUPPORTED,0);
}
/*************************************************************/
/* _kernel_oserror http_start(_kernel_swi_regs *r) */
/*************************************************************/
......@@ -387,8 +537,10 @@ static const char *http_method_text(const int method)
case method_HTTP_OPTIONS: return "OPTIONS";
case method_HTTP_TRACE: return "TRACE";
case method_HTTP_DELETE: return "DELETE";
default:
default: {
if (method < HTTP_OTHER_METHODS) return http_other_methods[method].name;
return NULL;
}
}
}
......@@ -422,7 +574,9 @@ static int http_validate_user_supplied_data(Session *ses)
}
if (ses->method == method_HTTP_GET || ses->method == method_HTTP_HEAD) {
if (ses->method == method_HTTP_GET || ses->method == method_HTTP_HEAD ||
(ses->method < HTTP_OTHER_METHODS &&
(http_other_methods[ses->method].flags & HTTP_OTHER_METHOD_FLAG_MASK_TYPE) == HTTP_OTHER_METHOD_FLAG_TYPE_GET)) {
/* ... and add any other HTTP methods that MUST NOT include an entity
* body along with the request
*/
......@@ -464,7 +618,9 @@ static int http_validate_user_supplied_data(Session *ses)
ses->data_len = 0; /* Forcibly ignore the extra data */
}
if (ses->method == method_HTTP_PUT || ses->method == method_HTTP_POST) {
if (ses->method == method_HTTP_PUT || ses->method == method_HTTP_POST ||
(ses->method < HTTP_OTHER_METHODS &&
(http_other_methods[ses->method].flags & HTTP_OTHER_METHOD_FLAG_MASK_TYPE) == HTTP_OTHER_METHOD_FLAG_TYPE_POST)) {
/* ... and add any other HTTP methods that MUST include an entity
* body along with the request
*/
......
......@@ -44,8 +44,8 @@ swi-decoding-table: HTTP,
20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
40, 41, 42, 43, 44, 45, 46, 47, 48, 49,
50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
60, AddCookie, ConsumeCookie, EnumerateCookies
50, 51, 52, 53, 54, 55, 56, 57, 58, RegisterMethod,
DeregisterMethod, AddCookie, ConsumeCookie, EnumerateCookies
; SWI handler.
swi-handler-code: swi_handler
......
/* Copyright 1998 Acorn Computers Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* HTTP (h.Httperror)
*
*/
/*************************************************************/
/* So the real error number that we have to return is: */
/*************************************************************/
#define HTTP_ERROR_NUM (ERROR_BASE+HTTP_OFFSET)
/*************************************************************/
/* The error message definitions */
/*************************************************************/
typedef enum {
HTTP_HOST_NOT_FOUND = HTTP_ERROR_NUM + 0, /* DNS lookup failed */
HTTP_HOST_CONNECT_ERROR = HTTP_ERROR_NUM + 1, /* connect() failed */
HTTP_DATA_READ_ERROR = HTTP_ERROR_NUM + 2, /* unexpected read error on socket */
HTTP_GENERAL_ERROR = HTTP_ERROR_NUM + 3, /* Misc. - not helpful */
HTTP_BAD_SESSION_ERROR = HTTP_ERROR_NUM + 4, /* Failed to locate session in session table */
HTTP_CONNECTION_FAILED = HTTP_ERROR_NUM + 5, /* Failed to establish connection */
HTTP_METHOD_UNSUPPORTED = HTTP_ERROR_NUM + 6, /* Unknown method in R2 */
HTTP_METHOD_INIT_ERR = HTTP_ERROR_NUM + 7, /* Failed to start a fetch completely */
HTTP_BAD_URL_PARSE = HTTP_ERROR_NUM + 8, /* Unable to parse URL (URL module too old?) */
HTTP_PROXY_NOT_FOUND = HTTP_ERROR_NUM + 9, /* Unable to contact the proxy */
HTTP_NO_SECURITY = HTTP_ERROR_NUM + 10, /* No SSL support present */
HTTP_BAD_PARAMETER = HTTP_ERROR_NUM + 11, /* Invalid parameter(s) passed to SWI */
HTTP_NO_RESOURCES = HTTP_ERROR_NUM + 12 /* Insufficient internal resources to handle SWI */
} http_internal_error_codes;
/* Copyright 1998 Acorn Computers Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* HTTP (h.start)
*
*/
extern void http_start_init(void);
extern void http_start_exit(void);
extern _kernel_oserror *http_register_method(_kernel_swi_regs *r);
extern _kernel_oserror *http_deregister_method(_kernel_swi_regs *r);
......@@ -46,8 +46,10 @@ SWIClass SETS AcornHTTPSWI_Name
AddSWI SecureReadData
AddSWI SecureStop
^ AcornHTTPSWI_Base + &3D
^ AcornHTTPSWI_Base + &3B
AddSWI RegisterMethod
AddSWI DeregisterMethod
AddSWI AddCookie
AddSWI ConsumeCookie
AddSWI EnumerateCookies
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment