@opra/core 0.22.0 → 0.23.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) hide show
  1. package/cjs/adapter/execution-context.host.js +48 -0
  2. package/cjs/adapter/http/express-adapter.host.js +24 -0
  3. package/cjs/adapter/http/express-adapter.js +12 -45
  4. package/cjs/adapter/http/helpers/concat-readable.js +20 -0
  5. package/cjs/adapter/http/helpers/multipart-helper.js +96 -0
  6. package/cjs/adapter/http/helpers/query-parsers.js +16 -0
  7. package/cjs/adapter/http/http-adapter-base.js +127 -0
  8. package/cjs/adapter/http/http-adapter.host.js +57 -0
  9. package/cjs/adapter/http/http-adapter.js +11 -133
  10. package/cjs/adapter/http/{impl/http-server-request.js → http-server-request.js} +11 -5
  11. package/cjs/adapter/http/{impl/http-server-response.js → http-server-response.js} +4 -4
  12. package/cjs/adapter/http/impl/http-incoming-message.host.js +148 -0
  13. package/cjs/adapter/http/impl/{http-outgoing-message-host.js → http-outgoing-message.host.js} +26 -38
  14. package/cjs/adapter/http/request-handlers/entity-request-handler.js +405 -0
  15. package/cjs/adapter/http/request-handlers/request-handler-base.js +27 -0
  16. package/cjs/adapter/http/request-handlers/storage-request-handler.js +134 -0
  17. package/cjs/adapter/operation-context.js +16 -0
  18. package/cjs/adapter/platform-adapter.host.js +107 -0
  19. package/cjs/adapter/request.host.js +1 -2
  20. package/cjs/adapter/request.js +2 -0
  21. package/cjs/adapter/response.js +2 -0
  22. package/cjs/adapter/services/logger.js +36 -0
  23. package/cjs/augmentation/collection.augmentation.js +2 -0
  24. package/cjs/augmentation/singleton.augmentation.js +2 -0
  25. package/cjs/augmentation/storage.augmentation.js +2 -0
  26. package/cjs/index.js +15 -9
  27. package/esm/adapter/execution-context.host.js +44 -0
  28. package/esm/adapter/http/express-adapter.host.js +20 -0
  29. package/esm/adapter/http/express-adapter.js +11 -20
  30. package/esm/adapter/http/helpers/concat-readable.js +16 -0
  31. package/esm/adapter/http/helpers/multipart-helper.js +91 -0
  32. package/esm/adapter/http/helpers/query-parsers.js +12 -0
  33. package/esm/adapter/http/http-adapter-base.js +123 -0
  34. package/esm/adapter/http/http-adapter.host.js +52 -0
  35. package/esm/adapter/http/http-adapter.js +11 -132
  36. package/esm/adapter/http/{impl/http-server-request.js → http-server-request.js} +12 -6
  37. package/esm/adapter/http/{impl/http-server-response.js → http-server-response.js} +4 -4
  38. package/esm/adapter/http/impl/http-incoming-message.host.js +144 -0
  39. package/esm/adapter/http/impl/{http-outgoing-message-host.js → http-outgoing-message.host.js} +25 -36
  40. package/esm/adapter/http/request-handlers/entity-request-handler.js +400 -0
  41. package/esm/adapter/http/request-handlers/request-handler-base.js +23 -0
  42. package/esm/adapter/http/request-handlers/storage-request-handler.js +129 -0
  43. package/esm/adapter/operation-context.js +13 -0
  44. package/esm/adapter/platform-adapter.host.js +102 -0
  45. package/esm/adapter/request.host.js +1 -2
  46. package/esm/adapter/request.js +1 -0
  47. package/esm/adapter/response.js +1 -0
  48. package/esm/adapter/services/logger.js +32 -0
  49. package/esm/augmentation/collection.augmentation.js +1 -0
  50. package/esm/augmentation/singleton.augmentation.js +1 -0
  51. package/esm/augmentation/storage.augmentation.js +1 -0
  52. package/esm/index.js +15 -9
  53. package/i18n/en/error.json +7 -2
  54. package/package.json +7 -6
  55. package/types/adapter/execution-context.d.ts +31 -0
  56. package/types/adapter/execution-context.host.d.ts +27 -0
  57. package/types/adapter/http/express-adapter.d.ts +12 -8
  58. package/types/adapter/http/express-adapter.host.d.ts +11 -0
  59. package/types/adapter/http/helpers/concat-readable.d.ts +3 -0
  60. package/types/adapter/http/helpers/multipart-helper.d.ts +25 -0
  61. package/types/adapter/http/helpers/query-parsers.d.ts +1 -0
  62. package/types/adapter/http/http-adapter-base.d.ts +23 -0
  63. package/types/adapter/http/http-adapter.d.ts +13 -29
  64. package/types/adapter/http/http-adapter.host.d.ts +18 -0
  65. package/types/adapter/http/{impl/http-server-request.d.ts → http-server-request.d.ts} +7 -6
  66. package/types/adapter/http/{impl/http-server-response.d.ts → http-server-response.d.ts} +2 -2
  67. package/types/adapter/http/impl/{http-incoming-message-host.d.ts → http-incoming-message.host.d.ts} +16 -12
  68. package/types/adapter/http/impl/{http-outgoing-message-host.d.ts → http-outgoing-message.host.d.ts} +12 -16
  69. package/types/adapter/http/request-handlers/entity-request-handler.d.ts +24 -0
  70. package/types/adapter/http/request-handlers/request-handler-base.d.ts +15 -0
  71. package/types/adapter/http/request-handlers/storage-request-handler.d.ts +23 -0
  72. package/types/adapter/interfaces/logger.interface.d.ts +7 -6
  73. package/types/adapter/interfaces/request-handler.interface.d.ts +4 -0
  74. package/types/adapter/operation-context.d.ts +11 -0
  75. package/types/adapter/{adapter.d.ts → platform-adapter.d.ts} +18 -28
  76. package/types/adapter/platform-adapter.host.d.ts +31 -0
  77. package/types/adapter/request.d.ts +11 -0
  78. package/types/adapter/request.host.d.ts +12 -21
  79. package/types/adapter/{interfaces/response.interface.d.ts → response.d.ts} +2 -2
  80. package/types/adapter/response.host.d.ts +2 -2
  81. package/types/adapter/services/logger.d.ts +14 -0
  82. package/types/augmentation/collection.augmentation.d.ts +112 -0
  83. package/types/augmentation/singleton.augmentation.d.ts +64 -0
  84. package/types/augmentation/storage.augmentation.d.ts +39 -0
  85. package/types/index.d.ts +15 -9
  86. package/cjs/adapter/adapter.js +0 -118
  87. package/cjs/adapter/http/impl/http-incoming-message-host.js +0 -127
  88. package/cjs/adapter/http/request-parsers/parse-collection-request.js +0 -165
  89. package/cjs/adapter/http/request-parsers/parse-request.js +0 -24
  90. package/cjs/adapter/http/request-parsers/parse-singleton-request.js +0 -96
  91. package/cjs/adapter/internal/metadata.resource.js +0 -26
  92. package/cjs/adapter/request-context.host.js +0 -44
  93. package/cjs/shared/collection-resource-base.js +0 -20
  94. package/esm/adapter/adapter.js +0 -113
  95. package/esm/adapter/http/impl/http-incoming-message-host.js +0 -122
  96. package/esm/adapter/http/request-parsers/parse-collection-request.js +0 -161
  97. package/esm/adapter/http/request-parsers/parse-request.js +0 -20
  98. package/esm/adapter/http/request-parsers/parse-singleton-request.js +0 -92
  99. package/esm/adapter/internal/metadata.resource.js +0 -23
  100. package/esm/adapter/request-context.host.js +0 -40
  101. package/esm/shared/collection-resource-base.js +0 -16
  102. package/types/adapter/http/request-parsers/parse-collection-request.d.ts +0 -4
  103. package/types/adapter/http/request-parsers/parse-request.d.ts +0 -4
  104. package/types/adapter/http/request-parsers/parse-singleton-request.d.ts +0 -4
  105. package/types/adapter/interfaces/request-context.interface.d.ts +0 -31
  106. package/types/adapter/interfaces/request.interface.d.ts +0 -15
  107. package/types/adapter/internal/metadata.resource.d.ts +0 -7
  108. package/types/adapter/request-context.host.d.ts +0 -22
  109. package/types/shared/collection-resource-base.d.ts +0 -11
  110. /package/cjs/adapter/{interfaces/request-context.interface.js → execution-context.js} +0 -0
  111. /package/cjs/adapter/http/{request-parsers/batch-request-parser.js → request-handlers/parse-batch-request.js} +0 -0
  112. /package/cjs/adapter/interfaces/{request.interface.js → request-handler.interface.js} +0 -0
  113. /package/cjs/adapter/{interfaces/response.interface.js → platform-adapter.js} +0 -0
  114. /package/esm/adapter/{interfaces/request-context.interface.js → execution-context.js} +0 -0
  115. /package/esm/adapter/http/{request-parsers/batch-request-parser.js → request-handlers/parse-batch-request.js} +0 -0
  116. /package/esm/adapter/interfaces/{request.interface.js → request-handler.interface.js} +0 -0
  117. /package/esm/adapter/{interfaces/response.interface.js → platform-adapter.js} +0 -0
  118. /package/types/adapter/http/{request-parsers/batch-request-parser.d.ts → request-handlers/parse-batch-request.d.ts} +0 -0
@@ -0,0 +1,123 @@
1
+ import { BadRequestError, HttpHeaderCodes, HttpStatusCodes, InternalServerError, IssueSeverity, OpraException, OpraSchema, wrapException } from '@opra/common';
2
+ import { ExecutionContextHost } from '../execution-context.host.js';
3
+ import { PlatformAdapterHost } from '../platform-adapter.host.js';
4
+ import { EntityRequestHandler } from './request-handlers/entity-request-handler.js';
5
+ import { StorageRequestHandler } from './request-handlers/storage-request-handler.js';
6
+ /**
7
+ *
8
+ * @class HttpAdapterBase
9
+ */
10
+ export class HttpAdapterBase extends PlatformAdapterHost {
11
+ constructor() {
12
+ super(...arguments);
13
+ this._protocol = 'http';
14
+ this._requestHandlers = [
15
+ new EntityRequestHandler(this),
16
+ new StorageRequestHandler(this)
17
+ ];
18
+ }
19
+ /**
20
+ * Main http request handler
21
+ * @param incoming
22
+ * @param outgoing
23
+ * @protected
24
+ */
25
+ async handleIncoming(incoming, outgoing) {
26
+ const context = new ExecutionContextHost(this.api, this.platform, { http: { incoming, outgoing } });
27
+ try {
28
+ /* istanbul ignore next */
29
+ if (!this._initialized)
30
+ throw new InternalServerError(`${Object.getPrototypeOf(this).constructor.name} has not been initialized yet`);
31
+ outgoing.setHeader(HttpHeaderCodes.X_Opra_Version, OpraSchema.SpecVersion);
32
+ // Expose headers if cors enabled
33
+ if (outgoing.getHeader(HttpHeaderCodes.Access_Control_Allow_Origin)) {
34
+ // Expose X-Opra-* headers
35
+ outgoing.appendHeader(HttpHeaderCodes.Access_Control_Expose_Headers, Object.values(HttpHeaderCodes)
36
+ .filter(k => k.toLowerCase().startsWith('x-opra-')));
37
+ }
38
+ await this.processRequest(context);
39
+ }
40
+ catch (error) {
41
+ context.errors.push(wrapException(error));
42
+ }
43
+ // If no response returned to the client we send an error
44
+ if (!outgoing.writableEnded) {
45
+ if (!context.errors.length)
46
+ context.errors.push(new BadRequestError(`Server can not process this request`));
47
+ await this.handleError(context);
48
+ }
49
+ }
50
+ async processRequest(context) {
51
+ try {
52
+ const { incoming, outgoing } = context.switchToHttp();
53
+ if (incoming.method === 'GET' &&
54
+ incoming.parsedUrl.path.length &&
55
+ incoming.parsedUrl.path[0].resource === '$metadata') {
56
+ outgoing.setHeader('content-type', 'application/json');
57
+ outgoing.end(JSON.stringify(this.api.exportSchema({ webSafe: true })));
58
+ return;
59
+ }
60
+ const { parsedUrl } = incoming;
61
+ if (!parsedUrl.path.length) {
62
+ // Batch
63
+ if (incoming.headers['content-type'] === 'multipart/mixed') {
64
+ // todo
65
+ }
66
+ throw new BadRequestError();
67
+ }
68
+ // Iterate through request handlers until one of them sends response (end outgoing stream)
69
+ for (const requestHandler of this._requestHandlers) {
70
+ await requestHandler.processRequest(context);
71
+ if (outgoing.writableEnded)
72
+ return;
73
+ if (context.errors.length) {
74
+ await this.handleError(context);
75
+ return;
76
+ }
77
+ }
78
+ }
79
+ finally {
80
+ await context.emitAsync('finish');
81
+ }
82
+ }
83
+ async handleError(context) {
84
+ const { errors } = context;
85
+ const { outgoing } = context.switchToHttp();
86
+ if (outgoing.headersSent) {
87
+ outgoing.end();
88
+ return;
89
+ }
90
+ errors.forEach(x => {
91
+ // todo. implement a better log mechanism
92
+ if (x instanceof OpraException)
93
+ this._logger.fatal(x);
94
+ else
95
+ this._logger.error(x);
96
+ });
97
+ const wrappedErrors = errors.map(wrapException);
98
+ // Sort errors from fatal to info
99
+ wrappedErrors.sort((a, b) => {
100
+ const i = IssueSeverity.Keys.indexOf(a.severity) - IssueSeverity.Keys.indexOf(b.severity);
101
+ if (i === 0)
102
+ return b.status - a.status;
103
+ return i;
104
+ });
105
+ let status = outgoing.statusCode || 0;
106
+ if (!status || status < Number(HttpStatusCodes.BAD_REQUEST)) {
107
+ status = wrappedErrors[0].status;
108
+ if (status < Number(HttpStatusCodes.BAD_REQUEST))
109
+ status = HttpStatusCodes.INTERNAL_SERVER_ERROR;
110
+ }
111
+ outgoing.statusCode = status;
112
+ const body = {
113
+ errors: wrappedErrors.map(x => this._i18n.deep(x.toJSON()))
114
+ };
115
+ outgoing.setHeader(HttpHeaderCodes.Content_Type, 'application/json; charset=utf-8');
116
+ outgoing.setHeader(HttpHeaderCodes.Cache_Control, 'no-cache');
117
+ outgoing.setHeader(HttpHeaderCodes.Pragma, 'no-cache');
118
+ outgoing.setHeader(HttpHeaderCodes.Expires, '-1');
119
+ outgoing.setHeader(HttpHeaderCodes.X_Opra_Version, OpraSchema.SpecVersion);
120
+ outgoing.send(JSON.stringify(body));
121
+ outgoing.end();
122
+ }
123
+ }
@@ -0,0 +1,52 @@
1
+ import http from 'http';
2
+ import { HttpStatusCodes, HttpStatusMessages, OpraURL, OpraURLPath, } from '@opra/common';
3
+ import { HttpAdapterBase } from './http-adapter-base.js';
4
+ import { HttpServerRequest } from './http-server-request.js';
5
+ import { HttpServerResponse } from './http-server-response.js';
6
+ /**
7
+ * @class HttpAdapterHost
8
+ */
9
+ export class HttpAdapterHost extends HttpAdapterBase {
10
+ constructor(api, options) {
11
+ super(api, options);
12
+ this._platform = 'http';
13
+ this._basePath = new OpraURLPath(options?.basePath);
14
+ this._server = http.createServer((incomingMessage, serverResponse) => this._serverListener(incomingMessage, serverResponse));
15
+ }
16
+ get basePath() {
17
+ return this._basePath;
18
+ }
19
+ get server() {
20
+ return this._server;
21
+ }
22
+ _serverListener(incomingMessage, serverResponse) {
23
+ const originalUrl = incomingMessage.url;
24
+ const parsedUrl = new OpraURL(originalUrl);
25
+ const relativePath = OpraURLPath.relative(parsedUrl.path, this.basePath);
26
+ if (!relativePath) {
27
+ serverResponse.statusCode = HttpStatusCodes.NOT_FOUND;
28
+ serverResponse.statusMessage = HttpStatusMessages[HttpStatusCodes.NOT_FOUND];
29
+ serverResponse.end();
30
+ return;
31
+ }
32
+ parsedUrl.path = relativePath;
33
+ incomingMessage.originalUrl = originalUrl;
34
+ incomingMessage.baseUrl = this.basePath.toString();
35
+ incomingMessage.parsedUrl = parsedUrl;
36
+ const req = HttpServerRequest.from(incomingMessage);
37
+ const res = HttpServerResponse.from(serverResponse);
38
+ this.handleIncoming(req, res)
39
+ .catch((e) => this._logger.fatal(e));
40
+ }
41
+ async close() {
42
+ await super.close();
43
+ if (this.server.listening)
44
+ await new Promise((resolve, reject) => {
45
+ this.server.close((err) => {
46
+ if (err)
47
+ return reject(err);
48
+ resolve();
49
+ });
50
+ });
51
+ }
52
+ }
@@ -1,134 +1,13 @@
1
- import { Task } from 'power-tasks';
2
- import { BadRequestError, Collection, HttpHeaderCodes, HttpStatusCodes, InternalServerError, isReadable, IssueSeverity, OpraException, OpraSchema, wrapException } from '@opra/common';
3
- import { OpraAdapter } from '../adapter.js';
4
- import { RequestContextHost } from '../request-context.host.js';
5
- import { ResponseHost } from '../response.host.js';
6
- import { parseRequest } from './request-parsers/parse-request.js';
1
+ import { HttpAdapterHost } from './http-adapter.host.js';
7
2
  /**
8
- *
9
- * @class OpraHttpAdapter
3
+ * @namespace HttpAdapter
10
4
  */
11
- export class OpraHttpAdapter extends OpraAdapter {
12
- /**
13
- * Main http request handler
14
- * @param incoming
15
- * @param outgoing
16
- * @protected
17
- */
18
- async handler(incoming, outgoing) {
19
- try {
20
- try {
21
- const request = await parseRequest(this._apiRoot, incoming);
22
- const task = this.createRequestTask(request, outgoing);
23
- await task.toPromise();
24
- }
25
- catch (e) {
26
- if (e instanceof OpraException)
27
- throw e;
28
- throw new BadRequestError(e);
29
- }
30
- }
31
- catch (error) {
32
- await this.handleError(incoming, outgoing, [error]);
33
- }
34
- }
35
- createRequestTask(request, outgoing) {
36
- return new Task(async () => {
37
- const response = new ResponseHost({ http: outgoing });
38
- const context = new RequestContextHost('http', this.platform, this.api, request, response);
39
- return this.executeRequest(context);
40
- }, {
41
- id: request.contentId,
42
- exclusive: request.crud !== 'read'
43
- });
44
- }
45
- async executeRequest(context) {
46
- const { request, response } = context;
47
- await super.executeRequest(context)
48
- .catch(e => {
49
- response.errors = response.errors || [];
50
- response.errors.push(e);
51
- });
52
- const outgoing = response.switchToHttp();
53
- if (response.errors?.length) {
54
- const errors = response.errors.map(e => wrapException(e));
55
- await this.handleError(request.switchToHttp(), outgoing, errors);
56
- return;
57
- }
58
- if (request.crud === 'create') {
59
- if (!response.value)
60
- throw new InternalServerError();
61
- // todo validate
62
- outgoing.statusCode = 201;
63
- }
64
- if (request.resource instanceof Collection &&
65
- request.crud === 'read' && request.many && request.args.count >= 0) {
66
- outgoing.setHeader(HttpHeaderCodes.X_Total_Count, String(response.count));
67
- }
68
- outgoing.statusCode = outgoing.statusCode || HttpStatusCodes.OK;
69
- outgoing.setHeader(HttpHeaderCodes.Cache_Control, 'no-cache');
70
- outgoing.setHeader(HttpHeaderCodes.Pragma, 'no-cache');
71
- outgoing.setHeader(HttpHeaderCodes.Expires, '-1');
72
- outgoing.setHeader(HttpHeaderCodes.X_Opra_Version, OpraSchema.SpecVersion);
73
- // Expose headers if cors enabled
74
- if (outgoing.getHeader(HttpHeaderCodes.Access_Control_Allow_Origin)) {
75
- // Expose X-Total-Count header
76
- outgoing.appendHeader(HttpHeaderCodes.Access_Control_Expose_Headers, [HttpHeaderCodes.X_Total_Count]);
77
- // Expose X-Opra-* headers
78
- outgoing.appendHeader(HttpHeaderCodes.Access_Control_Expose_Headers, Object.values(HttpHeaderCodes)
79
- .filter(k => k.toLowerCase().startsWith('x-opra-')));
80
- }
81
- if (response.value) {
82
- if (typeof response.value === 'object') {
83
- if (isReadable(response.value) || Buffer.isBuffer(response.value))
84
- outgoing.send(response.value);
85
- else {
86
- const body = this.i18n.deep(response.value);
87
- outgoing.setHeader(HttpHeaderCodes.Content_Type, 'application/json; charset=utf-8');
88
- outgoing.send(JSON.stringify(body));
89
- }
90
- }
91
- else
92
- outgoing.send(JSON.stringify(response.value));
93
- }
94
- outgoing.end();
95
- }
96
- async handleError(incoming, outgoing, errors) {
97
- errors.forEach(e => {
98
- this.log((e instanceof OpraException) ? 'error' : 'fatal', incoming, e); // todo. implement a better logger
99
- });
100
- errors = errors.map(wrapException);
101
- let status = outgoing.statusCode || 0;
102
- // Sort errors from fatal to info
103
- errors.sort((a, b) => {
104
- const i = IssueSeverity.Keys.indexOf(a.issue.severity) - IssueSeverity.Keys.indexOf(b.issue.severity);
105
- if (i === 0)
106
- return b.status - a.status;
107
- return i;
108
- });
109
- if (!status || status < Number(HttpStatusCodes.BAD_REQUEST)) {
110
- status = errors[0].status;
111
- if (status < Number(HttpStatusCodes.BAD_REQUEST))
112
- status = HttpStatusCodes.INTERNAL_SERVER_ERROR;
113
- }
114
- const body = this.i18n.deep({
115
- errors: errors.map(e => e.issue)
116
- });
117
- outgoing.statusCode = status;
118
- outgoing.setHeader(HttpHeaderCodes.Content_Type, 'application/json; charset=utf-8');
119
- outgoing.setHeader(HttpHeaderCodes.Cache_Control, 'no-cache');
120
- outgoing.setHeader(HttpHeaderCodes.Pragma, 'no-cache');
121
- outgoing.setHeader(HttpHeaderCodes.Expires, '-1');
122
- outgoing.setHeader(HttpHeaderCodes.X_Opra_Version, OpraSchema.SpecVersion);
123
- outgoing.send(JSON.stringify(body));
124
- outgoing.end();
125
- }
126
- log(logType, incoming, message, ...optionalParams) {
127
- const logFn = logType === 'fatal'
128
- ? this.logger?.fatal || this.logger?.error
129
- : this.logger?.[logType];
130
- if (!logFn)
131
- return;
132
- logFn.apply(this.logger, [String(message), ...optionalParams]);
133
- }
134
- }
5
+ export var HttpAdapter;
6
+ (function (HttpAdapter) {
7
+ async function create(api, options) {
8
+ const adapter = new HttpAdapterHost(api, options);
9
+ await adapter.init();
10
+ return adapter;
11
+ }
12
+ HttpAdapter.create = create;
13
+ })(HttpAdapter || (HttpAdapter = {}));
@@ -6,22 +6,28 @@ import accepts from 'accepts';
6
6
  import fresh from 'fresh';
7
7
  import parseRange from 'range-parser';
8
8
  import typeIs from 'type-is';
9
- import { isReadable, mergePrototype } from '@opra/common';
10
- import { HttpIncomingMessageHost } from './http-incoming-message-host.js';
9
+ import { isReadable, mergePrototype, OpraURL } from '@opra/common';
10
+ import { HttpIncomingMessageHost } from './impl/http-incoming-message.host.js';
11
11
  function isHttpIncomingMessage(v) {
12
12
  return v && Array.isArray(v.rawHeaders) && isReadable(v);
13
13
  }
14
14
  export var HttpServerRequest;
15
15
  (function (HttpServerRequest) {
16
- function create(instance) {
16
+ function from(instance) {
17
17
  if (!isHttpIncomingMessage(instance))
18
- instance = HttpIncomingMessageHost.create(instance);
18
+ instance = HttpIncomingMessageHost.from(instance);
19
19
  mergePrototype(instance, HttpServerRequestHost.prototype);
20
- return instance;
20
+ const req = instance;
21
+ req.baseUrl = req.baseUrl || '';
22
+ req.parsedUrl = req.parsedUrl || new OpraURL(req.url);
23
+ return req;
21
24
  }
22
- HttpServerRequest.create = create;
25
+ HttpServerRequest.from = from;
23
26
  })(HttpServerRequest || (HttpServerRequest = {}));
24
27
  class HttpServerRequestHost {
28
+ constructor() {
29
+ this.basePath = '/';
30
+ }
25
31
  get protocol() {
26
32
  const proto = this.header('X-Forwarded-Proto') || 'http';
27
33
  const index = proto.indexOf(',');
@@ -12,20 +12,20 @@ import path from 'path';
12
12
  import { toString } from 'putil-varhelpers';
13
13
  import vary from 'vary';
14
14
  import { HttpStatusCodes, isStream, mergePrototype } from '@opra/common';
15
- import { HttpOutgoingMessageHost } from './http-outgoing-message-host.js';
15
+ import { HttpOutgoingMessageHost } from './impl/http-outgoing-message.host.js';
16
16
  const charsetRegExp = /;\s*charset\s*=/;
17
17
  function isHttpIncomingMessage(v) {
18
18
  return v && typeof v.getHeaders === 'function' && isStream(v);
19
19
  }
20
20
  export var HttpServerResponse;
21
21
  (function (HttpServerResponse) {
22
- function create(instance) {
22
+ function from(instance) {
23
23
  if (!isHttpIncomingMessage(instance))
24
- instance = HttpOutgoingMessageHost.create(instance);
24
+ instance = new HttpOutgoingMessageHost(instance);
25
25
  mergePrototype(instance, HttpServerResponseHost.prototype);
26
26
  return instance;
27
27
  }
28
- HttpServerResponse.create = create;
28
+ HttpServerResponse.from = from;
29
29
  })(HttpServerResponse || (HttpServerResponse = {}));
30
30
  class HttpServerResponseHost {
31
31
  attachment(filename) {
@@ -0,0 +1,144 @@
1
+ /*
2
+ This file contains code blocks from open source NodeJs project
3
+ https://github.com/nodejs/
4
+ */
5
+ import { Duplex, Readable } from 'stream';
6
+ import { HTTPParser } from '@browsery/http-parser';
7
+ import { isAsyncIterable, isIterable } from '@opra/common';
8
+ import { concatReadable } from '../helpers/concat-readable.js';
9
+ import { convertToHeaders, convertToHeadersDistinct } from '../helpers/convert-to-headers.js';
10
+ import { convertToRawHeaders } from '../helpers/convert-to-raw-headers.js';
11
+ export const CRLF = Buffer.from('\r\n');
12
+ export const kHeaders = Symbol.for('kHeaders');
13
+ export const kHeadersDistinct = Symbol.for('kHeadersDistinct');
14
+ export const kTrailers = Symbol.for('kTrailers');
15
+ export const kTrailersDistinct = Symbol.for('kTrailersDistinct');
16
+ /**
17
+ *
18
+ * @class HttpIncomingMessageHost
19
+ */
20
+ export class HttpIncomingMessageHost extends Duplex {
21
+ constructor(init) {
22
+ super();
23
+ this.rawHeaders = [];
24
+ this.rawTrailers = [];
25
+ this.complete = false;
26
+ this.joinDuplicateHeaders = false;
27
+ if (init) {
28
+ this.complete = true;
29
+ this.httpVersionMajor = init.httpVersionMajor || 1;
30
+ this.httpVersionMinor = init.httpVersionMinor || 0;
31
+ this.method = (init.method || 'GET').toUpperCase();
32
+ this.url = init.url || '';
33
+ if (init.body != null) {
34
+ if (Buffer.isBuffer(init.body))
35
+ this.body = init.body;
36
+ else if (typeof init.body === 'string')
37
+ this.body = Buffer.from(init.body, 'utf-8');
38
+ else
39
+ this.body = Buffer.from(JSON.stringify(init.body), 'utf-8');
40
+ }
41
+ if (init.headers)
42
+ this.rawHeaders = Array.isArray(init.headers) ? init.headers : convertToRawHeaders(init.headers);
43
+ if (init.trailers)
44
+ this.rawTrailers = Array.isArray(init.trailers) ? init.trailers : convertToRawHeaders(init.trailers);
45
+ this.ip = init.ip || '';
46
+ this.ips = init.ips || (this.ip ? [this.ip] : []);
47
+ if (this.body && !this.headers['content-length'])
48
+ this.headers['content-length'] = String(this.body.length);
49
+ }
50
+ }
51
+ get httpVersion() {
52
+ return this.httpVersionMajor
53
+ ? this.httpVersionMajor + '.' + this.httpVersionMinor
54
+ : '';
55
+ }
56
+ get headers() {
57
+ if (!this[kHeaders])
58
+ this[kHeaders] = convertToHeaders(this.rawHeaders, {}, this.joinDuplicateHeaders);
59
+ return this[kHeaders];
60
+ }
61
+ set headers(headers) {
62
+ this[kHeaders] = headers;
63
+ }
64
+ get headersDistinct() {
65
+ if (!this[kHeadersDistinct])
66
+ this[kHeadersDistinct] = convertToHeadersDistinct(this.rawHeaders, {});
67
+ return this[kHeadersDistinct];
68
+ }
69
+ get trailers() {
70
+ if (!this[kTrailers])
71
+ this[kTrailers] = convertToHeaders(this.rawTrailers, {}, this.joinDuplicateHeaders);
72
+ return this[kTrailers];
73
+ }
74
+ set trailers(trailers) {
75
+ this[kTrailers] = trailers;
76
+ }
77
+ get trailersDistinct() {
78
+ if (!this[kTrailersDistinct])
79
+ this[kTrailersDistinct] = convertToHeadersDistinct(this.rawTrailers, {});
80
+ return this[kTrailersDistinct];
81
+ }
82
+ _read(size) {
83
+ if (!this.body) {
84
+ this.push(null);
85
+ return;
86
+ }
87
+ if (!this._readStream) {
88
+ if (isIterable(this.body) || isAsyncIterable(this.body))
89
+ this._readStream = Readable.from(this.body);
90
+ else if (typeof this.body === 'string') {
91
+ this._readStream = Readable.from(Buffer.from(this.body, 'utf-8'));
92
+ }
93
+ else
94
+ this._readStream = Readable.from(Buffer.from(JSON.stringify(this.body), 'utf-8'));
95
+ }
96
+ const chunk = this._readStream.read(size);
97
+ this.push(chunk);
98
+ // this.push(null);s
99
+ }
100
+ _write(chunk, encoding, callback) {
101
+ const error = this._httpParser?.execute(chunk);
102
+ if (error && typeof error === 'object')
103
+ callback(error);
104
+ else
105
+ callback();
106
+ }
107
+ static from(iterable) {
108
+ if (typeof iterable === 'object' && !(isIterable(iterable) || isAsyncIterable(iterable)))
109
+ return new HttpIncomingMessageHost(iterable);
110
+ const msg = new HttpIncomingMessageHost();
111
+ const parser = msg._httpParser = new HTTPParser(HTTPParser.REQUEST);
112
+ let bodyChunks;
113
+ parser[HTTPParser.kOnHeadersComplete] = (info) => {
114
+ msg.httpVersionMajor = info.versionMajor;
115
+ msg.httpVersionMinor = info.versionMinor;
116
+ msg.rawHeaders = info.headers;
117
+ msg.method = HTTPParser.methods[info.method];
118
+ msg.url = info.url;
119
+ };
120
+ parser[HTTPParser.kOnHeaders] = (trailers) => {
121
+ msg.rawTrailers = trailers;
122
+ };
123
+ parser[HTTPParser.kOnBody] = (chunk, offset, length) => {
124
+ bodyChunks = bodyChunks || [];
125
+ bodyChunks.push(chunk.subarray(offset, offset + length));
126
+ };
127
+ parser[HTTPParser.kOnMessageComplete] = () => {
128
+ msg.complete = true;
129
+ if (bodyChunks)
130
+ msg.body = Buffer.concat(bodyChunks);
131
+ };
132
+ const readable = concatReadable(Readable.from(iterable), Readable.from(CRLF));
133
+ msg.once('finish', () => parser.finish());
134
+ readable.pipe(msg);
135
+ return msg;
136
+ }
137
+ static async fromAsync(iterable) {
138
+ return new Promise((resolve, reject) => {
139
+ const msg = this.from(iterable);
140
+ msg.once('finish', () => resolve(msg));
141
+ msg.once('error', (error) => reject(error));
142
+ });
143
+ }
144
+ }
@@ -2,33 +2,40 @@
2
2
  This file contains code blocks from open source NodeJs project
3
3
  https://github.com/nodejs/
4
4
  */
5
- var _a;
6
- import * as stream from 'stream';
5
+ import { Duplex } from 'stream';
7
6
  import { validateHeaderName, validateHeaderValue, validateString } from '../helpers/common.js';
8
- export const kOutHeaders = Symbol('kOutHeaders');
9
- export const kOutTrailers = Symbol('kOutTrailers');
10
- export const kErrored = Symbol('kErrored');
7
+ export const kOutHeaders = Symbol.for('kOutHeaders');
8
+ export const kOutTrailers = Symbol.for('kOutTrailers');
11
9
  // noinspection JSUnusedLocalSymbols
12
10
  /**
13
11
  *
14
12
  * @class HttpOutgoingMessageHost
15
13
  */
16
- export class HttpOutgoingMessageHost {
17
- constructor() {
14
+ export class HttpOutgoingMessageHost extends Duplex {
15
+ constructor(init) {
16
+ super();
18
17
  this._headersSent = false;
19
- this._closed = false;
20
- this[_a] = null;
21
18
  this.finished = false;
22
- stream.Stream.apply(this);
19
+ if (init) {
20
+ this.req = init.req;
21
+ this.statusCode = init?.statusCode || 0;
22
+ this.statusMessage = init?.statusMessage || '';
23
+ this.chunkedEncoding = !!init?.chunkedEncoding;
24
+ this.sendDate = !!init?.sendDate;
25
+ this.strictContentLength = !!init?.strictContentLength;
26
+ if (init.headers)
27
+ this.setHeaders(Array.isArray(init.headers) ? new Map([init.headers]) : init.headers);
28
+ this.body = init.body;
29
+ }
23
30
  }
24
- get headersSent() {
25
- return this._headersSent;
31
+ get httpVersionMajor() {
32
+ return this.req?.httpVersionMajor || this._httpVersionMajor;
26
33
  }
27
- get closed() {
28
- return this._closed;
34
+ get httpVersionMinor() {
35
+ return this.req?.httpVersionMinor || this._httpVersionMinor;
29
36
  }
30
- get errored() {
31
- return this[kErrored];
37
+ get headersSent() {
38
+ return this._headersSent;
32
39
  }
33
40
  appendHeader(name, value) {
34
41
  if (this.headersSent)
@@ -181,25 +188,7 @@ export class HttpOutgoingMessageHost {
181
188
  //
182
189
  return this;
183
190
  }
184
- _readConfig(init) {
185
- this.req = init.req;
186
- this.statusCode = init?.statusCode || 0;
187
- this.statusMessage = init?.statusMessage || '';
188
- this.chunkedEncoding = !!init?.chunkedEncoding;
189
- this.sendDate = !!init?.sendDate;
190
- this.strictContentLength = !!init?.strictContentLength;
191
- if (init.headers) {
192
- this.setHeaders(Array.isArray(init.headers) ? new Map([init.headers]) : init.headers);
193
- }
194
- this.body = init.body;
195
- }
196
- static create(init) {
197
- const msg = new HttpOutgoingMessageHost();
198
- if (init)
199
- msg._readConfig(init);
200
- return msg;
191
+ static from(init) {
192
+ return new HttpOutgoingMessageHost(init);
201
193
  }
202
194
  }
203
- _a = kErrored;
204
- // Apply mixins
205
- Object.assign(HttpOutgoingMessageHost.prototype, stream.Stream.prototype);