Skip to content

Commit d6fe738

Browse files
committed
v0.1.1
1 parent 5b11179 commit d6fe738

12 files changed

+139
-66
lines changed

.github/workflows/publish.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ name: Publish to pub.dev
33
on:
44
push:
55
branches:
6-
- publish
6+
- main
77
pull_request:
8-
branches: publish
8+
branches: main
99
tags:
1010
- 'v[0-9]+.[0-9]+.[0-9]+*'
1111

README.md

+42-23
Original file line numberDiff line numberDiff line change
@@ -5,24 +5,21 @@
55
> A **lightweight**, **minimal**, and **incredibly fast** HTTP framework for productive and fun API
66
> development with dart.
77
8-
**This is a very new project under active development**
8+
**This is a new project under active development**
99

1010
**Do not use in production as it could break unexpectedly.**
1111

12-
You're welcome to try it out, see what breaks and give feedback.
12+
There's an express-like server framework called `Alfred` in the dart ecosystem.
1313

14-
There's already an express.js implementation called `Alfred` in the dart ecosystem.
15-
16-
This is the [H3](https://h3.unjs.io) implementation with similar design goals.
17-
Special thanks to [Pooya Parsa](https://github.com/pi0) and the [Unjs](https://github.com/unjs) community for making a great library.
14+
This is the [H3](https://h3.unjs.io) implementation with similar design goals. Special thanks to
15+
[Pooya Parsa](https://github.com/pi0) and the [Unjs](https://github.com/unjs) community for making a
16+
awesome http library.
1817

1918
## Features
2019

2120
- **Lightweight**: H4 ships with a small core and a set of composable utilities.
22-
- **Fast**: H4's trie-based router is incredibly fast, with support for route params and wildcard
23-
patterns.
2421
- **Middleware**: H4 comes with built-in `onRequest` and `onError` middleware.
25-
- **Generic Handlers**: Specify the return type of your handler functions for increased type safety.
22+
- **Generic Handlers**: Specify the return type of your handler functions.
2623

2724
## Getting Started
2825

@@ -59,13 +56,16 @@ void main() {
5956
### Manual Start
6057

6158
```dart
62-
var app = createApp(port: 4000, autoStart: false);
59+
void main() {
60+
var app = createApp(port: 4000, autoStart: false);
61+
var router = createRouter();
6362
64-
app.start().then((h4) => print(h4?.port));
63+
app.use(router);
6564
66-
var router = createRouter();
65+
router.get("/hi", (event) => "Hi")
6766
68-
app.use(router);
67+
app.start()
68+
}
6969
```
7070

7171
### Generic handlers
@@ -103,7 +103,7 @@ router.get('/error', (event) {
103103
// Code that could fail.
104104
}
105105
catch(e) {
106-
throw CreateError(message: 'Error - $e', errorCode: 400);
106+
throw CreateError(message: 'Womp Womp', errorCode: 400);
107107
}
108108
});
109109
```
@@ -148,15 +148,30 @@ router.get('/articles/**', (event) {
148148

149149
## Utilities
150150

151-
This is a design philosophy from [h3](https://h3.unjs.io).
151+
A set of composable utilities that help you add functionality to your server
152152

153-
I'm working on adding an exhaustive list of composable utilities for easily extending functionality of your server.
153+
### `readRequestBody`
154154

155-
More utilities will be added with each release and soon a guide to creating your own utils will be published.
155+
Reads the request body as `json` or `text` depending on the `contentType` of the request body.
156156

157-
### `readRequestBody`
157+
```dart
158+
router.post("/vamos", (event) async {
159+
var body = await readRequestBody(event);
160+
return body;
161+
});
162+
```
158163

159-
Reads the request body as `json` or `text` depending on the content type of the request body.
164+
### `getHeader`
165+
166+
Get the value of any of the incoming request headers. For convenience you can use the HTTPHeaders
167+
utility to get header strings.
168+
169+
```dart
170+
router.post("/vamos", (event) async {
171+
var header = getHeader(event, HttpHeaders.userAgentHeader);
172+
return body;
173+
});
174+
```
160175

161176
```dart
162177
router.post("/vamos", (event) async {
@@ -166,19 +181,23 @@ router.post("/vamos", (event) async {
166181
```
167182

168183
## Contributing
184+
Contributors needed!
169185

170-
We are looking for contributors!
171-
172-
There's still quite a bit of work to do to get H4 to 1.0.0 and ready for production use.
186+
There's quite a bit of work to do to get H4 to 1.0.0 and ready for production use.
173187

174188
If you find a bug or have an idea for a new feature, please
175189
[open an issue](https://github.com/iyifr/h4/issues/new) or submit a pull request.
176190

177191
### First Contribution
178-
179192
A good first PR would be helping me improve the test coverage of this library. Or adding one of the
180193
utilities listed [here](https://h3.unjs.io/utils).
181194

195+
### Running tests
196+
In the root directory run
197+
```bash
198+
dart test
199+
```
200+
182201
## Code of Conduct.
183202

184203
Show respect and consideration for others when creating issues and contributing to the library. Only

bin/run.dart

+3-9
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,13 @@
33
import 'dart:io';
44

55
import 'package:h4/create.dart';
6-
import 'package:h4/src/create_error.dart';
7-
import 'package:h4/src/logger.dart';
86
import 'package:h4/utils/get_header.dart';
97
import 'package:h4/utils/get_query.dart';
108
import 'package:h4/utils/read_request_body.dart';
119
import 'package:h4/utils/set_response_header.dart';
1210

1311
void main(List<String> arguments) async {
14-
initLogger();
15-
16-
var app = createApp(port: 4000, autoStart: false);
17-
18-
await app.start().then((h4) => logger.warning(h4?.port));
12+
var app = createApp(port: 5173);
1913

2014
var router = createRouter();
2115

@@ -32,7 +26,7 @@ void main(List<String> arguments) async {
3226

3327
router.post("/vamos", (event) async {
3428
var body = await readRequestBody(event);
35-
var header = getHeader(event, HttpHeaders.viaHeader);
29+
var header = getHeader(event, HttpHeaders.userAgentHeader);
3630
var query = getQueryParams(event);
3731
setResponseHeader(event, HttpHeaders.contentTypeHeader,
3832
value: 'application/json');
@@ -46,6 +40,6 @@ void main(List<String> arguments) async {
4640
});
4741

4842
router.get("/vamos", (event) {
49-
throw CreateError(message: 'A grave error happened', errorCode: 404);
43+
throw CreateError(message: 'A grave error happened');
5044
});
5145
}

lib/create.dart

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
export 'package:h4/src/create_error.dart';
12
import 'package:h4/src/h4.dart';
23
import 'package:h4/src/router.dart';
34

@@ -24,7 +25,7 @@ import 'package:h4/src/router.dart';
2425
/// final app = createApp(autoStart: false)
2526
/// await app.start().then((h4) => print('App started on ${h4.port}'))
2627
/// ```
27-
H4 createApp({int? port, bool autoStart = true}) {
28+
H4 createApp({int port = 3000, bool autoStart = true}) {
2829
return H4(port: port, autoStart: autoStart);
2930
}
3031

lib/src/create_error.dart

+7-5
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,21 @@
11
import 'dart:io';
22

3+
/// Custom HTTP exception class for creating and throwing errors.
34
class CreateError implements HttpException {
5+
/// Message to send to the client.
46
@override
5-
6-
/// Message to send to client.
77
final String message;
88

99
/// HTTP status code (defaults to 400).
1010
final int errorCode;
1111

12-
/// * Creates a new `Error` that can be used to handle both internal and runtime errors.
12+
/// Constructs a `CreateError` instance.
1313
///
14-
/// * Pass an error message
14+
/// Throws a custom error with the provided message and optional HTTP status code.
1515
///
16-
/// * Pass a http status code (defaults to 400) (optional)
16+
/// Parameters:
17+
/// - `message`: The error message to be sent to the client.
18+
/// - `errorCode`: The HTTP status code for the error (defaults to 400 if not provided).
1719
CreateError({
1820
required this.message,
1921
this.errorCode = 400,

lib/src/h4.dart

+22-17
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import 'dart:io';
22
import 'package:h4/src/create_error.dart';
33
import 'package:h4/src/error_middleware.dart';
44
import 'package:h4/src/logger.dart';
5+
import 'package:h4/src/port_taken.dart';
56
import 'package:h4/src/router.dart';
67

78
import '/src/index.dart';
@@ -22,7 +23,7 @@ class H4 {
2223
(e, s, event) => logger.severe(
2324
'Stack Trace \n $e \n $s \n Error occured at path -${event?.path}');
2425

25-
int? port;
26+
int port;
2627

2728
/// Constructs an instance of the `H4` class, which is the main entry point for
2829
/// your application.
@@ -45,22 +46,36 @@ class H4 {
4546
/// // Start the application on the default port (3000)
4647
/// final app = H4();
4748
/// ```
48-
H4({this.port, bool autoStart = true}) {
49-
autoStart ? start() : null;
49+
H4({this.port = 3000, bool autoStart = true}) {
50+
initLogger();
51+
52+
if (autoStart) {
53+
start();
54+
}
5055
}
5156

5257
/// Initializes the server on **localhost** and starts listening for requests.
5358
Future<H4?> start() async {
5459
try {
60+
var portReady = await isPortAvailable(port: port);
61+
62+
if (portReady == false) {
63+
logger.info(
64+
'Port $port is already taken, starting server on ${port + 1}');
65+
port = port + 1;
66+
67+
start();
68+
}
5569
server = await initializeHttpConnection(
56-
port: port ?? 3000,
70+
port: port,
5771
);
72+
logger.info('Server started on port $port');
5873
_bootstrap();
74+
return this;
5975
} catch (e) {
6076
logger.severe(e.toString());
6177
return null;
6278
}
63-
return this;
6479
}
6580

6681
/// Shuts down the server and stops listening to requests.
@@ -130,19 +145,9 @@ class H4 {
130145
}
131146

132147
_bootstrap() {
133-
// var newStream = server?.asBroadcastStream();
134-
135-
// newStream?.listen((HttpRequest req) {
136-
// var event = H4Event(req);
137-
// var params = router?.getParams(req.uri.path);
138-
// event.eventParams = params ?? {};
139-
// _onRequestHandler!(event);
140-
// });
141-
142148
server?.listen((HttpRequest request) {
143149
if (router == null) {
144-
print(
145-
"No router is defined, it is recommended to use createRouter() to define a router.");
150+
logger.warning("No router is defined!");
146151
}
147152

148153
// Find handler for that request
@@ -190,7 +195,7 @@ class H4 {
190195
statusCode: e.errorCode)(request);
191196
}
192197

193-
/// Catch non-explicity error when they occur and send a JSON payload to the client.
198+
// Catch non-explicity error when they occur and send a JSON payload to the client.
194199
catch (e, trace) {
195200
defineErrorHandler(_errorHandler,
196201
params: params,

lib/src/logger.dart

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,6 @@ var logger = Logger('H4');
55
initLogger() {
66
Logger.root.level = Level.ALL; // defaults to Level.INFO
77
Logger.root.onRecord.listen((record) {
8-
print('${record.level.name}: ${record.time}: ${record.message}');
8+
print('${record.level.name}: ${record.message}');
99
});
1010
}

lib/src/port_taken.dart

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import 'dart:io';
2+
3+
Future<bool> isPortAvailable({required int port}) async {
4+
try {
5+
ServerSocket socket = await ServerSocket.bind('localhost', port);
6+
await socket.close();
7+
return true;
8+
} on SocketException {
9+
return false;
10+
} catch (e) {
11+
return false;
12+
}
13+
}

lib/utils/get_header.dart

+38
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,56 @@ import 'dart:io';
22

33
import 'package:h4/src/event.dart';
44

5+
/// ### Retrieves the value of a specific header from an event instance.
6+
///
7+
/// The `getHeader` function is used to extract the value of a specified header from an `H4Event` object. The `H4Event` object is expected to have a `node` property that contains an HTTP request, and the `node["value"]` property is assumed to represent the incoming HTTP request.
8+
///
9+
/// Parameters:
10+
/// - `event`: An `H4Event` instance.
11+
/// - `header`: The name of the header to retrieve.
12+
///
13+
/// Returns:
14+
/// The value of the specified header, or `null` if the header is not found.
515
String? getHeader(H4Event event, String header) {
616
return event.node["value"]?.headers.value(header);
717
}
818

19+
/// Retrieves the headers of the incoming HTTP request from an `H4Event` instance.
20+
///
21+
/// The `getRequestHeaders` function is used to extract the headers of the incoming HTTP request from an `H4Event` object. The `H4Event` object is expected to have a `node` property that contains the HTTP request, and the `node["value"]` property is assumed to represent the incoming HTTP request.
22+
///
23+
/// Parameters:
24+
/// - `event`: An `H4Event` instance containing the HTTP request.
25+
///
26+
/// Returns:
27+
/// The headers of the incoming HTTP request.
928
HttpHeaders? getRequestHeaders(H4Event event) {
1029
return event.node["value"]?.headers;
1130
}
1231

32+
/// Retrieves the headers of the HTTP response from an `H4Event` instance.
33+
///
34+
/// The `getResponseHeaders` function is used to extract the headers of the HTTP response from an `H4Event` object. The `H4Event` object is expected to have a `node` property that contains the HTTP request, and the `node["value"]` property is assumed to represent the incoming HTTP request.
35+
///
36+
/// Parameters:
37+
/// - `event`: An `H4Event` instance containing the HTTP request.
38+
///
39+
/// Returns:
40+
/// The headers of the HTTP response, or `null` if the response is `null`.
1341
HttpHeaders? getResponseHeaders(H4Event event) {
1442
return event.node["value"]?.response.headers;
1543
}
1644

45+
/// Retrieves the value of a specific header in the HTTP response from an `H4Event` instance.
46+
///
47+
/// The `getResponseHeader` function is used to extract the value of a specified header from the HTTP response of an `H4Event` object. The `H4Event` object is expected to have a `node` property that contains the HTTP request, and the `node["value"]` property is assumed to represent the incoming HTTP request.
48+
///
49+
/// Parameters:
50+
/// - `event`: An `H4Event` instance containing the HTTP request.
51+
/// - `header`: The name of the header to retrieve.
52+
///
53+
/// Returns:
54+
/// The value of the specified header, or `null` if the header is not found or the response is `null`.
1755
String? getResponseHeader(H4Event event, String header) {
1856
return event.node["value"]?.response.headers.value(header);
1957
}

lib/utils/read_request_body.dart

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ import 'package:either_dart/either.dart';
66
import 'package:h4/src/event.dart';
77
import 'package:h4/src/logger.dart';
88

9-
10-
/// Read the body of the request.
9+
/// Read the body of the incoming event request.
10+
/// Returns the request body either as parsed json or a string.
1111
Future<dynamic> readRequestBody(H4Event event) async {
1212
var request = event.node["value"];
1313

0 commit comments

Comments
 (0)